尝试在 Kubernetes 中构建和自动化部署的新工具

尝试在 Kubernetes 中构建和自动化部署的新工具

你好! 最近,发布了许多很酷的自动化工具,用于构建 Docker 映像和部署到 Kubernetes。 在这方面,我决定尝试一下 GitLab,彻底研究它的功能,当然,还建立了管道。

这项工作的灵感来自于网站 Kubernetes.io,它是由生成的 源代码 自动,对于发送的每个池请求,机器人会自动生成包含您所做更改的站点预览版本,并提供用于查看的链接。

我尝试从头开始构建类似的流程,但完全基于 Gitlab CI 和我习惯用来将应用程序部署到 Kubernetes 的免费工具。 今天我终于要告诉你更多关于他们的事情了。

本文将讨论以下工具:
雨果, 魁北克, iko子, git 密码 и 亚搏体育app CI 随着动态环境的创建。

内容

  1. 认识雨果
  2. 准备 Dockerfile
  3. 认识卡尼科
  4. 了解qbec
  5. 尝试使用 Gitlab-runner 和 Kubernetes-executor
  6. 使用 qbec 部署 Helm 图表
  7. 介绍 git-crypt
  8. 创建工具箱图像
  9. 我们的第一个管道和按标签组装图像
  10. 部署自动化
  11. 推动掌握时的工件和装配
  12. 动态环境
  13. 评论应用

1. 认识雨果

作为我们项目的一个例子,我们将尝试创建一个基于 Hugo 的文档发布网站。 Hugo 是一个静态内容生成器。

对于那些不熟悉静态发生器的人,我会告诉你更多关于它们的信息。 与带有数据库和一些 PHP 的传统网站引擎(当用户请求时动态生成页面)不同,静态生成器的设计略有不同。 它们允许您获取源代码,通常是 Markdown 标记和主题模板中的一组文件,然后将它们编译成一个完整完成的网站。

也就是说,您将收到一个目录结构和一组生成的 HTML 文件,您可以简单地将其上传到任何廉价托管并获得一个可用的网站。

您可以在本地安装 Hugo 并尝试一下:

初始化新站点:

hugo new site docs.example.org

同时 git 存储库:

cd docs.example.org
git init

到目前为止,我们的网站是原始的,为了在其上显示某些内容,我们首先需要连接一个主题;主题只是一组模板和生成我们网站的指定规则。

对于我们将使用的主题 了解,在我看来,它非常适合文档站点。

我要特别注意的是,我们不需要将主题文件保存在项目存储库中;相反,我们可以简单地使用以下命令连接它 git子模块:

git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn

这样,我们的存储库将只包含与我们的项目直接相关的文件,并且连接的主题将保留为特定存储库的链接以及其中的提交,也就是说,它始终可以从原始源中拉取而不必担心不兼容的更改。

让我们更正一下配置 配置文件:

baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "My Docs Site"
theme = "learn"

在此阶段您可以运行:

hugo server

并在地址 http://localhost:1313/ 查看我们新创建的网站,目录中所做的所有更改都会自动更新浏览器中打开的页面,非常方便!

让我们尝试创建一个封面页 内容/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

新创建的页面的屏幕截图

尝试在 Kubernetes 中构建和自动化部署的新工具

要生成站点,只需运行:

hugo

目录内容 上市/ 并将成为您的网站。
是的,顺便说一下,我们立即将其添加到 的.gitignore:

echo /public > .gitignore

不要忘记提交我们的更改:

git add .
git commit -m "New site created"

2. 准备Dockerfile

是时候定义我们的存储库的结构了。 我通常使用类似的东西:

.
├── deploy
│   ├── app1
│   └── app2
└── dockerfiles
    ├── image1
    └── image2

  • dockerfiles/ — 包含包含 Dockerfile 的目录以及构建 Docker 映像所需的所有内容。
  • 部署/ — 包含用于将我们的应用程序部署到 Kubernetes 的目录

因此,我们将沿着路径创建第一个 Dockerfile dockerfiles/网站/Dockerfile

FROM alpine:3.11 as builder
ARG HUGO_VERSION=0.62.0
RUN wget -O- https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-64bit.tar.gz | tar -xz -C /usr/local/bin
ADD . /src
RUN hugo -s /src

FROM alpine:3.11
RUN apk add --no-cache darkhttpd
COPY --from=builder /src/public /var/www
ENTRYPOINT [ "/usr/bin/darkhttpd" ]
CMD [ "/var/www" ]

如您所见,Dockerfile 包含两个 ,这个机会称为 多阶段构建 并允许您从最终的 docker 镜像中排除所有不必要的内容。
因此,最终图像将仅包含 暗httpd (轻量级 HTTP 服务器)和 上市/ — 我们静态生成的网站的内容。

不要忘记提交我们的更改:

git add dockerfiles/website
git commit -m "Add Dockerfile for website"

3.认识卡尼科

作为一个 docker 镜像构建者,我决定使用 iko子,因为它的操作不需要docker守护进程,并且构建本身可以在任何机器上进行,并且缓存可以直接存储在注册表中,从而无需拥有成熟的持久存储。

要构建图像,只需运行容器即可 卡尼科执行人 并将当前构建上下文传递给它;这也可以通过 docker 在本地完成:

docker run -ti --rm 
  -v $PWD:/workspace 
  -v ~/.docker/config.json:/kaniko/.docker/config.json:ro 
  gcr.io/kaniko-project/executor:v0.15.0 
  --cache 
  --dockerfile=dockerfiles/website/Dockerfile 
  --destination=registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1

哪里 registry.gitlab.com/kvaps/docs.example.org/website — 您的 docker 镜像的名称;构建后,它将自动启动到 docker 注册表中。

参数 - 缓存 允许您在 docker 注册表中缓存层;对于给出的示例,它们将保存在 registry.gitlab.com/kvaps/docs.example.org/website/cache,但您可以使用参数指定另一个路径 --缓存存储库.

docker-registry 的截图

尝试在 Kubernetes 中构建和自动化部署的新工具

4. 了解qbec

魁北克 是一个部署工具,允许您以声明方式描述应用程序清单并将其部署到 Kubernetes。 使用 Jsonnet 作为主要语法可以大大简化跨多个环境的差异描述,并且几乎完全消除代码重复。

当您需要将应用程序部署到具有不同参数的多个集群并希望在 Git 中以声明方式描述它们时,尤其如此。

Qbec 还允许您通过向 Helm 图表传递必要的参数来渲染 Helm 图表,然后以与常规清单相同的方式操作它们,包括您可以对它们应用各种突变,而这反过来又使您无需使用图表博物馆。 也就是说,您可以直接从它们所属的 git 存储和渲染图表。

正如我之前所说,我们将把所有部署存储在一个目录中 部署/:

mkdir deploy
cd deploy

让我们初始化我们的第一个应用程序:

qbec init website
cd website

现在我们的应用程序的结构如下所示:

.
├── components
├── environments
│   ├── base.libsonnet
│   └── default.libsonnet
├── params.libsonnet
└── qbec.yaml

让我们看一下文件 qbec.yaml:

apiVersion: qbec.io/v1alpha1
kind: App
metadata:
  name: website
spec:
  environments:
    default:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443
  vars: {}

在这里我们主要感兴趣的是 规范环境,qbec 已经为我们创建了一个默认环境,并从我们当前的 kubeconfig 中获取了服务器地址和命名空间。
现在当部署到 默认 环境中,qbec 将始终仅部署到指定的 Kubernetes 集群和指定的命名空间,也就是说,您不再需要在上下文和命名空间之间切换来执行部署。
如有必要,您可以随时更新此文件中的设置。

您的所有环境均在中描述 qbec.yaml,并在文件中 params.libsonnet,其中表示从哪里获取它们的参数。

接下来我们看到两个目录:

  • 组件/ — 我们应用程序的所有清单都将存储在这里;它们可以在 jsonnet 和常规 yaml 文件中进行描述
  • 环境/ - 在这里我们将描述我们环境的所有变量(参数)。

默认情况下我们有两个文件:

  • 环境/base.libsonnet - 它将包含所有环境的通用参数
  • 环境/default.libsonnet — 包含环境覆盖的参数 默认

让我们打开 环境/base.libsonnet 并为我们的第一个组件添加参数:

{
  components: {
    website: {
      name: 'example-docs',
      image: 'registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1',
      replicas: 1,
      containerPort: 80,
      servicePort: 80,
      nodeSelector: {},
      tolerations: [],
      ingressClass: 'nginx',
      domain: 'docs.example.org',
    },
  },
}

我们还创建我们的第一个组件 组件/website.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.website;

[
  {
    apiVersion: 'apps/v1',
    kind: 'Deployment',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      replicas: params.replicas,
      selector: {
        matchLabels: {
          app: params.name,
        },
      },
      template: {
        metadata: {
          labels: { app: params.name },
        },
        spec: {
          containers: [
            {
              name: 'darkhttpd',
              image: params.image,
              ports: [
                {
                  containerPort: params.containerPort,
                },
              ],
            },
          ],
          nodeSelector: params.nodeSelector,
          tolerations: params.tolerations,
          imagePullSecrets: [{ name: 'regsecret' }],
        },
      },
    },
  },
  {
    apiVersion: 'v1',
    kind: 'Service',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      selector: {
        app: params.name,
      },
      ports: [
        {
          port: params.servicePort,
          targetPort: params.containerPort,
        },
      ],
    },
  },
  {
    apiVersion: 'extensions/v1beta1',
    kind: 'Ingress',
    metadata: {
      annotations: {
        'kubernetes.io/ingress.class': params.ingressClass,
      },
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      rules: [
        {
          host: params.domain,
          http: {
            paths: [
              {
                backend: {
                  serviceName: params.name,
                  servicePort: params.servicePort,
                },
              },
            ],
          },
        },
      ],
    },
  },
]

在此文件中,我们同时描述了三个 Kubernetes 实体,它们是: 部署, 服务 и 入口。 如果我们愿意,我们可以将它们放入不同的组件中,但在现阶段,一个对我们来说就足够了。

句法 jsonnet 与常规 json 非常相似,原则上,常规 json 已经是有效的 jsonnet,所以首先您可能会更容易使用在线服务,例如 yaml2json 将您常用的 yaml 转换为 json,或者,如果您的组件不包含任何变量,则可以以常规 yaml 的形式描述它们。

使用时 jsonnet 我强烈建议为您的编辑器安装一个插件

例如,有一个 vim 的插件 vim-jsonnet,打开语法高亮并自动执行 jsonnet fmt 每次保存时(需要安装 jsonnet)。

一切准备就绪,现在我们可以开始部署了:

要查看我们得到了什么,让我们运行:

qbec show default

在输出中,您将看到将应用于默认集群的渲染 yaml 清单。

太好了,现在申请:

qbec apply default

在输出中,您将始终看到集群中将执行的操作,qbec 会要求您输入同意更改 y 您将能够确认您的意图。

我们的应用程序已准备就绪并已部署!

如果您进行更改,您始终可以执行以下操作:

qbec diff default

查看这些更改将如何影响当前部署

不要忘记提交我们的更改:

cd ../..
git add deploy/website
git commit -m "Add deploy for website"

5. 尝试使用 Kubernetes-executor 来使用 Gitlab-runner

直到最近我只使用常规 gitlab-runner 在带有 shell 或 docker-executor 的预先准备好的机器(LXC 容器)上。 最初,我们在 gitlab 中全局定义了几个这样的运行程序。 他们收集了所有项目的 docker 镜像。

但实践表明,这种选择无论是从实用性还是安全性来看都不是最理想的。 为每个项目甚至每个环境部署单独的运行程序会更好,在意识形态上也更正确。

幸运的是,这根本不是问题,因为现在我们将部署 gitlab-runner 直接作为我们 Kubernetes 项目的一部分。

Gitlab 提供了一个现成的 Helm Chart,用于将 gitlab-runner 部署到 Kubernetes。 所以你需要做的就是找出 注册令牌 对于我们的项目 设置 -> CI / CD -> 跑步者 并将其传递给掌舵:

helm repo add gitlab https://charts.gitlab.io

helm install gitlab-runner 
  --set gitlabUrl=https://gitlab.com 
  --set runnerRegistrationToken=yga8y-jdCusVDn_t4Wxc 
  --set rbac.create=true 
  gitlab/gitlab-runner

在哪里:

  • https://gitlab.com — 您的 Gitlab 服务器的地址。
  • yga8y-jdCusVDn_t4Wxc — 您的项目的注册令牌。
  • rbac.create=true — 为运行者提供必要的权限,以便能够使用 kubernetes-executor 创建 pod 来执行我们的任务。

如果一切都正确完成,您应该会在 部分中看到已注册的跑步者 跑人,在您的项目设置中。

添加的跑步者的屏幕截图

尝试在 Kubernetes 中构建和自动化部署的新工具

真有这么简单吗? - 是的,就是这么简单! 不再需要手动注册跑步者的麻烦,从现在开始,跑步者将自动创建和销毁。

6. 使用 QBEC 部署 Helm 图表

既然我们决定考虑 gitlab-runner 作为我们项目的一部分,是时候在我们的 Git 存储库中描述它了。

我们可以将其描述为一个单独的组件 官网,但未来我们计划部署不同的副本 官网 很多时候,不像 gitlab-runner,每个 Kubernetes 集群仅部署一次。 因此,让我们为其初始化一个单独的应用程序:

cd deploy
qbec init gitlab-runner
cd gitlab-runner

这次我们不会手动描述 Kubernetes 实体,而是采用现成的 Helm 图表。 qbec 的优点之一是能够直接从 Git 存储库渲染 Helm 图表。

让我们使用 git 子模块连接它:

git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runner

现在目录 供应商/gitlab-runner 我们有一个带有 gitlab-runner 图表的存储库。

以类似的方式,您可以连接其他存储库,例如带有官方图表的整个存储库 https://github.com/helm/charts

我们来描述一下这个组件 组件/gitlab-runner.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.gitlabRunner;

std.native('expandHelmTemplate')(
  '../vendor/gitlab-runner',
  params.values,
  {
    nameTemplate: params.name,
    namespace: env.namespace,
    thisFile: std.thisFile,
    verbose: true,
  }
)

第一个参数 展开Helm模板 我们将路径传递给图表,然后 参数值,我们从环境参数中获取,然后得到对象

  • 名称模板 — 发布名称
  • 命名空间 — 命名空间转移到 helm
  • 这个文件 — 传递当前文件路径的必需参数
  • 详细 - 显示命令 舵模板 渲染图表时包含所有参数

现在让我们描述一下我们组件的参数 环境/base.libsonnet:

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
      },
    },
  },
}

注意 跑步者注册令牌 我们从外部文件中获取 秘密/base.libsonnet,让我们创建它:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

让我们检查一下是否一切正常:

qbec show default

如果一切正常,那么我们可以通过 Helm 删除之前部署的版本:

helm uninstall gitlab-runner

并以相同的方式部署它,但通过 qbec:

qbec apply default

7.git-crypt简介

Git 加密 是一个允许您为存储库设置透明加密的工具。

目前,我们的 gitlab-runner 目录结构如下所示:

.
├── components
│   ├── gitlab-runner.jsonnet
├── environments
│   ├── base.libsonnet
│   └── default.libsonnet
├── params.libsonnet
├── qbec.yaml
├── secrets
│   └── base.libsonnet
└── vendor
    └── gitlab-runner (submodule)

但是在 Git 中存储秘密并不安全,不是吗?所以我们需要对它们进行正确的加密。

通常,为了一个变量,这并不总是有意义的。 您可以将秘密转移到 魁北克 并通过 CI 系统的环境变量。
但值得注意的是,还有更复杂的项目可以包含更多秘密;通过环境变量将它们全部转移将非常困难。

而且,在这种情况下,我将无法告诉您这样一个出色的工具 git 密码.

git 密码 它还很方便,因为它允许您保存整个秘密历史记录,以及比较、合并和解决冲突,就像我们在 Git 中习惯做的那样。

安装后第一件事 git 密码 我们需要为我们的存储库生成密钥:

git crypt init

如果您有 PGP 密钥,那么您可以立即将自己添加为该项目的合作者:

git-crypt add-gpg-user [email protected]

这样,您始终可以使用您的私钥解密此存储库。

如果您没有 PGP 密钥并且不希望有它,那么您可以采用其他方法并导出项目密钥:

git crypt export-key /path/to/keyfile

因此,任何拥有出口的人 密钥文件 将能够解密您的存储库。

是时候建立我们的第一个秘密了。
让我提醒您,我们仍在目录中 部署/gitlab-runner/,我们有一个目录 秘密/,让我们加密其中的所有文件,为此我们将创建一个文件 秘密/.gitattributes 包含以下内容:

* filter=git-crypt diff=git-crypt
.gitattributes !filter !diff

从内容可以看出,所有文件都被屏蔽了 * 将被驱动通过 git 密码,除了大多数 .git 属性

我们可以通过运行来检查这一点:

git crypt status -e

输出将是存储库中启用加密的所有文件的列表

就这样,现在我们可以安全地提交更改了:

cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"

要阻止存储库,只需运行:

git crypt lock

所有加密的文件都会立即变成二进制文件,无法读取它们。
要解密存储库,请运行:

git crypt unlock

8. 创建工具箱镜像

工具箱映像是包含我们将用于部署项目的所有工具的映像。 Gitlab 运行程序将使用它将来执行典型的部署任务。

这里一切都很简单,让我们创建一个新的 dockerfiles/工具箱/Dockerfile 包含以下内容:

FROM alpine:3.11

RUN apk add --no-cache git git-crypt

RUN QBEC_VER=0.10.3 
 && wget -O- https://github.com/splunk/qbec/releases/download/v${QBEC_VER}/qbec-linux-amd64.tar.gz 
     | tar -C /tmp -xzf - 
 && mv /tmp/qbec /tmp/jsonnet-qbec /usr/local/bin/

RUN KUBECTL_VER=1.17.0 
 && wget -O /usr/local/bin/kubectl 
      https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/linux/amd64/kubectl 
 && chmod +x /usr/local/bin/kubectl

RUN HELM_VER=3.0.2 
 && wget -O- https://get.helm.sh/helm-v${HELM_VER}-linux-amd64.tar.gz 
     | tar -C /tmp -zxf - 
 && mv /tmp/linux-amd64/helm /usr/local/bin/helm

正如您所看到的,在此图像中,我们安装了用于部署应用程序的所有实用程序。 我们这里不需要它,除非 Kubectl,但您可能想在管道设置阶段使用它。

此外,为了能够与 Kubernetes 通信并部署到它,我们需要为 gitlab-runner 生成的 pod 配置角色。

为此,我们使用 gitlab-runner 进入目录:

cd deploy/gitlab-runner

并添加一个新组件 组件/rbac.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.rbac;

[
  {
    apiVersion: 'v1',
    kind: 'ServiceAccount',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
  },
  {
    apiVersion: 'rbac.authorization.k8s.io/v1',
    kind: 'Role',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
    rules: [
      {
        apiGroups: [
          '*',
        ],
        resources: [
          '*',
        ],
        verbs: [
          '*',
        ],
      },
    ],
  },
  {
    apiVersion: 'rbac.authorization.k8s.io/v1',
    kind: 'RoleBinding',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
    roleRef: {
      apiGroup: 'rbac.authorization.k8s.io',
      kind: 'Role',
      name: params.name,
    },
    subjects: [
      {
        kind: 'ServiceAccount',
        name: params.name,
        namespace: env.namespace,
      },
    ],
  },
]

我们还将在中描述新参数 环境/base.libsonnet,现在看起来像这样:

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
        runners: {
          serviceAccountName: $.components.rbac.name,
          image: 'registry.gitlab.com/kvaps/docs.example.org/toolbox:v0.0.1',
        },
      },
    },
    rbac: {
      name: 'gitlab-runner-deploy',
    },
  },
}

注意 $.components.rbac.name 指的是 姓名 对于组件 RBAC

让我们检查一下发生了什么变化:

qbec diff default

并将我们的更改应用到 Kubernetes:

qbec apply default

另外,不要忘记将我们的更改提交到 git:

cd ../..
git add dockerfiles/toolbox
git commit -m "Add Dockerfile for toolbox"
git add deploy/gitlab-runner
git commit -m "Configure gitlab-runner to use toolbox"

9. 我们的第一个管道和按标签组装图像

我们将在项目的根目录创建 .gitlab-ci.yml 包含以下内容:

.build_docker_image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug-v0.15.0
    entrypoint: [""]
  before_script:
    - echo "{"auths":{"$CI_REGISTRY":{"username":"$CI_REGISTRY_USER","password":"$CI_REGISTRY_PASSWORD"}}}" > /kaniko/.docker/config.json

build_toolbox:
  extends: .build_docker_image
  script:
    - /kaniko/executor --cache --context $CI_PROJECT_DIR/dockerfiles/toolbox --dockerfile $CI_PROJECT_DIR/dockerfiles/toolbox/Dockerfile --destination $CI_REGISTRY_IMAGE/toolbox:$CI_COMMIT_TAG
  only:
    refs:
      - tags

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_TAG
  only:
    refs:
      - tags

请注意我们使用 GIT_SUBMODULE_STRATEGY:正常 对于那些需要在执行前显式初始化子模块的作业。

不要忘记提交我们的更改:

git add .gitlab-ci.yml
git commit -m "Automate docker build"

我想我们可以放心地称之为一个版本 v0.0.1 并添加标签:

git tag v0.0.1

每当我们需要发布新版本时,我们都会添加标签。 Docker 镜像中的标签将与 Git 标签绑定。每次带有新标签的推送都会使用该标签初始化镜像的构建。

将执行 git push --标签,让我们看看我们的第一个管道:

第一个管道的屏幕截图

尝试在 Kubernetes 中构建和自动化部署的新工具

值得注意的是,通过标签组装适合构建 docker 镜像,但不适合将应用程序部署到 Kubernetes。 由于新标签可以分配给旧提交,在这种情况下,为它们初始化管道将导致旧版本的部署。

为了解决这个问题,通常将docker镜像的构建与标签绑定在一起,并将应用程序部署到分支 ,其中收集的图像的版本是硬编码的。 您可以在此处通过简单的恢复来初始化回滚 - 分支机构。

10. 部署自动化

为了让 Gitlab-runner 解密我们的秘密,我们需要导出存储库密钥并将其添加到我们的 CI 环境变量中:

git crypt export-key /tmp/docs-repo.key
base64 -w0 /tmp/docs-repo.key; echo

我们将把生成的行保存在 Gitlab 中;为此,我们进入项目设置:
设置 -> CI / CD -> 变量

让我们创建一个新变量:

Type


保护
蒙面
范围

File
GITCRYPT_KEY
<your string>
true (在培训期间您可以 false)
true
All environments

添加的变量的屏幕截图

尝试在 Kubernetes 中构建和自动化部署的新工具

现在让我们更新我们的 .gitlab-ci.yml 添加到它:

.deploy_qbec_app:
  stage: deploy
  only:
    refs:
      - master

deploy_gitlab_runner:
  extends: .deploy_qbec_app
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  before_script:
    - base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
  script:
    - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes

deploy_website:
  extends: .deploy_qbec_app
  script:
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes

在这里,我们为 qbec 启用了几个新选项:

  • --根一些/应用程序 — 允许您确定特定应用程序的目录
  • --force:k8s-context __incluster__ - 这是一个神奇的变量,表示部署将在 gtilab-runner 运行的同一集群中进行。 这是必要的,因为否则 qbec 会尝试在您的 kubeconfig 中找到合适的 Kubernetes 服务器
  • - 等待 — 强制 qbec 等待,直到它创建的资源进入就绪状态,然后才以成功的退出代码退出。
  • -是的 - 简单地禁用交互式 shell 你确定吗? 部署时。

不要忘记提交我们的更改:

git add .gitlab-ci.yml
git commit -m "Automate deploy"

之后 git推 我们将看到我们的应用程序是如何部署的:

第二条管道的截图

尝试在 Kubernetes 中构建和自动化部署的新工具

11. 推到master时的工件和装配

通常,上述步骤足以构建和交付几乎任何微服务,但我们不想每次需要更新站点时都添加标签。 因此,我们将采取更动态的路线,在 master 分支中设置摘要部署。

这个想法很简单:现在我们的形象 官网 每次你推入时都会重建 ,然后自动部署到Kubernetes。

让我们更新这两个作业 .gitlab-ci.yml:

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - mkdir -p $CI_PROJECT_DIR/artifacts
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
  artifacts:
    paths:
      - artifacts/
  only:
    refs:
      - master
      - tags

deploy_website:
  extends: .deploy_qbec_app
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

请注意我们添加了一个线程 к 裁判 为了工作 建立网站 我们现在使用 $CI_COMMIT_REF_NAME 而不是 $CI_COMMIT_TAG,也就是说,我们摆脱了 Git 中的标签,现在我们将推送一个带有初始化管道的提交分支名称的镜像。 值得注意的是,这也适用于标签,这将允许我们在 docker-registry 中保存具有特定版本的站点的快照。

当新版本站点的 docker 标签名称可以不变时,我们仍然必须向 Kubernetes 描述更改,否则它根本不会从新镜像重新部署应用程序,因为它不会注意到部署清单。

选项 —vm:ext-str 摘要=”$DIGEST” for qbec - 允许您将外部变量传递给 jsonnet。 我们希望随着应用程序的每个版本将其重新部署到集群中。 我们不能再使用标签名称,它现在是不可更改的,因为我们需要绑定到图像的特定版本并在其更改时触发部署。

在这里,我们将受益于 Kaniko 将摘要图像保存到文件的能力(选项 --摘要文件)
然后我们将传输这个文件并在部署时读取它。

让我们更新我们的参数 部署/网站/环境/base.libsonnet 现在看起来像这样:

{
  components: {
    website: {
      name: 'example-docs',
      image: 'registry.gitlab.com/kvaps/docs.example.org/website@' + std.extVar('digest'),
      replicas: 1,
      containerPort: 80,
      servicePort: 80,
      nodeSelector: {},
      tolerations: [],
      ingressClass: 'nginx',
      domain: 'docs.example.org',
    },
  },
}

完成,现在任何提交 初始化 docker 镜像的构建 官网,然后部署到Kubernetes。

不要忘记提交我们的更改:

git add .
git commit -m "Configure dynamic build"

我们稍后会检查 git推 我们应该看到这样的东西:

master 管道的屏幕截图

尝试在 Kubernetes 中构建和自动化部署的新工具

原则上,我们不需要在每次推送时重新部署 gitlab-runner,当然,除非它的配置没有任何变化,让我们修复它 .gitlab-ci.yml:

deploy_gitlab_runner:
  extends: .deploy_qbec_app
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  before_script:
    - base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
  script:
    - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes
  only:
    changes:
      - deploy/gitlab-runner/**/*

变化 将允许您监视变化 部署/gitlab-runner/ 并且只有在有任何情况时才会触发我们的工作

不要忘记提交我们的更改:

git add .gitlab-ci.yml
git commit -m "Reduce gitlab-runner deploy"

git推,这样更好:

更新后的管道的屏幕截图

尝试在 Kubernetes 中构建和自动化部署的新工具

12. 动态环境

是时候通过动态环境使我们的管道多样化了。

首先我们来更新一下作业 建立网站 在我们的 .gitlab-ci.yml,从中删除块 仅由,这将强制 Gitlab 在对任何分支的任何提交时触发它:

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - mkdir -p $CI_PROJECT_DIR/artifacts
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
  artifacts:
    paths:
      - artifacts/

然后更新作业 部署网站,在那里添加一个块 环境:

deploy_website:
  extends: .deploy_qbec_app
  environment:
    name: prod
    url: https://docs.example.org
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

这将允许 Gitlab 将作业与 环境并显示正确的链接。

现在让我们再添加两个作业:

deploy_website:
  extends: .deploy_qbec_app
  environment:
    name: prod
    url: https://docs.example.org
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

deploy_review:
  extends: .deploy_qbec_app
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: http://$CI_ENVIRONMENT_SLUG.docs.example.org
    on_stop: stop_review
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply review --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
  only:
    refs:
    - branches
  except:
    refs:
      - master

stop_review:
  extends: .deploy_qbec_app
  environment:
    name: review/$CI_COMMIT_REF_NAME
    action: stop
  stage: deploy
  before_script:
    - git clone "$CI_REPOSITORY_URL" master
    - cd master
  script:
    - qbec delete review --root deploy/website --force:k8s-context __incluster__ --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
  variables:
    GIT_STRATEGY: none
  only:
    refs:
    - branches
  except:
    refs:
      - master
  when: manual

它们将在推送到除 master 之外的任何分支时启动,并将部署站点的预览版本。

我们看到 qbec 的一个新选项: --应用程序标签 — 它允许您标记应用程序的已部署版本,并且仅在该标记内工作;在 Kubernetes 中创建和销毁资源时,qbec 将仅对它们进行操作。
这样我们就不能为每次评审创建单独的环境,而只是重复使用同一个环境。

这里我们也使用 qbec申请审查而不是 qbec 应用默认值 - 这正是我们尝试描述我们的环境差异(审核和默认)的时刻:

添加 检讨 环境在 部署/网站/qbec.yaml

spec:
  environments:
    review:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443

然后我们将在中声明它 部署/网站/params.libsonnet:

local env = std.extVar('qbec.io/env');
local paramsMap = {
  _: import './environments/base.libsonnet',
  default: import './environments/default.libsonnet',
  review: import './environments/review.libsonnet',
};

if std.objectHas(paramsMap, env) then paramsMap[env] else error 'environment ' + env + ' not defined in ' + std.thisFile

并记下它的自定义参数 部署/网站/环境/review.libsonnet:

// this file has the param overrides for the default environment
local base = import './base.libsonnet';
local slug = std.extVar('qbec.io/tag');
local subdomain = std.extVar('subdomain');

base {
  components+: {
    website+: {
      name: 'example-docs-' + slug,
      domain: subdomain + '.docs.example.org',
    },
  },
}

我们也来仔细看看jobu 停止评论,当分支被删除时会触发,这样gitlab就不会尝试检查它是否被使用 GIT_STRATEGY:无,稍后我们克隆 - 通过它分支和删除评论。
这有点令人困惑,但我还没有找到更漂亮的方法。
另一种选择是将每个评论部署到酒店名称空间,该名称空间始终可以完全拆除。

不要忘记提交我们的更改:

git add .
git commit -m "Enable automatic review"

git推, git checkout -b 测试, git推送原点测试, 查看:

Gitlab 中创建的环境截图

尝试在 Kubernetes 中构建和自动化部署的新工具

一切正常吗? - 太好了,删除我们的测试分支: git checkout主, git推送原点:测试,我们检查环境删除作业是否正常运行。

这里我想立即澄清,项目中的任何开发人员都可以创建分支,他也可以更改 .gitlab-ci.yml 文件和访问秘密变量。
因此,强烈建议仅允许将它们用于受保护的分支,例如 ,或为每个环境创建一组单独的变量。

13.审查应用程序

评论应用 这是 GitLab 的一项功能,允许您为存储库中的每个文件添加一个按钮,以便在部署的环境中快速查看它。

为了显示这些按钮,您需要创建一个文件 .gitlab/route-map.yml 并描述其中的所有路径转换;在我们的例子中,它会非常简单:

# Indices
- source: /content/(.+?)_index.(md|html)/ 
  public: '1'

# Pages
- source: /content/(.+?).(md|html)/ 
  public: '1/'

不要忘记提交我们的更改:

git add .gitlab/
git commit -m "Enable review apps"

git推,并检查:

“查看应用程序”按钮的屏幕截图

尝试在 Kubernetes 中构建和自动化部署的新工具

工作完成了!

项目来源:

感谢您的关注,希望您喜欢 尝试在 Kubernetes 中构建和自动化部署的新工具

来源: habr.com

添加评论