Helm、Kustomize、Kubetpl
- 本人实际没有使用过 Helm 和 Kustomiz 这两款工具,以下的对比,仅个人观点,仅供参考;如果有说的不对的地方,还望指点
Helm
- 在有网络的情况下
- Helm 可以很好的进行产品的迭代升级,只需要配置好 repo 仓库,拉取相应版本的 chart 包,修改对应的 values.yaml 文件就可以完成
- 对于需要快速部署一些主流的中间件或者服务的时候,可以不需要去考虑自己需要准备那些对象(deployment、service、configmap等等),因为仓库里面都有现成的,不需要自己造轮子,拿来就可以用
- 在没有网络的情况下
- 比如一些 saas 类型的产品交付公司,在一些银行、证券类的客户现场,大多数的生产环境是不通外网的,在这些苛刻条件下,如果使用 Helm 就会需要涉及 Helm 的私仓,相对于交付来说,架构会过于庞大且麻烦
Kustomize
- Helm 对于服务的定制仅限于预置变量,那么如果需要更多更灵活的的 YAML 定制就需要用到 Kustomize
- Kustomize 是一套采用合并思想,对 Kubernetes 原生配置进行管理的工具,使用无模板的方案定义应用配置
- 允许用户使用一系列的描述文件为基础,然后通过 overlay 的方式生成最终部署应用所需的描述文件
Kubetpl
- 有点类似于 Helm 的 values.yaml 的方式配置相应的变量,通过预先配置好的 yaml 文件模板,带入变量后生成新的 yaml 文件
- 可以定制化配置不同场景的模板,不过模板的管理会相对麻烦很多
- 比较轻量化,不需要任何服务器组件,只是单纯的配置和管理 yaml 文件
安装 Kubetpl
curl -sSL https://github.com/shyiko/kubetpl/releases/download/0.9.0/kubetpl-0.9.0-$( bash -c '[[ $OSTYPE == darwin* ]] && echo darwin || echo linux' )-amd64 -o kubetpl && chmod a+x kubetpl && sudo mv kubetpl /usr/local/bin/
Kubetpl 命令参数
参数选项
completion
-命令行参数自动补齐
help
-命令帮助
render
-渲染模板
completion - 参数自动补齐
- 可选参数:
bash
和zsh
# bash source <(kubetpl completion bash) # zsh source <(kubetpl completion zsh)
ender - 渲染模板
关于模板风格:$,go-template,template-kind
,主要区别在于变量引用的方式不同
$
-shell 的风格
$NAME
go-template
- go 语言 template 库的风格
{{ .VAR }}
-获取变量名称为 VAR 的变量值
{{ if isset "VAR" }} ... {{ end }}
-当定义了 VAR 变量的时候才会在 }} ... {{ 中间展示变量值
{{ get "VAR" "default" }}
-获取 VAR 的值,如果未设置,则返回 "default"
{{ .VAR | quote }}
-引用 VAR 值
{{ .VAR | indent 4 }}
-VAR 的缩进值(带 4 个空格)
{{ .VAR | b64enc }}
-VAR 值以 base64 编码展示
template-kind
-没太看懂....
go-template 语法
- 本人没有接触过 go 语言,临时找了一些资料来学习一些简单的使用
- go 语言的变量好像是不支持
-
出现在变量名称内的,go 语言使用的是驼峰
命名,不然可能会有这样的报错:
template: nginx-template.yaml:3: unexpected bad character U+002D '-' in range
注释
{{/* 注释 */}}
引用变量
- 输出
hostAlias
变量的值
{{ .hostAlias }}
- 当
hostAlias
是匿名字段时,可以访问其内部字段或方法
{{ .hostAlias.ip }}
- 如果需要引用外部变量,需要在前面加上
$
符号
{{ $.hostAlias }}
在 template 中定义变量
- 变量名称前面需要带上
$
符号 - 使用
:=
来对变量赋值
{{ $x := "hello" }}
- 定义变量的值引用其他变量的值
{{ $x := .hostAlias }}
条件判断
- if 后面可以是一个
条件表达式
(包括管道函数表达式。pipeline 即管道) - 也可以是一个
字符串变量
或布尔值变量
- 当为
字符串变量
时,如为空字符串则判断为false
,否则判断为true
{{ if .hostAlias }} - ip: {{ .ip }} hostnames: {{ .hostnames }} {{ end }}
遍历
- 普通用法,当变量值存在时才会有后面的内容输出
{{ range $.hostAliases }} - ip: {{ .ip }} hostnames: {{ .hostnames }} {{ end }}
- 另一种用法
{{ range $k,$v := .labels }} {{ $k }}: {{ $v }} {{ end }}
开始使用
简单使用
准备一个 yaml 模板
- 这里直接从 github 上面复制过来用了,以下所有的 yaml 文件只是用来模拟 kubetpl 生成 yaml 文件使用的,不能被直接使用的
# kubetpl:syntax:go-template
定义模板风格,如果不在模板文件内定义,则需要通过 --syntax 的方式申明,否则会有类似如下报错
does not appear to be a valid YAML (yaml: line 3: did not find expected key).
Did you forget to specify --syntax=<$|go-template|template-kind> / add "# kubetpl:syntax:<$|go-template|template-kind>"?
# kubetpl:syntax:go-template apiVersion: v1 kind: Service metadata: name: {{ .NAME }}-service spec: selector: app: {{ .NAME }} ports: - protocol: TCP port: 80 --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: {{ .NAME }}-deployment spec: replicas: {{ .REPLICAS }} template: metadata: labels: app: {{ .NAME }} spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
准备变量文件
- 支持的格式
*.env
-<var>=<value>
yaml/json 文件
*.env
的方式
cat << EOF > valus.env NAME=sample-app REPLICAS=2 EOF
yaml 文件
的方式
cat << EOF > env.yaml NAME: my-nginx REPLICAS: 3 EOF
生成新的 yaml 文件
- 两个变量文件定义的变量值是有差异的,这个单纯就是用来比较使用的,只是选择哪种变量文件,全看各自的选择
# 使用 valus.env kubetpl render nginx-template.yaml -i valus.env -o nginx-env.yaml # 使用 env.yaml kubetpl render nginx-template.yaml -i env.yaml -o nginx-yaml.yaml
稍微复杂一点的场景
- 因为 kubetpl 是通过
-i
的方式引入的变量,所以下面的变量引用都需要加上$
符号表示外部引入变量,才能正常识别,不然会有类似如下的报错: template: nginx-template.yaml:9:23: executing "nginx-template.yaml" at <.static.namespace>: map has no entry for key "static"
准备一个 yaml 模板
# kubetpl:syntax:go-template {{ range $getName := .staticInfo }} --- apiVersion: v1 kind: Service metadata: name: {{ $getName.name }}-svc namespace: {{ $.static.namespace }} spec: selector: app: {{ $getName.name }} ports: - protocol: TCP port: {{ $getName.port }} --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: {{ $getName.name }} namespace: {{ $.static.namespace }} spec: replicas: {{ $.static.replicas }} template: metadata: labels: app: {{ $getName.name }} spec: containers: - name: {{ $getName.name }} image: {{ $.global.Image }} imagePullPolicy: {{ $.global.ImagePull }} ports: - containerPort: 80 {{ end }}
准备变量文件
global: Image: nginx:1.16 ImagePull: IfNotPresent static: namespace: web-static replicas: 3 staticInfo: - name: app-static port: 80 - name: backend-static port: 8080
生成新的 yaml 文件
kubetpl render nginx-template.yaml -i env.yaml -o nginx.yaml
生成后的 yaml 文件
--- apiVersion: v1 kind: Service metadata: name: app-static-svc namespace: web-static spec: ports: - port: 80 protocol: TCP selector: app: app-static --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: app-static namespace: web-static spec: replicas: 3 template: metadata: labels: app: app-static spec: containers: - image: nginx:1.16 imagePullPolicy: IfNotPresent name: app-static ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: backend-static-svc namespace: web-static spec: ports: - port: 8080 protocol: TCP selector: app: backend-static --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: backend-static namespace: web-static spec: replicas: 3 template: metadata: labels: app: backend-static spec: containers: - image: nginx:1.16 imagePullPolicy: IfNotPresent name: backend-static ports: - containerPort: 80
kubetpl/data-from-file
- 当我们需要配置 configmap 的时候,可以通过文件带入的形式来配置 yaml 文件,这样可以单独维护 configmap 给所有的 yaml 文件来使用,对于维护来说,能减轻不少的成本
准备一个 yaml 模板
# kubetpl:syntax:go-template --- apiVersion: v1 kind: ConfigMap kubetpl/data-from-file: - ./nginx.conf metadata: name: {{ $.configmap.name }} namespace: {{ $.static.namespace }} data: {{ range $getName := .staticInfo }} --- apiVersion: v1 kind: Service metadata: name: {{ $getName.name }}-svc namespace: {{ $.static.namespace }} spec: selector: app: {{ $getName.name }} ports: - protocol: TCP port: {{ $getName.port }} --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: {{ $getName.name }} namespace: {{ $.static.namespace }} spec: replicas: {{ $.static.replicas }} template: metadata: labels: app: {{ $getName.name }} spec: containers: - name: {{ $getName.name }} image: {{ $.global.Image }} imagePullPolicy: {{ $.global.ImagePull }} ports: - containerPort: 80 volumeMounts: - name: nginx-conf mountPath: /workspace/nginx/conf volumes: - name: nginx-conf configMap: name: {{ $.configmap.name }} {{ end }}
准备变量文件
- 对比前面的,增加了一个
configmap
global: Image: nginx:1.16 ImagePull: IfNotPresent static: namespace: web-static replicas: 3 staticInfo: - name: app-static port: 80 - name: backend-static port: 8080 configmap: name: static-cm
准备 file 文件
- 命名为
nginx.conf
user root; worker_processes 4; error_log logs/nginx_error.log warn; pid logs/nginx.pid; worker_rlimit_nofile 51200; events { use epoll; worker_connections 51200; multi_accept on; } http { include mime.types; default_type application/octet-stream; server_names_hash_bucket_size 128; client_header_buffer_size 256k; large_client_header_buffers 16 256k; client_max_body_size 1m; keepalive_timeout 0; client_body_timeout 10; client_header_timeout 10; send_timeout 5; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 5; gzip_disable "MSIE [1-6]."; gzip_types text/plain application/x-javascript text/css text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json; gzip_vary on; ssi on; ssi_silent_errors on; ssi_types text/shtml; proxy_temp_path proxy_temp 1 2; client_body_temp_path client_body_temp 1 2; fastcgi_temp_path fastcgi_temp 1 2; uwsgi_temp_path uwsgi_temp 1 2; scgi_temp_path scgi_temp 1 2; set_real_ip_from 0.0.0.0/0; real_ip_header X-Forwarded-For; real_ip_recursive on; underscores_in_headers on; resolver kube-dns.kube-system valid=5s; resolver_timeout 5s; access_log off; server { listen 8080; server_name test.com; set $proto_host "${http_x_forwarded_proto}_${host}"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; location /static/ { location ~* \.(gif|jpg|jpeg|png|bmp|swf|ico)$ { expires 30d; } expires 1h; } } }
生成新的 yaml 文件
- 使用
kubetpl/data-from-file
的话,需要加上--allow-fs-access
参数,不然会出现类似如下的报错: nginx-template.yaml: access denied: nginx.conf (use --allow-fs-access and/or -c/--chroot=<root dir, e.g. '.'> to allow)
kubetpl render --allow-fs-access nginx-template.yaml -i env.yaml -o nginx.yaml
生成后的 yaml 文件
--- apiVersion: v1 data: nginx.conf: | user root; worker_processes 4; error_log logs/nginx_error.log warn; pid logs/nginx.pid; worker_rlimit_nofile 51200; events { use epoll; worker_connections 51200; multi_accept on; } http { include mime.types; default_type application/octet-stream; server_names_hash_bucket_size 128; client_header_buffer_size 256k; large_client_header_buffers 16 256k; client_max_body_size 1m; keepalive_timeout 0; client_body_timeout 10; client_header_timeout 10; send_timeout 5; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 5; gzip_disable "MSIE [1-6]."; gzip_types text/plain application/x-javascript text/css text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json; gzip_vary on; ssi on; ssi_silent_errors on; ssi_types text/shtml; proxy_temp_path proxy_temp 1 2; client_body_temp_path client_body_temp 1 2; fastcgi_temp_path fastcgi_temp 1 2; uwsgi_temp_path uwsgi_temp 1 2; scgi_temp_path scgi_temp 1 2; set_real_ip_from 0.0.0.0/0; real_ip_header X-Forwarded-For; real_ip_recursive on; underscores_in_headers on; resolver kube-dns.kube-system valid=5s; resolver_timeout 5s; access_log off; server { listen 8080; server_name test.com; set $proto_host "${http_x_forwarded_proto}_${host}"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; location /static/ { location ~* \.(gif|jpg|jpeg|png|bmp|swf|ico)$ { expires 30d; } expires 1h; } } } kind: ConfigMap metadata: name: static-cm namespace: web-static --- apiVersion: v1 kind: Service metadata: name: app-static-svc namespace: web-static spec: ports: - port: 80 protocol: TCP selector: app: app-static --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: app-static namespace: web-static spec: replicas: 3 template: metadata: labels: app: app-static spec: containers: - image: nginx:1.16 imagePullPolicy: IfNotPresent name: app-static ports: - containerPort: 80 volumeMounts: - mountPath: /workspace/nginx/conf name: nginx-conf volumes: - configMap: name: static-cm name: nginx-conf --- apiVersion: v1 kind: Service metadata: name: backend-static-svc namespace: web-static spec: ports: - port: 8080 protocol: TCP selector: app: backend-static --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: backend-static namespace: web-static spec: replicas: 3 template: metadata: labels: app: backend-static spec: containers: - image: nginx:1.16 imagePullPolicy: IfNotPresent name: backend-static ports: - containerPort: 80 volumeMounts: - mountPath: /workspace/nginx/conf name: nginx-conf volumes: - configMap: name: static-cm name: nginx-conf