在Kong中尝试使用API网关

Kong是什么意思?

这是一个可以在多云环境中使用一个入口来管理和运营微服务和API的API网关平台。

这些都是类似于亚马逊API网关、谷歌云端终点和API网关的服务。

在这里所说的”Kong”主要指的是Kong Gateway,但还有其他如Kong Mesh等等。

内部使用了Nginx/Cassandra/PostgreSQL等技术。

在这里,有企业版和开源版两种选择,但我们将选择使用开源版。

准备

在各种环境下安装OSS版有多种方法可以选择。

若是macOS系统的情况

$ brew tap kong/kong
$ brew install kong
$ kong version
2.7.0

$ mkdir kong
$ kong config init

暂时切换到无数据库模式。

$ export KONG_DATABASE=off
$ kong start
Kong started
$ kong stop
Kong stopped

如果ulimit出现警告的话,可以通过ulimit -n 4096进行修改。

如果是docker的情况下

$ git clone https://github.com/Kong/docker-kong
$ cod docker-kong/compose
$ docker-compose up

形成

我会准备三个API应用程序来使用Kong Gateway。

internet
└── kong-gateway (:8000, :8001)
   ├── node-web (:3002)
   ├── rails-web (:3000)
   └── rust-web (:3001)

使用Rails创建API

$ bundle exec rails new rails-web -T -d sqlite3 --api
$ bundle exec rails g scaffold User name:string email:string
$ vim db/seeds.rb
3.times { User.create name: Faker::Name.name, email: Faker::Internet.email }

$ bundle exec rails db:migrate db:seed

确认响应

$ bundle exec rails foreman start
$ curl -s http://localhost:3000/users | jq
[
  {
    "id": 1,
    "name": "Augustine Leannon",
    "email": "lang.heller@hudson.co",
    "created_at": "2021-12-25T07:13:32.878Z",
    "updated_at": "2021-12-25T07:13:32.878Z"
  },
  {
    "id": 2,
    "name": "Octavio Monahan",
    "email": "thaddeus@bauch.org",
    "created_at": "2021-12-25T07:13:32.883Z",
    "updated_at": "2021-12-25T07:13:32.883Z"
  },
  {
    "id": 3,
    "name": "Elsie Thiel",
    "email": "philip.barton@mayer.net",
    "created_at": "2021-12-25T07:13:32.887Z",
    "updated_at": "2021-12-25T07:13:32.887Z"
  }
]

使用Rust编写API

$ cargo new rust-web
[dependencies]
actix-web = "3.3.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Post {
    title: String,
    content: String,
}

#[get("/posts")]
async fn index_post() -> impl Responder {
    HttpResponse::Ok().json(Post {
        title: String::from("post title"),
        content: String::from("post description content"),
    })
}

#[post("/posts")]
async fn create_post(post: web::Json<Post>) -> impl Responder {
    println!("{:?}", post);
    HttpResponse::Ok().body("ok")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(index_post).service(create_post))
        .bind("127.0.0.1:3001")?
        .run()
        .await
}

启动并确认响应。

$ cargo run
$ curl -s http://localhost:3001/posts | jq
{
  "title": "post title",
  "content": "post description content"
}

使用Node.js创建API

$ mkdir node-web
$ cd node-web
$ yarn init
$ yarn add express
const express = require('express')
const app = express()
app.get('/messages', (req, res) => {
  const content = [
    { subject: 'Unforgiven', body: `Here's looking at you, kid.` },
    { subject: 'The Princess Bride', body: 'I love the smell of napalm in the morning.' },
    { subject: 'The Third Man', body: 'You talking to me?' },
  ]
  res.json(content)
})

app.listen(3002, () => console.log('Listening on port 3002'))

确认回复

$ node app.js
$ curl -s http://localhost:3002/messages | jq
[
  {
    "subject": "Unforgiven",
    "body": "Here's looking at you, kid."
  },
  {
    "subject": "The Princess Bride",
    "body": "I love the smell of napalm in the morning."
  },
  {
    "subject": "The Third Man",
    "body": "You talking to me?"
  }
]

控制器的设定

修改先前使用 kong init 创建的 kong.yml 文件。

services:
  - name: rails-web
    url: http://localhost:3000
    routes:
      - name: user-routes
        paths:
          - /api/rails

  - name: rust-web
    url: http://localhost:3001
    routes:
      - name: post-routes
        paths:
          - /api/rust
  - name: node-web
    url: http://localhost:3002
    routes:
      - name: message-routes
        paths:
          - /api/node

在与kong.yml相同的位置创建kong.conf文件。

database = off
declarative_config = ./kong.yml

我要启动Kong。

$ kong start -c kong.conf
Kong started

我会尝试通过Kong访问各自设置的URL。

$ curl -s http://localhost:8000/api/rails/users | jq
[
  {
    "id": 1,
    "name": "Augustine Leannon",
    "email": "lang.heller@hudson.co",
    "created_at": "2021-12-25T07:13:32.878Z",
    "updated_at": "2021-12-25T07:13:32.878Z"
  },
  {
    "id": 2,
    "name": "Octavio Monahan",
    "email": "thaddeus@bauch.org",
    "created_at": "2021-12-25T07:13:32.883Z",
    "updated_at": "2021-12-25T07:13:32.883Z"
  },
  {
    "id": 3,
    "name": "Elsie Thiel",
    "email": "philip.barton@mayer.net",
    "created_at": "2021-12-25T07:13:32.887Z",
    "updated_at": "2021-12-25T07:13:32.887Z"
  }
]

$ curl -s http://localhost:8000/api/rust/posts | jq
{
  "title": "post title",
  "content": "post description content"
}

$ curl -s http://localhost:8000/api/node/messages | jq
[
  {
    "subject": "Unforgiven",
    "body": "Here's looking at you, kid."
  },
  {
    "subject": "The Princess Bride",
    "body": "I love the smell of napalm in the morning."
  },
  {
    "subject": "The Third Man",
    "body": "You talking to me?"
  }
]

每个人都通过Kong Gateway成功获得了API。

我试着设置插件。

由于有很多选择,所以这次我将尝试设置以下插件。其中一些插件可能需要使用Enterprise版才能使用,因此我选择了在OSS环境中可用的插件。

    • Authentication

JWT

Traffic Control

Proxy Cache
Rate Limitting

现金设置

kong.yml 的中文翻译

plugins:
  - name: proxy-cache
    service: node-web
    config:
      response_code:
        - 200
      request_method:
        - GET
        - HEAD
      content_type:
        - text/plain
        - application/json
      cache_ttl: 300
      strategy: memory
$ curl -i http://localhost:8000/api/node/messages
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 204
Connection: keep-alive
X-Cache-Key: 7c55750e1a2f5e73df7a0d9bf9892880
X-Cache-Status: Bypass
X-Powered-By: Express
ETag: W/"cc-gw3x3v8iu3ri0ITrNa7RwN1YRnI"
Date: Sat, 25 Dec 2021 09:00:22 GMT
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 2
Via: kong/2.7.0

[{"subject":"Unforgiven","body":"Here's looking at you, kid."},{"subject":"The Princess Bride","body":"I love the smell of napalm in the morning."},{"subject":"The Third Man","body":"You talking to me?"}]

增加了X-Cache-*相关内容。

设置速率限制

plugins:
  - name: rate-limiting
    service: rust-web
    config:
      second: 5 # 秒間5回
      minute: 10 # 分間10回
      hour: 10000 # 1時間10,000回
      policy: local
      fault_tolerant: true
      hide_client_headers: false
      redis_ssl: false
      redis_ssl_verify: false

第一次访问时

$ curl -i http://localhost:8000/api/rust/posts
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 59
Connection: keep-alive
RateLimit-Limit: 5
RateLimit-Remaining: 4
RateLimit-Reset: 1
X-RateLimit-Limit-Second: 5
X-RateLimit-Limit-Minute: 10
X-RateLimit-Limit-Hour: 10000
X-RateLimit-Remaining-Hour: 9999
X-RateLimit-Remaining-Second: 4
X-RateLimit-Remaining-Minute: 9
date: Sat, 25 Dec 2021 09:07:08 GMT
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 2
Via: kong/2.7.0

{"title":"post title","content":"post description content"}

限制被施加的状态 (Xianzhi bei shijia de zhuangtai)

curl -i http://localhost:8000/api/rust/posts
HTTP/1.1 429 Too Many Requests
Date: Sat, 25 Dec 2021 09:07:36 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
RateLimit-Limit: 10
RateLimit-Remaining: 0
RateLimit-Reset: 24
Retry-After: 24
X-RateLimit-Limit-Second: 5
X-RateLimit-Limit-Minute: 10
X-RateLimit-Limit-Hour: 10000
X-RateLimit-Remaining-Hour: 9990
X-RateLimit-Remaining-Second: 5
X-RateLimit-Remaining-Minute: 0
Content-Length: 41
X-Kong-Response-Latency: 1
Server: kong/2.7.0

{
  "message":"API rate limit exceeded"
}

JWT的配置

plugins:
  - name: jwt
    service: rails-web
    config:
      secret_is_base64: false
      run_on_preflight: true
consumers:
  - username: hime-chan
    custom_id: abcd123
jwt_secrets:
  - consumer: hime-chan
    algorithm: HS256
    key: key1234
    secret: hogehoge1234

建议使用类似”$ head /dev/urandom | hexdump | sha256sum”这样的方法来生成密钥和秘钥。如果您正在使用数据库,可以通过API创建用户(由于本次情景无需使用数据库,此步骤可以省略)。

$ curl -d "username=hime-chan&custom_id=abcd123" http://localhost:8001/consumers

确认设置的信息

$ curl -s http://localhost:8001/consumers/hime-chan/jwt | jq
{
  "data": [
    {
      "key": "key1234",
      "created_at": 1640426398,
      "rsa_public_key": null,
      "algorithm": "HS256",
      "secret": "hogehoge1234",
      "tags": null,
      "consumer": {
        "id": "b84d0924-e7b6-5c17-bfb3-c4eacce71cca"
      },
      "id": "d6f2fe3c-d64a-5577-beac-d35e04b23d8b"
    }
  ],
  "next": null
}

在没有 JWT 的情况下进行访问时,请确保请求头中没有该项。

$ curl -i http://localhost:8000/api/rails/users
HTTP/1.1 401 Unauthorized
Date: Sat, 25 Dec 2021 09:29:56 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 26
X-Kong-Response-Latency: 1
Server: kong/2.7.0

{"message":"Unauthorized"}

用一个合适的JWT进行访问。

curl -i http://localhost:8000/api/rails/users -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
HTTP/1.1 401 Unauthorized
Date: Sat, 25 Dec 2021 10:03:48 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 42
X-Kong-Response-Latency: 0
Server: kong/2.7.0

{"message":"No mandatory 'iss' in claims"}

在 https://jwt.io 上创建令牌。

将以下内容进行汉语的本土化转述,只需要给出一种选择:
支付费用给

{
  "iss": "key1234",
  "name": "Hanako",
  "user_id": 100
}

使用设置在”VERIFY SIGNATURE”部分的秘密密码”hogehoge1234″来生成。

使用创建的令牌进行API调用。

$ curl -i http://localhost:8000/api/rails/users -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrZXkxMjM0IiwibmFtZSI6IkhhbmFrbyIsInVzZXJfaWQiOjEwMH0.roghKCqrODUgni7MM8RM6XU-9lpozDysctKqWkhgOXE'
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Vary: Accept
ETag: W/"f3fb8adbc998b02efefb6493f1f588d7"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: e6fce311-e8d4-4d56-b14f-bc167be07e1d
X-Runtime: 0.019405
Server-Timing: start_processing.action_controller;dur=0.11962890625, sql.active_record;dur=0.701904296875, instantiation.active_record;dur=0.044921875, process_action.action_controller;dur=1.932861328125
X-Kong-Upstream-Latency: 25
X-Kong-Proxy-Latency: 2
Via: kong/2.7.0

[{"id":1,"name":"Augustine Leannon","email":"lang.heller@hudson.co","created_at":"2021-12-25T07:13:32.878Z","updated_at":"2021-12-25T07:13:32.878Z"},{"id":2,"name":"Octavio Monahan","email":"thaddeus@bauch.org","created_at":"2021-12-25T07:13:32.883Z","updated_at":"2021-12-25T07:13:32.883Z"},{"id":3,"name":"Elsie Thiel","email":"philip.barton@mayer.net","created_at":"2021-12-25T07:13:32.887Z","updated_at":"2021-12-25T07:13:32.887Z"}]

除此之外,还有其他引起我的注意的插件。

    • Key Authentication

 

    • OAuth 2.0 Authentication

 

    • Session

 

    • ACL

 

    • IP Restriction

 

    • Datadog

 

    • Prometheus

 

    • GraphQL

 

    • gRPC

 

    Logging関連

好多啊

我所感受到的

无论如何,我认为能够集中管理在不同公司开发的系统提供的独特API是一个重要的进展。即使是认证,也可以通过Kong来进行整合,从而实现效率的提升。监控和日志管理也可以在这里进行管理。此外,Kong还拥有许多插件,这可能是开源社区的特色之一。

因为Kong的插件可以用Lua或Go编写,所以如果使用的服务没有合适的插件,也可以自己开发一个。

然而,由于不是大型供应商提供的托管服务,因此在这方面,在生产环境中可能需要投入更多成本。可能需要在digitalocean上构建,作为Docker容器运行,使用Kubernetes进行操作等等,需要各种创新。

我想尝试一下未来的Kong Mesh。

广告
将在 10 秒后关闭
bannerAds