修改Kong官方Docker镜像

前言


开源项目Kong是当下最火的API Gateway项目之一,官方也在Docker Hub上提供了Kong的镜像。但我在使用官方镜像来部署Kong的过程中发现,我需要对官方镜像做一些改动来适应我的需求:

  • 在Docker启动时只需要给定镜像、启动命令即可完成部署(包括初次部署),方便迁移k8s;
  • 方便Kong切换不同的配置文件;
  • 将Kong所有的日志存放在指定目录,方便挂载存储。

这篇文章记录的就是我改动的思路和过程。

注:本文所使用的运行环境为Ubuntu 18.04 LTS+Docker 18.09.6,Kong使用的数据库均为PostgreSQL,使用的Kong镜像版本为1.1.2-alpine

官方镜像分析

Kong的官方文档(Docker Installation)给出了一个简单的Kong部署教程,记录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 创建docker网络
docker network create kong-net
# 创建数据库
docker run -d --name kong-database \
--network=kong-net \
-p 5432:5432 \
-e "POSTGRES_USER=kong" \
-e "POSTGRES_DB=kong" \
postgres:9.6
# 初始化数据库
docker run --rm \
--network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
kong:latest kong migrations bootstrap
# 启动Kong
docker run -d --name kong \
--network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
-p 8000:8000 \
-p 8443:8443 \
-p 8001:8001 \
-p 8444:8444 \
kong:latest

从以上教程可以看出,我们可以通过为官方镜像设置环境变量来配置Kong;而且,该镜像还有一个默认的启动命令,可以直接启动Kong主进程。
然后,我们再来看看kong:1.1.2-alpine这个官方镜像的Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM alpine:3.6
LABEL maintainer="Kong Core Team <team-core@konghq.com>"

ENV KONG_VERSION 1.1.2
ENV KONG_SHA256 0d7509fa2ef653b4aba14a1a1fd20339bccb4f8d386429102c42b7af6d8b6bdb

RUN adduser -Su 1337 kong \
... # 省略
&& chmod -R g=u /usr/local/kong

COPY docker-entrypoint.sh /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]

EXPOSE 8000 8443 8001 8444

STOPSIGNAL SIGTERM

CMD ["kong", "docker-start"]

从Dockerfile可以看出,官方镜像默认的启动命令为ENTRYPOINT+CMD;再结合前文官方教程里初始化数据库的命令,我们可以知道:

  • 初始化数据库的容器内启动命令为:/docker-entrypoint.sh kong migrations bootstrap
  • 启动Kong主进程的容器内启动命令为:/docker-entrypoint.sh kong docker-start

有了这些信息,我们就可以对官方镜像进行修改了。

自定义Dockerfile、启动脚本

考虑到我有多套Kong配置分别用于不同环境(本地调试、测试、实际应用等),而这些配置中有许多重复的内容,所以我选择将这些重复的配置项放入Dockerfile中,不重复的配置项放入自定义启动脚本中。Dockerfile如下:

1
2
3
4
5
6
7
8
9
10
FROM kong:1.1.2-alpine
LABEL maintainer="orange_wolf <orange_wolf@163.com>"

COPY . /gateway
WORKDIR /gateway
# KONG_PREFIX指定工作目录,Kong的日志默认存储在 KONG_PREFIX/logs/ 目录下
ENV KONG_PREFIX=/gateway KONG_ADMIN_LISTEN=0.0.0.0:8001 KONG_DATABASE=postgres
EXPOSE 8000 8001
# 自定义启动脚本
ENTRYPOINT ["./run-kong.sh"]

自定义启动脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env sh
case ${1} in
"local")
export KONG_PG_HOST=192.168.0.100
export KONG_PG_USER=xxxxxx
export KONG_PG_PASSWORD=xxxxxx
... # 省略
;;
"pro")
export KONG_PG_HOST=xxx.xxx.xxx.xxx
export KONG_PG_USER=xxxxxx
export KONG_PG_PASSWORD=xxxxxx
... # 省略
;;
... # 省略
esac

/docker-entrypoint.sh kong migrations bootstrap # 初始化数据库
/docker-entrypoint.sh kong docker-start # 启动Kong

于是,我用自定义的启动脚本代替了原有的启动脚本,这样就能让我通过在启动容器时输入不同指令来载入不同Kong配置,也能让Kong在连接到空数据库时自动实现初始化动作(而不是直接退出)。而这种部署方式也更灵活,更容易从Docker迁移到k8s等其它平台。实际部署流程如下:

1
2
3
4
docker build . -t my-kong-image
docker run -d --name kong -p 8000:8000 -p 8001:8001 my-kong-image local # 载入本地调试配置
docker run -d --name kong -p 8000:8000 -p 8001:8001 my-kong-image test # 载入测试配置
docker run -d --name kong -p 8000:8000 -p 8001:8001 my-kong-image pro # 载入实际应用配置

以root命令启动Nginx

在自定义了Dockerfile和启动脚本后,似乎一切都能满足我的需求了。但不幸的时,以这个镜像启动容器时我遇到了如下错误:

1
nginx: [alert] could not open error log file: open() "/gateway/logs/error.log" failed (13: Permission denied)

很明显,nginx没有足够的权限将日志写入我指定的日志文件夹中。由于在不指定KONG_PREFIX环境变量时,nginx和Kong能正常将日志文件写入默认的/usr/local/kong/logs/目录下,所以我尝试在启动脚本run-kong.sh中通过chmodchown命令,将/gateway/logs/目录的权限与/usr/local/kong/logs/目录的权限同步,但还是出现了一样的错误提示。
上文提到,官方镜像的启动脚本为/docker-entrypoint.sh。我找到了这份和官方Dockerfile放在一起的启动脚本,发现nginx是以kong用户的身份运行的:

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
... # 省略
exec su-exec kong /usr/local/openresty/nginx/sbin/nginx \
-p "$PREFIX" \
-c nginx.conf
fi
fi
fi

exec "$@"

既然是这样,那么我在自己的启动脚本内修改官方的启动脚本,让nginx以root权限运行就可以解决问题了。修改后的自定义启动脚本如下:

1
2
3
4
5
...  # 省略

sed -i 's/su-exec kong/su-exec root/g' /docker-entrypoint.sh # 修改启动脚本,以root身份启动nginx
/docker-entrypoint.sh kong migrations bootstrap # 初始化数据库
/docker-entrypoint.sh kong docker-start # 启动Kong

顺便一提,如果想在容器内让Kong的默认端口由8000变为80KONG_PROXY_LISTEN=0.0.0.0:80)或其它低于1024的端口,同样需要让nginx以root身份运行。

后记

开源项目官方提供的Docker镜像,往往要比自己用AlpineCentOSDebian等基础镜像一步步安装项目构成的镜像来得方便、稳定、好用;但后者又往往拥有前者无可比拟的自由度。其实,通过分析并“继承”官方Dockerfile、编写自己的启动脚本,往往能够同时拥有前两种镜像的优点——既方便,又自由。


修改Kong官方Docker镜像
https://www.yooo.ltd/2019/05/30/修改Kong官方Docker镜像/
作者
OrangeWolf
发布于
2019年5月30日
许可协议