helm是k8s的一个包管理工具,可以简化k8s应用的部署和管理,可以理解为yum和或者apt等包管理工具。 helm有几个非常重要的概念
helm create myapp
# tree myapp/
myapp/
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
可以找一台机器用来作为chart的存储仓库,类似于harbor一样,当然helm也可以使用helm作为仓库来存储chart,但这里还是通过将chart存储到本地目录里,然后通过web把改目录映射出去即可
helm serve --address 192.168.0.130:8879 --repo-path /data/helm/repository/ --url http://192.168.0.130:8879/charts/ &
helm repo index /data/helm/repository --url http://192.168.0.130:8879
–repo-path 指定存储chart的目录 –address 指定仓库地址 这样我们就把仓库创建好了,下面会涉及到打包应用以及将应用放到仓库里的操作。
在本地打包应用之前需要先初始化一下,否则会打包失败报错
Error: open /home/jenkins/.helm/repository/local/index.yaml: no such file or directory
初始化操作
helm init --client-only
前面创建了一个myapp的应用,现在需要将他打包
# 需要进到myapp的同级目录
helm package myapp
打包完成会生成一个tar包,将此tar包拷贝到helm仓库中
cp myapp-0.1.0.tgz /data/helm/repository/
拷贝完成后可以通过访问192.168.0.130:8879
看到我们的myapp这个应用包
添加远程仓库
在k8s的目标主机上执行此命令来完成远程仓库的添加
helm repo add cicd http://192.168.0.130:8879/charts
添加完成后执行下update
helm repo update
安装一个release
helm install -f values.yaml test-helm cicd/myapp
test-helm即为release的名称
删除release
helm delete --purge test-helm
helm内置了一些对象,这些对象可以从模板引擎传递到模板中,这样我们在使用的时候就可以通过传入不通的参数来完成多个应用的部署操作了 下面介绍两个常用的对象
内置对象一般首字母大写,Release对象描述了release本身,Release对象中又包含了几个子对象:
Release
Release.Name release的名称
Release.Time release的时间
Release.Namespace release的namespace
Release.Revision 此release的修订版本号,从1开始,每helm upgrade一次,就会增加1
Values对象是从values.yaml文件中读取或者命令行传入的值里传入模板中,而Values中我们可以自定义一些类似于变量的东西,类似于下面的示例:
replicaCount: 1
image:
repository: harbor.devilf.cc
tag: latest
cat deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicas}}
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
name: {{ .Release.Name }}
template:
metadata:
labels:
name: {{ .Release.Name }}
spec:
containers:
- name: {{ .Release.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: IfNotPresent
知道这两个基本上就足够用了,Values这个对象非常的常用和灵活,可以从上面的deployment类型的资源文件里看到,如同变量替换一样方便,我们以后部署的时候再也不会通过使用sed或者其他操作来完成镜像地址的替换了!
通常在k8s里部署一个pod时,需要一个deployment类型的文件,一个service类型的文件,有时还需要ingress类型的文件,如果只有几个应用的话我们可以通过脚本或者手动的方式去写yaml文件来完成部署操作,但如果项目非常多,这个时候对于yaml文件的管理将会非常不便!!! 下面以一个java程序为例,通过制作一个deployment类型的模板和service类型的模板,来完成该项目的部署
cat myapp/templates/app-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicas}}
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
name: {{ .Release.Name }}
template:
metadata:
labels:
name: {{ .Release.Name }}
spec:
containers:
- name: {{ .Release.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: {{ .Values.resources.limits.cpu }}
memory: {{ .Values.resources.limits.memory }}
requests:
cpu: {{ .Values.resources.requests.cpu }}
memory: {{ .Values.resources.requests.memory }}
{{ if .Values.volumeMounts.name }}
volumeMounts:
- name: {{ .Values.volumeMounts.name }}
mountPath: {{ quote .Values.volumeMounts.path }}
readOnly: false
subPath: {{ .Values.volumeMounts.subpath }}
{{ end }}
{{ if .Values.imageOther.repository }}
- name: {{ .Release.Name }}-other
image: "{{ .Values.imageOther.repository }}:{{ .Values.imageOther.tag }}"
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: {{ .Values.resources.limits.cpu }}
memory: {{ .Values.resources.limits.memory }}
requests:
cpu: {{ .Values.resources.requests.cpu }}
memory: {{ .Values.resources.requests.memory }}
{{ end }}
{{ if .Values.livenessProbe.path }}
livenessProbe:
httpGet:
path: {{ .Values.livenessProbe.path }}
port: {{ .Values.livenessProbe.port }}
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 60
{{ end }}
{{ if .Values.volumeMounts.name }}
volumes:
- name: {{ .Values.volumeMounts.name }}
persistentVolumeClaim:
claimName: {{ .Values.volumeMounts.name }}
{{ end }}
imagePullSecrets:
- name: {{ .Values.imagePullSecrets }}
cat myapp/templates/app-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
labels:
name: {{ .Release.Name }}
spec:
ports:
- name: {{ .Release.Name }}
protocol: TCP
targetPort: {{ .Values.containerPort.inner }}
port: {{ .Values.containerPort.inner }}
nodePort: {{ .Values.containerPort.outer }}
{{ if .Values.containerPortOther.inner }}
- name: {{ .Release.Name }}-other
protocol: TCP
targetPort: {{ .Values.containerPortOther.inner }}
port: {{ .Values.containerPortOther.inner }}
{{ if .Values.containerPortOther.outer }}
nodePort: {{ .Values.containerPortOther.outer }}
{{ end }}
{{ end }}
selector:
name: {{ .Release.Name }}
sessionAffinity: None
type: NodePort
以上用到了一些流程控制,有时候可能一个pod会启动两个容器,或者映射多个端口,这时候我们可以使用简单的if
进行流程控制
然后修改values.yaml文件,来满足我们模板中定义的一些对象
cat myapp/values.yaml
replicaCount: 1
image:
repository:
tag:
imageOther:
repository:
tag:
containerPort:
inner:
outer:
containerPortOther:
innerPort:
outerPort:
imagePullSecrets: mima
resources:
limits:
cpu: 400m
memory: 4096Mi
requests:
cpu: 100m
memory: 128Mi
volumeMounts:
name:
path:
subpath:
livenessProbe:
path:
port:
chart.yaml参考
cat myapp/Chart.yaml
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: myapp
version: 0.1.0
整体的一个目录结构如下
# tree myapp
myapp
├── Chart.yaml
├── templates
│ ├── app-deployment.yaml
│ ├── app-svc.yaml
│ └── _helpers.tpl
└── values.yaml
可以把多余的一些文件或者目录删除掉
进入到myapp的同级目录执行
helm package myapp
cp myapp-0.1.0.tgz /data/helm/repository/
添加远程仓库地址
helm repo add cicd http://192.168.0.130:8879/charts
helm repo update
配置values.yaml文件,也可以通过--set
进行指定
我们先测试指定一个镜像地址
cat values.yaml
replicaCount: 1
image:
repository: harbor.devilf.cc/onair/user-manager
tag: 202002101644-2.0.0-SNAPSHOT-1ed8d49
imageOther:
repository:
tag:
containerPort:
inner: 9999
outer: 32222
containerPortOther:
innerPort:
outerPort:
imagePullSecrets: mima
resources:
limits:
cpu: 400m
memory: 4096Mi
requests:
cpu: 100m
memory: 128Mi
volumeMounts:
name:
path:
subpath:
livenessProbe:
path:
port:
安装应用
# helm upgrade -i -f values.yaml test-helm cicd/myapp
Release "test-helm" does not exist. Installing it now.
NAME: test-helm
LAST DEPLOYED: Thu Feb 13 15:59:12 2020
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Pod(related)
NAME AGE
test-helm-7c8b8584cd-w2vk4 0s
==> v1/Service
NAME AGE
test-helm 0s
==> v1beta1/Deployment
NAME AGE
test-helm 0s
下面再测试下两个镜像地址
# cat values.yaml
replicaCount: 1
image:
repository: alpha-harbor.yunshicloud.com/onair/user-manager
tag: 202002101644-2.0.0-SNAPSHOT-1ed8d49
imageOther:
repository: alpha-harbor.yunshicloud.com/onair/user-api
tag: 202002110943-2.0.0-SNAPSHOT-5c011f4
containerPort:
inner: 9999
outer: 32222
containerPortOther:
innerPort: 8088
outerPort: 32223
imagePullSecrets: mima
resources:
limits:
cpu: 400m
memory: 4096Mi
requests:
cpu: 100m
memory: 128Mi
volumeMounts:
name:
path:
subpath:
livenessProbe:
path:
port:
安装方式是一样的,这里先把之前的给删除掉
# helm delete --purge test-helm
删除完成后,添加一个镜像地址,然后再次运行
# helm upgrade -i -f values.yaml test-helm cicd/myapp
这里我请求另一个服务的健康检查接口
也是没有问题的
到这里,基本上helm就可以结合Jenkins完成CD工作了,只需要创建一个values.yaml文件即可。