
你好 最近,已经发布了许多很酷的自动化工具,用于构建Docker映像以及将其部署到Kubernetes。 在这方面,我决定使用gitlab,研究如何研究它的功能,当然还要配置管道。
这项工作的灵感来自网站kubernetes.io ,该网站是从源代码自动生成的,对于每个发送的池,机器人会自动生成包含您所做更改的网站预览版本,并提供查看链接。
我尝试从头开始构建一个类似的过程,但完全基于Gitlab CI和我用来在Kubernetes中部署应用程序的免费工具。 今天,我最终将向您详细介绍它们。
本文将考虑以下工具:
Hugo , qbec , kaniko , git-crypt和GitLab CI以及动态环境的创建。
目录内容
- 雨果简介
- 准备Dockerfile
- 与kaniko相识
- qbec简介
- 用Kubernetes-executor尝试Gitlab-runner
- 使用qbec部署Helm图表
- 引入git-crypt
- 创建工具箱图像
- 我们的第一个管道和标签的图像组装
- 部署自动化
- 工件和推入构建大师
- 动态环境
- 评论应用
1.雨果简介
作为我们项目的一个示例,我们将尝试创建一个网站来发布基于Hugo的文档。 Hugo是静态内容生成器。
对于那些不熟悉静态生成器的人,我会告诉您更多有关它们的信息。 与常规数据库站点引擎和某些php引擎不同,当用户提示时,它们会即时生成页面,而静态生成器的排列方式略有不同。 它们允许您获取源,通常它是Markdown标记和主题模板中的一组文件,然后将它们编译成一个完整的网站。
也就是说,在输出中,您将获得目录结构和一组生成的html文件,可以将其简单地上载到任何便宜的主机并获得一个工作站点。
雨果可以在本地安装并试用:
我们初始化新站点:
hugo new site docs.example.org
并同时git存储库:
cd docs.example.org git init
到目前为止,我们的网站是原始的,要使它首先出现在其中,我们需要关联一个主题,一个主题-它只是生成我们网站所用的一组模板和预设规则。
作为主题,我们将使用Learn ,我认为它最适合包含文档的网站。
我要特别注意一个事实,我们不需要将主题文件保存在我们项目的存储库中,而是可以使用git submodule进行简单连接:
git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn
因此,在我们的存储库中将只有与项目直接相关的文件,并且连接的主题将保持为指向特定存储库的链接的形式并在其中提交,即始终可以将其从原始源中拉出而不必担心更改不兼容。
修复config.toml配置:
baseURL = "http://docs.example.org/" languageCode = "en-us" title = "My Docs Site" theme = "learn"
在此阶段,您可以运行:
hugo server
在http:// localhost:1313 /检查我们新创建的站点,对目录所做的所有更改都会自动更新,并且在浏览器中打开页面非常方便!
让我们尝试在content / _index.md中创建封面:
# My docs site ## Welcome to the docs! You will be very smart :-)
要生成一个站点,只需运行:
hugo
public /目录的内容将成为您的站点。
是的,顺便说一句,让我们立即将其添加到.gitignore :
echo /public > .gitignore
不要忘记提交我们的更改:
git add . git commit -m "New site created"
2.准备Dockerfile
现在是确定存储库结构的时候了。 通常我使用类似:
. ├── deploy │ ├── app1 │ └── app2 └── dockerfiles ├── image1 └── image2
- dockerfiles--包含Dockerfiles目录以及构建docker映像所需的一切。
- deploy / -包含用于将我们的应用程序部署到Kubernetes的目录
因此,我们将沿着路径dockerfiles / website / Dockerfile创建我们的第一个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包含两个FROM,此功能称为多阶段构建 ,可让您从最终docker映像中排除所有不必要的内容。
因此,最终图像将仅包含darkhttpd (轻量级HTTP服务器)和public / -静态生成的站点的内容。
不要忘记提交我们的更改:
git add dockerfiles/website git commit -m "Add Dockerfile for website"
3.认识加尼子
作为docker映像的收集者,我决定使用kaniko ,因为它不需要docker守护进程来工作,并且程序集本身可以在任何计算机上执行并将缓存直接存储在注册表中,从而消除了对完整持久性存储的需求。 。
要构建映像,只需使用kaniko executor启动容器并将当前的构建上下文传递给它,您可以通过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是您的泊坞窗映像的名称,在组装后它将被自动推送到泊坞窗寄存器中。
--cache参数允许您在docker注册表中缓存图层,对于给定的示例,它们将保存在Registry.gitlab.com/kvaps/docs.example.org/website/cache中 ,但是您可以使用--cache-参数指定其他路径回购
4.认识qbec
Qbec是一个部署工具,可让您声明性地描述应用程序的清单并将其部署到Kubernetes。 使用Jsonnet作为主要语法,可以大大简化几种环境下差异的描述,并且几乎完全消除了代码的可重复性。
在需要将应用程序部署到具有不同参数的多个集群中并且想要在Git中声明性地描述它们的情况下,尤其如此。
Qbec还允许您通过传递必要的参数并随后对其进行操作以及正常的声明(包括可以叠加在其上的各种突变)来渲染Helm图表,从而消除了使用ChartMuseum的需要。 也就是说,您可以直接从git所在的位置存储和渲染图表。
如前所述,我们将所有部署存储在deploy /目录中:
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: {}
在这里,我们主要对spec.environments感兴趣,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', }, }, }
我们还将创建我们的第一个组件component / 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实体,它们是: Deployment , Service和Ingress 。 如果需要,我们可以将它们包含在不同的组件中,但是在此阶段,一个就足够了。
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
直到最近,我仅在带有shell或docker-executor的预先准备好的机器(LXC容器)上使用普通的gitlab-runner 。 最初,我们在hitlab中全局定义了其中一些跑步者。 他们收集了所有项目的docker映像。
但是,正如实践所示,从实用性和安全性角度来看,此选项都不是最理想的选择。 为每个项目甚至为每个环境部署单独的运行器会更好,并且在思想上更正确。
幸运的是,这根本不是问题,因为现在我们将直接将gitlab-runner作为我们项目的一部分直接部署到Kubernetes。
Gitlab提供了一个现成的舵图,用于在Kubernetes中部署gitlab-runner。 因此,您需要做的就是在设置-> CI / CD-> Runners中找到我们项目的注册令牌 ,并将其传递给头盔:
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创建吊舱以执行我们的任务。
如果一切正确完成,则应该在项目设置的“运行者”部分中看到已注册的运行者 。
这么简单吗? -是的,很简单! 手动注册跑步者不再麻烦,从现在开始,跑步者将被自动创建和销毁。
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
现在, vendor / 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, } )
expandHelmTemplate的第一个参数是传递路径到图表,然后是params.values ,它是从环境参数中获取的,然后是一个带有
- nameTemplate-发布名称
- 名称空间 -传递给舵机的名称空间
- thisFile-将路径传递到当前文件的必需参数
- 详细 -呈现图表时显示带有所有参数的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, }, }, }, }
注意我们从外部secrets / base.libsonnet文件中获取的RunnerRegistrationToken ,让我们创建它:
{ runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc', }
检查一切是否正常:
qbec show default
如果一切正常,那么我们可以通过Helm版本删除较早的版本:
helm uninstall gitlab-runner
并部署它,但是已经通过qbec进行了部署:
qbec apply default
7.引入git-crypt
Git-crypt是一种工具,可让您为存储库设置透明加密。
目前,我们gitlab-runner的目录结构如下:
. ├── components │ ├── gitlab-runner.jsonnet ├── environments │ ├── base.libsonnet │ └── default.libsonnet ├── params.libsonnet ├── qbec.yaml ├── secrets │ └── base.libsonnet └── vendor └── gitlab-runner (submodule)
但是在Git中保密并不安全,对吗? 因此,我们需要对其进行适当的加密。
通常,为了单个变量,这并不总是有意义。 您可以将机密传递给qbec并通过CI系统的环境变量。
但是,值得注意的是,还有更复杂的项目可以包含更多的秘密;通过环境变量将它们全部转移将极为困难。
另外,在这种情况下,我将无法告诉您有关git-crypt之类的出色工具的信息。
git-crypt也很方便,因为它允许您保存所有秘密历史记录,以及以与以前在Git情况下相同的方式比较,合并和解决冲突。
首先,在安装git-crypt之后,我们需要为存储库生成密钥:
git crypt init
如果您具有PGP密钥,则可以立即将自己添加为该项目的协作者:
git-crypt add-gpg-user kvapss@gmail.com
因此,您始终可以使用私钥解密该存储库。
如果您没有PGP密钥并且不期望使用,则可以采用另一种方法导出项目密钥:
git crypt export-key /path/to/keyfile
这样,拥有导出密钥文件的任何人都可以解密您的存储库。
现在是设置我们的第一个秘密的时候了。
让我提醒您,我们仍然位于deploy / gitlab-runner /目录中,在该目录中有secrets /目录,让我们加密其中的所有文件,为此,我们创建具有以下内容的secrets / .gitattributes文件 :
* filter=git-crypt diff=git-crypt .gitattributes !filter !diff
正如您从内容中看到的那样,除.gitattributes本身之外,所有带掩码*的文件都将通过git-crypt运行
我们可以通过运行以下命令进行验证:
git crypt status -e
在输出中,我们获得存储库中已启用加密的所有文件的列表
就是这样,现在我们可以安全地进行更改了:
cd ../.. git add . git commit -m "Add deploy for gitlab-runner"
为了阻止存储库,只需执行以下操作:
git crypt lock
然后所有加密的文件都变成二进制文件,将无法读取它们。
要解密存储库,请执行以下操作:
git crypt unlock
工具箱映像就是包含用于部署项目的所有工具的映像。 gitlab运行程序将使用它来执行典型的部署任务。
这里的一切都很简单,我们创建一个具有以下内容的新dockerfiles / toolbox / 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
并添加一个新的组件components / 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:normal 。
不要忘记提交我们的更改:
git add .gitlab-ci.yml git commit -m "Automate docker build"
我认为您可以放心地将其命名为v0.0.1版本并挂一个标签:
git tag v0.0.1
每当需要发布新版本时,我们都会挂起标签。 Docker映像中的标签将绑定到Git标签。 带有新标签的每次推送都会初始化带有该标签的图像集合。
运行git push --tags ,然后看一下我们的第一个管道:
值得关注的事实是,按标签组装适用于组装Docker映像,但不适用于在Kubernetes中部署应用程序。 由于新标签也可以分配给旧提交,因此在这种情况下,对它们的管道初始化将导致旧版本的部署。
为了解决此问题,通常将docker-images的程序集附加到标签,然后将应用程序部署到master分支,在该分支中,所收集映像的版本将进行硬编码。 在这种情况下,您可以使用简单的还原主标签来初始化回滚。
10.部署自动化
为了让Gitlab-runner解密我们的机密,我们需要导出存储库密钥,并将其添加到CI的环境变量中:
git crypt export-key /tmp/docs-repo.key base64 -w0 /tmp/docs-repo.key; echo
将结果字符串保存在Gitlab中,为此,我们将转到项目的设置:
设置-> CI / CD->变量
:
.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:
- --root some/app —
- --force:k8s-context __incluster__ — , gtilab-runner. , qbec Kubernetes- kubeconfig
- --wait — qbec , Ready exit-code.
- --yes — Are you sure? .
:
git add .gitlab-ci.yml git commit -m "Automate deploy"
git push :
11. push master
, . digest master-.
: website push 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"
, master refs build_website $CI_COMMIT_REF_NAME $CI_COMMIT_TAG , Git . , , docker-registry.
docker- , Kubernetes, , .
--vm:ext-str digest="$DIGEST" qbec — jsonnet. . , , , .
Kaniko digest ( --digest-file )
.
deploy/website/environments/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', }, }, }
, master docker- website , Kubernetes.
:
git add . git commit -m "Configure dynamic build"
, git push - :
gitlab-runner push, , , , .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/**/*
changes deploy/gitlab-runner/
:
git add .gitlab-ci.yml git commit -m "Reduce gitlab-runner deploy"
git push , - :
12. Dynamic environments
.
build_website .gitlab-ci.yml , only , 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 , environment :
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 prod .
:
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
push master preview .
qbec: --app-tag — , Kubernetes qbec .
review, .
qbec apply review , qbec apply default — (review default):
review deploy/website/qbec.yaml
spec: environments: review: defaultNamespace: docs server: https://kubernetes.example.org:8443
deploy/website/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
deploy/website/environments/review.libsonnet :
stop_review , gitlab checkout GIT_STRATEGY: none , master - review .
, .
review , .
:
git add . git commit -m "Enable automatic review"
git push , git checkout -b test , git push origin test , :
? — , : git checkout master , git push origin :test , environment .
, , .gitlab-ci.yml .
protected-, master , .
13. Review Apps
Review Apps , .
, .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 push , :
Job is done!
:
, 