容器-13-Kubernetes实战-静态网站部署优化2-InitContainer

我们在上一篇已经将Dockerfile精简为了:

1
2
FROM nginx:alpine
COPY ./dist /usr/share/nginx/html

但相信你也发现了,内容中还是对web服务器有着强依赖。当我们想换成其他版本的Nginx镜像,或换成其他Web服务器,就必须修改源代码中的Dockerfile,重新制作镜像。
这种情况并非不可能。典型的场景之一:我们镜像所依赖的Nginx或tomcat版本出现了某个安全事故,而该问题可以通过将web服务器或web容器版本升级到最新版本解决。
所以我们希望能在Dockerfile中将Nginx的痕迹彻底抹除,只在Kubernetes的YAML中指定web服务器。

一个很自然的想法就是:我们使用一个默认的web服务器镜像。在使用该镜像的容器启动之前,将静态网站的文件拷贝到相应目录,就像在前一篇从ConfigMap获取配置文件一样。
这就是initContainer的作用。

1. initContainer

我们首先将镜像改为alpine,并修改COPY的路径:

1
2
FROM alpine:latest
COPY ./dist /html

重新编译为latest版本(这是为了强制每次重新拉取镜像):

1
docker build -t 10.16.34.197:5000/staticsite .

然后我们将deployment的YAML修改为如下:

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
31
32
33
34
35
36
37
38
39
40
41
apiVersion: apps/v1
kind: Deployment
metadata:
name: poc-web
labels:
app: poc-web
spec:
replicas: 2
selector:
matchLabels:
app: poc-web
template:
metadata:
labels:
app: poc-web
spec:
initContainers:
- image: 10.16.34.197:5000/staticsite:latest
name: poc-web-dist
command: ["cp", "/html", "/website"]
args: ["-r"]
volumeMounts:
- mountPath: "/website"
name: poc-web-volume
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
name: web
volumeMounts:
- name: poc-web-config
mountPath: /etc/nginx/conf.d
- name: poc-web-volume
mountPath: "/usr/share/nginx"
volumes:
- name: poc-web-config
configMap:
name: poc-web-config
- name: poc-web-volume
emptyDir: {}

解释一下几个改动点。

1.1 挂载emptyDir

首先我们除了ConfigMap之外,增加挂载了一个类型为emptyDir的卷。emptyDir是一个生命周期和Pod相同的空目录,作用是为多容器Pod内的容器提供一个公共盘来共享文件。当Pod从Node上被移除后,emptyDir也会随之被永久删除。缺省情况下,emptyDir使用主机磁盘进行存储的。也可以设置emptyDir.medium字段的值为Memory,来提高IO速度:

1
2
3
4
volumes:
...
- name: poc-web-volume
emptyDir: {}

1.2 增加initContainer和复制命令

然后我们在containers的平级增加一个initContainers。initContainer内定义的容器会比spec.containers内定义的容器先启动。启动的流程图可以参见下图:
Kubernetes Pod Init Process

在poc-web-dist这个容器启动后,会将emptyDir挂载到/website路径,并执行以下命令:

1
cp -r /html /website

即将包含所有的静态网站文件的html目录复制到emptyDir中。
PS. 我尝试过将command和args参数改为:

1
2
command: ["cp", "/html/*", "/website"]
args: ["-r"]

这会导致Pod启动报错。明明cp -r /html/* /website这个命令是可以正常执行的。。。目前还没找到原因。

1.3 将镜像改为nginx并挂载emptyDir

在执行完命令后initContainer完成使命退出。然后spec.container内的容器开始启动。
我们将自定义镜像改为普通的nginx镜像,并在镜像的/usr/share/nginx路径上挂载emptyDir。emptyDir中的html目录会替换nginx镜像的/usr/share/nginx/html目录,达成和之前相同的效果。

在添加了initContainer后,启动速度略微变慢,会经历一个为时十几秒的PodInitializing状态,然后正常启动:

1
poc-web-657d957f68-6m7xw     0/1     PodInitializing         0          12s

万事开头难。虽然我们目前只完成了一个静态网站的部署,但应该已经对Kubernetes有了基本的认识。

2. 参考资料

官方的Demo,通过wget下载网页后也是加载到/usr/share/nginx/html目录
Configure Pod Initialization - Kubernetes

本文永久链接 [ https://galaxyyao.github.io/2019/07/04/容器-13-Kubernetes实战-静态网站部署优化2-InitContainer/ ]