文章目录
- 问题
- 分析
- 方案
- initContainer
- 延伸
- docker-compose depends_on与Helm dependencies区别
- 参考文章
问题
docker-compose的yaml描述文件中对于依赖组件容器,我们常常会使用depends_on来申明,非常方便。比如下面这段:
service:
worker:
build: ./worker
image: xxxxxxxxxxxx
depends_on:
- redis
- postgres
那么到了kubernetes里面,容易习惯性的在脑袋中思考一个问题,我的依赖容器要怎么申明呢?或者更进一步说如何控制我容器的依赖启动顺序呢?
分析
确实,docker-compose作为"单点"编排工具的优势就是简单便捷啊,这一点毋庸置疑。Kubernetes作为功能超级强大的分布式容器编排工具,考虑的点和docker-compose不在同一个平面上。Kubernetes云原生的理念要求任何应用应该是“独立的”,应用自身是可以处理未连接或者重连接的情况,而不是交由Kuberntest集群层面来做,因为对于Kubernetes来说,Pod或者Container是随时会被调度/重起的,Kubernetes自身会检测所有的pod里的容器并确保它们活着并给他们贴上Healthy的标签,所以应该是尽量独立解耦的,不应该被绑定/关联太多才可以启动。
这段话你可以这么来理解,既然Pod你可以理解为“VM”,一台“virtual host”,那么你肯定不希望你的应用所在的“host”启动需要依赖于其它“host”的启动,而是你希望你的app所在的“host”——即Pod不论db状态如何,它都是可以“开机”的,app自身可以独立的去处理db的连接/重连问题。
方案
换句话说,在Kubernetes的体系中,并没有严格意义上和docker-compose的depends_on对等的"申明"语句。当然Kubernetes对于依赖容器也是可以透过一些特别的机制来实现类似的目的,不过可能需要修改app的逻辑。比如,我们可以在app的entrypoint或者initContainer当中去申明应用的启动逻辑,或者可以通过readiness探针来决定Kubernetes是否要启动Pod,同样也可以通过liveness探针来检测所依赖的服务是否健康。
initContainer
以initContainer为例,它是一个或多个先于应用容器启动的 Init 容器,每个都必须在下一个启动之前成功完成。默认的策略是若 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。下面例子是官方例子,定义了一个具有 2 个 Init 容器的简单 Pod。 第一个等待 myservice
启动,第二个等待 mydb
启动。 一旦这两个 Init容器 都启动完成,Pod 将启动spec
区域中的应用容器。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
注意initContainer里面的command,我们写了一段shell脚本一直循环运行,直到检测到myservice
和mydb
都启动完毕才退出,并启动myapp容器。
注意:
- 在 Pod 上使用
activeDeadlineSeconds
和在容器上使用livenessProbe
可以避免 Init 容器一直重复失败。activeDeadlineSeconds
时间包含了 Init 容器启动的时间;- initContainers不支持readiness probes因为它们必须在Pod ready之前完成运行。
其中的myservice和mydb的定义如下:
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
再来看个ELK的例子,也是写了一段shell脚本并通过初始化容器的方式来实现的。
spec:
initContainers:
- name: wait-for-grafana
image: darthcabs/tiny-tools:1
args:
- /bin/bash
- -c
- >
set -x;
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://grafana:3000/login)" != "200" ]]; do
echo '.'
sleep 15;
done
containers:
.
.
(your other containers)
.
.
延伸
docker-compose depends_on与Helm dependencies区别
Helm的requirements.yaml中也申明了Chart包的依赖,如下所示,那么这个dependencies和docker-compose的depends_on的区别在哪呢?
dependencies:
- name: mysql
version: 3.2.1
repository: http://another.example/charts
首先我们来看Helm dependencies怎么使用?
# 根据requirements.yaml配置,将依赖的应用包从仓库中拉取到charts目录,移除旧的
# 同时会生成requirements.lock
helm dependency update [flags] CHART
# 基于requirements.lock,重新构建charts中的应用
# 如果没有lock文档,类似update操作
helm dependency build [flags] CHART
# 展示应用依赖的所有子应用包
helm dependency list [flags] CHART
这里我们可以看到requirements.yaml是先转换为lock文件配置,然后Helm再基于lock文件去安装依赖服务,但是,这里需要明确的一点就是:Helm只是管理依赖并依据lock去创建pod,它并不会保证任何运行的顺序,以上面这个申明为例,Helm会创建2个pod,但它并不关心mysql pod可能要20s才完成启动而你的app容器只需要2s。
参考文章
- InitContainer官方文档
- How do we map depends_on in Kubernetes yamls ?
- What is the equivalent for depends_on in kubernetes