我们在上一篇已经将Dockerfile精简为了:
1 | FROM nginx:alpine |
但相信你也发现了,内容中还是对web服务器有着强依赖。当我们想换成其他版本的Nginx镜像,或换成其他Web服务器,就必须修改源代码中的Dockerfile,重新制作镜像。
这种情况并非不可能。典型的场景之一:我们镜像所依赖的Nginx或tomcat版本出现了某个安全事故,而该问题可以通过将web服务器或web容器版本升级到最新版本解决。
所以我们希望能在Dockerfile中将Nginx的痕迹彻底抹除,只在Kubernetes的YAML中指定web服务器。
一个很自然的想法就是:我们使用一个默认的web服务器镜像。在使用该镜像的容器启动之前,将静态网站的文件拷贝到相应目录,就像在前一篇从ConfigMap获取配置文件一样。
这就是initContainer的作用。
1. initContainer
我们首先将镜像改为alpine,并修改COPY的路径:
1 | FROM alpine:latest |
重新编译为latest版本(这是为了强制每次重新拉取镜像):
1 | docker build -t 10.16.34.197:5000/staticsite . |
然后我们将deployment的YAML修改为如下:
1 | apiVersion: apps/v1 |
解释一下几个改动点。
1.1 挂载emptyDir
首先我们除了ConfigMap之外,增加挂载了一个类型为emptyDir的卷。emptyDir是一个生命周期和Pod相同的空目录,作用是为多容器Pod内的容器提供一个公共盘来共享文件。当Pod从Node上被移除后,emptyDir也会随之被永久删除。缺省情况下,emptyDir使用主机磁盘进行存储的。也可以设置emptyDir.medium字段的值为Memory,来提高IO速度:
1 | volumes: |
1.2 增加initContainer和复制命令
然后我们在containers的平级增加一个initContainers。initContainer内定义的容器会比spec.containers内定义的容器先启动。启动的流程图可以参见下图:
在poc-web-dist这个容器启动后,会将emptyDir挂载到/website路径,并执行以下命令:
1 | cp -r /html /website |
即将包含所有的静态网站文件的html目录复制到emptyDir中。
PS. 我尝试过将command和args参数改为:
1 | command: ["cp", "/html/*", "/website"] |
这会导致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