Infraestrutura de construção de projeto com janela de encaixe

Já existem materiais no Habr sobre como configurar o docker- container para a compilação do projeto. Por exemplo, usando o Docker para criar e executar um projeto C ++ . Neste artigo, como no anterior, será considerada a questão da construção do projeto, mas aqui gostaria de ir além do tutorial e considerar mais profundamente os problemas do uso de contêineres nessas tarefas, bem como a construção da infraestrutura de construção com o docker .


Um pouco sobre estivador


Para maior clareza das discussões, é necessário fornecer uma descrição de alguns componentes da janela de encaixe .


Imagem


A imagem do Docker é um modelo somente leitura com instruções para criar um contêiner. Para criar a imagem, você precisa criar um Dockerfile , que descreve todas as etapas da montagem. Cada uma dessas etapas cria uma camada separada dentro da imagem . Cada camada subseqüente é sobreposta a todas as anteriores e contém apenas as alterações que precisam ser feitas na camada anterior.


Por exemplo, para um Dockerfile :


FROM ubuntu:18.04 ADD app.sh /app ENTRYPOINT /bin/bash /app/app.sh 

A imagem da janela de encaixe terá a seguinte estrutura:



As camadas dentro da imagem são armazenadas em cache e podem ser reutilizadas se nenhuma alteração for detectada. Se a camada for alterada (adicionada / excluída) , todas as subseqüentes serão criadas do zero. Para fazer alterações na imagem do contêiner (e, consequentemente, no ambiente do processo iniciado), basta consertar o Dockerfile e começar a criar a imagem.


Container


Um contêiner de janela de encaixe é uma instância de inicialização da imagem . Ele pode ser criado, iniciado, parado, excluído etc. Por padrão, os contêineres são isolados um do outro e do sistema host. No início, o contêiner inicia um comando, que pode ser especificado em ENTRYPOINT ou CMD , e para quando é concluído. Uma situação aceitável é quando o CMD e o ENTRYPOINT estão presentes , pois eles interagem descrito na documentação .


Quando você cria cada contêiner, uma nova camada é adicionada sobre todas as existentes. É gravável no contêiner atual e é destruído junto com o contêiner. Todas as operações de gravação e criação de novos arquivos quando o contêiner está em execução são aplicadas a essa camada, a imagem sempre permanece inalterada. Assim, a estrutura da camada do contêiner criado será parecida com:



Ao usar o docker run , um novo contêiner será criado a cada vez, com sua própria camada para gravação. Nas tarefas de construção, isso significa que, toda vez que é iniciado, ele cria um novo ambiente limpo que não tem nada a ver com execuções anteriores. A lista de contêineres criados pode ser visualizada executando o comando: docker container ls -a .


Coletamos o projeto no contêiner


Para maior clareza, descrevemos brevemente o processo de criação de um aplicativo em um contêiner; esse processo é descrito em mais detalhes nos artigos 1 e 2 .


As etapas esquematicamente possíveis para criar o aplicativo na janela de encaixe podem ser representadas da seguinte maneira:


Vamos analisar as etapas mostradas:


  1. Usamos o Dockerfile , que descreve o ambiente, comandos para montar e copiar os resultados e, com base nele, criamos uma imagem do contêiner.
  2. Usamos a imagem resultante para criar e iniciar o contêiner com o docker run . Montamos a pasta de origem e a pasta em que o resultado da montagem será copiado para o contêiner.
  3. Após a conclusão do contêiner, os artefatos de montagem serão colocados no diretório montado.

Um exemplo é dado no artigo .


Como o docker run é usado aqui, para cada inicialização, um contêiner separado será criado com sua própria camada para gravação , para que arquivos temporários de montagens anteriores não entrem na atual. Lembre-se de limpar os recipientes parados.


A montagem do diretório de origem facilita a depuração do assembly. Mas isso traz riscos - você pode coletar uma liberação do código que não passou no controle de qualidade ou não foi adicionado ao sistema de controle de versão. Para evitar isso, você pode clonar o repositório git dentro do contêiner em cada construção, como, por exemplo, no arquivo :


 FROM ubuntu:bionic RUN apt-get update \ && apt-get install -y apt-utils RUN apt-get update \ && apt-get install -y make gcc g++ qt5-default git RUN mkdir -p /app/src WORKDIR /app/build #       ENTRYPOINT git -C /app/src clone https://github.com/sqglobe/SimpleQtProject.git \ && qmake /app/src/SimpleQtProject/SimpleQtProject.pro \ && make \ && cp SimpleQtProject /app/res/SimpleQtProject-ubuntu-bionic 

Aqui, a clonagem é feita em ENTRYPOINT , não na instrução RUN , devido ao cache. ENTRYPOINT sempre é executado quando o contêiner é iniciado e o resultado do comando RUN pode ser obtido do cache .


Construir infraestrutura


Para criar um projeto para diferentes sistemas operacionais ou distribuições Linux, uma certa configuração de servidores (máquinas de compilação, servidores com um sistema de controle de versão etc.) pode ser usada. Na prática, tive que lidar com a seguinte infraestrutura:



Aqui, o usuário acessa o servidor web através do qual o projeto é construído em máquinas com Ubuntu e Red Hat . Em seguida, em cada máquina, o repositório git é clonado com o projeto em um diretório temporário e o assembly é iniciado. O usuário pode baixar os arquivos resultantes da mesma página a partir da qual ele iniciou todo o processo.


Essa montagem é repetível porque os desenvolvedores usam o mesmo ambiente.


Das desvantagens - é necessário manter uma infraestrutura inteira, administrar vários servidores, eliminar bugs em scripts e aplicativos da web , etc.


Simplifique com janela de encaixe


O suporte à infraestrutura mostrada acima requer certos custos, tanto monetários quanto humanos. Se sua equipe estiver trabalhando em uma pequena inicialização ou você for o único desenvolvedor, poderá usar contêineres de encaixe para implementar sua infraestrutura de construção.


Considere um projeto Qt trivial criado usando qmake - SimpleQtProject . A pasta de janela de encaixe do projeto especificado contém vários arquivos:



Esses arquivos implementam a idéia de clonar o código-fonte dentro de um contêiner.


A montagem inteira é iniciada usando o Makefile . É muito curto e contém comentários suficientes. Sua base é a criação de uma imagem e o lançamento do contêiner:


 %: %.docker docker build -t simple-qt-$(strip $(subst .docker,, $< )) --file $< . docker run --mount type=bind,source=$(RELEASE_DIR),target=/app/res simple-qt-$(strip $(subst .docker,, $< )) 

Nesta fase da montagem, uma imagem do contêiner é criada com o nome que consiste no prefixo simple-qt- e o nome do sistema (para o centos 7 será simple-qt-centos7 ). Como o Dockerfile , o arquivo correspondente com a permissão. Em seguida, o contêiner é iniciado com base na imagem criada e uma pasta é montada nele para copiar artefatos de montagem.


Após executar o make no diretório docker , a pasta docker / releases conterá os resultados da construção para várias plataformas.


Assim, nossa infraestrutura para a criação do SimpleQtProject terá a seguinte aparência:



Vantagens desta configuração:


  1. Localidade . O desenvolvedor coleta um projeto para várias plataformas em sua máquina local, o que elimina a necessidade de conter uma frota de servidores, configurar a cópia de artefatos entre servidores na rede, enviar e processar comandos de rede.
  2. Isolamento do meio ambiente . O contêiner fornece um ambiente completamente isolado para a construção de um aplicativo específico. É possível criar projetos com ambientes incompatíveis na mesma máquina (por exemplo, aqueles que requerem versões diferentes da mesma biblioteca).
  3. Versionamento Ao colocar o Dockerfile no repositório git, é possível rastrear alterações no ambiente de construção com o lançamento de novos releases, reverter para versões anteriores do ambiente de construção, etc.
  4. Mobilidade . Se necessário, essa infraestrutura é implantada sem problemas em outro computador. A tecnologia para criar uma imagem de contêiner permite fazer alterações na própria imagem com muita facilidade - basta atualizar o Dockerfile e começar a criar a imagem.
  5. Auto-documentação . Essencialmente, um Dockerfile contém etapas para implantar um ambiente de montagem. Portanto, se necessário, implante esse ambiente, mas já em um sistema regular, você pode usar os comandos dele.
  6. Leveza . O contêiner inicia no momento em que a montagem é iniciada e para automaticamente após a conclusão. Não desperdiça tempo de CPU e RAM.

No entanto, há um sinal de menos - a montagem do projeto exigirá a montagem da imagem do contêiner. Quando você inicia, pode demorar muito tempo. Porém, com os repetidos, especialmente se o Dockerfile não foi alterado, a imagem é montada usando o cache muitas vezes mais rapidamente.


Também é necessário lembrar de limpar os recipientes parados.


Conclusão


Concluindo, gostaria de observar que o docker não é a única tecnologia de contêiner. Mas existem alguns recursos que o distinguem favoravelmente para tarefas de montagem do mesmo LXC :


  1. Você pode criar um contêiner usando um Dockerfile de texto. Este é um arquivo com sintaxe simples, você pode adicioná-lo ao repositório do projeto (como sempre faço) e mantê-lo constantemente à mão.
  2. A cada vez, iniciando o contêiner do docker com o docker run obtemos um ambiente limpo , como se estivéssemos fazendo tudo pela primeira vez. Arquivos temporários entre montagens não são salvos.
  3. O contêiner não inicia todo o sistema operacional, mas apenas o processo de montagem necessário.

Source: https://habr.com/ru/post/pt457870/


All Articles