查看原文
其他

详解 K8S Helm CI/CD发布流程

点击关注👉 DevOps技术栈 2024-02-25
原文链接:https://piotrminkowski.com/2023/02/28/create-and-release-your-own-helm-chart/

注:译文,可能有些文字含义会有偏差

在本文中,您将学习如何创建 Helm chart 并将其发布到公共存储库中。我们将为基于 Spring Boot REST 的应用程序准备一个 Helm Chart 作为练习。目标是拥有一个完全自动化的过程来构建、测试和发布它。为此,我们将在 CircleCI 中定义一个管道。此 CI/CD 管道将在公共Artifact Hub[1]中发布 Helm Chart。

源代码

如果您想自己尝试,可以随时查看本次演示中的源代码。为此,您需要克隆 GitHub 存储库[2]

创建 Helm Chart

在这部分练习中,我们将使用 helm CLI。在整个过程中,本地安装的 Helm 不是必需的,但可以帮助您了解接下来的步骤会发生什么。因此,最好安装它。请参考 Helm 官方文档[3]以找到安装方法。

在第一步中,我们将创建一个示例 Chart。它是网络应用程序的典型 Chart。例如,它在容器外部公开 8080 端口,或者允许我们定义检查 HTTP 端点的活动性和就绪性探测。这个 Helm chart 不能太复杂,也不能太简单,因为我们要为它创建自动化测试。

这是我们的Deployment模板。它将一些标准标签添加到部署清单中。它还设置资源请求和限制,正如我之前提到的,Chart 已经添加了 liveness probe 和 readiness probe。并公开端口 8080 。我们也可以直接设置环境变量或从 ConfigMapSecret 中注入环境变量。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.app.name }}
  labels: # (1)
    app: {{ .Values.app.name }}
    env: {{ .Values.app.environment }}
    owner: {{ .Values.app.owner }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Values.app.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.app.name }}
        env: {{ .Values.app.environment }}
    spec:
      containers:
        - name: {{ .Values.app.name }}
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          resources: # (2)
            {{- toYaml .Values.resources | nindent 12 }}
          livenessProbe: # (3)
            initialDelaySeconds: {{ .Values.liveness.initialDelaySeconds }}
            httpGet:
              port: {{ .Values.liveness.port }}
              path: {{ .Values.liveness.path }}
            failureThreshold: {{ .Values.liveness.failureThreshold }}
            successThreshold: {{ .Values.liveness.successThreshold }}
            timeoutSeconds: {{ .Values.liveness.timeoutSeconds }}
            periodSeconds: {{ .Values.liveness.periodSeconds }}
          readinessProbe: # (4)
            initialDelaySeconds: {{ .Values.readiness.initialDelaySeconds }}
            httpGet:
              port: {{ .Values.readiness.port }}
              path: {{ .Values.readiness.path }}
            failureThreshold: {{ .Values.readiness.failureThreshold }}
            successThreshold: {{ .Values.readiness.successThreshold }}
            timeoutSeconds: {{ .Values.readiness.timeoutSeconds }}
            periodSeconds: {{ .Values.readiness.periodSeconds }}
          ports: # (5)
          {{- range .Values.ports }}
          - containerPort: {{ .value }}
            name: {{ .name }}
          {{- end }}
          {{- if .Values.envs }}
          env: # (6)
          {{- range .Values.envs }}
          - name: {{ .name }}
            value: {{ .value }}
          {{- end }}
          {{- end }}
          {{- if or .Values.extraEnvVarsConfigMap .Values.extraEnvVarsSecret }}
          envFrom:
          {{- if or .Values.extraEnvVarsConfigMap }}
          - configMapRef:
              name: {{ .Values.extraEnvVarsConfigMap }}
          {{- end }}
          {{- if or .Values.extraEnvVarsSecret }}
          - secretRef:
              name: {{ .Values.extraEnvVarsSecret }}
          {{- end }}
          {{- end }}
          securityContext:
            runAsNonRoot: true

还有一个Service对象模板。

apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.app.name }}
  labels:
    app: {{ .Values.app.name }}
    env: {{ .Values.app.environment }}
    owner: {{ .Values.app.owner }}
spec:
  type: {{ .Values.service.type }}
  selector:
    app: {{ .Values.app.name }}
  ports:
  {{- range .Values.ports }}
  - port: {{ .value }}
    name: {{ .name }}
  {{- end }}

现在,可以用默认值填充模板。示例存储库中提供了以下values.yaml 文件。

replicaCount: 1

app:
  name: sample-spring-boot-api
  environment: dev
  owner: default

image:
  repository: piomin/sample-spring-kotlin-microservice
  tag: "1.1"

nameOverride: ""
fullnameOverride: ""

service:
  type: ClusterIP

ports:
  - name: http
    value: 8080

resources:
  limits:
    cpu: 1000m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 256Mi

liveness:
  initialDelaySeconds: 10
  port: http
  path: /actuator/health/liveness
  failureThreshold: 3
  successThreshold: 1
  timeoutSeconds: 3
  periodSeconds: 5

readiness:
  initialDelaySeconds: 10
  port: http
  path: /actuator/health/readiness
  failureThreshold: 3
  successThreshold: 1
  timeoutSeconds: 3
  periodSeconds: 5

envs:
  - name: INFO
    value: Spring Boot REST API

您可以使用helm CLI 轻松测试新创建的模板。为此,只需在存储库根目录中执行以下命令。将看到从示例模板创建的 YAML 清单。

$ helm template charts/spring-boot-api-app

这样的测试方法是可以的,但是只是在 Chart 开发过程中在本地运行它。假设我们需要创建一个交付管道,我们需要一个更高级的工具。

Helm Chart 的单元测试

在我看来,CI/CD 流水线中最重要的是自动化测试。没有它,我们将发布未经验证的软件,这可能会导致许多并发症。单个 Helm Chart 可以被多个应用程序使用,因此我们应该尽一切努力对其进行详细测试。幸运的是,有一些专门用于 Helm Chart 测试的工具。

我的选择了helm-unittest[4]。它允许我们用纯 YAML 编写单元测试文件。我们可以将其安装为 Helm 插件或在 Docker 容器中运行。在将其推送到 Git 存储库之前,在本地进行验证工作:

$ helm plugin install https://github.com/helm-unittest/helm-unittest

我们应该将单元测试放在 Chart 中的test目录中。这是 Chart 存储库的结构:

第一步,我们创建单元测试文件。如前所述,我们可以使用 YAML 符号创建测试,这非常直观。我们需要传递值文件的位置和经过测试的 Helm 模板的位置。在测试部分,我们必须定义一个断言列表。重要的是我可以轻松测试每个 YAML 清单的路径。它可以是精确比较或正则表达式。它还支持用于 mappings 和 arrays 的 JsonPath。这是测试用到的deployment_test.yaml

suite: test deployment
values:
  - ./values/test.yaml # (1)
templates:
  - templates/deployment.yaml # (2)
chart:
  version: 0.3.4+test
  appVersion: 1.0.0
tests:
  - it: should pass all kinds of assertion
    template: templates/deployment.yaml
    documentIndex: 0
    asserts: # (3)
      - equal:
          path: spec.template.spec.containers[?(@.name == "sample-spring-boot-api")].image
          value: piomin/sample-spring-kotlin-microservice:1.1
      - equal:
          path: metadata.labels.app
          value: sample-spring-boot-api
      - equal:
          path: metadata.labels.env
          value: dev
      - equal:
          path: metadata.labels.owner
          value: default
      - matchRegex:
          path: metadata.name
          pattern: ^.*-api$
      - contains:
          path: spec.template.spec.containers[?(@.name == "sample-spring-boot-api")].ports
          content:
            containerPort: 8080
            name: http
      - notContains:
          path: spec.template.spec.containers[?(@.name == "sample-spring-boot-api")].ports
          content:
            containerPort: 80
      - isNotEmpty:
          path: spec.template.spec.containers[?(@.name == "sample-spring-boot-api")].livenessProbe
      - isNotEmpty:
          path: spec.template.spec.containers[?(@.name == "sample-spring-boot-api")].readinessProbe
      - isKind:
          of: Deployment
      - isAPIVersion:
          of: apps/v1

现在,可以通过在项目根目录下执行以下命令来在本地验证测试:

$ helm unittest charts/*

目前,charts目录中只有一个 Chart。假设我们有很多,它会运行所有 Chart 的测试。这是得到的结果:

如果在测试中改变了一些东西来破坏它。现在,结果将如下所示:


CircleCI 中的 Helm Chart 发布管道

一旦我们创建了 Chart 和测试,我们就可以继续进行交付管道。在 CircleCI 管道中,不仅要执行与之前相同的步骤,还需要包括一个发布部分。

首先,将使用 GitHub Releases 和 GitHub Pages 来发布和托管 Chart。为了简化流程,我们可能会使用一个专门用于发布 Helm Chart 的工具:Chart Releaser[5]

我们还需要创建一个个人令牌以传递给 Helm Chart Release 工作流程。访问 Settings > Developer Settings > Personal Access Token。使用repo范围的权限生成个人令牌。然后,应该将这个标记放入 CircleCI 上下文中。您可以为上下文选择任何名称,但环境变量的名称必须是CR_TOKEN,Chart Releaser 需要该名称。我的上下文的名称是GitHub.

以下是需要在管道中执行的步骤列表:

  1. helm在机器上安装 CLI(我们将使用cimg/base镜像作为测试执行器)
  2. 安装 Helm unit-test插件
  3. 运行单元测试
  4. 只有当我们在master分支中进行更改时,才会进行发布部分。第一步,需要用helm package命令打包 Chart
  5. 安装 Chart Releaser
  6. 使用 Chart Releaser 的upload命令在 GitHub 中发布 Chart
  7. 生成 Chart index.yaml 并将其发布到 GitHub Pages

现在让我们定义的 CircleCI 管道。首先,需要在存储库根目录中创建.circleci目录并将config.yml文件放在那里。我们可以使用helm orb 来简化 helm CLI 安装的过程。一旦我们安装了 helm CLI,我们就可以安装unit-test插件并运行单元测试。然后我们定义一个过滤master分支的规则。如果更改被推送到master分支,我们将 Chart 打包为 TAR 存档并将其放在.deploy目录中。然后我们安装 Chart Releaser 并创建一个 GitHub release。在最后一步中,我们使用 Chart Releaser 生成 index.yaml 文件并将其提交到gh-pages分支。

version: 2.1

orbs:
  helm: circleci/helm@2.0.1 # (1)

jobs:
  build:
    docker:
      - image: cimg/base:2023.02
    steps:
      - checkout
      - helm/install-helm-client # (2)
      - run:
          name: Install Helm unit-test
          command: helm plugin install https://github.com/helm-unittest/helm-unittest
      - run: # (3)
          name: Run unit tests
          command: helm unittest charts/*
      - when:
          condition: # (4)
            equal: [ master, << pipeline.git.branch >> ]
          steps:
            - run:
                name: Package chart # (5)
                command: helm package charts/* -d .deploy
            - run:
                name: Install chart releaser
                command: |
                  curl -L -o /tmp/cr.tgz https://github.com/helm/chart-releaser/releases/download/v1.5.0/chart-releaser_1.5.0_linux_amd64.tar.gz
                  tar -xv -C /tmp -f /tmp/cr.tgz
                  mv /tmp/cr ~/bin/cr
            - run:
                name: Release chart # (6)
                command: cr upload -o piomin -r helm-charts -p .deploy
            - run:
                name: Create index on GitHub pages # (7)
                command: |
                  git config user.email "job@circleci.com"
                  git config user.name "CircleCI"
                  git checkout --orphan gh-pages
                  cr index -i ./index.yaml -p .deploy -o piomin -r helm-charts
                  git add index.yaml
                  git commit -m "New release"
                  git push --set-upstream --force origin gh-pages

workflows:
  helm_test:
    jobs:
      - build:
          context: GitHub

执行 Helm Chart 发布管道

一旦我们将更改推送到helm-charts存储库,管道就会启动。如您所见,管道成功完成。我们正在发布0.3.5 Chart 版本。

让我们看看 GitHub 发布的列表。如您所见,该0.3.5版本已经发布。


如何访问 Helm 存储库。为了检查它,请转到存储库Settings > Pages。该存储库的 GitHub 页面地址是 Helm 存储库的地址。在那里发布index.yaml,内容包含了存储库中 Chart 定义的文件。如您所见,Helm 存储库的地址是piomin.github.io/helm-charts

我们可以通过调用https://piomin.github.io/helm-charts/index.yaml URL 来查看index.yaml文件的结构。这是当前发布版本的index.yaml片段。

apiVersion: v1
entries:
  spring-boot-api-app:
  - apiVersion: v2
    appVersion: 1.0.0
    created: "2023-02-28T13:06:28.835693321Z"
    description: A Helm chart for Kubernetes
    digest: b308cbdf9f93f79baf5b39de8a7c509834d7b858f33d79d0c76b528e0cd7ca11
    name: spring-boot-api-app
    type: application
    urls:
    - https://github.com/piomin/helm-charts/releases/download/spring-boot-api-app-0.3.5/spring-boot-api-app-0.3.5.tgz
    version: 0.3.5

假设我们想要使用 Helm Chart,可以轻松访问它。首先,使用 CLI 添加 Helm 存储库:

$ helm repo add piomin https://piomin.github.io/helm-charts/

然后,可以验证存储库中存在的 Helm Chart 列表:

$ helm search repo piomin
NAME                            CHART VERSION   APP VERSION     DESCRIPTION
piomin/spring-boot-api-app      0.3.5           1.0.0           A Helm chart for Kubernetes

将 Helm Chart 发布到 Artifact Hub

为了在 Artifact Hub 上发布您的 Helm 存储库和 Chart,您需要转到该artifacthub 站点[6]并创建一个帐户。完成后,只需单击按钮即可添加新存储库。然后你只需要选择你的 repo 的名称并输入正确的地址。

现在,我们可以在包列表中找到我们的spring-boot-api-app Chart。

可以看到它的细节。值得在README.md文件中发布一些说明文字。完成后,您可以在 Artifact Hub 的 Chart 详细信息中查看它。

最后,我们可以轻松地使用 Chart 部署 Spring Boot 应用程序,例如使用 Argo CD。

- END -

 推荐阅读 





基于Kubernetes构建完整的DevOps体系 ES+Redis+MySQL,这套高可用架构设计太顶了!
Jenkins 自动化发布前端项目
Ngnix IP封禁以及实现自动封禁IP
Nginx 可视化配置神器,一键生成!
这篇文章学会 Ansible 足够了!40个 Nginx 常问面试题Linux运维工程师 50个常见面试题一台服务器最大能支持多少条TCP连接?K8S运维必知必会的 Kubectl 命令总结
16 张图硬核讲解 Kubernetes 网络
史上最全 Jenkins Pipeline流水线详解9 个实用 Shell 脚本,建议收藏!主流监控系统 Prometheus 学习指南搭建一套完整的企业级 K8s 集群(二进制方式)


点亮,服务器三年不宕机
继续滑动看下一个

详解 K8S Helm CI/CD发布流程

点击关注👉 DevOps技术栈
向上滑动看下一个

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

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