## 安裝和測試
### 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
```

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有損壞

先停掉所有容器
```
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/

:::
### 測試
分別對8000和8001 port發送請求
```
curl --head localhost:8000
curl --head localhost:8001
```
回傳為200則都沒問題

查看kong的資訊
```
curl -i -X GET --url http://localhost:8001/services
```

### 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/)如下:

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

:::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
```

**更新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
```

**更新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
```

## 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 層的功能。

### 官方範例
:::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 上查看後台管理

## 自訂義 docker image
https://docs.konghq.com/gateway/3.10.x/install/docker/build-custom-images/