

文/江晓宇
前言
从本篇起公众号开启了Kubernetes文章系列,在这个系列中,我们会为大家分享云计算技术栈的一些“干货”。
Kubernetes是Google公司开源的容器云平台,在Docker技术的基础上,为应用提供容器跨多个服务器主机的容器部署和管理、服务发现、负载均衡和动态伸缩等一系列完整功能,可方便地进行大规模容器集群管理。因Kubernetes的首尾K和S之间共有8个字母,因此也被称为K8S。


从logo上看,Docker是一头鲸鱼,背上驮着一层层的集装箱,鲸鱼代表底层操作系统,集装箱代表容器镜像底层操作系统上的多层镜像。
Kubernetes一词来源于希腊语κυβερνήτης:,本意是“舵手”,而Kubernetes的logo正是一把船舵,代表着一名优秀的领航员,引领着船队前进。
Deployment是什么?
Deployment是Kubernetes中的一种副本控制器,用来部署应用。在Kubernetes容器云平台中,应用运行在容器内部,但Kubernetes能够管理的最小单元并不是容器而是pod。pod原意是豌豆荚,可以把容器想象成一个个的豌豆,而pod里面可以运行一个或者多个容器,因此pod被称为容器组,也被称为副本。
一个副本中可以运行一个容器,或者多个相互之间有关联的容器。如下图所示,一个pod中运行了两个容器,左边的容器中运行文件服务程序,右边容器中运行应用程序。这种模式利用到了docker的优势,运行在同一pod中的不同容器,共享存储与主机网络栈。

只运行一个pod无法让应用实现高可用,如希望对pod进行横向扩展,则需要用到副本控制器Deployment。我们可以认为应用运行在容器中,也可以认为应用运行在pod中,但应用并不运行在Deployment中,从运行实体的角度来看,Deployment只是逻辑上的概念。
Deployment能做什么
除了横向扩展外,Deployment能做到的事情还有很多,下面用一个实际的Deployment资源的yaml编排文件,来讲解Deployment副本控制器的主要功能。
apiVersion: apps/v1kind: Deploymentmetadata:name: deployment-examplenamespace: hetospec:replicas: 4selector:matchLabels:app: deployment-exampletemplate:metadata:labels:app: deployment-examplespec:containers:- name: java-agentimage: harbor.xyz/library/java-agent:v1.0imagePullPolicy: AlwaysvolumeMounts:- name: properties-volumemountPath: /deployments/application.propertiessubPath: application.propertiesenv:- name: TZvalue: Asia/Shanghaiports:- containerPort: 8080livenessProbe:tcpSocket:port: 8080readinessProbe:httpGet:path: /helthCheckport: 8080scheme: HTTPinitialDelaySeconds: 40periodSeconds: 15timeoutSeconds: 10resources:limits:cpu: 1memory: 2Gi- name: mysqlimage: example.com/library/mysql:v1.0resources:limits:cpu: 4memory: 8Gienv:- name: TZvalue: Asia/ShanghaiimagePullPolicy: Alwaysports:- containerPort: 8360livenessProbe:exec:command: [“test”, “-e”, “/opt/healthy”]volumes:- name: properties-volumeconfigMap:name: application-properties-configmap
通过标签匹配副本
Deployment管理pod的方式是通过标签来进行匹配,标签是以键值对的形式出现的。在spec.template.metadata.labels字段中可以定义pod标签,通过spec.selector.matchLabels字段进行匹配,从而实现对pod的管理。
spec:replicas: 4selector:matchLabels:app: deployment-exampletemplate:metadata:labels:app: deployment-example
在本例中,需要被管理的pod的标签为app: deployment-example,编排文件通过匹配所有标签为app: deployment-example的pod,实现对pod的批量管理。
扩容与缩容
可以通过spec.replicas字段定义应用运行时期望得到的pod数量,当pod数量实际值大于期望值时,会通过删减pod的方式达到期望值,同理,当pod数量实际值小于期望值时,会通过新建pod的方式来满足期望值。
spec:replicas: 4
通过删除pod来达到停止应用服务的做法是错误的,如果应用已经正常启动,当前的pod个数为4,删除一个pod后,pod实际值为3,小于期望值4,这时k8s会新增一个pod来达到预期。因此会发现pod永远无法删除干净,永远在重启。
正确的让应用停止服务的方式,是保留Deployment资源,而将spec.replicas值设为0,这种方式会通知k8s保持该应用的pod数保持为0,应用不再提供服务,如想重新启动应用,将spec.replicas值设置为期望pod数量即可。
镜像获取策略
通过imagePullPolicy字段设置容器的镜像拉取策略。
imagePullPolicy: Always
共有三种镜像拉取策略:
1.Always: 总是从仓库中获取镜像;
2.IfNotPresent: 仅当本地镜像不存在时,才从镜像仓库中获取镜像;
3.Never: 禁止从镜像仓库中下载镜像,仅使用本地镜像。
通常情况下,推荐使用Always或者IfNotPresent策略。
暴露端口
通过docker run的方式运行一个镜像,如想从宿主机上访问该容器提供的服务,需要通过 -p 参数,通过NAT机制将服务端口暴露出来,如运行下面镜像,将容器的8080端口映射至宿主机的80端口:
docker run -d -ti nginx:1.14.2 -p 80:8080 /bin/bash
Kubernetes中,不同pod的IP地址处在同一网络平面,不同容器之间可以访问对方端口提供的服务,可以通过containerPort字段显式指定容器端口,默认提供TCP服务。
ports:- containerPort: 80
如应用需暴露多个端口,也可在ports中指定多组containerPort。值得注意的是,同一pod中的容器共享主机网络栈,因此同一pod中的不同容器不允许暴露相同端口。
环境变量
通过设置env字段,可将环境变量以键值对的形式注入至容器中,如时区、jvm启动参数、日志级别、编码格式等。
env:- name: TZvalue: Asia/Shanghai- name: LOG_LEVELvalue: info
Docker容器的默认时区与东八区(北京时间)相差8小时,如希望显示北京时间,建议在容器中配置示例中提供的时区参数。
节点选择器
Pod运行在集群工作节点宿主机上,宿主机可以是一台物理机或者是一台虚拟机。Kubernetes的kube-scheduler守护进程负责在各个工作节点中基于系统资源的可用性等指标挑选一个来运行待创建的pod对象,因此用户无需关心pod的具体运行位置。
如需为不同宿主机分组,从而运行不同类型的应用,则可对宿主机打标签,再通过配置pod中的nodeSelector字段,指定某应用的pod只能运行在拥有相应标签的宿主机中。
如集群中存在三台工作节点node01~node03,各自的标签如下所示:
| 节点名称 | 标签 | |
node01 | app=web | disktype=ssd |
node02 | app=web | disktype=nas |
node03 | app=ios | disktype=ssd |
如希望应用仅运行在node01节点上,则需添加nodeSelector字段,指定应用的节点标签选择器,指定为node01上的两个标签app=web、disktype=ssd。
nodeSelector:app=webDisktype=ssd
容器重启策略
容器内程序如发生崩溃,会导致pod状态异常,此时是否应该重建pod,取决于重启策略restartPolicy的定义:
1.Always:pod终止就将其重启,默认设定;
2.OnFailure:仅在pod出现错误时才重启;
3.Never:从不重启。
示例中没有定义restartPolicy属性,因此默认使用Always。
探测机制
在容器中可以定义以下两种探针:
1.存活性探针livenessProbe:判定容器是否处于running状态,一旦此类检查未通过,Kubernetes将杀死容器并根据pod的重启策略(restartPolicy)决定是否将其重启。
2.就绪性探针ReadinessProbe:用于判断容器是否准备就绪并可对外提供服务;未通过检测的容器意味着其尚未准备就绪,Kubernetes会将该pod从路由表中摘除,检测通过后,会将其再次添加至路由表中。
示例deployment-example所管理的pod中有java-agent和mysql两个容器,对两个容器分别配置了探针。
java-agent容器中通过livenessProbe探针设置了TCPSocketAction探测,对容器内的8080端口进行TCP连接测试;通过readinessProbe探针设置了HTTPGetAction探测,对容器的8080端口的/healthCheck地址进行周期性的HTTP请求。
readinessProbe:initialDelaySeconds: 40timeoutSeconds: 10periodSeconds: 15httpGet:path: /helthCheckport: 8080scheme: HTTP
Mysql容器通过livenessProbe探针设置了ExecAction探测,对容器内的/opt/healthy文件进行存在性验证,若文件存在则返回状态码0,表示通过探测。
livenessProbe:exec:command: [“test”, “-e”, “/opt/healthy”]
资源限制
为容器分配的最大的计算资源,包括CPU资源和内存资源。通过容器内的resources.limits属性进行设置。本例中,java-agent容器的资源限制为1C2G,mysql容器的资源限制为4C8G。
挂载配置文件
传统应用的配置文件存在于应用包中,如springboot应用的配置application.properties文件通常存在于应用包中的,对于一个指定版本的springboot应用镜像来说,在应用镜像不变的情况下仅修改应用中的配置文件是很方便的。
Kubernetes支持将配置文件以configMap的形式外置,做到程序与配置分离。
在本例中,在pod中定义一个名为volume卷,卷的实际内容是名为application-cm的configMap资源。
volumes:- name: properties-volumeconfigMap:name: application-cm
在java-agent容器中,将定义的的properties-volume卷挂载到容器中,将application.properties文件挂载到了容器的/deployments目录下,实现了容器程序与配置的分离。
volumeMounts:- name: properties-volumemountPath: /deployments/application.propertiessubPath: application.properties
回滚应用
如需将应用回退至上一个版本,可使用rollout undo命令:
kubectl rollout undo deployments deployment-example
其他类型的副本控制器
除Deployment外,Kubernetes还提供了其他类型的副本控制器:
1.ReplicationController:早期的副本控制器,现已被Deployment取代。
2.StatefulSet:pod资源管理器的一种实现,用来管理有状态副本集。
3.DaemonSet:在集群的全部工作节点上同时运行一份指定的pod副本,使用场景例如在各个节点上运行监控系统的代理守护进程Prometheus Node Exporter等。
4.Job:用于调配pod运行一次性任务,任务执行完毕后pod会变为Completed状态。
5.CornJob:不同于运行一次性任务的Job,CornJob可以以类似于Linux操作系统的周期性任务作业计划corntab的方式控制其运行的时间点及重复运行的方式。


顾问:许国平 李湘宜
赵晓玲 张刚
总编:孙鹏晖
编辑:江晓宇
美编:戴路

长按二维码,关注我们吧!

-本文为“数风云”第5期文章;
-转载本公众号文章请联系我们;
-欢迎来稿:请按“题目-作者”格式命名发送到sunpenghui@abchina.com。





