前言
容器受限于“单进程模型”,往往一个容器只运行一个主进程,但是一个复杂任务又需要多个进程相互配合工作,所以推荐给每个进程都启动一个容器。这些相互配合的容器必然存在依赖关系,它们之间往往会发生频繁的基于存储卷或 Socket 的数据交换行为,因此它们其实形成了一个“容器组”的概念,它们必须成组调度到同一台机器上,否则这些数据交换处理起来会很麻烦。kubernetes 中的 Pod 其实就是容器组的概念,一个 Pod 可以包含一个或多个容器,这些容器共享存储卷和网络,可以使用 localhost 直接通信。
所以,在使用 kubernetes 部署应用时,我们应该思考这样一个问题:当面对多个容器时,我们是应该部署多个 Pod,还是在一个 Pod 里面启动多个容器?
Pod中的多容器
Pod 是 Kubernetes 的最小调度单位,一个 Pod 里面可以启动一个或多个容器,这些容器共享网络、存储卷、生命周期。
一个 Pod 中的多个容器,它们通常围绕一个核心任务紧密协作,多容器 Pod 应用场景主要有两类:
- 主从容器模式:主容器提供核心功能,从容器提供辅助性功能,例如:日志采集上报、服务监控等
- Sidecar 模式:边车模式,利用辅助容器来增强主容器的功能,而无需修改主容器代码。例如:辅助容器拦截进出流量,实现数据加工,请求路由等功能。
相比将多个功能打包到一个容器中,使用多容器 Pod 更符合“单一职责原则”,每个容器专注于一个任务,便于维护和扩展。同时,容器之间通过共享资源(如网络、存储)减少冗余,但彼此仍保持进程级隔离,避免直接干扰。多容器 Pod 具备灵活的扩展性,通过 Sidecar 模式,增加辅助容器来增强主容器,而无需修改主容器代码,减少侵入性。
部署多个 Pod 还是一个 Pod 启动多个容器?
这要根据业务需求、容器协作模式和运维复杂度综合决策。遵循的原则简单概括如下:
- 单 Pod 多容器:容器之间需要高频交互(localhost 通信,共享存储卷)、且强依赖(一个容器的崩溃会影响其它容器)。举例:日志收集、服务监控、网络代理等
- 多 Pod 单容器:容器间需要松耦合,且职责独立,甚至需要独立弹性。举例:Web服务和数据库虽然也会发生数据交换,但不适合部署在同一 Pod 里面,通信直接通过 service 即可。
多容器Pod实战
实战案例:通过 Pod 部署一个包含3个容器的应用,一个 Nginx 容器对外提供服务,运行中会产生日志;一个 log-process 容器,负责收集并处理 Nginx 才生的日志;一个 monitor 容器,负责监控 Nginx 服务状态。
这是个非常适合在一个 Pod 里面启动多个容器的例子,主容器是 Nginx,辅助容器是 log-process 和 monitor,Nginx 一旦挂了,辅助容器就没法工作也没有意义了。它们之间是强依赖的,且会发生频繁的数据交换行为,log-process 必然要共享 Nginx 的日志文件,monitor 必然要和 Nginx 建立网络连接。
如下是 Pod 对象的描述文件,spec.containers 是一个数组,配置了三个容器,分别是 nginx、log-process、monitor。因为要共享日志文件,所以声明了一个 volume,名称是 log-volume,类型是 emptyDir,只在容器运行时有效。log-volume 会被同时挂载到 Nginx 和 log-process,这样 log-process 就能读取到 Nginx 产生的日志文件了。monitor 没有额外配置啥,它直接通过 localhost 访问 Nginx 来监控服务状态即可。
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
labels:
app: multi-container
spec:
containers:
- name: nginx
image: docker.io/library/mynginx:1.0
imagePullPolicy: Never
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/log/nginx
name: log-volume
- name: log-process
image: docker.io/library/log-process:1.0
imagePullPolicy: Never
volumeMounts:
- mountPath: /app/log
name: log-volume
env:
- name: LOG_FILE_PATH
value: "/app/log/nginx_access.log"
- name: monitor
image: docker.io/library/monitor:1.0
imagePullPolicy: Never
volumes:
- name: log-volume
emptyDir: {}
kubectl apply -f pod.yaml
部署这个 Pod,过一会发现,三个容器都已经启动成功了,且 Pod 被分配的 IP 是 10.244.1.17。
请求这个 IP,是可以成功访问到 Nginx 的。随着我们的访问,此时就已经产生访问日志了。
kubectl exec -it multi-container-pod -c log-process sh
进入 log-process 容器,可以访问到 nginx_access.log,同时脚本会实时处理读取到的日志,并写入 output.log。
同理,monitor 容器会持续访问 Nginx 记录响应的状态码,来监控服务状态。
尾巴
通过 kubernetes 部署应用,我们无需关心应用会被部署到哪台机器上,但是我们要关心如何设计 Pod。面对多容器时,是部署多个 Pod,还是在一个Pod里面启动多个容器,是我们要思考的问题。