对分区分服滚服业务Helm如何管理,需要不同小区对应一个value?或是不同小区对应一个templates的yaml小区模板? 那样太复杂了! 另外对某些小区yaml个性化的编排配置如何管理呢?我告诉你答案!欢迎大家一起交流。
1.项目背景
目前容器化业务多为全区全服业务,管理上多为一个集群一套环境,从头部署完成后,不需要考虑区服复用deployment/statefulset等,大多一对一的场景(一个deployment/statefulset,对应一个模块或用应用),配置文件也可以部署时,确定好后,后期变更较少。然而,滚服业务存在着大量复用deployment/statefulset/configmap/service/ingress等等控制器yaml模版,实现的方式也有多种,这里具体说下一个chart对应一个value管理多区服业务。
2.Helm
2.1 Helm介绍
Helm是一个Kubernetes的包管理工具,就像Linux下的包管理器,如yum/apt等,可以很方便的将之前打包好的yaml文件部署到kubernetes上。
Helm有3个重要概念:
- **helm:**一个命令行客户端工具,主要用于Kubernetes应用chart的创建、打包、发布和管理。
- **Chart:**应用描述,一系列用于描述 k8s 资源相关文件的集合。
- **Release:**基于Chart的部署实体,一个 chart 被 Helm 运行后将会生成对应的一个 release;将在k8s中创建出真实运行的资源对象。
2.2 Helm安装
使用helm很简单,你只需要下载一个二进制客户端包即可,会通过kubeconfig配置(通常$HOME/.kube/config)来连接Kubernetes。
wget https://get.helm.sh/helm-v3.7.2-linux-amd64.tar.gz
tar zxvf helm-v3.7.2-linux-amd64.tar.gz
mv linux-amd64/helm /usr/bin/
3.制作一个Chart
Helm核心是模板,即模板化K8s YAML文件。部署多个应用时,将需要改动的字段进行模板化,可动态传入。
下面我们开始制作第一个Chart模拟创建多个区服,镜像使用nginx代替。
首先是value变量的配置(这里只放最基础的也是最常用的变量,如果需要自定义变量最好是通过配置文件挂载卷的方式挂载)
# values.yaml
# 测试用例
yqcr: #项目名
serverinfo: # 区服信息(这里只做游戏逻辑服最简单的示例,实际业务这一层还有其他全局模块等,可以灵活规划)
10001: # 区服id
cpu: 1
mem: 1Gi
images1: nginx
tag1: 1.21.4
replicas: 2
10002:
cpu: 2
mem: 2Gi
images1: nginx
tag1: 1.20.2
replicas: 1
10003:
cpu: 2
mem: 2Gi
10004:
cpu: 2
10005:
images1: nginx
tag1: 1.20.2
replicas: 1
# 默认配置信息(可以减少value行数,更加清晰,当使用默认配置时,只需要10001:即可)
Gamedefault:
cpu: 1
mem: 1Gi
images1: nginx
tag1: 1.20.2
replicas: 2
template实例:
# 默认值定义
{{ $default_res := .Values.Gamedefault.replicas }}
{{ $default_cpu := .Values.Gamedefault.cpu }}
{{ $default_mem := .Values.Gamedefault.mem }}
{{ $default_images1 := .Values.Gamedefault.images1 }}
{{ $default_tag1 := .Values.Gamedefault.tag1 }}
# 遍历区服map
{{- range $key, $value := .Values.yqcr.serverinfo }}
# 如果区服使用特殊配置
{{- if $value }}
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: chatservice-chat-{{ $key }}
name: chatservice-chat-{{ $key }}
namespace: jhxjy-tiyan
spec:
progressDeadlineSeconds: 600 # deploy升级的最大时间
# 进一步判断是否有这个key,如果有则使用
{{- if hasKey $value "replicas" }}
replicas: {{ $value.replicas }}
# 没有这个key,则使用默认值
{{- else }}
replicas: {{ $default_res }}
{{- end }}
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: chatservice-chat-{{ $key }}
strategy:
rollingUpdate: {maxSurge: 25%, maxUnavailable: 25%}
type: RollingUpdate
template:
metadata:
labels:
k8s-app: chatservice-chat-{{ $key }}
spec:
containers:
# 模拟某些程序启动时需要注入serverid或者类似的变量
- args: ["bash","-c","echo {{ $key }} && sleep 600000" ]
env:
- {name: EXP_DATA_DOMAIN, value: xxx}
{{- if and (hasKey $value "images1") (hasKey $value "tag1") }}
image: '{{ $value.images1 }}:{{ $value.tag1 }}'
{{- else }}
image: '{{ $default_images1 }}:{{ $default_tag1 }}'
{{- end }}
imagePullPolicy: Always
name: chatservice-chat-{{ $key }}
ports:
- containerPort: 80
protocol: TCP
# 如果一个container有多个对外的端口可以通过计算来得出,只是示例用法,具体其实这一层的端口完全可以固定,在ingress那里需要通过serverid来动态计算
- containerPort: {{ add 2 (mul $key 2) }}
protocol: TCP
resources:
limits:
{{- if hasKey $value "cpu" }}
cpu: {{ $value.cpu | quote }}
{{- else }}
cpu: {{ $default_cpu | quote }}
{{- end }}
{{- if hasKey $value "mem" }}
memory: {{ $value.mem }}}
{{- else }}
memory: {{ $default_mem }}
{{- end }}
requests:
{{- if hasKey $value "cpu" }}
cpu: {{ $value.cpu | quote }}
{{- else }}
cpu: {{ $default_cpu | quote }}
{{- end }}
{{- if hasKey $value "mem" }}
memory: {{ $value.mem }}}
{{- else }}
memory: {{ $default_mem }}
{{- end }}
securityContext: {privileged: false}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- {mountPath: /data/config, name: config}
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
# 配置精准挂载(如:10001/game/gameserver)
- hostPath: {path: "/data/config/{{ $key }}/chatservice-chat", type: ''}
name: config
---
apiVersion: v1
kind: Service
metadata:
name: chatservice-chat-{{ $key }}
namespace: jhxjy-tiyan
spec:
ports:
- name: "80"
port: {{ $key }}
protocol: TCP
targetPort: 80
selector:
k8s-app: chatservice-chat-{{ $key }}
sessionAffinity: None
type: ClusterIP
---
# 如果使用默认配置
{{- else }}
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: chatservice-chat-{{ $key }}
name: chatservice-chat-{{ $key }}
namespace: jhxjy-tiyan
spec:
progressDeadlineSeconds: 600
replicas: {{ $default_res }}
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: chatservice-chat-{{ $key }}
strategy:
rollingUpdate: {maxSurge: 25%, maxUnavailable: 25%}
type: RollingUpdate
template:
metadata:
labels:
k8s-app: chatservice-chat-{{ $key }}
spec:
containers:
- args: ["bash","-c","echo {{ $key }} && sleep 600000" ]
env:
- {name: EXP_DATA_DOMAIN, value: xxx}
image: '{{ $default_images1 }}:{{ $default_tag1 }}'
imagePullPolicy: Always
name: chatservice-chat-{{ $key }}
ports:
- containerPort: 80
protocol: TCP
resources:
limits:
cpu: {{ $default_cpu | quote }}
memory: {{ $default_mem }}
requests:
cpu: {{ $default_cpu | quote }}
memory: {{ $default_mem }}
securityContext: {privileged: false}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- {mountPath: /data/config, name: config}
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- hostPath: {path: "/data/config/{{ $key }}/chatservice-chat", type: ''}
name: config
---
apiVersion: v1
kind: Service
metadata:
name: chatservice-chat-{{ $key }}
namespace: jhxjy-tiyan
spec:
ports:
- name: "80"
port: {{ $key }}
protocol: TCP
targetPort: 80
selector:
k8s-app: chatservice-chat-{{ $key }}
sessionAffinity: None
type: ClusterIP
---
{{- end }}
{{- end }}
3.1 Yaml调试渲染
yaml 文件写好就该是同步和部署了,在部署前可以先用以下命令测试下,渲染不执行:
helm install releasename chart目录 --dry-run --debug
#部署
helm install deploy20211214 test -n namespace
#查看release
helm ls -n namespace
#卸载
helm uninstall deploy20211214 -n namespace
4.总结
通过这种方式,覆盖了游戏版本更新,硬件配置升级,配置精准挂载等通用化操作,都只需要维护一个value,并且拓补清晰,降低了运维维护成本,同时又通用高效