本文主要记录一次 基于 Docker + API 网关(Kong) 的后端服务线上部署实践,个人觉得还有很多细节需要完善。

整体目标是:

后端服务容器化部署

通过 API 网关统一入口

对外仅暴露 Nginx 的 HTTP 端口

整体请求链路说明

一次接口请求的完整流程如下:

客户端(Apifox / 浏览器)
        ↓
Nginx(宿主机,仅暴露 80 端口)
        ↓
Kong API Gateway(Docker 容器)
        ↓
后端服务容器(user-service / order-service)

具体到请求示例:

访问:http://域名或者IP地址/api/user/health
  ↓
Nginx 将请求转发至:
http://127.0.0.1:8000/api/user/health   (8000 端口由 Kong 使用)
  ↓
Kong 根据路由规则转发至:
http://user-service:30122/health

其中:

8000 端口 仅在宿主机本地监听

30122 / 30222 等端口 仅存在于 Docker 内部网络

部署前准备

在开始部署前,需要准备:

1、已完成开发的后端服务(我这里是user-service和order-service) ,有一个/health接口 返回{“status”:“ok”} 表示服务正常启动

2、一台服务器:已安装 Docker 与 Docker Compose、Nginx

常用的docker compose命令

docker compose up -d --build  #强制重新构建所有镜像并启动
docker compose up -d # 若本地已有镜像,仅启动容器
docker compose up -d --build order-service # 仅构建并启动指定服务

服务器目录结构:
在这里插入图片描述

docker-compose.yml 配置说明

version: "3.9"

networks:
  backend:
    driver: bridge

services:
  kong:
    image: kong:3.6
    container_name: kong
    restart: unless-stopped #自动重启
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /kong/kong.yml
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_LOG_LEVEL: info
    volumes:
      - ./kong/kong.yml:/kong/kong.yml
    ports:
      - "127.0.0.1:8000:8000" # 仅宿主机可访问
      - "127.0.0.1:8001:8001" # Kong Admin(本地管理)
    networks:
      - backend

  user-service:
    build:
      context: ./user-service
      dockerfile: Dockerfile
    image: user-service:latest
    container_name: user-service
    restart: unless-stopped
    expose:
      - "30122" # 仅 Docker 网络内可见
      - "31122"
    networks:
      - backend

  order-service:
    build:
      context: ./order-service
      dockerfile: Dockerfile
    image: order-service:latest
    container_name: order-service
    restart: unless-stopped
    expose:
      - "30222"
      - "31222"
    networks:
      - backend

关键点说明:

expose 不会占用宿主机端口

后端服务只能被 Kong 或同一 Docker 网络内的容器访问

外部流量必须经过 Nginx → Kong

后端服务镜像构建(多阶段构建)

我的后端服务是使用kratos写的,如果是springboot项目,可以让ai去给你生成一个 Dockerfile文件
Dockerfile内容如下

FROM golang:1.22 AS builder

COPY . /src
WORKDIR /src

RUN GOPROXY=https://goproxy.cn make build

FROM debian:stable-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates \
        netbase \
        tzdata \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /src/bin /app
COPY ./configs /data/conf

VOLUME /logs
WORKDIR /app

EXPOSE 30122
EXPOSE 31122
#运行服务
CMD ["./user-service", "-conf", "/data/conf", "-env", ""]

Kong 配置(声明式)

修改 kong.yml 后,需要重启 Kong 容器:

docker compose restart kong

kong.yml 内容如下:

_format_version: "3.0"
#定义apikey的值,所有服务只要使用下面某个apikey的值,都可以鉴权成功
consumers:
  - username: user-service-consumer
    keyauth_credentials:
      - key: test1

  - username: order-service-consumer
    keyauth_credentials:
      - key: test2

services:
  - name: user-service
   # 因为 Kong 和 user-service 在同一个 Docker 网络里
    url: http://user-service:30122
    routes:
      - name: user-route
        paths:
          - /api/user
        strip_path: true #把 /api/user 从路径中去掉,再转发给后端,因为我后端写的api地址是/health,前面没有/api/user
    plugins:
      - name: key-auth # 使用Kong 官方的 API Key 鉴权插件
        config:
          key_names:
            - apikey
          hide_credentials: true # apikey不转发给后端接口,只给kong鉴权用

  - name: order-service
    url: http://order-service:30222
    routes:
      - name: order-route
        paths:
          - /api/order
        strip_path: true
    plugins:
      - name: key-auth
        config:
          key_names:
            - apikey
          hide_credentials: true


Nginx 配置(宿主机)

server {
    listen 80; # 监听80端口
    server_name <填写域名或者ip地址>;
	#匹配并处理所有以 /api/ 开头的请求路径
    location /api/ {
        proxy_pass http://127.0.0.1:8000;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 3s;
        proxy_send_timeout 10s;
        proxy_read_timeout 10s;
    }
}

避坑点:location 与 proxy_pass 末尾的 /
在使用 Nginx 反向代理时,location 与 proxy_pass 末尾是否带 /,会直接影响请求路径的转发结果,这是一个非常容易被忽略但影响极大的问题。

测试

携带正确的apikey
在这里插入图片描述
携带错误的apikey
在这里插入图片描述

整体方案总结

后端服务全部运行在 Docker 容器中,服务端口仅在 Docker 内部网络暴露

外部访问统一经由 Nginx + Kong,并且访问接口时,需要在headler中携带apikey

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐