在上一篇文章中,介绍了 K8s 的基本使用。本文中会介绍 Helm 的作用和基本使用。
在使用 Kubernetes 时,通常需要通过 kubectl apply -f
命令逐个创建资源。如果切换到另一个 namespace,或者部署到另一套 Kubernetes 集群时,往往需要重复这些操作。
Helm 的出现正是为了解决这个问题。它帮助我们在 Kubernetes 中部署服务时,只需一个命令即可完成整个过程。Helm 是管理 Kubernetes 应用的最佳工具,能够查找、分享和使用软件构建 Kubernetes 应用。其主要特点包括:
- 复杂性管理:即使是最复杂的应用,Helm Chart 也可以完整描述,并提供可重复安装的单点授权应用程序。
- 易于升级:随时随地的升级和自定义钩子使得升级变得简单而高效。
- 简单分发:Helm Chart 可以轻松发布在公共或私有服务器上,便于分发和部署。
- 轻松回滚:使用
helm rollback
命令,可以轻松回滚到之前的发布版本。
有关安装方法,可以参考官网教程。
创建 Helm Chart
本地创建 Chart
下面介绍如何创建自己的 Helm Chart,我们将通过创建一个 hello-helm
Chart 来部署 Kubernetes 资源。
首先,建议使用 helm create
命令来生成一个初始的 Chart,这个命令会自动生成一些 Kubernetes 资源定义的初始文件,并按照官方推荐的目录结构组织文件,如下所示:
生成的目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| . └── hello-helm ├── Chart.yaml ├── charts ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── tests │ └── test-connection.yaml └── values.yaml
|
我们将 templates
目录下默认生成的 yaml
文件删除,并替换为之前教程中创建的 yaml
文件。最终的结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| . ├── hello-helm │ ├── Chart.yaml │ ├── charts │ ├── templates │ │ ├── hellok8s-configmaps.yaml │ │ ├── hellok8s-deployment.yaml │ │ ├── hellok8s-secret.yaml │ │ ├── hellok8s-service.yaml │ │ ├── ingress.yaml │ │ ├── nginx-deployment.yaml │ │ └── nginx-service.yaml │ ├── values-dev.yaml │ └── values.yaml └── image ├── HelloKubernetes.java └── dockerfile
|
image
文件夹中包含用于构建镜像的内容,其中 HelloKubernetes.java
定义了 hellok8s:v6
版本的代码,主要是从系统环境中获取 MESSAGE
、NAMESPACE
、DB_URL
、DB_PASSWORD
这几个环境变量,并将它们拼接成字符串返回。这些环境变量的来源会通过 helm values
和 Kubernetes 配置来设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpExchange;
import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.InetAddress; import java.net.UnknownHostException;
public class HelloKubernetes {
public static void main(String[] args) throws IOException { final String[] hostHolder = new String[1]; try { hostHolder[0] = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { e.printStackTrace(); hostHolder[0] = "unknown"; }
String message = System.getenv("MESSAGE"); String namespace = System.getenv("NAMESPACE"); String dbURL = System.getenv("DB_URL"); String dbPassword = System.getenv("DB_PASSWORD");
HttpServer server = HttpServer.create(new InetSocketAddress(3000), 0);
server.createContext("/", new HttpHandler() { @Override public void handle(HttpExchange exchange) throws IOException { String response = String.format( "[v6] Hello, Helm! Message from helm values: %s, From namespace: %s, From host: %s, Get Database Connect URL: %s, Database Connect Password: %s", message, namespace, hostHolder[0], dbURL, dbPassword ); exchange.sendResponseHeaders(200, response.getBytes().length); OutputStream os = exchange.getResponseBody(); os.write(response.getBytes()); os.close(); } });
server.setExecutor(null); server.start(); } }
|
下面是 Dockerfile 的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| FROM openjdk:8-jdk-alpine AS builder
WORKDIR /src
COPY . .
RUN javac HelloKubernetes.java
FROM gcr.io/distroless/java-debian10
WORKDIR /
COPY --from=builder /src/ /app/
ENTRYPOINT ["java", "-cp", "/app", "HelloKubernetes"]
EXPOSE 3000
|
进入 image
文件夹,进行镜像的构建。注意,将 tangrl177
替换为你自己的 DockerHub 账号名。
1 2
| docker build . -t tangrl177/hellok8s:v6 docker push tangrl177/hellok8s:v6
|
接下来,我们修改根目录下的 values.yaml
文件,用于定义自定义的配置信息。将 Kubernetes 资源文件中容易变化的参数提取出来,放在 values.yaml
文件中。注意,将 tangrl177
替换为你自己的 DockerHub 账号名。完整的 values.yaml
配置信息如下:
1 2 3 4 5 6 7 8 9 10 11 12
| application: name: hellok8s hellok8s: image: tangrl177/hellok8s:v6 replicas: 3 message: "It works with Helm Values[v6]!" database: url: "http://DB_ADDRESS_DEFAULT" password: "db_password" nginx: image: nginx replicas: 2
|
在 Helm 中,默认使用 Go template 语法来引用这些值。例如,我们可以将 ConfigMap 资源定义成如下的 hellok8s-configmaps.yaml
文件,其中 metadata.name
的值使用 {{ .Values.application.name }}-config
,会从 values.yaml
中获取 application.name
的值 hellok8s
,并拼接 -config
,生成的 ConfigMap 名称就是 hellok8s-config
。
1 2 3 4 5 6 7
| apiVersion: v1 kind: ConfigMap metadata: name: {{ .Values.application.name }}-config data: DB_URL: {{ .Values.application.hellok8s.database.url }}
|
同理,可以将 Secret 资源定义成如下的 hellok8s-secret.yaml
文件,并使用 b64enc
方法将值进行 Base64 编码:
1 2 3 4 5 6 7
| apiVersion: v1 kind: Secret metadata: name: {{ .Values.application.name }}-secret data: DB_PASSWORD: {{ .Values.application.hellok8s.database.password | b64enc }}
|
最后,修改 hellok8s-deployment.yaml
文件,将所有变量引用改为从 values.yaml
文件中获取,并添加代码中需要的 NAMESPACE
环境变量,使用 Helm 内置的 .Release.Namespace
对象获取当前命名空间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Values.application.name }}-deployment spec: replicas: {{ .Values.application.hellok8s.replicas }} selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: {{ .Values.application.hellok8s.image }} name: hellok8s-container env: - name: DB_URL valueFrom: configMapKeyRef: name: {{ .Values.application.name }}-config key: DB_URL - name: DB_PASSWORD valueFrom: secretKeyRef: name: {{ .Values.application.name }}-secret key: DB_PASSWORD - name: NAMESPACE value: {{ .Release.Namespace }} - name: MESSAGE value: {{ .Values.application.hellok8s.message }}
|
类似地,ingress.yaml
文件的 metadata.name
也可以改为从 values.yaml
中获取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ .Values.application.name }}-ingress annotations: nginx.ingress.kubernetes.io/ssl-redirect: "false" spec: rules: - http: paths: - path: /hello pathType: Prefix backend: service: name: service-hellok8s-clusterip port: number: 3000 - path: / pathType: Prefix backend:
service: name: service-nginx-clusterip port: number: 4000
|
nginx-deployment.yaml
文件的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: {{ .Values.application.nginx.replicas }} selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: {{ .Values.application.nginx.image }} name: nginx-container
|
nginx-service.yaml
和 hellok8s-service.yaml
文件的内容没有变化。
1 2 3 4 5 6 7 8 9 10 11 12
| apiVersion: v1 kind: Service metadata: name: service-hellok8s-clusterip spec: type: ClusterIP selector: app: hellok8s ports: - port: 3000 targetPort: 3000
|
1 2 3 4 5 6 7 8 9 10 11 12
| apiVersion: v1 kind: Service metadata: name: service-nginx-clusterip spec: type: ClusterIP selector: app: nginx ports: - port: 4000 targetPort: 80
|
在 hello-helm
目录下执行 helm upgrade
命令进行安装。安装成功后,执行 curl
命令可以直接查看结果,并通过 helm
管理所有资源:
1 2 3 4 5
| minikube addons enable ingress
helm upgrade --install hello-helm --values values.yaml .
|
如果使用 minikube 或 Docker Desktop,则可能需要公开服务。可以使用 minikube
提供的命令来查看服务列表,并通过 curl
进行测试:
1 2 3 4 5 6 7
| minikube service list minikube service ingress-nginx-controller -n ingress-nginx --url
curl http://127.0.0.1:55201/hello curl http://127.0.0.1:55201/
|
这样,你就可以通过 Helm 一键管理和部署所有 Kubernetes 资源了!
Rollback
Helm 提供了强大的 Rollback 功能,允许你快速回滚到之前的版本。如果一次更新出现了问题,你可以轻松地恢复到之前的状态。
首先,我们先通过修改 values.yaml
文件,将 message
字段更新为 "It works with Helm Values[v2]!"
,以表示这是一个新的版本。
1 2 3 4 5 6 7 8 9 10 11 12 13
| application: name: hellok8s hellok8s: image: tangrl177/hellok8s:v6 replicas: 3 message: "It works with Helm Values[v2]!" database: url: "http://DB_ADDRESS_DEFAULT" password: "db_password" nginx: image: nginx replicas: 2
|
然后,使用 helm upgrade
命令更新 Kubernetes 资源。你可以通过 curl
命令来验证资源是否已更新成功。
1 2 3 4 5 6 7 8
| helm upgrade --install hello-helm --values values.yaml . minikube service list minikube service ingress-nginx-controller -n ingress-nginx --url # 输出如下 # http://127.0.0.1:55201 http # http://127.0.0.1:55202 https curl http://127.0.0.1:55201/hello curl http://127.0.0.1:55201/
|
如果这次更新出现问题,你可以使用 helm rollback
命令快速回滚到之前的版本。需要注意的是,与 Kubernetes 中的 Deployment 回滚类似,Helm 回滚后的 REVISION 版本号会增加。例如,如果你回滚到 REVISION 1,回滚后的 REVISION 版本号将是 3,而不是直接回到 1。回滚后,使用 curl
命令时会看到返回的字符串恢复为 v1 版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| helm ls # NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION # hello-helm default 2 2024-08-26 17:08:22.20669 +0800 CST deployed hello-helm-0.1.0 1.16.0
helm rollback hello-helm 1 # Rollback was a success! Happy Helming!
helm ls # NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION # hello-helm default 3 2024-08-26 17:11:46.380842 +0800 CST deployed hello-helm-0.1.0 1.16.0
minikube service list minikube service ingress-nginx-controller -n ingress-nginx --url # 输出如下 # http://127.0.0.1:55201 http # http://127.0.0.1:55202 https curl http://127.0.0.1:55201/hello curl http://127.0.0.1:55201/
|
通过上述步骤,你可以看到 Helm 的 Rollback 功能如何在版本更新出现问题时帮助你迅速恢复到之前的稳定版本。这使得管理 Kubernetes 资源更加灵活和安全。
多环境配置
多环境配置
使用 Helm 进行多环境部署非常方便。首先,你可以为不同的环境创建不同的 values
文件。例如,创建一个 values-dev.yaml
文件,用于自定义 dev
环境的配置信息。
1 2 3 4 5 6 7
| application: hellok8s: message: "It works with Helm Values values-dev.yaml!" database: url: "http://DB_ADDRESS_DEV" password: "db_password_dev"
|
在部署时,你可以使用多次指定 --values
或 -f
参数的方式来加载不同的 values
文件。最后指定的文件(即最右边的文件)具有最高的优先级,因此 values-dev.yaml
文件中的值会覆盖 values.yaml
中相同的值。同时,使用 -n dev
可以在名为 dev
的 namespace 中创建 Kubernetes 资源。执行命令后,通过 curl
命令可以看到返回的字符串中读取的是 values-dev.yaml
文件中的配置,并且显示 From namespace = dev
。
1 2 3
| helm upgrade --install hello-helm -f values.yaml -f values-dev.yaml -n dev .
kubectl get pods -n dev
|
除了使用 values
文件外,你还可以通过 --set
直接在命令行中设置独立的值。例如,使用以下命令可以覆盖 values.yaml
和 values-dev.yaml
文件中的 message
字段:
1
| helm upgrade --install hello-helm -f values.yaml -f values-dev.yaml --set application.hellok8s.message="It works with set helm values" -n dev .
|
这种方法在 CI/CD 中非常常见,因为它允许你在部署过程中灵活地修改配置,而无需更改 values
文件的内容。
打包和发布 Helm Chart
在前面的例子中,我们展示了如何用一行命令在一个新的环境中安装所有需要的 Kubernetes 资源!接下来,我们将讨论如何将 Helm Chart 打包、分发并发布,以便其他人可以下载和使用它。
官方提供了两种教程来实现这一目标:一种是使用 GCS(Google Cloud Storage) 存储,另一种是使用 GitHub Pages 存储 Chart。本教程将采用第二种方法,并使用 chart-releaser-action 进行自动发布。
这个 GitHub Action 会自动将 Helm Chart 发布到 gh-pages
分支上。例如,本教程中的 hellok8s
Helm Chart 就发布在 gh-pages 分支的 index.yaml
文件中。
手动打包与发布
在使用 GitHub Action 自动生成和发布 Chart 之前,我们先了解一下如何手动完成这些操作。
在 hello-helm
目录下,使用 helm package
命令将 Chart 目录打包成一个 .tgz
文件。接着,使用 helm repo index
命令基于包含已打包 Chart 的目录生成仓库的索引文件 index.yaml
。
最后,你可以使用 helm upgrade --install
命令来安装该指定包:
1 2 3 4 5
| helm package hello-helm
helm repo index .
helm upgrade --install hello-helm hello-helm-0.1.0.tgz
|
通过上述步骤,我们可以看到,所谓的 Helm 打包与发布过程,其实就是生成并上传 hello-helm-0.1.0.tgz
和 index.yaml
文件。而 Helm 的下载与安装过程则是将 .tgz
和 index.yaml
文件下载并使用 helm upgrade --install
命令进行安装。
自动发布到 GitHub Pages
为了将生成的 hellok8s
Helm Chart 自动发布,我们可以先删除手动生成的 hello-helm-0.1.0.tgz
和 index.yaml
文件,然后使用 GitHub Actions 自动生成和发布这些文件。
以下是 GitHub Action 的配置示例,来自 官方文档 或本教程的源码仓库 .github/workflows/release.yml
文件。该配置会在每次代码推送到远程仓库时,自动将 helm-charts
目录下的所有 Charts 打包并发布到 gh-pages
分支(确保 gh-pages
分支已存在,否则会报错)。
可以通过下面的命令创建新分支:
1 2 3 4 5 6
| git checkout --orphan gh-pages git rm -rf . echo '# helm chart repo' >> README.md git add README.md git commit -m 'new branch' git push origin gh-pages
|
下面是 .github/workflows/release.yml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| name: Release Charts
on: push: branches: - main
jobs: release: permissions: contents: write runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0
- name: Configure Git run: | git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm uses: azure/setup-helm@v1 with: version: v3.8.1
- name: Run chart-releaser uses: helm/chart-releaser-action@v1.4.0 with: charts_dir: helm-charts env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
接着,前往 GitHub 仓库的 Settings -> Pages -> Build and deployment -> Branch
,选择 gh-pages
分支,GitHub 会自动在 https://username.github.io/project
上发布 Helm Chart。
使用发布后的 Chart
将 Chart 发布到 Github Pages 后,可以通过下面的命令进行快速安装。其中 helm repo add
表示将我创建好的 hellok8s chart 添加到自己本地的仓库当中,helm install
表示从仓库中安装 hellok8s/hello-helm 到 k8s 集群当中。
1 2 3 4 5 6 7 8
| helm repo add hellok8s https://rongliangtang.github.io/helm-demo/ # "hellok8s" has been added to your repositories
helm install my-hello-helm hellok8s/hello-helm --version 0.1.0 # NAME: my-hello-helm # NAMESPACE: default # STATUS: deployed # REVISION: 1
|
发布到社区
最后,你可以将自己的 Helm Charts 发布到社区中,例如 ArtifactHub,这样更多人可以使用你的 Chart。像本教程中的 hellok8s
Helm Chart 就已经发布在 ArtifactHub 上。通过这些步骤,你就可以将自己的 Helm Chart 打包、发布并共享给全球的开发者使用了。
代码仓库
https://github.com/rongliangtang/helm-demo
参考
Helm 官方文档
Kubernetes 练习手册