在Kubernetes中实现Sidecar类型的Container
2020-10-24 10:25:51 +08 字数:1824 标签: K8s在一个Pod中,某个Container运行主要业务的同时,需要另一个Container协同 ——这是一个常见的业务场景,这个协同Container通常称为Sidecar。 主要的Container在运行时,Sidecar需要已经就绪;而当主要的Container停止后,Sidecar也需要停止。
这一功能,虽然是Pod级别的,但基本上也只有Job类业务会使用。 如果是Deployment,其实根本不需要区分Sidecar类型,大家默认都不会停,让外部统一决定生命周期即可。
目前(v1.20版本),官方仍然没有正式支持Sidecar类型的Container。 本文先介绍一个网上流传的乌龙,再介绍一种自己实现的土办法。
官方支持(乌龙) ¶
Kubernetes自1.18版本,正式支持了Sidecar,作为一种
经实测证明,这是个乌龙,实际上该功能还未正式进入主线。lifestyle.type
。
一般网上给出的样例,调整成一个直接拿去可以跑的形式,大概是这样的:
---
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
spec:
restartPolicy: Never
containers:
- name: main
image: alpine:3.12
args:
- sleep
- 5
- name: sidecar
image: alpine:3.12
- sleep
- 100
lifecycle:
type: Sidecar
理论上,官方支持后,会以containers.lifecycle.type
的方式,使用Sidecar
类型。
也就是说,以上Job会自动在5秒后结束,而不需要等待100秒。
但既然功能还未进主线,相关MR也已被关闭。
误导文章如下:
Sidecar Containers improvement in Kubernetes 1.18 | by Chandra Sekar | Medium- Sidecar container lifecycle changes in Kubernetes 1.18 : kubernetes
- Sidecar container lifecycle changes in Kubernetes 1.18 · Banzai Cloud
- 重磅!K8S 1.18版本将内置支持SideCar容器。 - 云+社区 - 腾讯云
- 细数k8s支持的4种类型的container - 知乎
其中还有一个像模像样的动态图:
相关PR与issue如下:
- Add container.lifecycle.type to api (sidecars KEP) by Joseph-Irving · Pull Request #79649 · kubernetes/kubernetes
- Sidecar kubelet implementation by Joseph-Irving · Pull Request #80744 · kubernetes/kubernetes
- sidecar: Address all concerns raised by rata · Pull Request #1980 · kubernetes/enhancements
- Sidecar Containers · Issue #753 · kubernetes/enhancements
这事的情况,大概是在PR#79649
提出Sidecar功能。
但是PR#1980
被merge后,实现的基础消失了,lifecycle.type
因其它原因被删除了。
而新提的PR#80744
也是基于它的,也没被merge。
相关文章,属于提前吹捧,结果现实打脸。
Hack方案 ¶
既然官方没有,而且即使在v1.18添加,CCE也没有更新,那么只能自己准备Sidecar方案。 Sidecar类型的Container,其生命周期需要满足的条件和前面那个动图没有区别。
一个方案是,在主要的容器中,维护一个文件锁,而在其它容器中,根据这个锁的存在性决定自己的生命周期。
---
apiVersion: batch/v1
kind: Job
metadata:
name: sidecar
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: main
image: alpine:3.12
imagePullPolicy: IfNotPresent
args:
- 'sh'
- '-c'
- >
for i in 1 2 3;
do
date
sleep 3s;
done;
rm /tmp/lifecycle/running.lock
lifecycle:
postStart:
exec:
command: ["touch", "/tmp/lifecycle/running.lock"]
preStop:
exec:
command: ["rm", "/tmp/lifecycle/running.lock"]
volumeMounts:
- mountPath: /tmp/lifecycle/
name: lifecycle
- name: sidecar
image: alpine:3.12
imagePullPolicy: IfNotPresent
command:
- 'sh'
- '-c'
- >
tail -f /dev/null &
while [ -f /tmp/lifecycle/running.lock ];
do
date
sleep 1s;
done;
jobs -p | xargs kill -9
volumeMounts:
- mountPath: /tmp/lifecycle/
name: lifecycle
volumes:
- name: lifecycle
emptyDir: {}
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
以上代码示例中,每一行包含/tmp/lifecycle
的,都是Hack的关键。
这里通过挂载/tmp/lifecycle/
目录,并确保在main
中的一个入口(postStart
)和两个出口(preStop
与正常退出)之间,
维持/tmp/lifecycle/running.lock
文件的存在。
在sidecar
容器中,通过主进程轮询/tmp/lifecycle/running.lock
文件,并在其消失后随之退出。
此外,监控检查也是一个可行方案。
livenessProbe:
exec:
command:
- cat
- /tmp/lifecycle/running.lock
initialDelaySeconds: 3
periodSeconds: 1
但它有一些细节问题要调整,比如重试次数。 而最大的副作用是,Job总是失败的,这一点不能接受,因此放弃。
在实际使用中,可以用Python等高级语言,实现更复杂的Sidecar容器守护启动器。 除了定时检查文件锁以外,还需要监控子进程的运行状态。 以上的简单Shell样例中,子进程(其实是业务逻辑上的主进程)如果已经退出,主进程是不知道的。
结论 ¶
目前Kubernetes官方对Sidecar类型的容器,仍然缺乏原生支持。 网上流传的文案,其实都是错的。 而由于原MR的实现基础被删除,新方案还遥遥无期。
在Kubernetes正式支持Sidecar之前,仍然只能用各种Hack手段来实现其等价功能。 虽然对Sidecar容器做健康检查,可以实现类似生命周期,但是Pod总是以失败结束。 因此副作用最小的思路,还是让Sidecar容器的服务被一个特殊daemon守护,它轮询主要容器的状态,决定服务生死。 细节上,轮询无论使用文件锁还是网络通信,都是可以的。