Docker 部署 Kong云原生API网关
本指南提供了在 Docker Compose 上配置 Kong Gateway 的步骤,基于有数据库模式的配置。本指南中使用的数据库是 PostgreSQL。
前置条件
准备一台Ubuntu服务器:
- 节点IP: 192.168.73.11
- 操作系统: Ubuntu 24.04
- 已安装docker及docker-compose
部署kong网关
创建docker-compose文件
root@user-service:/data/apps/kong# cat docker-compose.yaml
name: 'kong-gateway'
services:
kong-db:
image: postgres:17.5
container_name: kong-db
restart: always
environment:
POSTGRES_DB: kong
POSTGRES_USER: kong
POSTGRES_PASSWORD: kongpass
volumes:
- kong_db_data:/var/lib/postgresql/data
networks:
- kong-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kong -d kong"]
interval: 10s
timeout: 5s
retries: 5
kong-migrations:
image: kong:3.9.1
container_name: kong-migrations
depends_on:
kong-db:
condition: service_healthy
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-db
KONG_PG_USER: kong
KONG_PG_PASSWORD: kongpass
command: "kong migrations bootstrap"
networks:
- kong-net
restart: on-failure
kong:
image: kong:3.9.1
container_name: kong-gateway
restart: always
depends_on:
kong-migrations:
condition: service_completed_successfully
environment:
# kong database
KONG_DATABASE: postgres
KONG_PG_HOST: kong-db
KONG_PG_DATABASE: kong
KONG_PG_USER: kong
KONG_PG_PASSWORD: kongpass
# kong proxy
KONG_PROXY_LISTEN: 0.0.0.0:8000
KONG_PROXY_LISTEN_SSL: 0.0.0.0:8443
# kong admin
KONG_ADMIN_LISTEN: 0.0.0.0:8001, 0.0.0.0:8444 ssl
# kong manager
KONG_ADMIN_GUI_LISTEN: 0.0.0.0:8002, 0.0.0.0:8445 ssl
# kong logs
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ERROR_LOG: /dev/stderr
ports:
- "8000:8000" # Proxy
- "8443:8443" # Proxy SSL
- "8001:8001" # Admin API
- "8444:8444" # Admin API SSL
- "8002:8002" # Kong Manager
- "8445:8445" # Kong Manager SSL
networks:
- kong-net
healthcheck:
test: ["CMD-SHELL", "kong health"]
interval: 15s
timeout: 10s
retries: 3
networks:
kong-net:
driver: bridge
volumes:
kong_db_data:
driver: local
启动服务
docker compose up -d
确认容器启动状态
root@user-service:/data/apps/kong# docker compose ps -a
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
kong-db postgres:17.5 "docker-entrypoint.s…" kong-db 3 days ago Up 3 days (healthy) 5432/tcp
kong-gateway kong:3.9.1 "/docker-entrypoint.…" kong 24 minutes ago Up 24 minutes (healthy) 0.0.0.0:8000-8002->8000-8002/tcp, [::]:8000-8002->8000-8002/tcp, 0.0.0.0:8443-8445->8443-8445/tcp, [::]:8443-8445->8443-8445/tcp
kong-migrations kong:3.9.1 "/docker-entrypoint.…" kong-migrations 3 days ago Exited (0) 24 minutes ago
root@user-service:/data/apps/kong#
浏览器访问kong manager GUI
部署示例应用
下面是一个简单且贴近实际业务场景的系统,模拟一个订单系统的调用流程,基于docker构建:
- A 是前端页面,用户点击“创建订单”按钮;
- A 通过 Kong 网关调用后端 B(订单服务);
- B 接收到请求后,再通过 Kong 网关调用后端 C(库存服务)进行扣减库存;
- 最后返回响应到前端。
Kong 网关地址为:http://192.168.73.11:8000
创建相关目录
mkdir -p /data/app/order-app{order-ui,order-servcie,inventory-service}
cd /data/app/order-app
1. 后端 C:库存服务(inventory-service)
inventory-service/app.py
root@ubuntu:~# cat /data/app/order-app/inventory-service/app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/inventory/decrease", methods=["POST"])
def decrease_inventory():
print("库存扣减成功")
return jsonify({"msg": "库存扣减成功"}), 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5002)
inventory-service/Dockerfile
root@ubuntu:~# cat /data/app/order-app/inventory-service/Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY app.py .
RUN pip install flask
EXPOSE 5002
CMD ["python", "app.py"]
2. 后端 B:订单服务(order-service)
order-service/app.py
root@ubuntu:~# cat /data/app/order-app/order-service/app.py
from flask import Flask, jsonify
import requests
app = Flask(__name__)
@app.route("/order/create", methods=["POST"])
def create_order():
print("收到创建订单请求,调用库存服务扣减库存")
try:
# 通过Kong调用库存服务
resp = requests.post("http://192.168.73.11:8000/inventory/decrease")
return jsonify({"msg": "订单创建成功", "inventory_response": resp.json()})
except Exception as e:
return jsonify({"msg": "订单创建失败", "error": str(e)}), 500
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5001)
order-service/Dockerfile
root@ubuntu:~# cat /data/app/order-app/order-service/Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY app.py .
RUN pip install flask requests
EXPOSE 5001
CMD ["python", "app.py"]
3. 前端 A:订单创建页面
order-ui/index.html
root@ubuntu:~# cat /data/app/order-app/order-ui/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>订单系统</title>
</head>
<body>
<h1>订单系统</h1>
<button onclick="createOrder()">创建订单</button>
<pre id="result"></pre>
<script>
function createOrder() {
fetch('http://192.168.73.11:8000/order/create', { method: 'POST' })
.then(async res => {
const text = await res.text();
try {
const json = JSON.parse(text);
document.getElementById("result").innerText = JSON.stringify(json, null, 2);
} catch (err) {
console.error("非 JSON 响应内容:", text); // 只打印在控制台
document.getElementById("result").innerText =
"请求失败:服务暂时不可用,请稍后再试。";
}
})
.catch(err => {
console.error("请求错误:", err);
document.getElementById("result").innerText =
"网络异常或服务器未响应,请检查后端服务状态。";
});
}
</script>
</body>
</html>
order-ui/Dockerfile
root@ubuntu:~# cat /data/app/order-app/order-ui/Dockerfile
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
4. Docker Compose
构建docker镜像
root@ubuntu:~# cd /data/app/order-app/order-ui
docker build -t registry.cn-shenzhen.aliyuncs.com/cnmirror/order-ui:v1.0 .
root@ubuntu:~# cd /data/app/order-app/order-service
docker build -t registry.cn-shenzhen.aliyuncs.com/cnmirror/order-service:v1.0 .
root@ubuntu:~# cd /data/app/order-app/order-service
docker build -t registry.cn-shenzhen.aliyuncs.com/cnmirror/inventory-service:v1.0 .
创建docker-compose
root@ubuntu:~# cat /data/app/order-app/docker-compose.yaml
name: "order-app"
services:
ui:
image: registry.cn-shenzhen.aliyuncs.com/cnmirror/order-ui:v1.0
container_name: order-ui
ports:
- "8080:80"
networks:
- order-net
order-service:
image: registry.cn-shenzhen.aliyuncs.com/cnmirror/order-service:v1.0
container_name: order-service
ports:
- "5001:5001"
networks:
- order-net
inventory-service:
image: registry.cn-shenzhen.aliyuncs.com/cnmirror/inventory-service:v1.0
container_name: inventory-service
ports:
- "5002:5002"
networks:
- order-net
networks:
order-net:
driver: bridge
四、Kong配置建议(注册B和C)
创建 Service(服务)
服务代表你后端的 API 服务,告诉 Kong 请求最终要发到哪里。
# 注册服务B(订单)
curl -i -X POST http://192.168.73.11:8001/services \
--data name=order-service \
--data url=http://192.168.73.11:5001
# 注册服务C(库存)
curl -i -X POST http://192.168.73.11:8001/services \
--data name=inventory-service \
--data url=http://192.168.73.11:5002
查看注册的服务
创建 Route(路由)
路由是入口规则,告诉 Kong 哪些路径/域名/方法应该映射到哪个 Service。
# order-service 的路由(例如 /order)
curl -i -X POST http://192.168.73.11:8001/services/order-service/routes \
--data name=order-route \
--data paths[]=/order \
--data 'strip_path=false'
# inventory-service 的路由(例如 /inventory)
curl -i -X POST http://192.168.73.11:8001/services/inventory-service/routes \
--data name=inventory-route \
--data paths[]=/inventory \
--data 'strip_path=false'
查看注册的路由
全局启用cores插件,解决跨域问题
curl -i -X POST http://192.168.73.11:8001/plugins \
--data "name=cors" \
--data "config.origins=*" \
--data "config.methods[]=GET" \
--data "config.methods[]=POST" \
--data "config.methods[]=PUT" \
--data "config.methods[]=DELETE" \
--data "config.methods[]=OPTIONS" \
--data "config.headers[]=Accept" \
--data "config.headers[]=Authorization" \
--data "config.headers[]=Content-Type" \
--data "config.exposed_headers[]=X-Custom-Header" \
--data "config.credentials=true" \
--data "config.max_age=3600"
演示应用请求调用
演示流程:
- 启动服务和Kong;
- 注册 B 和 C 到 Kong;
- 浏览器访问
http://192.168.73.11:8080
; - 点击“创建订单”;
- 查看链路:A → B → C。
验证inventory-service接口
root@user-service:~# curl -s -X POST http://192.168.73.11:5002/inventory/decrease | jq
{
"msg": "库存扣减成功"
}
验证order-service接口
root@user-service:~# curl -s -X POST http://192.168.73.11:5001/order/create | jq
{
"inventory_response": {
"msg": "库存扣减成功"
},
"msg": "订单创建成功"
}
验证通过aloha网关请求inventory-service接口
root@user-service:~# curl -s -X POST http://192.168.73.11:8000/inventory/decrease | jq
{
"msg": "库存扣减成功"
}
验证通过aloha网关请求order-service接口
root@user-service:~# curl -s -X POST http://192.168.73.11:8000/order/create | jq
{
"inventory_response": {
"msg": "库存扣减成功"
},
"msg": "订单创建成功"
}
浏览器输出