---
# System prepended metadata

title: kong研究

---

## 安裝和測試

### 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/