乐趣区

关于devops:如臂使指勇往直前-使用Helm-Charts的最佳实践

一、背景

Kubernetes 是由谷歌开源的容器集群管理系统,是目前最为风行、成为事实标准的旨在主动部署、扩大和运行利用容器的开源平台。咱们晓得,容器(Container)的转义是“集装箱”,而 Kubernetes 的名字则来源于希腊语中的“舵手”。正如名字所预示的,咱们心愿 Kubernetes 这个舵手可能帮忙咱们把装满集装箱(利用容器)的货轮(容器集群)顺利、安稳地驶向目的地。

好舵手的胜利驾驶,离不开得心应手的工具——船舵,而利用的 Helm charts(Helm 的转义就是操作船舵的方向盘),无疑为 Kubernetes 这个舵手提供了如臂使指的好工具。Helm charts 是能够从 Helm 仓库获取的一个文件汇合,用以形容利用对应的一组互相关联的 K8s 资源。以最无效的形式制作利用的 Helm charts,可能助力 Kubernetes 在将利用容器部署到生产环境时逾越浅滩、畏缩不前。当然,正如咱们开发那些用以部署产品的公开公布的 K8s charts 时所发现的那样,某些不当的 Helm charts 也会导致咱们的容器货轮到处流浪、彷徨不进。

在开发过程中,每次提交拉取申请(PR,Pull Request)时 Helm 社区提供的反馈,都指引咱们采纳某些 Helm charts 的最佳实际,从而使得运维和更新利用容器都取得了现实的成果。当编写为社区或客户应用的公开公布的 K8s charts 时,以下这些事项是须要认真思考的:

· 须要定义哪些依赖关系?

· 利用是否须要保护长久化的状态?

· 如何通过私密和权限来解决安全性?

· 如何管制运行中的容器?

· 如何确保利用试运行的,而且可能接管申请?

· 如何对外公开利用提供的服务?

· 如何测试编写的 K8s charts?

本文列出了咱们开发过程中利用的一些最佳实际,使得开发出的 Helm charts 可能具备更好的结构化和针对性。心愿这些实际可能帮忙 K8s 驾驶您的容器货轮顺利地达到此岸。

二、起步

在启航之前,心愿你能首先相熟开发 Helm charts 的基本概念和过程。请参考 Helm 的官网文档,https://helm.sh/docs/developing_charts/。

在本文中,咱们将遵循这些最佳实际来创立一个 Helm chart,用以部署一个基于 Express.js 框架的,两层的,针对 Mongo 数据库进行增、删、改、查(CRUD)的利用。该利用的示例代码位于 https://github.com/jainishshah17/express-mongo-crud。

三、创立 Helm chart 并填写 chart 信息

3.1 创立模板

首先,利用 helm 客户端的 create 命令创立咱们的 helm chart 模板:

$ helm create express-crud

该命令将会为名为“express-crud”的 Helm chart 创立相应的目录构造。

3.2 填写 chart 信息

批改刚刚生成的模板目录中的 Chart.yaml 文件,增加形容 chart 信息的元数据,包含失当的:

· apiVersion(必选):chart 应用的 Helm API 的版本,目前固定为“v1”;

· 名称 name(必选):Helm chart 的名称;

· 版本 version(必选):Helm chart 的版本,是合乎语义化版本 2.0 的版本字符串;

· 形容 description(可选):对 Helm chart 利用范畴的形容;

· 利用版本号 appVerions(可选):Helm chart 蕴含的利用版本,可用作对应的 docker 镜像的标签 tag(能够不遵循语义化版本的命名规定);

· 源码地位 source(可选):我的项目源代码的地位;

· 维护者 maintainer(可选):记录 Helm chart 维护者的根本信息;

· 图标 icon(可选):为 Helm chart 选个个性化的图标吧。

Chart.yaml 里还有其余一些可选项,请参考 Helm 的文档 https://helm.sh/docs/developing_charts/。

本例中 Chart.yaml 的内容如下:

apiVersion: v1

appVersion: “1.0.0”

description: A Helm chart for express-crud application

name: express-crud

version: 0.1.0

sources:

– https://github.com/jainishshah17/express-mongo-crud

maintainers:

– name: myaccount

email: myacount@mycompany.com

icon: https://github.com/mycompany17/mycompany.com/blob/master/app/public/images/logo.jpg

home: http://mycompany.com/

3.3 定义依赖关系

如果这个利用有内部依赖,也就是须要援用其余的 Helm charts,那就须要在 Helm chart 的模板目录中减少 requirements.yaml 文件,来记录这些依赖关系。因为本例中的利用须要用到 mongodb 数据库,所以须要创立 requirements.yaml 文件,把 mongodb 退出到其中的依赖列表当中。

本例中 requirements.yaml 的内容如下:

dependencies:

– name: mongodb

version: 3.0.4

repository: https://kubernetes-charts.storage.googleapis.com/

condition: mongodb.enabled

一旦创立了 requirements.yaml 文件,须要运行 Helm 客户端的依赖更新命令。该命令验证目前存储的依赖 Helm charts 是否满足依赖列表中的定义,并按需下载并更新依赖 chart 包。下载的依赖 chart 包都存储在模板目录的 charts/ 子目录下。

$ helm dep update

四、创立部署文件

部署文件位于模板目录的 templates/ 子目录,用以指定 K8s 如何部署利用容器。而在开发部署文件时,须要作出一些关键性的决策:

4.1 部署对象还是状态集对象

创立的部署文件依据 K8s 治理利用的形式分为部署对象和状态集对象两种。

部署对象是指在名为 deployment.yaml 的文件中申明的无状态利用,其 kind 参数被指定为 deployment。

状态集对象是指有状态的利用,通常在分布式系统中应用。这些对象在名为 stateless.yaml 的文件里申明,其 kind 参数被指定为 stateful。

部署对象

状态集对象

部署对象是指无状态利用,通常较为轻量级。

状态集对象用于必须长久化保留状态的场景。通过在长久卷(PV,persistent volumes)上应用 volumeClaimTemplates 来确保组件重启时可能放弃状态。

如果利用是无状态的,或者状态是在启动时由后盾零碎创立时,应用部署对象。

如果利用是有状态的,或者心愿在 K8s 上部署有状态的存储时,应用状态集对象。

因为本例中的利用不须要放弃状态,所以应用了部署对象。文件 deployment.yaml 曾经在执行 helm create 命令时主动创立了。

在本例中应用 appVersion 作为利用 Docker 镜像的标签 tag。这使得咱们为新版本利用降级 Helm chart 时,只需批改 Chart.yaml 文件中的值即可。

image: “{{.Values.image.repository}}:{{default .Chart.AppVersion .Values.image.tag}}”

4.2 私密(Secret)还是配置映射(ConfigMap)

须要确定哪些认证信息或配置数据适宜于存储为私密信息,或者做为配置映射数据。

私密信息是如明码等敏感信息,须要 K8s 以加密格局进行存储。

配置映射(ConfigMap)是记录了被利用所共享的配置数据的一个文件。配置映射中的数据是不加密的,所以不应该蕴含任何敏感信息。

私密

配置映射

将这些信息处理为私密,比把它们放在 K8s 的 Pod 定义,或 Docker 镜像当中更平安、更灵便

配置映射能够将配置数据从镜像内容中分离出来,以放弃容器化利用的可移植性

用于认证信息

用于非认证信息

示例用法:API 密钥、明码、Token 和 ssh 密钥

示例用法: 日志宰割设置、无认证数据的配置

在本例中,咱们容许 Helm 利用镜像拉取的私密信息从公有 Docker 镜像核心拉取 Docker 镜像。这个过程依赖于 K8s 集群中蕴含可用的私密信息,用于指定公有仓库的登录认证信息。这个私密能够通过如下的 kubectl 命令创立:

$ kubectl create secret docker-registry regsecret

–docker-server=$DOCKER\_REGISTRY\_RUL –docker-username=$USERNAME

–docker-password=$PASSWORD –docker-email=$EMAIL

在 Helm chart 的 values.yaml 文件里,能够将这个私密名称映射到一个配置项的值,如本例中:

imagePullSecrets: regsecret

而后 Helm 就能够利用这个私密的配置项去拜访 Docker 镜像核心,如本例中 deployment.yaml 中定义的这些行:

{{- if .Values.imagePullSecrets}}

imagePullSecrets:

– name: {{.Values.imagePullSecrets}}

{{- end}}

应该把利用须要用到的私密都间接加到 values.yaml 文件当中。如本例中,为了配置利用可能通过事后创立的用户和数据库来拜访 mongodb,须要将以下信息增加到 values.yaml 当中:

mongodb:

enabled: true

mongodbRootPassword:

mongodbUsername: admin

mongodbPassword:

mongodbDatabase: test

须要留神的是,咱们并没有把缺省的认证信息硬编码到 Helm chart 里。相同,当明码没有通过 -set 参数或 values.yaml 提供时,咱们应用逻辑来随机生成明码。

在本例中,咱们最终在 deployment.yaml 文件中的下列行,把 mongodb 的认证信息通过私密传递给了利用:

env:

– name: DATABASE_PASSWORD

valueFrom:

secretKeyRef:

name: {{.Release.Name}}-mongodb

key: mongodb-password

4.3 初始化容器还是容器生命周期钩子(Hook)

能够通过特定的初始化容器(InitContainers)或容器生命周期钩子(Container Lifecycle Hooks)来管制 kubelet 容器的运行。

初始化容器

容器生命周期钩子

初始化容器是在利用容器之前运行的非凡容器,其中蕴含不在利用镜像之内的工具或设置脚本。

容器能够应用容器生命周期钩子框架来运行由其生命周期中的事件触发的代码。

一个 Pod 能够蕴含一个或多个初始化容器。这些容器在应用程序容器启动之前运行。

一个 Pod 只能蕴含一个 PostStart hook 或 PreStop hook

PostStart hook 在创立容器后立刻执行。然而,不能保障 hook 会在容器的入口点(ENTRYPOINT)之前执行。

没有参数传递给 hook 的处理程序。

例如,将应用配置映射或秘密装入的文件挪动到不同的地位。

PreStop hook 在容器被终止之前立刻调用。它是阻塞的,这意味着它是同步的,因而必须先实现该 hook 的解决能力发送删除容器的调用。

例如,优雅地敞开利用。

能够应用初始化容器增加期待,来查看依赖的微服务是否失常工作,而后再持续运行。

能够应用 PostStart hook 来更新同一个 Pod 里的文件,如更新蕴含服务 IP 的配置文件。

在本例中,deployments.yaml 文件中减少了下列初始化容器的定义,来暂停利用的启动,直到数据库启动并失常运行:

initContainers:

– name: wait-for-db

image: “{{.Values.initContainerImage}}”

command:

– ‘sh’

– ‘-c’

– >

until nc -z -w 2 {{.Release.Name}}-mongodb 27017 && echo mongodb ok;

do sleep 2;

done

4.4 增加就绪探针(Readimess Probe)和存活探针(Liveness Probe)

增加就绪探针和存活探针来查看利用的继续运行状况是很好的实际。如果不这样做,那么利用可能看起来还在运行,但理论曾经出错了,不再响应调用或查问。

在 deployment.yaml 文件中的下列代码中减少了这样的探针来执行定期检查:

livenessProbe:

httpGet:

path: ‘/health’

port: http

initialDelaySeconds: 60

periodSeconds: 10

failureThreshold: 10

readinessProbe:

httpGet:

path: ‘/health’

port: http

initialDelaySeconds: 60

periodSeconds: 10

failureThreshold: 10

4.5 增加 RBAC 反对

当利用须要时,下列过程能够在 Helm chart 中减少对基于角色访问控制(RBAC)的反对:

步骤 1: 创立角色 ,在 role.yaml 文件里增加下列内容:

角色只能在同一个命名空间中授予资源的拜访权限。

{{- if .Values.rbac.create}}

apiVersion: rbac.authorization.k8s.io/v1

kind: Role

metadata:

labels:

app: {{template “express-crud.name” .}}

chart: {{template “express-crud.chart” .}}

heritage: {{.Release.Service}}

release: {{.Release.Name}}

name: {{template “express-crud.fullname” .}}

rules:

{{toYaml .Values.rbac.role.rules}}

{{- end}}

步骤 2: 创立角色绑定 ,在 rolebinding.yaml 文件中增加下列内容:

集群角色(ClusterRole)能够和角色一样用来受权雷同的权限,然而因为它们是集群范畴的,只能用于受权拜访到:

· 集群范畴的资源(如 node)

· 非资源的端点(如“/healthz”)

· 跨命名空间的命名空间资源(如 pod)

{{- if .Values.rbac.create}}

apiVersion: rbac.authorization.k8s.io/v1

kind: RoleBinding

metadata:

labels:

app: {{template “express-crud.name” .}}

chart: {{template “express-crud.chart” .}}

heritage: {{.Release.Service}}

release: {{.Release.Name}}

name: {{template “express-crud.fullname” .}}

subjects:

– kind: ServiceAccount

name: {{template “express-crud.serviceAccountName” .}}

roleRef:

kind: Role

apiGroup: rbac.authorization.k8s.io

name: {{template “express-crud.fullname” .}}

{{- end}}

步骤 3: 创立服务账户 ,在 serviceacount.yaml 文件中增加下列内容:

服务账户(Service Account)为在 Pod 中运行的过程提供标识。

{{- if .Values.serviceAccount.create}}

apiVersion: v1

kind: ServiceAccount

metadata:

labels:

app: {{template “express-crud.name” .}}

chart: {{template “express-crud.chart” .}}

heritage: {{.Release.Service}}

release: {{.Release.Name}}

name: {{template “express-crud.serviceAccountName” .}}

{{- end}}

步骤 4:应用 helper 模板设置服务账户名称

在_helpers.tpl 文件中增加下列内容:

{{/*

Create the name of the service account to use

*/}}

{{- define “express-crud.serviceAccountName” -}}

{{- if .Values.serviceAccount.create -}}

{{default (include “express-crud.fullname” .) .Values.serviceAccount.name }}

{{- else -}}

{{default “default” .Values.serviceAccount.name}}

{{- end -}}

{{- end -}}

4.6 增加服务

当初到了通过服务(Service)来对外公开咱们利用的时候了。

服务使得利用可能通过一个 IP 地址来接管流量。通过指定类型,能够通过不同的形式来公开服务:

集群 IP(ClusterIP)

服务只能通过集群中的一个对外 IP 来拜访

节点端口(NodePort)

服务能够通过 NodeIP 和 NodePort 从集群外拜访

负载均衡器(LoadBalancer)

服务能够通过内部负载均衡器从集群外拜访。

能够利用 K8s 的 Ingress 来拜访服务。

本例中通过在 service.yaml 文件减少下列内容来增加服务:

apiVersion: v1

kind: Service

metadata:

name: {{template “express-crud.fullname” .}}

labels:

app: {{template “express-crud.name” .}}

chart: {{template “express-crud.chart” .}}

release: {{.Release.Name}}

heritage: {{.Release.Service}}

spec:

type: {{.Values.service.type}}

ports:

– port: {{.Values.service.externalPort}}

targetPort: http

protocol: TCP

name: http

selector:

app: {{template “express-crud.name” .}}

release: {{.Release.Name}}

留神,在上述代码中,服务的类型援用了 values.yaml 文件中的配置:

service:

type: LoadBalancer

internalPort: 3000

externalPort: 80

五、values.yaml 总览

在 values.yaml 文件中定义配置是放弃 Helm charts 可维护性的最佳实际。

本例中的 values.yaml 文件记录了咱们针对之前探讨的许多个性而定义的各种配置:

# Default values for express-mongo-crud.

# This is a YAML-formatted file.

# Declare variables to be passed into your templates.

## Role Based Access Control

## Ref: https://kubernetes.io/docs/admin/authorization/rbac/

rbac:

create: true

role:

## Rules to create. It follows the role specification

rules:

– apiGroups:

– ”

resources:

– services

– endpoints

– pods

verbs:

– get

– watch

– list

## Service Account

## Ref: https://kubernetes.io/docs/admin/service-accounts-admin/

serviceAccount:

create: true

## The name of the ServiceAccount to use.

## If not set and create is true, a name is generated using the fullname template

name:

## Configuration values for the mongodb dependency

## ref: https://github.com/kubernetes/charts/blob/master/stable/mongodb/README.md

mongodb:

enabled: true

image:

tag: 3.6.3

pullPolicy: IfNotPresent

persistence:

size: 50Gi

# resources:

# requests:

# memory: “12Gi”

# cpu: “200m”

# limits:

# memory: “12Gi”

# cpu: “2”

## Make sure the –wiredTigerCacheSizeGB is no more than half the memory limit!

## This is critical to protect against OOMKill by Kubernetes!

mongodbExtraFlags:

  • “–wiredTigerCacheSizeGB=1”

mongodbRootPassword:

mongodbUsername: admin

mongodbPassword:

mongodbDatabase: test

livenessProbe:

initialDelaySeconds: 60

periodSeconds: 10

readinessProbe:

initialDelaySeconds: 30

periodSeconds: 30

ingress:

enabled: false

annotations: {}

# kubernetes.io/ingress.class: nginx

# kubernetes.io/tls-acme: “true”

path: /

hosts:

– chart-example.local

tls: []

# – secretName: chart-example-tls

# hosts:

# – chart-example.local

initContainerImage: “alpine:3.6”

imagePullSecrets:

replicaCount: 1

image:

repository: jainishshah17/express-mongo-crud

# tag: 1.0.1

pullPolicy: IfNotPresent

service:

type: LoadBalancer

internalPort: 3000

externalPort: 80

resources: {}

# We usually recommend not to specify default resources and to leave this as a conscious

# choice for the user. This also increases chances charts run on environments with little

# resources, such as Minikube. If you do want to specify resources, uncomment the following

# lines, adjust them as necessary, and remove the curly braces after ‘resources:’.

# limits:

# cpu: 100m

# memory: 128Mi

# requests:

# cpu: 100m

# memory: 128Mi

nodeSelector: {}

tolerations: []

affinity: {}

六、测试和装置 Helm chart

测试咱们的 Helm chart 是十分重要的,能够通过 Helm 的 lint 命令执行:

$ helm lint ./

## Output

==> Linting ./

Lint OK

1 chart(s) linted, no failures

应用 Helm 的 install 命令,基于 Helm chart 将利用部署到 K8s 环境:

$ helm install –name test1 ./

## Output

NAME: test1

LAST DEPLOYED: Sat Sep 15 09:36:23 2018

NAMESPACE: default

STATUS: DEPLOYED

RESOURCES:

==> v1beta1/Deployment

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE

test1-mongodb 1 1 1 0 0s

==> v1beta2/Deployment

test1-express-crud 1 1 1 0 0s

==> v1/Secret

NAME TYPE DATA AGE

test1-mongodb Opaque 2 0s

==> v1/PersistentVolumeClaim

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE

test1-mongodb Pending standard 0s

==> v1/ServiceAccount

NAME SECRETS AGE

test1-express-crud 1 0s

==> v1/Service

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

test1-mongodb ClusterIP 10.19.248.205 27017/TCP 0s

test1-express-crud LoadBalancer 10.19.254.169 80:31994/TCP 0s

==> v1/Role

NAME AGE

test1-express-crud 0s

==> v1/RoleBinding

NAME AGE

test1-express-crud 0s

==> v1/Pod(related)

NAME READY STATUS RESTARTS AGE

test1-mongodb-67b6697449-tppk5 0/1 Pending 0 0s

test1-express-crud-dfdbd55dc-rdk2c 0/1 Init:0/1 0 0s

NOTES:

1. Get the application URL by running these commands:

NOTE: It may take a few minutes for the LoadBalancer IP to be available.

You can watch the status of by running ‘kubectl get svc -w test1-express-crud’

export SERVICE_IP=$(kubectl get svc –namespace default test1-express-crud -o jsonpath='{.status.loadBalancer.ingress[0].ip}’)

echo [http://$SERVICE\_IP:80](http://$SERVICE_IP:80)

运行上述的 Helm 装置命令将为负载均衡器创立一个内部 IP,能够通过这个 IP 运行咱们的利用。

以下就是咱们的利用运行起来的样子:

七、总结

正如本文示例所展现的,Helm 是一个用处极其宽泛的零碎,使得在架构和开发 Helm chart 时有很大的灵活性。合乎 Helm 社区常规的形式将有助于简化提交您的 Helm charts 供公开应用的过程,并使得这些 chart 在您更新利用时更易于保护。

本文示例的残缺 Helm chart 保留在 GitHub 的 express-crud 我的项目:https://github.com/jainishshah17/express-mongo-crud。您能够通过查看这些可失常运行的文件,来帮忙您更彻底地理解它们的工作原理。


** 欢送观看 JFrog 杰蛙每周二在线课堂,点击报名:

https://www.bagevent.com/even…**

退出移动版