
Oi Recentemente, muitas ferramentas interessantes de automação foram lançadas para criar imagens do Docker e implantar no Kubernetes. Nesse sentido, decidi brincar com o gitlab, como estudar suas capacidades e, é claro, configurar o pipeline.
A inspiração para este trabalho foi o site kubernetes.io , que é gerado automaticamente a partir dos códigos-fonte e, para cada pool enviado, o robô gera automaticamente uma versão de visualização do site com suas alterações e fornece um link para visualização.
Tentei criar um processo semelhante do zero, mas totalmente baseado no Gitlab CI e nas ferramentas gratuitas que costumava usar para implantar aplicativos no Kubernetes. Hoje finalmente vou lhe contar mais sobre eles.
O artigo considerará ferramentas como:
Hugo , qbec , kaniko , git-crypt e GitLab CI com a criação de ambientes dinâmicos.
Conteúdo
- Apresentando Hugo
- Preparando o Dockerfile
- Familiaridade com Kaniko
- Apresentando qbec
- Tentando o Gitlab-runner com o Kubernetes-executor
- Implantação de gráficos Helm com qbec
- Apresentando o git-crypt
- Criar uma imagem da caixa de ferramentas
- Nosso primeiro pipeline e montagem de imagens por tags
- Automação de implantação
- Artefatos e push build in master
- Ambientes dinâmicos
- Analisar aplicativos
1. Apresentando Hugo
Como exemplo do nosso projeto, tentaremos criar um site para publicar a documentação criada no Hugo. Hugo é um gerador de conteúdo estático.
Para aqueles que não estão familiarizados com geradores estáticos, vou falar um pouco mais sobre eles. Diferente dos mecanismos regulares de sites de banco de dados e de alguns mecanismos php que, quando solicitados pelo usuário, geram páginas dinamicamente, os geradores estáticos são organizados de maneira um pouco diferente. Eles permitem que você pegue a fonte, como regra, é um conjunto de arquivos na marcação Markdown e nos modelos de tema e os compile em um site totalmente acabado.
Ou seja, na saída, você obterá uma estrutura de diretórios e um conjunto de arquivos html gerados que podem ser simplesmente enviados para qualquer hospedagem barata e obter um site de trabalho.
Hugo pode ser instalado localmente e testá-lo:
Inicializamos o novo site:
hugo new site docs.example.org
E ao mesmo tempo o repositório git:
cd docs.example.org git init
Até agora, nosso site é primitivo e, para que algo apareça primeiro, precisamos conectar um tema, um tema - é apenas um conjunto de modelos e regras predefinidas pelas quais nosso site é gerado.
Como tópico, usaremos o Learn , que, na minha opinião, é o mais adequado para um site com documentação.
Gostaria de prestar atenção especial ao fato de que não precisamos salvar arquivos de tema no repositório do nosso projeto. Em vez disso, podemos simplesmente conectá-lo usando o submodule git :
git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn
Assim, apenas os arquivos diretamente relacionados ao nosso projeto estarão em nosso repositório, e o tópico conectado permanecerá na forma de um link para um repositório específico e será confirmado, ou seja, sempre poderá ser extraído da fonte original e não ter medo de alterações incompatíveis.
Corrija a configuração config.toml :
baseURL = "http://docs.example.org/" languageCode = "en-us" title = "My Docs Site" theme = "learn"
Já nesta fase, você pode executar:
hugo server
E em http: // localhost: 1313 / verifique nosso site recém-criado, todas as alterações feitas no diretório são atualizadas automaticamente e a página aberta no navegador é muito conveniente!
Vamos tentar criar uma página de rosto em content / _index.md :
# My docs site ## Welcome to the docs! You will be very smart :-)
Captura de tela da página recém-criada Para gerar um site, basta executar:
hugo
O conteúdo do diretório público / será o seu site.
Sim, a propósito, vamos adicioná-lo imediatamente ao .gitignore :
echo /public > .gitignore
Não se esqueça de confirmar nossas alterações:
git add . git commit -m "New site created"
2. Preparando o Dockerfile
Está na hora de determinar a estrutura do nosso repositório. Normalmente eu uso algo como:
. ├── deploy │ ├── app1 │ └── app2 └── dockerfiles ├── image1 └── image2
- dockerfiles / - contém diretórios com Dockerfiles e tudo o que você precisa para criar nossas imagens do docker.
- deploy / - contém diretórios para implantar nossos aplicativos no Kubernetes
Assim, criaremos nosso primeiro Dockerfile ao longo do caminho dockerfiles / website / 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" ]
Como você pode ver, o Dockerfile contém dois FROMs, esse recurso é chamado de compilação em vários estágios e permite excluir tudo o que é desnecessário da imagem final do Docker.
Assim, a imagem final conterá apenas darkhttpd (um servidor HTTP leve) e público / - o conteúdo do nosso site gerado estaticamente.
Não se esqueça de confirmar nossas alterações:
git add dockerfiles/website git commit -m "Add Dockerfile for website"
3. Familiaridade com Kaniko
Como coletor de imagens do docker, decidi usar o kaniko , pois ele não requer que um daemon do docker funcione, e o próprio conjunto pode ser executado em qualquer máquina e armazenar o cache diretamente no registro, eliminando a necessidade de um armazenamento persistente completo .
Para criar a imagem, basta iniciar o contêiner com o executor kaniko e passar o contexto de compilação atual para ele, você pode fazer isso localmente, via 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
Onde registry.gitlab.com/kvaps/docs.example.org/website é o nome da sua imagem do docker, após a montagem, ela será automaticamente enviada ao registro do docker.
O parâmetro --cache permite que você armazene em cache as camadas no registro do docker, para o exemplo fornecido elas serão salvas em registry.gitlab.com/kvaps/docs.example.org/website/cache , mas você pode especificar outro caminho usando o parâmetro --cache- repo .
Captura de tela docker-registry
4. Familiaridade com o qbec
O Qbec é uma ferramenta de implantação que permite descrever declarativamente o manifesto do seu aplicativo e implantá-lo no Kubernetes. O uso do Jsonnet como sintaxe principal permite simplificar bastante a descrição das diferenças para vários ambientes e também elimina quase completamente a repetibilidade do código.
Isso pode ser especialmente verdadeiro nos casos em que você precisa implantar um aplicativo em vários clusters com parâmetros diferentes e deseja descrevê-los declarativamente no Git.
O Qbec também permite renderizar gráficos do Helm passando os parâmetros necessários e, posteriormente, operando-os, bem como manifestos regulares, incluindo várias mutações que podem ser aplicadas a eles, e isso, por sua vez, elimina a necessidade de usar o ChartMuseum. Ou seja, você pode armazenar e renderizar gráficos diretamente do git, onde eles estão.
Como eu disse antes, armazenaremos todas as implantações no diretório deploy / :
mkdir deploy cd deploy
Vamos inicializar nossa primeira aplicação:
qbec init website cd website
Agora a estrutura do nosso aplicativo fica assim:
. ├── components ├── environments │ ├── base.libsonnet │ └── default.libsonnet ├── params.libsonnet └── qbec.yaml
veja o arquivo qbec.yaml :
apiVersion: qbec.io/v1alpha1 kind: App metadata: name: website spec: environments: default: defaultNamespace: docs server: https://kubernetes.example.org:8443 vars: {}
Aqui estamos interessados principalmente em spec.environments , o qbec já criou um ambiente padrão para nós e pegou o endereço do servidor, bem como o namespace do nosso kubeconfig atual.
Agora, ao implantar no ambiente padrão , o qbec sempre implantará apenas no cluster Kubernetes especificado e no espaço de nomes especificado, ou seja, você não precisará mais alternar entre contextos e espaços de nome para executar uma implantação.
Se necessário, você sempre pode atualizar as configurações neste arquivo.
Todos os seus ambientes são descritos em qbec.yaml e no arquivo params.libsonnet , onde diz onde você precisa obter parâmetros para eles.
Em seguida, vemos dois diretórios:
- componentes / - todos os manifestos para nossa aplicação serão armazenados aqui, eles podem ser descritos em jsonnet e em arquivos yaml comuns
- ambientes / - aqui descreveremos todas as variáveis (parâmetros) para nossos ambientes.
Por padrão, temos dois arquivos:
- ambientes / base.libsonnet - conterá parâmetros gerais para todos os ambientes
- environment / default.libsonnet - contém parâmetros substituídos pelo ambiente padrão
Vamos abrir environment / base.libsonnet e adicionar os parâmetros para o nosso primeiro componente lá:
{ 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', }, }, }
Também criaremos nosso primeiro componente components / 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, }, }, ], }, }, ], }, }, ]
Neste arquivo, descrevemos imediatamente três entidades do Kubernetes, são elas: Implantação , Serviço e Ingress . Se desejado, poderíamos carregá-los em componentes diferentes, mas, neste estágio, um é suficiente para nós.
A sintaxe do jsonnet é muito semelhante ao json regular, em princípio o json regular já é válido jsonnet, portanto, a princípio, pode ser mais fácil usar serviços online como o yaml2json para converter o yaml normal em json ou, se seus componentes não contiverem variáveis, então eles podem ser descritos na forma de yaml comum.
Ao trabalhar com o jsonnet, eu recomendo instalar um plugin para o seu editor
Por exemplo, para o vim, existe um plug-in vim-jsonnet que ativa o destaque de sintaxe e executa automaticamente o jsonnet fmt cada vez que é salvo (ele requer a instalação do jsonnet).
Tudo está pronto, agora podemos iniciar a implantação:
Para ver o que aconteceu, faremos:
qbec show default
Na saída, você verá manifestos yaml renderizados que serão aplicados ao cluster padrão.
Ok, agora aplique:
qbec apply default
Na saída, você sempre verá o que será feito no seu cluster, o qbec solicitará que você aceite as alterações, digitando y, você poderá confirmar suas intenções.
Feito, agora nosso aplicativo está ancorado!
Se você fizer alterações, sempre poderá:
qbec diff default
para ver como essas alterações afetarão a implantação atual
Não se esqueça de confirmar nossas alterações:
cd ../.. git add deploy/website git commit -m "Add deploy for website"
5. Tentando o Gitlab-runner com o Kubernetes-executor
Até recentemente, eu usava apenas o habitual gitlab-runner em uma máquina pré-preparada (contêiner LXC) com um shell ou docker-executor. Inicialmente, tínhamos vários desses corredores definidos globalmente em nosso hitlab. Eles coletaram imagens do docker para todos os projetos.
Mas, como a prática demonstrou, essa opção não é a mais ideal, tanto em termos de praticidade quanto em termos de segurança. É muito melhor e ideologicamente mais correto ter corredores separados implantados para cada projeto e até para cada ambiente.
Felizmente, isso não é um problema, pois agora vamos implantar o gitlab-runner diretamente como parte do nosso projeto diretamente no Kubernetes.
O Gitlab fornece um gráfico de leme pronto para implantar o gitlab-runner no Kubernetes. Portanto, tudo o que você precisa fazer é descobrir o token de registro do nosso projeto em Configurações -> CI / CD -> Corredores e passar o comando:
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
Onde:
- https://gitlab.com é o endereço do seu servidor Gitlab.
- yga8y-jdCusVDn_t4Wxc - token de registro para seu projeto.
- rbac.create = true - concede ao corredor o número necessário de privilégios para poder criar pods para executar nossas tarefas usando o kubernetes-executor.
Se tudo for feito corretamente, você deverá ver o corredor registrado na seção Corredores , nas configurações do seu projeto.
Captura de tela do corredor adicionado Isso é simples? - sim, tão simples! Não há mais problemas com o registro manual de corredores, a partir de agora os corredores serão criados e destruídos automaticamente.
6. Implantação de gráficos Helm com QBEC
Como decidimos considerar o gitlab-runner parte do nosso projeto, é hora de descrevê-lo em nosso repositório Git.
Poderíamos descrevê-lo como um componente separado do site , mas no futuro planejamos implantar cópias diferentes do site com muita frequência, diferentemente do gitlab-runner , que será implantado apenas uma vez para cada cluster do Kubernetes. Então, vamos inicializar um aplicativo separado para ele:
cd deploy qbec init gitlab-runner cd gitlab-runner
Desta vez, não descreveremos as entidades do Kubernetes manualmente, mas faremos um gráfico Helm pronto. Uma das vantagens do qbec é a capacidade de renderizar gráficos Helm diretamente do repositório Git.
Vamos conectá-lo usando o submódulo git:
git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runner
Agora, o diretório vendor / gitlab-runner contém nosso repositório com um gráfico para o gitlab-runner.
Da mesma forma, você pode conectar outros repositórios, por exemplo, todo o repositório aos gráficos oficiais https://github.com/helm/charts
Vamos descrever o componente components / 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, } )
O primeiro argumento para expandHelmTemplate passa o caminho para o gráfico, depois params.values , que extraímos dos parâmetros do ambiente e, em seguida, um objeto com
- nameTemplate - nome da versão
- namespace - namespace passado ao leme
- thisFile - parâmetro necessário passando o caminho para o arquivo atual
- detalhado - mostra o comando do modelo de leme com todos os argumentos ao renderizar o gráfico
Agora descreveremos os parâmetros para nosso componente em environment / base.libsonnet :
local secrets = import '../secrets/base.libsonnet'; { components: { gitlabRunner: { name: 'gitlab-runner', values: { gitlabUrl: 'https://gitlab.com/', rbac: { create: true, }, runnerRegistrationToken: secrets.runnerRegistrationToken, }, }, }, }
Preste atenção ao runnerRegistrationToken que extraímos do arquivo externo secrets / base.libsonnet , vamos criá-lo:
{ runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc', }
Verifique se tudo funciona:
qbec show default
se tudo estiver em ordem, podemos remover nossa versão anterior através da versão Helm:
helm uninstall gitlab-runner
e implantá-lo, mas já através do qbec:
qbec apply default
7. Apresentando o git-crypt
Git-crypt é uma ferramenta que permite configurar criptografia transparente para o seu repositório.
No momento, a estrutura do nosso diretório para o gitlab-runner está assim:
. ├── components │ ├── gitlab-runner.jsonnet ├── environments │ ├── base.libsonnet │ └── default.libsonnet ├── params.libsonnet ├── qbec.yaml ├── secrets │ └── base.libsonnet └── vendor └── gitlab-runner (submodule)
Mas guardar segredos no Git não é seguro, é? Então, precisamos criptografá-los adequadamente.
Geralmente, por uma única variável, isso nem sempre faz sentido. Você pode passar segredos para o qbec e pelas variáveis de ambiente do seu sistema de IC.
Mas é importante notar que existem projetos mais complexos que podem conter muito mais segredos; será extremamente difícil transferi-los por meio de variáveis de ambiente.
Além disso, neste caso, eu não seria capaz de falar sobre uma ferramenta tão maravilhosa como o git-crypt .
O git-crypt também é conveniente porque permite salvar todo o histórico de segredos, além de comparar, mesclar e resolver conflitos da mesma maneira que costumávamos fazer no caso do Git.
Primeiro de tudo, depois de instalar o git-crypt, precisamos gerar chaves para o nosso repositório:
git crypt init
Se você possui uma chave PGP, pode se adicionar imediatamente como colaborador deste projeto:
git-crypt add-gpg-user kvapss@gmail.com
Assim, você sempre pode descriptografar este repositório usando sua chave privada.
Se você não possui uma chave PGP e não é o esperado, pode seguir o outro caminho e exportar a chave do projeto:
git crypt export-key /path/to/keyfile
Dessa forma, qualquer pessoa com um arquivo-chave exportado poderá descriptografar seu repositório.
É hora de estabelecer nosso primeiro segredo.
Deixe-me lembrá-lo de que ainda estamos no diretório deploy / gitlab-runner / , onde temos o diretório secrets / , vamos criptografar todos os arquivos nele, para isso criamos o arquivo secrets / .gitattributes com o seguinte conteúdo:
* filter=git-crypt diff=git-crypt .gitattributes !filter !diff
Como você pode ver no conteúdo, todos os arquivos por mask * serão executados no git-crypt , exceto o próprio .gitattributes
Podemos verificar isso executando:
git crypt status -e
Na saída, obtemos uma lista de todos os arquivos no repositório para os quais a criptografia está ativada
Isso é tudo, agora podemos confirmar com segurança nossas alterações:
cd ../.. git add . git commit -m "Add deploy for gitlab-runner"
Para bloquear o repositório, basta:
git crypt lock
e então todos os arquivos criptografados se transformam em algo binário, será impossível lê-los.
Para descriptografar um repositório, faça:
git crypt unlock
Uma imagem da caixa de ferramentas é uma imagem com todas as ferramentas que usaremos para implantar nosso projeto. Ele será usado pelo corredor gitlab para executar tarefas típicas de implantação.
Tudo é simples aqui, criamos um novo dockerfiles / toolbox / Dockerfile com o seguinte conteúdo:
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
Como você pode ver, nesta imagem instalamos todos os utilitários que usamos para implantar nosso aplicativo. Não precisamos do kubectl aqui , mas convém brincar com ele no estágio de configuração do pipeline.
Além disso, para poder se comunicar com o Kubernetes e executar uma implantação nele, precisamos configurar a função dos pods gerados pelo gitlab-runner.
Para fazer isso, acesse o diretório com gitlab-runner:
cd deploy/gitlab-runner
e adicione um novo componente 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, }, ], }, ]
Também descreveremos os novos parâmetros em environment / base.libsonnet , que agora se parece com isso:
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', }, }, }
Nota $ .components.rbac.name refere-se ao nome do componente rbac
Vamos verificar o que mudou:
qbec diff default
e aplique nossas alterações ao Kubernetes:
qbec apply default
Também não se esqueça de confirmar nossas alterações no 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. Nosso primeiro pipeline e montagem de imagens por tags
Na raiz do projeto, criaremos .gitlab-ci.yml com o seguinte conteúdo:
.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
Observe que usamos GIT_SUBMODULE_STRATEGY: normal para os trabalhos em que você precisa inicializar explicitamente os submódulos antes da execução.
Não se esqueça de confirmar nossas alterações:
git add .gitlab-ci.yml git commit -m "Automate docker build"
Eu acho que você pode chamá-lo com segurança de versão v0.0.1 e pendurar uma tag:
git tag v0.0.1
Penduraremos as tags sempre que precisarmos lançar uma nova versão. As tags nas imagens do Docker serão vinculadas às tags Git. Cada push com uma nova tag inicializará a montagem de imagens com essa tag.
Execute git push --tags e veja nosso primeiro pipeline:
Captura de tela do primeiro pipeline Vale a pena prestar atenção ao fato de que a montagem por tags é adequada para montar imagens do docker, mas não para implantar um aplicativo no Kubernetes. Como novas tags também podem ser atribuídas a confirmações antigas, nesse caso, a inicialização do pipeline para elas levará à implantação da versão antiga.
Para resolver esse problema, geralmente o conjunto de imagens do Docker é anexado às tags e a implantação do aplicativo na ramificação principal , na qual as versões das imagens coletadas são codificadas. É nesse caso que você pode inicializar a reversão com uma simples marca mestre de reversão.
10. Automação de implantação
Para que o Gitlab-runner decifre nossos segredos, precisamos exportar a chave do repositório e adicioná-la às variáveis de ambiente do nosso IC:
git crypt export-key /tmp/docs-repo.key base64 -w0 /tmp/docs-repo.key; echo
salve a string resultante no Gitlab, para isso iremos para as configurações do nosso projeto:
Configurações -> CI / CD -> Variáveis
:
.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!
:
, 