在ACK集群中配置工作负载(Deployment、StatefulSet、DaemonSet、Job、CronJob等)时,您需考虑多种因素,以确保应用可以稳定、可靠地运行。
声明每个Pod的resource(Request和Limit)
在使用Kubernetes集群时,经常会遇到在一个节点上调度了太多的Pod,导致节点负载过高,无法正常对外提供服务的问题。
为避免上述问题,在Kubernetes集群中部署Pod时,您可以指定该Pod所需的Request及Limit资源,Kubernetes在部署这个Pod的时候,就会根据Pod的需求找一个具有充足空闲资源的节点部署这个Pod。下面的例子中,声明Nginx这个Pod需要1核CPU,1024MB的内存,运行中实际使用不能超过2核CPU和4096MB内存。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
resources: # 资源声明
requests:
memory: "1024Mi"
cpu: "1000m"
limits:
memory: "4096Mi"
cpu: "2000m"
Kubernetes采用静态资源调度方式,对于每个节点上的剩余资源的计算为节点剩余资源=节点总资源-已经分配出去的资源
,并不是实际使用的资源。如果您自己手动运行一个资源消耗较大的程序,Kubernetes并不能感知到。
此外,所有Pod都要声明resources
。没有声明resources
的Pod被调度到某个节点后,Kubernetes不会在对应节点上减去这个Pod使用的资源,继而可能导致该节点上被调度过多Pod。
您可以使用ACK提供的资源画像功能,基于资源使用量的历史数据获得容器粒度的资源规格推荐,简化为容器配置Request和Limit的复杂度。更多信息,请参见资源画像。
启动时等待下游服务,不要直接退出
有些应用可能会有一些外部依赖,例如需要从数据库(DB)读取数据或者依赖另外一个服务的接口。应用启动时,外部依赖未必都能满足。手动运维时,通常采用依赖不满足立即退出的方式,也就是所谓的failfast,但在Kubernetes中,这种策略不再适用。这是因为Kubernetes中多数运维操作都是自动操作,无需人工介入。例如,部署应用时,无需自己选择节点,再到节点上启动应用;应用fail时,也无需手动重启,Kubernetes会自动重启应用;负载增高时,还可以通过HPA自动扩容。
启动时依赖不满足的场景下,可以假设有两个应用A和B,A依赖B且运行在同一个节点上,此节点因为某些原因重启,重启后,A首先启动,B还没启动,对于A来说就是依赖不满足。如果A还是按照传统的方式直接退出,当B启动之后,A也不会再启动,必须人工介入处理。
在Kubernetes集群中,推荐您启动时检查依赖,如果不满足,则进行轮询等待,而不是直接退出。可以通过 Init Container完成这个功能。
配置restart policy
Pod运行过程中进程退出十分常见。代码中存在Bug、占用内存太多等,都会导致应用进程退出,继而导致Pod退出。您可在Pod上配置restartPolicy,实现Pod退出后自动启动。
apiVersion: v1
kind: Pod
metadata:
name: tomcat
spec:
containers:
- name: tomcat
image: tomcat
restartPolicy: OnFailure
restartPolicy有三个可选值:
Always
:总是自动重启。OnFailure
:异常退出时自动重启(进程退出状态非0)。Never
:从不重启。
配置Liveness Probe和Readiness Probe
Pod处于Running状态和Pod能正常提供服务是完全不同的概念。一个Running状态的Pod,里面的进程可能发生了死锁而无法提供服务。但是因为Pod还是Running的,Kubernetes也不会自动重启这个Pod。所以我们要在所有Pod上配置Liveness Probe,探测Pod是否真的存活,是否还能提供服务。如果Liveness Probe发现了问题,Kubernetes会重启Pod。
Readiness Probe用于探测Pod是否可以对外提供服务。应用启动过程中需要一些时间完成初始化,在这个过程中是无法对外提供服务的,通过Readiness Probe,可以告诉Ingress或者Service是否可以将流量转发到这个Pod上。当Pod出现问题时,Readiness Probe可以避免新流量继续转发到这个Pod。
apiVersion: v1
kind: Pod
metadata:
name: tomcat
spec:
containers:
- name: tomcat
image: tomcat
livenessProbe:
httpGet:
path: /index.jsp
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /index.jsp
port: 8080
每个进程一个容器
很多刚刚接触容器的人喜欢按照旧习惯把容器当作虚拟机(VM)使用,在一个容器里放置多个进程:监控进程、日志进程、sshd进程,甚至整个Systemd。这样操作存在两个问题:
判断Pod整体的资源占用会变复杂,不方便实施Pod Resource涉及到的Request和Limit。
容器内只有一个进程的情况下,如果进程中断,外部的容器引擎可以清楚地感知到,并且重启容器。如果容器内有多个进程,某个进程挂了,容器未必受影响,外部的容器引擎无法感知到容器内有进程退出,也不会对容器做任何操作,但是实际上容器已经不能正常工作了。
如果有几个进程需要协同工作,在Kubernetes里也可以实现,例如:nginx和php-fpm,通过Unix domain socket通信,我们可以用一个包含两个容器的Pod,将Unix socket放在两个容器的共享Volume中。
确保不存在SPOF(Single Point of Failure)
如果应用只有一个实例,当实例失败的时候,虽然Kubernetes能够重启实例,但是中间不可避免地存在一段时间的不可用。甚至更新应用,发布一个新版本的时候,也会出现这种情况。在Kubernetes里,尽量避免直接使用Pod,尽可能使用Deployment/StatefulSet,并且让应用的Pod在两个以上。
相关文档
ACK可实现应用灰度发布、蓝绿发布等,请参见应用部署。
关于应用管理的最佳实践,请参见应用管理最佳实践。