## 安裝和測試 ### docker compose docker-kong專案 https://github.com/Kong/docker-kong.git kong的安裝有分兩種 * 含DB,kong會將admin API的configure紀錄在資料庫中,使用者可以修改 * DB-less,kong會將admin API的configure紀錄在memory中,並且資料是read-only 把專案clone下來後進到compose目錄 ``` git clone https://github.com/Kong/docker-kong.git cd docker-kong/compose ``` 啟動DB-less模式(compose file預設跑的是DB-less) ``` docker compose up -d ``` 如果要跑有DB的模式要用以下指令 ``` KONG_DATABASE=postgres docker compose --profile database up -d ``` 正確啟用的結果 ``` $ docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS compose-db-1 postgres:9.5 "docker-entrypoint.s…" db 4 minutes ago Up 4 minutes (healthy) 5432/tcp compose-kong-1 kong:latest "/docker-entrypoint.…" kong 4 minutes ago Up 4 minutes (healthy) 0.0.0.0:8000-8002->8000-8002/tcp, [::]:8001-8002->8001-8002/tcp, 0.0.0.0:8443-8444->8443-8444/tcp, [::]:8444->8444/tcp ``` ![image](https://hackmd.io/_uploads/BJdDFhrMgl.png) kong的設定會放在config/kong.yaml,之後可以繼續研究[設定檔的寫法](https://docs.konghq.com/gateway/latest/production/deployment-topologies/db-less-and-declarative-config/#declarative-configuration-format) :::info 如果出現以下錯誤的話,是由於docker network cache的metadata有損壞 ![image](https://hackmd.io/_uploads/rkGBO2rfee.png) 先停掉所有容器 ``` docker stop $(docker ps -aq) 2>/dev/null ``` 刪除所有容器 ``` docker rm -f $(docker ps -aq) 2>/dev/null ``` 刪掉所有網路(除了 bridge、host、none) ``` docker network prune -f ``` 重新啟動 Docke ``` sudo systemctl restart docker ``` ::: :::info 如果系統docker-compose無法跑的話,需要額外安裝docker-compose的plugin https://docs.docker.com/compose/install/linux/ ![image](https://hackmd.io/_uploads/H1I_3jHzlg.png) ::: ### 測試 分別對8000和8001 port發送請求 ``` curl --head localhost:8000 curl --head localhost:8001 ``` 回傳為200則都沒問題 ![image](https://hackmd.io/_uploads/ByYiF2SGee.png) 查看kong的資訊 ``` curl -i -X GET --url http://localhost:8001/services ``` ![image](https://hackmd.io/_uploads/BkCf53BMlx.png) ### docker compose 設定: * `POSTGRES_USER` and `POSTGRES_DB`: 設定為kong gateway的預設值 * `POSTGRES_PASSWORD`: 可以是任意的 * `KONG_DATABASE`: 指定kong用的資料庫種類 * `KONG_PG_HOST`: 資料庫container的名稱 * `KONG_PASSWORD` (Enterprise only): 好像只有企業版才有後臺管理者 * `KONG_PG_PASSWORD`: 資料庫的密碼 * `--name` and `--network`: container的名字 以及 要連上的 docker 網路 * `KONG_DATABASE`: 指定kong用的資料庫種類 * `KONG_PG_HOST`: 資料庫container的名稱 * `KONG_PG_USER` and `KONG_PG_PASSWORD`: The Postgres username and password. * `All_LOG` parameters: log相關設定,包含log的file path * `KONG_ADMIN_LISTEN`: The port that the Kong Admin API listens on for requests. * `KONG_ADMIN_GUI_URL`: 後台GUI介面 * `KONG_LICENSE_DATA`: (Enterprise only) If you have a license file and have saved it as an environment variable, this parameter pulls the license from your environment. ## 設定Route、Service kong 中的[path way](https://docs.konghq.com/gateway/latest/get-started/services-and-routes/)如下: ![image](https://hackmd.io/_uploads/Bk_1VTz7xl.png) **kong service** [Service](https://docs.konghq.com/gateway/latest/get-started/services-and-routes/)是一個某一個upstream application的接口(抽象層),等於是我們系統的每個服務都會有一個kong的service與其對接。且在kong的service可以存取一系列的設定,包括protocol, host, port,path、policy等 **kong Route** [Route](https://docs.konghq.com/gateway/3.10.x/key-concepts/routes/) 是一條通往 Service 的「進入點」,負責將來自用戶的請求根據規則導向對應的 Service。Route 可以設定條件(如路徑、主機名稱、HTTP 方法、Header 等),這些條件用來判斷:使用者發送的請求應該轉發給哪個 Service **範例** 如果我有一個kong service `order-service` 提供以下API: * `GET /orders` * `GET /orders/:id` * `POST /orders` * `GET /invoices` * `GET /invoices/:id` 想讓這些API透過Kong暴露出來,而可以用兩條Route來管理: * 一條 Route 處理所有 `/orders` 開頭的 API * 另一條 Route 處理所有 `/invoices` 開頭的 API ```json { "name": "orders-route", "paths": ["/orders"], "methods": ["GET", "POST"], "strip_path": false, "service": { "name": "order-service" } } ``` 這條 Route 可處理以下 endpoint: * GET /orders * GET /orders/123 * POST /orders ```json { "name": "invoices-route", "paths": ["/invoices"], "methods": ["GET"], "strip_path": false, "service": { "name": "order-service" } } ``` 這條 Route 可處理以下 endpoint: * GET /invoices * GET /invoices/456 ### 創建一個service + Route 一個kong serivce需要對應到一個真實存在的服務(某個微服務、外部API等),因此我們這裡用Flask寫一個簡單服務: :::info **注意:** Kong 的 service.url 應該只包含「protocol + host + port」,不能包含路徑(例如:/hello) ::: ```py= # app.py from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/') def hello(): return jsonify({'success': True, 'message': 'Hello'}) if __name__ == '__main__': app.run(host="0.0.0.0") ``` 啟動flask ``` pip install flask python3 app.py ``` flask預設會跑在 5000 上,這裡我會使用 http://172.22.87.129:5000/ ![image](https://hackmd.io/_uploads/S18NZkXXll.png) :::info 這裡不使用localhost的原因是因為我們的kong是跑在docker裡,因此讀不到本機localhost ::: ### 創建Service 可以列出當前的所有service ``` curl -X GET http://localhost:8001/services ``` **創建Service** 我們可以向 admin API `/service` 發送POST,新增一個service ``` curl -i -s -X POST http://localhost:8001/services \ --data name=example_service \ --data url='http://172.22.87.129:5000/' ``` 可以透過訪問 `/services/{service name or id}`得到service的設定 ``` curl -X GET http://localhost:8001/services/example_service ``` ![image](https://hackmd.io/_uploads/HyCi-1mmex.png) **更新Service** 比如我想更新service的行為,我們可以幫他打個PATCH ``` curl --request PATCH \ --url localhost:8001/services/example_service \ --data retries=6 ``` ### 創建Route 列出所有routes ``` curl http://localhost:8001/routes ``` 新增routes 為 `example_service` service 新增 `/mock` path,訪問`/services/{service name or id}` ``` curl -i -X POST http://localhost:8001/services/example_service/routes \ --data 'paths[]=/mock' \ --data name=example_route ``` **確認route設定** 可以訪問以下兩個連結做確認 * /services/{service name or id}/routes/{route name or id} * /routes/{route name or id} ``` curl -X GET http://localhost:8001/services/example_service/routes/example_route ``` ![image](https://hackmd.io/_uploads/H1F8XAzQeg.png) **更新routes** 也是一頁打一個patch,訪問 `/services/<service_name>/routes/<route_name>` ``` curl --request PATCH \ --url localhost:8001/services/example_service/routes/example_route \ --data tags="tutorial" ``` ### 測試 對`https://httpbin.konghq.com/`(當初service設定的url) 或者 `http://localhost:8000/mock` (預設) 發送請求 ``` curl -X GET http://localhost:8000/mock ``` ![image](https://hackmd.io/_uploads/BJZ0bJ77ex.png) ## Kong gRPC請求 ### Kong gRPC v.s gRPC gateway 🔍 1. Kong 的 gRPC 支援是怎麼運作的? Kong 是直接在 L4/L7 Proxy 層處理 gRPC over HTTP/2 的請求,支援: * 原生 gRPC (HTTP/2, content-type: application/grpc) * gRPCS (TLS 加密的 gRPC) 這是透過 Kong 的 Nginx 基礎與 HTTP/2 的支援,讓它可以直接 proxy gRPC 流量到後端的 gRPC server,而不是把它轉成 REST。 📌 你只要設定 Service 的 protocol: grpc 或 grpcs,就能處理 gRPC 請求。 🧩 2. 那 gRPC-Gateway 是什麼? gRPC-Gateway 是 Google 開源的工具,它的工作是: * 將 gRPC 接口轉譯為 RESTful HTTP JSON API * 適用於客戶端無法使用 gRPC 協定時(如瀏覽器、IoT) 這是個 程式碼層的轉譯,通常在 gRPC server 旁邊部署,並不是 Gateway/Proxy 層的功能。 ![image](https://hackmd.io/_uploads/Hki5XLTQeg.png) ### 官方範例 :::info 由於我們使用yaml檔事先設定,因此我們可以使用DB-less mode啟動即可 ``` docker compose up -d ``` ::: 打開 ./config/kong.yaml,可以在裡面設定gRPC-gateway 以下使用[官方的範例](https://docs.konghq.com/hub/kong-inc/grpc-gateway/how-to/) ```yaml # kong.yaml _format_version: "2.1" services: - protocol: grpc host: localhost port: 9000 routes: - protocols: - name: http paths: - / plugins: - name: grpc-gateway config: proto: path/to/hello-gateway.proto ``` `hello-gateway.proto`的內容 ```protobuf syntax = "proto3"; package hello; service HelloService { rpc SayHello(HelloRequest) returns (HelloResponse) { option (google.api.http) = { get: "/v1/messages/{name}" additional_bindings { get: "/v1/messages/legacy/{name=**}" } post: "/v1/messages/" body: "*" }; } } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloResponse { string message = 1; } ``` ## 設定kong服務 官方建議安裝: * jq,jq可以在cli介面處理json response ``` # Debian sudo apt-get install jq ``` 查看Kong Gateway configuration ``` curl -s localhost:8001 | jq '.configuration' ``` 有GUI的話可以在 http://localhost:8002 上查看後台管理 ![image](https://hackmd.io/_uploads/S1CmIASGlg.png) ## 自訂義 docker image https://docs.konghq.com/gateway/3.10.x/install/docker/build-custom-images/