查看原文
其他

使用Helm管理kubernetes原生应用 - 创建私有chart仓库指南

宋净超 K8S中文社区 2019-12-19

作者介绍:宋净超

曾就职于科大讯飞,在大数据平台架构、云平台的开发运维、容器平台构建方面有丰富经验,目前在TalkingData从事Kubernetes和云原生应用的研究和推广工作。《Cloud Native Go》图书译者、QCon讲师,个人博客 https://jimmysong.io。


Helm系列第一部分:使用Helm管理kubernetes原生应用 - 创建私有chart仓库指南


完本文后您应该可以自己创建chart,并创建自己的私有chart仓库。


Helm是一个kubernetes应用的包管理工具,用来管理charts——预先配置好的安装包资源,有点类似于Ubuntu的APT和CentOS中的yum。


Helm chart是用来封装kubernetes原生应用程序的yaml文件,可以在你部署应用的时候自定义应用程序的一些metadata,便与应用程序的分发。


Helm和charts的主要作用:


  • 应用程序封装

  • 版本管理

  • 依赖检查

  • 便于应用程序分发


本文同时归档到kubernetes-handbook。


1

安装Helm


前提要求


  • Kubernetes1.5以上版本

  • 集群可访问到的镜像仓库

  • 执行helm命令的主机可以访问到kubernetes集群


安装步骤


首先需要安装helm客户端

  1. curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh

  2. chmod 700 get_helm.sh

  3. ./get_helm.sh

创建tiller的serviceaccount和clusterrolebinding

  1. kubectl create serviceaccount --namespace kube-system tiller

  2. kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller

然后安装helm服务端tiller

  1. helm init -i sz-pg-oam-docker-hub-001.tendcloud.com/library/kubernetes-helm-tiller:v2.3.1

我们使用-i指定自己的镜像,因为官方的镜像因为某些原因无法拉取。

为应用程序设置serviceAccount:

  1. kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

检查是否安装成功:

  1. $ kubectl -n kube-system get pods|grep tiller

  2. tiller-deploy-2372561459-f6p0z         1/1       Running   0          1h

  3. $ helm version

  4. Client: &version.Version{SemVer:"v2.3.1", GitCommit:"32562a3040bb5ca690339b9840b6f60f8ce25da4", GitTreeState:"clean"}

  5. Server: &version.Version{SemVer:"v2.3.1", GitCommit:"32562a3040bb5ca690339b9840b6f60f8ce25da4", GitTreeState:"clean"}


2

创建自己的chart


我们创建一个名为mychart的chart,看一看chart的文件结构。

  1. $ helm create mongodb

  2. $ tree mongodb

  3. mongodb

  4. ├── Chart.yaml #Chart本身的版本和配置信息

  5. ├── charts #依赖的chart

  6. ├── templates #配置模板目录

  7. │   ├── NOTES.txt #helm提示信息

  8. │   ├── _helpers.tpl #用于修改kubernetes objcet配置的模板

  9. │   ├── deployment.yaml #kubernetes Deployment object

  10. │   └── service.yaml #kubernetes Serivce

  11. └── values.yaml #kubernetes object configuration

  12. 2 directories, 6 files


2.1模板


Templates目录下是yaml文件的模板,遵循Go template语法。使用过Hugo的静态网站生成工具的人应该对此很熟悉。

我们查看下deployment.yaml文件的内容。

  1. apiVersion: extensions/v1beta1

  2. kind: Deployment

  3. metadata:

  4.  name: {{ template "fullname" . }}

  5.  labels:

  6.    chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"

  7. spec:

  8.  replicas: {{ .Values.replicaCount }}

  9.  template:

  10.    metadata:

  11.      labels:

  12.        app: {{ template "fullname" . }}

  13.    spec:

  14.      containers:

  15.      - name: {{ .Chart.Name }}

  16.        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

  17.        imagePullPolicy: {{ .Values.image.pullPolicy }}

  18.        ports:

  19.        - containerPort: {{ .Values.service.internalPort }}

  20.        livenessProbe:

  21.          httpGet:

  22.            path: /

  23.            port: {{ .Values.service.internalPort }}

  24.        readinessProbe:

  25.          httpGet:

  26.            path: /

  27.            port: {{ .Values.service.internalPort }}

  28.        resources:

  29. {{ toYaml .Values.resources | indent 12 }}

这是该应用的Deployment的yaml配置文件,其中的双大括号包扩起来的部分是Go template,其中的Values是在values.yaml文件中定义的:

Default values for mychart. 

This is a YAML-formatted file. 

Declare variables to be passed into your templates.

  1. replicaCount: 1

  2. image:

  3.  repository: nginx

  4.  tag: stable

  5.  pullPolicy: IfNotPresent

  6. service:

  7.  name: nginx

  8.  type: ClusterIP

  9.  externalPort: 80

  10.  internalPort: 80

  11. resources:

  12.  limits:

  13.    cpu: 100m

  14.    memory: 128Mi

  15.  requests:

  16.    cpu: 100m

  17.    memory: 128Mi

比如在Deployment.yaml中定义的容器镜像image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"其中的:

  • .Values.image.repository就是nginx

  • .Values.image.tag就是stable


以上两个变量值是在create chart的时候自动生成的默认值。


我们将默认的镜像地址和tag改成我们自己的镜像sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx:1.9。


2.2检查配置和模板是否有效


当使用kubernetes部署应用的时候实际上讲templates渲染成最终的kubernetes能够识别的yaml格式。

使用helm install --dry-run --debug 命令来验证chart配置。该输出中包含了模板的变量配置与最终渲染的yaml文件。

  1. $ helm install --dry-run --debug mychart

  2. Created tunnel using local port: '58406'

  3. SERVER: "localhost:58406"

  4. CHART PATH: /Users/jimmy/Workspace/github/bitnami/charts/incubator/mean/charts/mychart

  5. NAME:   filled-seahorse

  6. REVISION: 1

  7. RELEASED: Tue Oct 24 18:57:13 2017

  8. CHART: mychart-0.1.0

  9. USER-SUPPLIED VALUES:

  10. {}

  11. COMPUTED VALUES:

  12. image:

  13.  pullPolicy: IfNotPresent

  14.  repository: sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx

  15.  tag: 1.9

  16. replicaCount: 1

  17. resources:

  18.  limits:

  19.    cpu: 100m

  20.    memory: 128Mi

  21.  requests:

  22.    cpu: 100m

  23.    memory: 128Mi

  24. service:

  25.  externalPort: 80

  26.  internalPort: 80

  27.  name: nginx

  28.  type: ClusterIP

  29. HOOKS:

  30. MANIFEST:

  31. ---

  32. # Source: mychart/templates/service.yaml

  33. apiVersion: v1

  34. kind: Service

  35. metadata:

  36.  name: filled-seahorse-mychart

  37.  labels:

  38.    chart: "mychart-0.1.0"

  39. spec:

  40.  type: ClusterIP

  41.  ports:

  42.  - port: 80

  43.    targetPort: 80

  44.    protocol: TCP

  45.    name: nginx

  46.  selector:

  47.    app: filled-seahorse-mychart

  48. ---

  49. # Source: mychart/templates/deployment.yaml

  50. apiVersion: extensions/v1beta1

  51. kind: Deployment

  52. metadata:

  53.  name: filled-seahorse-mychart

  54.  labels:

  55.    chart: "mychart-0.1.0"

  56. spec:

  57.  replicas: 1

  58.  template:

  59.    metadata:

  60.      labels:

  61.        app: filled-seahorse-mychart

  62.    spec:

  63.      containers:

  64.      - name: mychart

  65.        image: "sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx:1.9"

  66.        imagePullPolicy: IfNotPresent

  67.        ports:

  68.        - containerPort: 80

  69.        livenessProbe:

  70.          httpGet:

  71.            path: /

  72.            port: 80

  73.        readinessProbe:

  74.          httpGet:

  75.            path: /

  76.            port: 80

  77.        resources:

  78.            limits:

  79.              cpu: 100m

  80.              memory: 128Mi

  81.            requests:

  82.              cpu: 100m

  83.              memory: 128Mi

我们可以看到Deployment和Service的名字前半截由两个随机的单词组成,最后才是我们在values.yaml中配置的值。


2.3部署到kubernetes


在mychart目录下执行下面的命令将nginx部署到kubernetes集群上。

  1. helm install .

  2. NAME:   eating-hound

  3. LAST DEPLOYED: Wed Oct 25 14:58:15 2017

  4. NAMESPACE: default

  5. STATUS: DEPLOYED

  6. RESOURCES:

  7. ==> v1/Service

  8. NAME                  CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE

  9. eating-hound-mychart  10.254.135.68  <none>       80/TCP   0s

  10. ==> extensions/v1beta1/Deployment

  11. NAME                  DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE

  12. eating-hound-mychart  1        1        1           0          0s

  13. NOTES:

  14. 1. Get the application URL by running these commands:

  15.  export POD_NAME=$(kubectl get pods --namespace default -l "app=eating-hound-mychart" -o jsonpath="{.items[0].metadata.name}")

  16.  echo "Visit http://127.0.0.1:8080 to use your application"

  17.  kubectl port-forward $POD_NAME 8080:80

现在nginx已经部署到kubernetes集群上,本地执行提示中的命令在本地主机上访问到nginx实例。

  1. export POD_NAME=$(kubectl get pods --namespace default -l "app=eating-hound-mychart" -o jsonpath="{.items[0].metadata.name}")

  2. echo "Visit http://127.0.0.1:8080 to use your application"

  3. kubectl port-forward $POD_NAME 8080:80

在本地访问http://127.0.0.1:8080即可访问到nginx。

查看部署的relaese

  1. $ helm list

  2. NAME        REVISIONUPDATED                 STATUS  CHART        NAMESPACE

  3. eating-hound1       Wed Oct 25 14:58:15 2017DEPLOYEDmychart-0.1.0default

删除部署的release

$ helm delete eating-hound release "eating-hound" deleted


2.4打包分享


我们可以修改Chart.yaml中的helm chart配置信息,然后使用下列命令将chart打包成一个压缩文件。


helm package .


打包出mychart-0.1.0.tgz文件。


2.5依赖


我们可以在requirement.yaml中定义应用所依赖的chart,例如定义对mariadb的依赖:


dependencies:

- name: mariadb

  version: 0.6.0

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


使用helm lint .命令可以检查依赖和模板配置是否正确。


2.6安装源


我们在前面安装chart可以通过HTTP server的方式提供。


$ helm serve

Regenerating index. This may take a moment.

Now serving you on 127.0.0.1:8879


访问http://localhost:8879可以看到刚刚安装的chart。



点击链接即可以下载chart的压缩包。


3

部署MEAN测试案例


MEAN是用来构建网站和web应用的免费开源的JavaScript软件栈,该软件栈包括MongoDB、Express.js、Angular和Node.js。


下载charts

  1. $ git clone https://github.com/bitnami/charts.git

  2. $ cd charts/incubator/mean

  3. $ helm dep list

  4. NAME       VERSION REPOSITORY                                          STATUS

  5. mongodb    0.4.x   https://kubernetes-charts.storage.googleapis.com/   missing

缺少mongodb的依赖,需要更新一下chart。

注:https://kubernetes-charts.storage.googleapis.com/是Google维护的chart库,访问该地址可以看到所有的chart列表。

  1. $ helm dep update

  2. Hang tight while we grab the latest from your chart repositories...

  3. ...Unable to get an update from the "local" chart repository (http://127.0.0.1:8879/charts):

  4.    Get http://127.0.0.1:8879/charts/index.yaml: dial tcp 127.0.0.1:8879: getsockopt: connection refused

  5. ...Successfully got an update from the "stable" chart repository

  6. Update Complete. ⎈Happy Helming!⎈

  7. Saving 1 charts

  8. Downloading mongodb from repo https://kubernetes-charts.storage.googleapis.com/

所有的image都在 values.yaml 文件中配置。

下载缺失的chart。

  1. $ helm dep build

  2. Hang tight while we grab the latest from your chart repositories...

  3. ...Unable to get an update from the "local" chart repository (http://127.0.0.1:8879/charts):

  4.    Get http://127.0.0.1:8879/charts/index.yaml: dial tcp 127.0.0.1:8879: getsockopt: connection refused

  5. ...Successfully got an update from the "stable" chart repository

  6. Update Complete. ⎈Happy Helming!⎈

  7. Saving 1 charts

  8. Downloading mongodb from repo https://kubernetes-charts.storage.googleapis.com/

修改mongodb chart配置

将刚才下载的charts/mongodb-0.4.17.tgz给解压后,修改其中的配置:


  • 将persistence下的enabled设置为false

  • 将image修改为我们的私有镜像:sz-pg-oam-docker-hub-001.tendcloud.com/library/bitnami-mongodb:3.4.9-r1


执行helm install --dry-run --debug .确定模板无误。


将修改后的mongodb chart打包,在mongodb的目录下执行:


helm package .


现在再访问前面启动的helm server http://localhost:8879将可以在页面上看到mongodb-0.4.17这个chart。


我们对官方chart配置做了如下修改后推送到了自己的chart仓库:


  • requirements.yaml和requirements.lock文件中的repository为http://localhost:8879

  • 将values.yaml中的storageClass设置为null

  • 将values.yaml中的Image都改为私有镜像

  • repositroy都设置为http://localhost:8879


注:因为我们没有使用PVC所以将所有的关于持久化存储的配置都设置为false了。


部署MEAN


在mean目录下执行:

  1. helm install .

  2. NAME:   orbiting-platypus

  3. LAST DEPLOYED: Wed Oct 25 16:21:48 2017

  4. NAMESPACE: default

  5. STATUS: DEPLOYED

  6. RESOURCES:

  7. ==> v1/Secret

  8. NAME                       TYPE    DATA  AGE

  9. orbiting-platypus-mongodb  Opaque  2     2s

  10. ==> v1/ConfigMap

  11. NAME                    DATA  AGE

  12. orbiting-platypus-mean  1     2s

  13. ==> v1/Service

  14. NAME                       CLUSTER-IP      EXTERNAL-IP  PORT(S)    AGE

  15. orbiting-platypus-mongodb  10.254.144.208  <none>       27017/TCP  2s

  16. orbiting-platypus-mean     10.254.165.23   <none>       80/TCP     2s

  17. ==> extensions/v1beta1/Deployment

  18. NAME                       DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE

  19. orbiting-platypus-mean     1        1        1           0          2s

  20. orbiting-platypus-mongodb  1        1        1           0          2s

  21. NOTES:

  22. Get the URL of your Node app  by running:

  23.  export POD_NAME=$(kubectl get pods --namespace default -l "app=orbiting-platypus-mean" -o jsonpath="{.items[0].metadata.name}")

  24.  echo http://127.0.0.1:8080/

  25.  kubectl port-forward $POD_NAME 8080:80

这样MEAN软件栈就部署到你的kuberentes集群里面了(默认是在default namespace下)。

验证检查


为了验证MEAN是否安装成功过,可以使用kubectl get pods查看pod是否启动完成,会先启动mongodb的pod,然后启动MEAN中的4步init。


访问Web UI


在Ingress中增加如下配置:


  1.  - host: mean.jimmysong.io

  2.    http:

  3.      paths:

  4.      - backend:

  5.          serviceName: orbiting-platypus-mean

  6.          servicePort: 80

  7.        path: /

然后在页面中更新ingress:

  1. kubectl repalce -f ingress.yaml

关于Ingress配置请参考:边缘节点配置

然后在本地的/etc/hosts文件中增加一条配置:

  1. 172.20.0.119 mean.jimmysong.io

注:172.20.0.119即边缘节点的VIP。

因为该页面需要加载google的angularjs、还有两个css在国内无法访问,可以使用curl测试:

  1. curl mean.jimmysong.io

将会返回HTML内容:

  1. <!doctype html>

  2. <!-- ASSIGN OUR ANGULAR MODULE -->

  3. <html ng-app="scotchTodo">

  4. <head>

  5.    <!-- META -->

  6.    <meta charset="utf-8">

  7.    <meta name="viewport" content="width=device-width, initial-scale=1">

  8.    <!-- Optimize mobile viewport -->

  9.    <title>Node/Angular Todo App</title>

  10.    <!-- SCROLLS -->

  11.    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">

  12.    <!-- load bootstrap -->

  13.    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">

  14.    <style>

  15.        html {

  16.            overflow-y: scroll;

  17.        }

  18.              body {

  19.            padding-top: 50px;

  20.        }

  21.                #todo-list {

  22.            margin-bottom: 30px;

  23.        }

  24.                #todo-form {

  25.            margin-bottom: 50px;

  26.        }

  27.    </style>

  28.    <!-- SPELLS -->

  29.    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>

  30.    <!-- load angular -->

  31.    <script src="js/controllers/main.js"></script>

  32.    <!-- load up our controller -->

  33.    <script src="js/services/todos.js"></script>

  34.    <!-- load our todo service -->

  35.    <script src="js/core.js"></script>

  36.    <!-- load our main application -->

  37. </head>

  38. <!-- SET THE CONTROLLER -->

  39. <body ng-controller="mainController">

  40.    <div class="container">

  41.        <!-- HEADER AND TODO COUNT -->

  42.        <div class="jumbotron text-center">

  43.            <h1>I'm a Todo-aholic <span class="label label-info">{{ todos.length }}</span></h1>

  44.        </div>

  45.        <!-- TODO LIST -->

  46.        <div id="todo-list" class="row">

  47.            <div class="col-sm-4 col-sm-offset-4">

  48.                <!-- LOOP OVER THE TODOS IN $scope.todos -->

  49.                <div class="checkbox" ng-repeat="todo in todos">

  50.                    <label>

  51.                        <input type="checkbox" ng-click="deleteTodo(todo._id)"> {{ todo.text }}

  52.                    </label>

  53.                </div>

  54.                <p class="text-center" ng-show="loading">

  55.                    <span class="fa fa-spinner fa-spin fa-3x"></span>

  56.                </p>

  57.            </div>

  58.        </div>

  59.        <!-- FORM TO CREATE TODOS -->

  60.        <div id="todo-form" class="row">

  61.            <div class="col-sm-8 col-sm-offset-2 text-center">

  62.                <form>

  63.                    <div class="form-group">

  64.                        <!-- BIND THIS VALUE TO formData.text IN ANGULAR -->

  65.                        <input type="text" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever" ng-model="formData.text">

  66.                    </div>

  67.                    <!-- createToDo() WILL CREATE NEW TODOS -->

  68.                    <button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">Add</button>

  69.                </form>

  70.            </div>

  71.        </div>

  72.        <div class="text-center text-muted">

  73.            <p>A demo by <a href="http://scotch.io">Scotch</a>.</p>

  74.            <p>Read the <a href="http://scotch.io/tutorials/javascript/creating-a-single-page-todo-app-with-node-and-angular">tutorial</a>.</p>

  75.        </div>

  76.    </div>

  77. </body>

  78. </html>

访问 http://mean.jimmysong.io 可以看到如下界面,我在其中添加几条todo:



注:Todo中的文字来自What does the fox say?


测试完成后可以使用下面的命令将mean chart推送的本地chart仓库中。


在mean目录下执行:


helm package .


再次刷新http://localhost:8879将可以看到如下三个chart:


  • mean

  • mean-0.1.3

  • mongodb

  • mongodb-0.4.17

  • mychart

  • mychart-0.1.0


4

参考


  • Deploy, Scale And Upgrade An Application On Kubernetes With Helm

  • https://docs.bitnami.com/kubernetes/how-to/deploy-application-kubernetes-helm/

  • Helm charts

  • https://github.com/kubernetes/helm/blob/master/docs/charts.md

  • Go template

  • https://golang.org/pkg/text/template/

  • Helm docs

  • https://github.com/kubernetes/helm/blob/master/docs/index.md

  • How To Create Your First Helm Chart

  • https://docs.bitnami.com/kubernetes/how-to/create-your-first-helm-chart/





更多信息可点击查看原文

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存