Olá a todos! Meu nome é André e sou engenheiro DevOps na empresa Exness, na equipe de desenvolvimento. Minha principal atividade está relacionada à construção, implantação e suporte de aplicativos em docker sob o sistema operacional Linux (doravante — SO). Recentemente, tive a tarefa de realizar as mesmas atividades, mas com Windows Server como sistema operacional alvo do projeto e um conjunto de projetos em C++. Para mim, essa foi a primeira interação intensa com contêineres docker no SO Windows e, de modo geral, com aplicativos em C++. Graças a isso, adquiri uma experiência interessante e aprendi algumas sutilezas da contêinerização de aplicativos no SO Windows.

Neste artigo, quero compartilhar as dificuldades que enfrentei e como consegui resolvê-las. Espero que isso seja útil para resolver suas tarefas atuais e futuras. Boa leitura!
Por que contêineres?
A empresa possui uma infraestrutura existente de orquestradores de contêineres Hashicorp Nomad e componentes relacionados — Consul e Vault. Portanto, a contêinerização de aplicativos foi escolhida como um método unificado para entrega de soluções prontas. Como na infraestrutura do projeto existem hosts docker com versões do SO Windows Server Core 1803 e 1809, é necessário construir separadamente as versões das imagens docker para 1803 e 1809. Na versão 1803, é importante lembrar que o número de revisão do host de construção docker deve corresponder ao número de revisão da imagem docker base e ao host onde o contêiner a partir dessa imagem será iniciado. A versão 1809 não possui essa desvantagem. Mais detalhes podem ser lidos .
Por que multi-stage?
Os engenheiros das equipes de desenvolvimento não têm acesso aos hosts de construção ou têm acesso muito limitado, não há como gerenciar rapidamente o conjunto de componentes para a construção de aplicativos nesses hosts, por exemplo, instalar um conjunto de ferramentas adicional ou carga de trabalho para o Visual Studio. Portanto, decidimos — instalar todos os componentes necessários para a construção do aplicativo na imagem docker de construção. Se necessário, é possível alterar rapidamente apenas o dockerfile e iniciar o pipeline de criação dessa imagem.
Da teoria à prática
Em uma ideal montagem multi-stage do Docker, a preparação do ambiente para construir a aplicação acontece no mesmo script do dockerfile que a própria construção da aplicação. Mas no nosso caso, foi adicionado um elo intermediário, nomeadamente, uma etapa de pré-criação da imagem Docker com tudo o que é necessário para a construção da aplicação. Isso foi feito porque queríamos aproveitar a possibilidade de cache do Docker, para reduzir o tempo de instalação de todas as dependências.
Vamos analisar os principais pontos do script do dockerfile para a formação desta imagem.
Para criar imagens de diferentes versões do SO no dockerfile, pode-se definir um argumento, através do qual é passado o número da versão durante a construção, que também é a tag da imagem base.
A lista completa de tags das imagens do Microsoft Windows Server pode ser encontrada .
ARG WINDOWS_OS_VERSION=1809
FROM mcr.microsoft.com/windows/servercore:$WINDOWS_OS_VERSIONPor padrão, os comandos na instrução RUN dentro do dockerfile no Windows são executados no console cmd.exe. Para facilitar a escrita de scripts e expandir a funcionalidade dos comandos usados, vamos redefinir o console de execução de comandos para o Powershell através da instrução SHELL.
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';"]A próxima etapa é instalar o gerenciador de pacotes Chocolatey e os pacotes necessários:
COPY chocolatey.pkg.config .
RUN Set-ExecutionPolicy Bypass -Scope Process -Force ;
[System.Net.ServicePointManager]::SecurityProtocol =
[System.Net.ServicePointManager]::SecurityProtocol -bor 3072 ;
$env:chocolateyUseWindowsCompression = 'true' ;
iex ((New-Object System.Net.WebClient).DownloadString(
'https://chocolatey.org/install.ps1')) ;
choco install chocolatey.pkg.config -y --ignore-detected-reboot ;
if ( @(0, 1605, 1614, 1641, 3010) -contains $LASTEXITCODE ) {
refreshenv; } else { exit $LASTEXITCODE; } ;
Remove-Item 'chocolatey.pkg.config'Para instalar pacotes usando o Chocolatey, você pode simplesmente passar uma lista deles ou instalá-los um a um caso precise passar parâmetros exclusivos para cada pacote. Na nossa situação, usamos um arquivo de manifesto em formato XML, que especifica a lista de pacotes necessários e seus parâmetros. O conteúdo é assim:
Em seguida, vamos instalar o ambiente de construção da aplicação, especificamente, o MS Build Tools 2019 - uma versão leve do Visual Studio 2019, que contém o conjunto mínimo necessário de componentes para compilar código.
Para trabalhar plenamente com nosso projeto C++, precisaremos de componentes adicionais, especificamente:
- Workload C++ tools
- Toolset v141
- Windows 10 SDK (10.0.17134.0)
A instalação do conjunto ampliado de ferramentas em modo automático pode ser feita através de um arquivo de configuração no formato JSON. O conteúdo do arquivo de configuração:
A lista completa dos componentes disponíveis pode ser encontrada no site de documentação. .
{
"version": "1.0",
"components": [
"Microsoft.Component.MSBuild",
"Microsoft.VisualStudio.Workload.VCTools;includeRecommended",
"Microsoft.VisualStudio.Component.VC.v141.x86.x64",
"Microsoft.VisualStudio.Component.Windows10SDK.17134"
]
}O script de instalação é executado no dockerfile, e para facilitar, o caminho para os arquivos executáveis do build tools é adicionado à variável de ambiente. caminhos. Também é desejável remover arquivos e diretórios desnecessários para reduzir o tamanho da imagem.
COPY buildtools.config.json .
RUN Invoke-WebRequest 'https://aka.ms/vs/16/release/vs_BuildTools.exe'
-OutFile '.vs_buildtools.exe' -UseBasicParsing ;
Start-Process -FilePath '.vs_buildtools.exe' -Wait -ArgumentList
'--quiet --norestart --nocache --config C:buildtools.config.json' ;
Remove-Item '.vs_buildtools.exe' ;
Remove-Item '.buildtools.config.json' ;
Remove-Item -Force -Recurse
'C:Program Files (x86)Microsoft Visual StudioInstaller' ;
$env:PATH = 'C:Program Files (x86)Microsoft Visual Studio2019BuildToolsMSBuildCurrentBin;' + $env:PATH;
[Environment]::SetEnvironmentVariable('PATH', $env:PATH,
[EnvironmentVariableTarget]::Machine)Nesta etapa, nossa imagem para compilar o aplicativo C++ está pronta, e podemos começar diretamente a criação do aplicativo usando o docker multi-stage.
Multi-stage em ação
Usaremos a imagem criada como imagem de construção que contém todas as ferramentas. Como no script anterior do dockerfile, adicionaremos a capacidade de especificar dinamicamente o número da versão/tag da imagem para facilitar a reutilização do código. É importante adicionar a etiqueta as builder à imagem de construção na instrução FROM.
ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builderChegou a hora de construir o aplicativo. Aqui tudo é bastante simples: copiar o código-fonte e tudo relacionado, e iniciar o processo de compilação.
COPY myapp .
RUN nuget restore myapp.sln ;
msbuild myapp.sln /t:myapp /p:Configuration=ReleaseA etapa final da criação da imagem final é especificar a imagem base do aplicativo, onde todos os artefatos de compilação e arquivos de configuração estarão localizados. Para copiar os arquivos compilados da imagem de construção intermediária, é necessário indicar o parâmetro --from=builder na documentação COPY.
FROM mcr.microsoft.com/windows/servercore:$WINDOWS_OS_VERSION
COPY --from=builder C:/x64/Release/myapp/ ./
COPY ./configs ./Agora, resta adicionar as dependências necessárias para o funcionamento do nosso aplicativo e indicar o comando de execução através das instruções. ENTRYPOINT ou CMD.
Conclusão
Neste artigo, explico como criar um ambiente completo para compilar aplicativos em C++ dentro de um contêiner no Windows e como utilizar os recursos das montagens em múltiplas etapas do Docker para criar imagens robustas do nosso aplicativo.
Fonte: habr.com
