关于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…**

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理