K8S实战技巧(一)
一. 概述
K8S是谷歌开源的一个容器编排管理工具,可以帮助业务实现自动化部署、故障发现、容灾、扩缩容、流量管理等,大大提升业务的服务能力;k8s通过yaml描述文件实现容器的部署,本文介绍一些实际应用过程中可能用到一些k8s的特性及配置方法,适用于对k8s有一定了解的读者。
二. 共享进程命名空间
K8S以POD为基本的调度单位,通常一个POD中运行着互相配合的几个容器;设想这样一个场景:在原先架构中,一个对外服务通常包含以下几个进程:业务进程(主要的业务处理逻辑)、配置代理进程(负责实时和配置中心通信,拉取最新的配置,并通知业务进程读取配置)、网络进程(负责接管业务进程对外通信链接);当部署到K8S之后,就是这样一个业务POD,包含业务进程容器,配置代理进程容器和网络进程容器;这样就有一个问题,即配置代理进程拉取到配置之后,如何通知业务进程读取最新配置呢?一个方案是:发消息通知业务进程;这里介绍另外一个方案,即业务进程注册信号处理函数,代理进程发信号给业务进程,业务进程收到信号之后重载配置。
那么问题来了,业务进程和配置代理进程运行在不同的容器中,怎么让配置代理进程给业务进程发信号呢?其中一个办法是把两个进程放在一个容器中,这样确实可以解决问题,但是由于二者在一个容器中,以后配置代理进程更新会和业务进程的服务耦合在一起,这也不符合配置代理进程的设计初衷;所幸K8S提供了一种POD中运行的多个容器共享进程中间的机制,配置方式如下:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: shareProcessNamespace: true containers: - name: nginx image: nginx:1.14.2 containers: - name: config-agent image: config-agent:lastest
1)运行在配置容器中的进程可以看到业务容器的进程,反之亦然,即一个POD中的多个容器内运行的进程在一个进程空间之下
2)在配置代理容器中,给业务进程容器的业务进程发信号,业务进程容器中的业务进程可以收到
3)当配置代理容器中的镜像需要更新时,直接原地更新即可,不会对业务进程所在的容器有影响
共享卷
同样还是上述的场景,由于容器之间文件系统是隔离的,所以默认情况下config-agent容器里面的文件nginx容器里面的进程是看不到的,那么怎么可以让nginx容器里面的进程可以读到config-agent容器里面的文件呢?K8S提供了卷(volume)的机制,解决这种Pod上多个容器共享文件的场景。K8S支持多种共享卷机制,这里介绍HostPath类共享卷的用法:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: shareProcessNamespace: true containers: - name: nginx image: nginx:1.14.2 volumeMounts: - mountPath: /home/walker/nginx/conf name: global-config containers: - name: config-agent image: config-agent:lastest volumeMounts: - mountPath: /home/walker/config name: global-config volumes: - name: config hostPath: path: /home/walker type: DirectoryOrCreate
所谓hostpath是指我们在Pod所在node上创建一个目录,这个目录作为我们多个容器的指定目录的挂载点,在本例中我们有:
1)在.spec.volumnes下创建了一个挂载目录,名字叫config,路径为node的/home/walker目录下,类型为hostpath,表示是主机的路径,type为DirectoryOrCreate,表示若是目录不存在的化,会自动创建一个新的目录;
2)nginx容器的配置.spec.containers.volumeMounts表示nginx容器里面的目录/home/walker/nginx/conf,会挂载到node节点的路径下/home/walker下,此后任何主机上/home/walker目录下的修改都会被nginx容器里面的进程通过/home/walker/nginx/conf读取到,反之任何nginx容器内/home/walker/nginx/conf目录的修改,都会被同样挂载到这个目录的进程所感知到。
3)需要注意的是,一旦设置了挂载点,如本例nginx的/home/walker/nginx/conf挂载到node的/home/walker路径下,在容器启动之初,容器的/home/walker/nginx/conf会被清空,即若是在打镜像的时候,这个路径下有其他文件因为设置共享卷的缘故,原镜像打进去的东西会被清空掉。
Chart部署
上述主要介绍了生产环境下Pod内多个容器如何共享文件和进程命名空间,但是一般情况下,我们技术人员在发布之前会有一个私有的测试环境进行验证,按照上述方式进行部署私有测试环境的话,会有一个问题就是挂载路径相同,导致不同人的私有测试环境互相覆盖;这种不仅限于挂载点,还有端口、环境变量等一些列的差异化;当然,我们可以每个研发人员将yaml文件拉取下来,各自修改,但是这种方式麻烦且容易出错。因此我们希望有一种机制,即建立一个yaml文件模板将需要定制化的参数变量化,在实际部署的时候,根据我们提供的变量将这个模板渲染成我们需要的yaml文件——Helm和Chart帮助我们实现了这一个目标。
helm是K8S的包管理工具,而Chart,是Helm的应用打包格式,由一系列文件组成,这些文件描述了K8S部署应用所需的资源,chart的目录结构及对应文件作用如下:
nginx/ Chart.yaml # 包含当前 chart 信息的 YAML 文件 LICENSE # 可选:包含 chart 的 license 的文本文件 README.md # 可选:一个可读性高的 README 文件 values.yaml # 当前 chart 的默认配置 values values.schema.json # 可选: 一个作用在 values.yaml 文件上的 JSON 模式 charts/ # 包含该 chart 依赖的所有 chart 的目录 crds/ # Custom Resource Definitions templates/ # 模板目录,与 values 结合使用时,将渲染生成 Kubernetes 资源清单文件 templates/NOTES.txt # 可选: 包含简短使用使用的文本文件