Helm 和 命名空间
# Helm 和 命名空间
# 命名空间
如果一个集群中部署了多个应用,所有应用都在一起,就不太好管理,也可以导致名字冲突等。
我们可以使用 namespace 把应用划分到不同的命名空间,跟代码里的 namespace 是一个概念,只是为了划分空间。
Namespace 在很多情况下用于实现多用户的资源隔离,通过将集群内部的资源对象分配到不同的 Namespace 中, 形成逻辑上的分组,便于不同的分组在共享使用整个集群的资源同时还能被分别管理。
Kubernetes 集群在启动后,会创建一个名 default 的 Namespace,如果不特别指明 Namespace, 则用户创建的 Pod,RC,Service 都将 被系统 创建到这个默认的名为 default 的 Namespace 中。
# 创建命名空间
kubectl create namespace testapp
# 部署应用到指定的命名空间
kubectl apply -f app.yml --namespace testapp
# 查询
kubectl get pod --namespace kube-system
2
3
4
5
6
也可以在 yaml 中时指定 namespace
apiVersion: v1
kind: Namespace
metadata:
name: development
---
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kubectl apply -f app2.yml
kubectl get pods --namespace=development
2
可以用 kubens (opens new window) 快速切换 namespace
# 切换命名空间
kubens kube-system
# 回到上个命名空间
kubens -
# 切换集群
kubectx minikube
2
3
4
5
6
# Helm
Helm 类似 npm,pip,docker hub,Helm 是一个 Kubernetes 的包管理工具,可以理解为是一个软件库,可以方便快速的为我们的集群安装一些第三方软件,可以很方便的将之前打包好的 yaml 文件部署到 kubernetes 上。
使用 Helm 我们可以非常方便的就搭建出来 MongoDB / MySQL 副本集群,YAML 文件别人都给我们写好了,直接使用。官网 (opens new window),应用中心 (opens new window)
Helm 有 3 个重要概念:
- helm:一个命令行客户端工具,主要用于 Kubernetes 应用 chart 的创建、打包、发布和管理。
- Chart:应用描述,一系列用于描述 k8s 资源相关文件的集合。
- Release:基于 Chart 的部署实体,一个 chart 被 Helm 运行后将会生成对应的一个 release;将在 k8s 中创建出真实运行的资源对象。
# 安装 Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
heml 常用命令
| 命令 | 描述 |
|---|---|
| create | 创建一个 chart 并指定名字 |
| dependency | 管理 chart 依赖 |
| get | 下载一个 release。可用子命令:all、hooks、manifest、notes、values |
| history | 获取 release 历史 |
| install | 安装一个 chart |
| list | 列出 release |
| package | 将 chart 目录打包到 chart 存档文件中 |
| pull | 从远程仓库中下载 chart 并解压到本地 helm pull stable/mysql --untar |
| repo | 添加,列出,移除,更新和索引 chart 仓库。可用子命令:add、index、list、remove、update |
| rollback | 从之前版本回滚 |
| search | 根据关键字搜索 chart。可用子命令:hub、repo |
| show | 查看 chart 详细信息。可用子命令:all、chart、readme、values |
| status | 显示已命名版本的状态 |
| template | 本地呈现模板 |
| uninstall | 卸载一个 release |
| upgrade | 更新一个 release |
| version | 查看 helm 客户端版本 |
# 配置镜像
配置国内 chart 仓库
- 微软仓库(http://mirror.azure.cn/kubernetes/charts/)这个仓库推荐,基本上官网有的 chart 这里都有。
- 阿里云仓库(https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts )
- 官方仓库(https://hub.kubeapps.com/charts/incubator)官方 chart 仓库,国内有点不好使。
helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo update
2
3
# 安装 MongoDB 示例
# 安装
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-mongo bitnami/mongodb
# 指定密码和架构
helm install my-mongo bitnami/mongodb --set architecture="replicaset",auth.r ootPassword="mongopass"
# 删除
helm ls
helm delete my-mongo
# 查看密码
kubectl get secret my-mongo-mongodb -o json
kubectl get secret my-mongo-mongodb -o yaml > secret.yaml
# 临时运行一个包含 mongo client 的 debian 系统
kubectl run mongodb-client --rm --tty -i --restart='Never' --image docker.io/bitnami/mongodb:4.4.10-debian-10-r20 --command -- bash
# 进去 mongodb
mongo --host "my-mongo-mongodb" -u root -p mongopass
# 也可以转发集群里的端口到宿主机访问 mongodb
kubectl port-forward svc/my-mongo-mongodb 27017:27018
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 安装前自定义 chart
自定义选项是因为并不是所有的 chart 都能按照默认配置运行成功,可能会需要一些环境 依赖,例如 PV。 所以我们需要自定义 chart 配置选项,安装过程中有两种方法可以传递配置数据:
- --values(或 - f):指定带有覆盖的 YAML 文件。这可以多次指定,最右边的文件优先
- --set:在命令行上指定替代。如果两者都用,--set 优先级高
--values 使用,先将修改的变量写到一个文件中
helm show values stable/mysql
cat config.yaml
2
persistence:
enabled: true
storageClass: "managed-nfs-storage"
accessMode: ReadWriteOnce
size: 8Gi
mysqlUser: "k8s"
mysqlPassword: "123456"
mysqlDatabase: "k8s"
2
3
4
5
6
7
8
安装
helm install db -f config.yaml stable/mysql
kubectl get pods
# 进入容器
kubectl run -it db-client --rm --restart=Never --image=mysql:5.7 -- bash
2
3
4
测试 mysql 连接
mysql -hdb-mysql -uk8s -p123456
当然可以用命令行替代变量
helm install db --set persistence.storageClass="managed-nfs-storage" stable/mysql
也可以把 chart 包下载下来查看详情
helm pull stable/mysql --untar
# 构建一个 Helm Chart
helm create mychart
tree mychart/
2
mychart/
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ └── service.yaml
└── values.yaml
2
3
4
5
6
7
8
9
10
- Chart.yaml:用于描述这个 Chart 的基本信息,包括名字、描述信息以及版本等。
- values.yaml :用于存储 templates 目录中模板文件中用到变量的值。
- Templates: 目录里面存放所有 yaml 模板文件。
- charts:目录里存放这个 chart 依赖的所有子 chart。
- NOTES.txt :用于介绍 Chart 帮助信息, helm install 部署后展示给用户。例如:如何使用这个 Chart、列出缺省的设置等。
- _helpers.tpl:放置模板助手的地方,可以在整个 chart 中重复使用
创建 Chart 后,接下来就是将其部署:
helm install web mychart/
也可以打包推送的 charts 仓库共享别人使用
helm package mychart/
mychart-0.1.0.tgz
# chart 模板
Helm 最核心的就是模板,即模板化的 K8S manifests 文件。
它本质上就是一个 Go 的 template 模板。Helm 在 Go template 模板的基础上,还会增加很多东西。如一些自定义的元数据信息、扩展的库以及一些类似于编程形式的工作流,例如条件语句、管道等等。这些东西都会使得我们的模板变得更加丰富。
有了模板,我们怎么把我们的配置融入进去呢?用的就是这个 values 文件。这两部分内容其实就是 chart 的核心功能。
接下来,部署 nginx 应用,熟悉模板使用
helm create nginx
vim nginx/Chart.yaml
2
apiVersion: v2
name: nginx
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: 1.15
2
3
4
5
6
vim nginx/values.yaml
replicas: 3
image: nginx
tag: 1.15
serviceport: 80
targetport: 80
label: nginx
2
3
4
5
6
vim nginx/templates/NOTES.txt
hello
vim nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: {{ .Values.label }}
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
app: {{ .Values.label }}
template:
metadata:
labels:
app: {{ .Values.label }}
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.tag }}
name: web
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vim nginx/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: {{ .Values.label }}
name: {{ .Release.Name }}
spec:
ports:
- port: {{ .Values.serviceport }}
protocol: TCP
targetPort: {{ .Values.targetport }}
selector:
app: {{ .Values.label }}
type: NodePort
2
3
4
5
6
7
8
9
10
11
12
13
14
查看实际的模板被渲染过后的资源文件
helm get manifest web
helm install web nginx/
2
NAME: web
LAST DEPLOYED: Fri May 2916:09:462020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
hello
2
3
4
5
6
7
8
这个 deployment 就是一个 Go template 的模板,这里定义的 Release 模板对象属于 Helm 内置的一种对象,是从 values 文件中读取出来的。这样一来,我们可以将需要变化的地方都定义变量。
# 调试
Helm 也提供了 --dry-run 、 --debug 调试参数,帮助你验证模板正确性。在执行 helminstall 时候带上这两个参数就可以把对应的 values 值和渲染的资源清单打印出来,而不会真正的去部署一个 release。
比如我们来调试上面创建的 chart 包:
helm install web --dry-run nginx/
# 内置对象
刚刚我们使用 \{\{.Release.Name\}\} 将 release 的名称插入到模板中。这里的 Release 就是 Helm 的内置对象,下面是一些常用的内置对象:
| Release.Name | release 名称 |
|---|---|
| Release.Name | release 名字 |
| Release.Namespace | release 命名空间 |
| Release.Service | release 服务的名称 |
| Release.Revision | release 修订版本号,从 1 开始累加 |
# Values
Values 对象是为 Chart 模板提供值,这个对象的值有 4 个来源:
- chart 包中的 values.yaml 文件
- 父 chart 包的 values.yaml 文件
- 通过 helm install 或者 helm upgrade 的 -f 或者 --values 参数传入的自定义的 yaml 文件
- 通过 --set 参数传入的值
chart 的 values.yaml 提供的值可以被用户提供的 values 文件覆盖,而该文件同样可以被 --set 提供的参数所覆盖。
# 升级、回滚和删除
发布新版本的 chart 时,或者当您要更改发布的配置时,可以使用该 helm upgrade 命令。
helm upgrade --set imageTag=1.17 web nginx
helm upgrade -f values.yaml web nginx
2
如果在发布后没有达到预期的效果,则可以使用 helm rollback 回滚到之前的版本。 例如将应用回滚到第一个版本:
helm rollback web 1
卸载发行版,请使用以下 helm uninstall 命令:
helm uninstall web
查看历史版本配置信息
helm get all --revision 1 web
# 管道与函数
前面讲的模块,其实就是将值传给模板引擎进行渲染,模板引擎还支持对拿到数据进行二次处理。
例如从.Values 中读取的值变成字符串,可以使用 quote 函数实现:
app: {{ quote .Values.label.app }}
quote .Values.label.app 将后面的值作为参数传递给 quote 函数。
模板函数调用语法为:functionName arg1 arg2...
另外还会经常使用一个 default 函数,该函数允许在模板中指定默认值,以防止该值被忽略掉,以及其他函数
name: {{ .Values.name | default "nginx" }} # 默认值
{{ .Values.resources | indent 12 }} # 缩进
{{ upper .Values.resources }} # 大写
{{ title .Values.resources }} # 首字母大写
2
3
4
# 流程控制
# if
if/else 块是用于在模板中有条件地包含文本块的方法,条件块的基本结构如下
{{ if PIPELINE }}
# Do something
{{ elseif OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
2
3
4
5
6
7
案例
template:
metadata:
labels:
app: nginx
{{ if eq .Values.devops "k8s" }}
devops: 123
{{ else }}
devops: 456
{{ end }}
2
3
4
5
6
7
8
9
在上面条件语句使用了 eq 运算符判断是否相等,除此之外,还支持 ne、 lt、 gt、 and、 or 等运算符。
helm install --dry-run web ../mychart/
模板引擎来渲染一下会发现有多余的空行,这是因为当模板引擎运行时,会将控制指令删除,所有之前占的位置也就空白了,需要使用 \{\{- if ...\}\} 的方式消除此空行:
env:
{{- ifeq .Values.env.hello "world" }}
- name: hello
value: 123
{{- end }}
2
3
4
5
使用 -\}\} 需谨慎,因为 -\}\} 它删除了双方的换行符,条件判断就是判断条件是否为真,如果值为以下几种情况则为 false:
- 一个布尔类型的 false
- 一个数字 零
- 一个 空的字符串
- 一个空的集合( map、 slice、 tuple、 dict、 array)
env:
{{- ifeq .Values.env.hello "world" -}}
- hello: true
{{- end }}
2
3
4
渲染后就会变成
env:- hello: true
# range
在 Helm 模板语言中,使用 range 关键字来进行循环操作。
values.yaml
test:
- 1
- 2
- 3
2
3
4
循环打印
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}
data:
test: |
{{- range .Values.test }}
{{ . }}
{{- end }}
2
3
4
5
6
7
8
9
循环内部我们使用的是一个 . ,这是因为当前的作用域就在当前循环内,这个 . 引用的当前读取的元素。
# with
with :控制变量作用域
. 就是表示当前范围的引用, .Values 就是告诉模板在当前范围中查找 Values 对象的值。
而 with 语句就可以来控制变量的作用域范围,其语法和一个简单的 if 语句比较类似:
{{ with PIPELINE }}
# restricted scope
{{ end }}
2
3
with 语句可以允许将当前范围。设置为特定的对象,比如我们前面一直使用的 .Values.label,我们可以使用 with 来将。范围指向 .Values.label
values.yaml
nodeSelector:
team: a
gpu: yes
2
3
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
{{- with .Values.nodeSelector }} # 在end之内 .替换成.Values.nodeSelector
nodeSelector:
team: {{ .team }}
gpu: {{ .gpu }}
{{- end }}
containers:
- image: nginx:1.16
name: nginx
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
还能进一步优化
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
2
3
4
with 是一个循环构造。使用.Values.nodeSelector 中的值:将其转换为 Yaml。toYaml 之后的点是循环中.Values.nodeSelector 的当前值
# 变量
values.yaml
env:
NAME: "gateway"
JAVA_OPTS: "-Xmx1G"
2
3
deployment.yaml
env:
{{- range $k, $v := .Values.env }}
- name: {{ $k }}
value: {{ $v | quote }}
{{- end }}
2
3
4
5
结果如下
env:
- name: JAVA_OPTS
value: "-Xmx1G"
- name: NAME
value: "gateway"
2
3
4
5
range 循环中使用 $key 和 $value 两个变量来接收后面列表循环的键和值。
with 语句块内不能再 .Release.Name 对象,否则报错
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-deployment
spec:
replicas: {{ .Values.replicas }}
template:
metadata:
labels:
project: {{ .Values.label.project }}
app: {{ quote .Values.label.app }}
{{- with .Values.label }}
project: {{ .project }}
app: {{ .app }}
release: {{ .Release.Name }}
{{- end }}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上面会出错
{{- $releaseName := .Release.Name -}}
{{- with .Values.label }}
project: {{ .project }}
app: {{ .app }}
release: {{ $releaseName }}
# 或者可以使用$符号,引入全局命名空间
release: {{ $.Release.Name }}
{{- end }}
2
3
4
5
6
7
8
可以看到在 with 语句上面增加了一句 \{\{-$releaseName:=.Release.Name-\}\} ,其中 $releaseName 就是后面的对象的一个引用变量,它的形式就是 $name ,赋值操作使用 := ,这样 with 语句块内部的 $releaseName 变量仍然指向的是 .Release.Name
# 命名模板
需要复用代码的地方用。
命名模板:使用 define 定义,template 引入,在 templates 目录中默认下划线 开头的文件为公共模板 (helpers.tpl)
_helpers.tpl
{{- define "demo.fullname" -}}
{{- .Chart.Name -}}-{{ .Release.Name }}
{{- end -}}
2
3
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template"demo.fullname" . }}
2
3
4
template 指令是将一个模板包含在另一个模板中的方法。但是,template 函数不能用于 Go 模板管道。为了解决该问题,增加 include 功能。
_helpers.tpl
{{- define "demo.labels" -}}
app: {{ template"demo.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
{{- end -}}
2
3
4
5
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include"demo.fullname" . }}
labels:
{{- include "demo.labels" . | nindent 4 }}
2
3
4
5
6
上面包含一个名为 demo.labels 的模板,然后将值。传递给模板,最后将该模板的输出传递给 nindent 函数。
# 构建流程
- 先创建模板
- 修改 Chart.yaml,Values.yaml,添加常用的变量
- 在 templates 目录下创建部署镜像所需要的 yaml 文件,并变量引用 yaml 里经常变动的字段
# heml 安装时一直 pending
helm ls # 查看helm应用名称
kubectl describe pvc mongodb #查询命名空间
kubectl describe po mongodb -n default #查询错误信息
2
3

错误信息 pod has unbound immediate PersistentVolumeClaims 没有提供相对应的 pvc
我们首先创建相对应的 pv,如果没有创建 sc 先创建 sc,注意 name 要与 helm 中自动创建的 pvc 名称相当于
先查询 helm 应用 pvc 名称,Pending 状态的就是,如下面的 mongodb 名称的 pvc
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-redis-ha-server-0 Bound data-redis-ha-server-0 10Gi RWO 64m
mongodata Bound my-mysql 20Gi RWO local-storage 112m
mongodb Pending 48s
2
3
4
然后我们创建相对应的 pv,一般设置 8G+,大部分应用都要求 8G+
mongodb-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodb
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
2
3
4
5
6
7
8
9
10
11
12
13
创建
kubectl apply -f mongodb-pv.yaml
重新查询 pvc
kubectl get pvc
如果状态变为 Bound 则成功绑定 pv
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-redis-ha-server-0 Bound data-redis-ha-server-0 10Gi RWO 64m
mongodata Bound my-mysql 20Gi RWO local-storage 113m
mongodb Bound mongodb 10Gi RWO 87s
2
3
4
查看 pod
kubectl get pod