在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。