{"id":69351,"date":"2020-02-18T14:44:06","date_gmt":"2020-02-18T11:44:06","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker"},"modified":"2020-03-03T16:15:00","modified_gmt":"2020-03-03T13:15:00","slug":"sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","title":{"rendered":"Cria\u00e7\u00e3o de uma cadeia CI\/CD e automa\u00e7\u00e3o do trabalho com Docker","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Eu criei meus primeiros sites no final dos anos 90. Naquela \u00e9poca, coloc\u00e1-los em funcionamento era muito simples. Havia um servidor Apache em algum hospedagem compartilhada, a qual podia ser acessada via FTP, digitando na barra do navegador algo como <code>ftp:\/\/ftp.example.com<\/code>. Depois era necess\u00e1rio inserir o nome de usu\u00e1rio e a senha e fazer o upload dos arquivos para o servidor. Eram outros tempos, tudo era mais f\u00e1cil do que agora.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\"><img decoding=\"async\" alt=\"Cria\u00e7\u00e3o de uma cadeia CI\/CD e automa\u00e7\u00e3o do trabalho com Docker\" src=\"\/wp-content\/uploads\/2020\/02\/0861cdd82770ef4695ffac7a09777c52.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>Nas duas d\u00e9cadas que se passaram desde ent\u00e3o, muita coisa mudou. Os sites se tornaram mais complexos, e antes de serem lan\u00e7ados em produ\u00e7\u00e3o, precisam ser compilados. Um \u00fanico servidor se transformou em m\u00faltiplos servidores trabalhando atr\u00e1s de balanceadores de carga, e o uso de sistemas de controle de vers\u00f5es tornou-se algo comum.<\/p>\n<p>Para o meu projeto pessoal, eu tinha uma configura\u00e7\u00e3o especial. E eu sabia que precisava da capacidade de implantar o site em produ\u00e7\u00e3o fazendo apenas uma a\u00e7\u00e3o: gravando o c\u00f3digo em uma branch <code>master<\/code> no GitHub. Al\u00e9m disso, eu sabia que para garantir o funcionamento do meu pequeno aplicativo web, n\u00e3o queria lidar com a gest\u00e3o de um enorme cluster Kubernetes, ou usar a tecnologia Docker Swarm, ou manter um parque de servidores com pods, agentes e todas essas outras complexidades. Para atingir o objetivo de maximizar a simplicidade do trabalho, precisei me familiarizar com CI\/CD.<\/p>\n<p>Se voc\u00ea tem um pequeno projeto (no nosso caso, um projeto Node.js) e gostaria de saber como automatizar a implanta\u00e7\u00e3o desse projeto, garantindo que o que est\u00e1 armazenado no reposit\u00f3rio corresponda exatamente ao que est\u00e1 funcionando em produ\u00e7\u00e3o, ent\u00e3o, creio que este artigo pode ser do seu interesse.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Requisitos preliminares<\/h2>\n<p>\nEspera-se que o leitor deste artigo tenha conhecimentos b\u00e1sicos sobre como trabalhar com a linha de comando e escrever scripts Bash. Al\u00e9m disso, ele precisar\u00e1 de contas <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex> e <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/p>\n<h2>Objetivos<\/h2>\n<p>\nN\u00e3o diria que este artigo pode ser chamado incondicionalmente de \"manual de instru\u00e7\u00f5es\". \u00c9 mais um documento onde compartilho o que aprendi e descrevo o processo de teste e implanta\u00e7\u00e3o de c\u00f3digo em produ\u00e7\u00e3o que me agrada, realizado em uma \u00fanica passagem automatizada.<\/p>\n<p>Aqui est\u00e1 como ficou o meu fluxo de trabalho.<\/p>\n<p>Para o c\u00f3digo enviado a qualquer branch do reposit\u00f3rio, exceto <code>master<\/code>, s\u00e3o realizadas as seguintes a\u00e7\u00f5es:<\/p>\n<ul>\n<li>A constru\u00e7\u00e3o do projeto no Travis CI est\u00e1 sendo iniciada.<\/li>\n<li>Todos os testes unit\u00e1rios, de integra\u00e7\u00e3o e de ponta a ponta est\u00e3o sendo executados.<\/li>\n<\/ul>\n<p>\nSomente para o c\u00f3digo que entra em <code>master<\/code>, o seguinte \u00e9 executado:<\/p>\n<ul>\n<li>Tudo o que foi mencionado acima, mais...<\/li>\n<li>Constru\u00e7\u00e3o da imagem Docker com base no c\u00f3digo atual, configura\u00e7\u00f5es e ambiente.<\/li>\n<li>Hospedagem da imagem no Docker Hub.<\/li>\n<li>Conex\u00e3o ao servidor de produ\u00e7\u00e3o.<\/li>\n<li>Carregamento da imagem do Docker Hub para o servidor.<\/li>\n<li>Interrompendo o cont\u00eainer atual e iniciando um novo, baseado na nova imagem.<\/li>\n<\/ul>\n<p>\nSe voc\u00ea n\u00e3o sabe nada sobre Docker, imagens ou cont\u00eaineres \u2014 n\u00e3o se preocupe. Eu vou te contar tudo sobre isso.<\/p>\n<h2>O que \u00e9 CI\/CD?<\/h2>\n<p>\nA sigla CI\/CD significa \"continuous integration\/continuous deployment\" \u2014 \"integra\u00e7\u00e3o cont\u00ednua\/implanta\u00e7\u00e3o cont\u00ednua\".<\/p>\n<h3>\u258dIntegra\u00e7\u00e3o Cont\u00ednua<\/h3>\n<p>\nIntegra\u00e7\u00e3o cont\u00ednua \u00e9 o processo pelo qual os desenvolvedores realizam commits no reposit\u00f3rio principal do c\u00f3digo-fonte do projeto (geralmente no branch <code>master<\/code>). A qualidade do c\u00f3digo \u00e9 garantida por meio de testes automatizados.<\/p>\n<h3>\u258dImplanta\u00e7\u00e3o Cont\u00ednua<\/h3>\n<p>\nImplanta\u00e7\u00e3o cont\u00ednua \u00e9 a implanta\u00e7\u00e3o automatizada e frequente de c\u00f3digo em produ\u00e7\u00e3o. A segunda parte da sigla CI\/CD \u00e0s vezes \u00e9 desdobrada como \"continuous delivery\" (\"entrega cont\u00ednua\"). Isso, em geral, \u00e9 o mesmo que \"implanta\u00e7\u00e3o cont\u00ednua\", mas \"entrega cont\u00ednua\" implica a necessidade de aprova\u00e7\u00e3o manual das mudan\u00e7as antes do in\u00edcio do processo de implanta\u00e7\u00e3o do projeto.<\/p>\n<h2>In\u00edcio<\/h2>\n<p>\nO aplicativo no qual estou aprendendo \u00e9 chamado de <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. Este \u00e9 um projeto web no qual estou trabalhando, destinado a fazer anota\u00e7\u00f5es. No in\u00edcio, tentei criar um <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">projeto JAMStack<\/a><\/noindex>, ou apenas um aplicativo frontend sem servidor, para aproveitar as capacidades padr\u00e3o de hospedagem e implanta\u00e7\u00e3o de projetos que o <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>oferece. \u00c0 medida que a complexidade do aplicativo aumentava, precisei criar tamb\u00e9m sua parte do servidor, e isso significava que eu precisaria desenvolver minha pr\u00f3pria estrat\u00e9gia de integra\u00e7\u00e3o e implanta\u00e7\u00e3o automatizadas do projeto.<\/p>\n<p>No meu caso, o aplicativo \u00e9 um servidor Express que roda em um ambiente Node.js, servindo um aplicativo React de p\u00e1gina \u00fanica e suportando uma API segura. Essa arquitetura segue uma estrat\u00e9gia que pode ser encontrada em <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/full-stack-cookies-localstorage-react-express\/\">este<\/a><\/noindex> guia sobre autentica\u00e7\u00e3o full-stack.<\/p>\n<p>Conversei com <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">um amigo<\/a><\/noindex>, que \u00e9 um especialista em automa\u00e7\u00e3o, e perguntei a ele o que eu precisava fazer para que tudo funcionasse como eu queria. Ele me deu a ideia de como deveria ser o fluxo de trabalho automatizado, descrito na se\u00e7\u00e3o \"Objetivos\" deste artigo. O fato de eu ter estabelecido objetivos assim significou que eu precisava entender como usar o Docker.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker \u00e9 uma ferramenta que, gra\u00e7as \u00e0 tecnologia de containeriza\u00e7\u00e3o, facilita a distribui\u00e7\u00e3o de aplicativos, al\u00e9m de permitir sua implanta\u00e7\u00e3o e execu\u00e7\u00e3o no mesmo ambiente, mesmo que a pr\u00f3pria plataforma Docker opere em diferentes ambientes. Para come\u00e7ar, eu precisava das ferramentas de linha de comando (CLI) do Docker. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/install\/\">O guia<\/a><\/noindex> para instala\u00e7\u00e3o do Docker n\u00e3o pode ser considerado muito claro ou compreens\u00edvel, mas dele \u00e9 poss\u00edvel aprender que, para dar o primeiro passo na instala\u00e7\u00e3o, \u00e9 necess\u00e1rio baixar o Docker Desktop (para Mac ou Windows).<\/p>\n<p>O Docker Hub \u00e9 mais ou menos o que <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/\">GitHub<\/a><\/noindex> para reposit\u00f3rios git, ou um registro <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/\">npm<\/a><\/noindex> para pacotes JavaScript. \u00c9 um reposit\u00f3rio online para imagens Docker. \u00c9 a ele que o Docker Desktop se conecta.<\/p>\n<p>Assim, para come\u00e7ar a trabalhar com o Docker, voc\u00ea precisa fazer duas coisas:<\/p>\n<ul>\n<li>Instale <noindex><a rel=\"nofollow\" href=\"https:\/\/www.docker.com\/get-started\">Docker Desktop<\/a><\/noindex>.<\/li>\n<li>Cadastre-se em <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nDepois disso, voc\u00ea pode verificar se o Docker CLI est\u00e1 funcionando, executando o seguinte comando para verificar a vers\u00e3o do Docker:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nEm seguida, fa\u00e7a login no Docker Hub, digitando, quando solicitado, seu nome de usu\u00e1rio e senha:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nPara usar o Docker, voc\u00ea deve entender os conceitos de imagens e cont\u00eaineres.<\/p>\n<h3>\u258dImagens<\/h3>\n<p>\nUma imagem \u00e9 como um plano que cont\u00e9m instru\u00e7\u00f5es para montar um cont\u00eainer. \u00c9 uma captura de tela imut\u00e1vel do sistema de arquivos e configura\u00e7\u00f5es do aplicativo. Os desenvolvedores podem trocar imagens com facilidade.<\/p>\n<pre><code class=\"plaintext\"># \u0412\u044b\u0432\u043e\u0434 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u043e\u0431\u0440\u0430\u0437\u0430\u0445\ndocker images<\/code><\/pre>\n<p>\nEsse comando produzir\u00e1 uma tabela com o seguinte cabe\u00e7alho:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nA seguir, vamos analisar alguns exemplos de comandos no mesmo formato \u2014 primeiro vem o comando com um coment\u00e1rio, e depois \u2014 um exemplo do que ele pode gerar.<\/p>\n<h3>\u258dCont\u00eaineres<\/h3>\n<p>\nUm cont\u00eainer \u00e9 um pacote execut\u00e1vel que cont\u00e9m tudo o que \u00e9 necess\u00e1rio para executar um aplicativo. Com essa abordagem, o aplicativo sempre funcionar\u00e1 de forma id\u00eantica, independentemente da infraestrutura: em um ambiente isolado e na mesma configura\u00e7\u00e3o. O que est\u00e1 em discuss\u00e3o \u00e9 que diferentes inst\u00e2ncias da mesma imagem s\u00e3o executadas em diferentes ambientes.<\/p>\n<pre><code class=\"plaintext\"># \u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432\ndocker ps -a\nCONTAINER ID \u00a0 \u00a0 IMAGE     COMMAND \u00a0 \u00a0 CREATED     STATUS     PORTS \u00a0 \u00a0 NAMES\n---<\/code><\/pre>\n<p><\/p>\n<h3>\u258dTags<\/h3>\n<p>\nUma tag \u00e9 uma indica\u00e7\u00e3o de uma vers\u00e3o espec\u00edfica da imagem.<\/p>\n<h3>\u258dResumo das Comandos Docker<\/h3>\n<p>\nAqui est\u00e1 uma vis\u00e3o geral de alguns comandos Docker frequentemente usados.<\/p>\n<p>Time<\/p>\n<p>Contexto<\/p>\n<p>A\u00e7\u00e3o<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">docker build<\/a><\/noindex><\/p>\n<p>Modelo<\/p>\n<p>Construindo uma imagem a partir de um Dockerfile<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/tag\/\">docker tag<\/a><\/noindex><\/p>\n<p>Modelo<\/p>\n<p>Tagueando uma imagem<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/images\/\">docker images<\/a><\/noindex><\/p>\n<p>Modelo<\/p>\n<p>Listando as imagens<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/run\/\">docker run<\/a><\/noindex><\/p>\n<p>O cont\u00eainer<\/p>\n<p>Executando um cont\u00eainer baseado na imagem<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/push\/\">docker push<\/a><\/noindex><\/p>\n<p>Modelo<\/p>\n<p>Enviando a imagem para o registro<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/about:blank\">docker pull<\/a><\/noindex><\/p>\n<p>Modelo<\/p>\n<p>Baixando a imagem do registro<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/ps\/\">docker ps<\/a><\/noindex><\/p>\n<p>O cont\u00eainer<\/p>\n<p>Listando os cont\u00eaineres<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/system_prune\/\">docker system prune<\/a><\/noindex><\/p>\n<p>Imagem\/Cont\u00eainer<\/p>\n<p>Removendo cont\u00eaineres e imagens n\u00e3o utilizados<\/p>\n<h3>\u258dArquivo Dockerfile<\/h3>\n<p>\nEu sei como executar um aplicativo localmente para produ\u00e7\u00e3o. Tenho uma configura\u00e7\u00e3o Webpack projetada para construir um aplicativo React pronto. A seguir, tenho um comando que inicia um servidor baseado em Node.js, na porta <code>5000<\/code>. Isso se parece com:<\/p>\n<pre><code class=\"plaintext\">npm i \u00a0 \u00a0 \u00a0 \u00a0 # instalando depend\u00eancias\nnpm run build # construindo o aplicativo React\nnpm run start # iniciando o servidor Node<\/code><\/pre>\n<p>\n\u00c9 importante notar que n\u00e3o tenho um aplicativo de exemplo para este material. Mas para experimentos, qualquer aplicativo Node simples servir\u00e1.<\/p>\n<p>Para usar o cont\u00eainer, voc\u00ea precisar\u00e1 fornecer instru\u00e7\u00f5es ao Docker. Isso \u00e9 feito atrav\u00e9s de um arquivo chamado <code>Dockerfile<\/code>, que est\u00e1 localizado na raiz do projeto. Esse arquivo, a princ\u00edpio, pode parecer um pouco confuso.<\/p>\n<p>Mas o que ele cont\u00e9m \u00e9 simplesmente uma descri\u00e7\u00e3o, usando comandos especiais, de algo semelhante \u00e0 configura\u00e7\u00e3o de um ambiente de trabalho. Aqui est\u00e3o alguns desses comandos:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#from\">FROM<\/a><\/noindex> \u2014 Este comando inicia o arquivo. Ele especifica a imagem base na qual o cont\u00eainer ser\u00e1 constru\u00eddo.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#copy\">COPY<\/a><\/noindex> \u2014 Copiando arquivos de uma fonte local para o cont\u00eainer.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#workdir\">WORKDIR<\/a><\/noindex> \u2014 Definindo o diret\u00f3rio de trabalho para os comandos seguintes.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#run\">RUN<\/a><\/noindex> \u2014 Executando comandos.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#expose\">EXPOSE<\/a><\/noindex> \u2014 Configurando a porta.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#entrypoint\">ENTRYPOINT<\/a><\/noindex> \u2014 Especificando o comando a ser executado.<\/li>\n<\/ul>\n<p>\n<code>Dockerfile<\/code> pode parecer algo assim:<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0437\nFROM node:12-alpine\n\n# \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e app\/\nCOPY . app\/\n\n# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c app\/ \u0432 \u0440\u043e\u043b\u0438 \u0440\u0430\u0431\u043e\u0447\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438\nWORKDIR app\/\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 (\u043a\u043e\u043c\u0430\u043d\u0434\u0430 npm ci \u043f\u043e\u0445\u043e\u0436\u0430 npm i, \u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0441\u0431\u043e\u0440\u043e\u043a)\nRUN npm ci --only-production\n\n# \u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0435 React-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0430\nRUN npm run build\n\n# \u041f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442\nEXPOSE 5000\n\n# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Node-\u0441\u0435\u0440\u0432\u0435\u0440\nENTRYPOINT npm run start<\/code><\/pre>\n<p>\nDependendo da imagem base escolhida, voc\u00ea pode precisar instalar depend\u00eancias adicionais. O fato \u00e9 que algumas imagens base (como Node Alpine Linux) s\u00e3o criadas com o objetivo de serem o mais compactas poss\u00edvel. Como resultado, podem faltar alguns programas que voc\u00ea espera.<\/p>\n<h3>\u258dConstru\u00e7\u00e3o, marca\u00e7\u00e3o e execu\u00e7\u00e3o do cont\u00eainer<\/h3>\n<p>\nA constru\u00e7\u00e3o e a execu\u00e7\u00e3o locais do cont\u00eainer \u2014 s\u00e3o, depois que temos <code>Dockerfile<\/code>, tarefas bastante simples. Antes de enviar a imagem para o Docker Hub, ela precisa ser testada localmente.<\/p>\n<h3>\u258dConstru\u00e7\u00e3o<\/h3>\n<p>\nPrimeiro, voc\u00ea precisa construir <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">a imagem<\/a><\/noindex>, especificando um nome e, opcionalmente, uma tag (se a tag n\u00e3o for definida, o sistema atribuir\u00e1 automaticamente a tag <code>latest<\/code>).<\/p>\n<pre><code class=\"plaintext\"># \u0421\u0431\u043e\u0440\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t &lt;image&gt;:&lt;tag&gt; .<\/code><\/pre>\n<p>\nAp\u00f3s a execu\u00e7\u00e3o desta comando, voc\u00ea poder\u00e1 observar como o Docker realiza a constru\u00e7\u00e3o da imagem.<\/p>\n<pre><code class=\"plaintext\">Enviando contexto de constru\u00e7\u00e3o para o daemon Docker \u00a0 2.88MB\nPasso 1\/9 : FROM node:12-alpine\n\u00a0---&gt; ...executando etapas de constru\u00e7\u00e3o...\nConstru\u00eddo com sucesso 123456789123\nTagueado com sucesso &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nA constru\u00e7\u00e3o pode demorar alguns minutos \u2014 aqui tudo depende de quantas depend\u00eancias voc\u00ea possui. Ap\u00f3s a conclus\u00e3o da constru\u00e7\u00e3o, voc\u00ea pode executar o comando <code>docker images<\/code> e ver a descri\u00e7\u00e3o da sua nova imagem.<\/p>\n<pre><code class=\"plaintext\">REPOSIT\u00d3RIO\u00a0 \u00a0 \u00a0 \u00a0 TAG               ID DA IMAGEM\u00a0 \u00a0 \u00a0 \u00a0 CRIADO              TAMANHO\n&lt;image&gt; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 latest            123456789123\u00a0 \u00a0 \u00a0 \u00a0 H\u00e1 cerca de um minuto   x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dExecu\u00e7\u00e3o<\/h3>\n<p>\nA imagem foi criada. Isso significa que agora voc\u00ea pode executar um cont\u00eainer com base nela. Como quero poder acessar o aplicativo funcionando no cont\u00eainer pelo endere\u00e7o <code>localhost:5000<\/code>, eu, na parte esquerda do par <code>5000:5000<\/code> na seguinte comando estabeleci <code>5000<\/code>. Na parte direita est\u00e1 a porta do cont\u00eainer.<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u043f\u0443\u0441\u043a \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u0442\u0430 5000 \u0438 \u043f\u043e\u0440\u0442\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 5000\ndocker run -p 5000:5000 &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nAgora que o cont\u00eainer foi criado e iniciado, voc\u00ea pode usar o comando <code>docker ps<\/code> para ver as informa\u00e7\u00f5es sobre este cont\u00eainer (ou pode usar o comando <code>docker ps -a<\/code>, que mostra informa\u00e7\u00f5es sobre todos os cont\u00eaineres, n\u00e3o apenas os que est\u00e3o em funcionamento).<\/p>\n<pre><code class=\"plaintext\">ID DO CONT\u00caINER        IMAGEM               COMANDO                     CRIADO              STATUS                   PORTAS                    NOME\n987654321234                     \"\/bin\/sh -c 'npm run\u2026\"   h\u00e1 6 segundos        Em funcionamento h\u00e1 6 segundos             0.0.0.0:5000-&gt;5000\/tcp   stoic_darwin<\/code><\/pre>\n<p>\nSe voc\u00ea agora acessar <code>localhost:5000<\/code> \u2014 poder\u00e1 ver a p\u00e1gina do aplicativo em funcionamento, que \u00e9 id\u00eantica \u00e0 p\u00e1gina do aplicativo em um ambiente de produ\u00e7\u00e3o.<\/p>\n<h3>\u258dAtribui\u00e7\u00e3o de tag e publica\u00e7\u00e3o<\/h3>\n<p>\nPara usar uma das imagens criadas no servidor de produ\u00e7\u00e3o, precisamos ter a capacidade de carregar essa imagem do Docker Hub. Isso significa que primeiro precisamos criar um reposit\u00f3rio no Docker Hub para o projeto. Depois disso, teremos um local para onde podemos enviar a imagem. A imagem deve ser renomeada para que seu nome comece com nosso nome de usu\u00e1rio no Docker Hub. Em seguida, deve vir o nome do reposit\u00f3rio. No final do nome, pode haver qualquer tag. Abaixo est\u00e1 um exemplo de nomea\u00e7\u00e3o de imagens seguindo esse esquema.<\/p>\n<p>Agora podemos construir a imagem com um novo nome e executar o comando <code>docker push<\/code> para envi\u00e1-la ao reposit\u00f3rio do Docker Hub.<\/p>\n<pre><code class=\"plaintext\">docker build -t \\\/:\\ .\ndocker tag \\\/:\\ \\\/:latest\ndocker push \\\/:\\\n\n# Na pr\u00e1tica isso pode ser feito assim:\ndocker build -t user\\\/app:v1.0.0 .\ndocker tag user\\\/app:v1.0.0 user\\\/app:latest\ndocker push user\\\/app:v1.0.0<\/code><\/pre>\n<p>\nSe tudo correr como esperado, a imagem estar\u00e1 dispon\u00edvel no Docker Hub e poder\u00e1 ser facilmente carregada no servidor ou compartilhada com outros desenvolvedores.<\/p>\n<h2>Pr\u00f3ximos passos<\/h2>\n<p>\nAt\u00e9 este momento, confirmamos que o aplicativo, na forma de um cont\u00eainer Docker, est\u00e1 funcionando localmente. Carregamos o cont\u00eainer no Docker Hub. Tudo isso significa que j\u00e1 avan\u00e7amos muito em dire\u00e7\u00e3o ao nosso objetivo. Agora precisamos resolver mais duas quest\u00f5es:<\/p>\n<ul>\n<li>Configura\u00e7\u00e3o da ferramenta CI para testes e implementa\u00e7\u00e3o do c\u00f3digo.<\/li>\n<li>Configura\u00e7\u00e3o do servidor de produ\u00e7\u00e3o para que ele possa carregar e executar nosso c\u00f3digo.<\/li>\n<\/ul>\n<p>\nNo nosso caso, estamos utilizando como solu\u00e7\u00e3o CI\\\/CD <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex>. Como servidor \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex>.<\/p>\n<p>\u00c9 importante notar que \u00e9 poss\u00edvel usar uma combina\u00e7\u00e3o diferente de servi\u00e7os. Por exemplo, em vez do Travis CI, podemos usar CircleCI ou Github Actions. E em vez do DigitalOcean \u2014 AWS ou Linode.<\/p>\n<p>Decidimos trabalhar com o Travis CI, e neste servi\u00e7o j\u00e1 tenho algumas configura\u00e7\u00f5es feitas. Portanto, agora vou explicar brevemente como prepar\u00e1-lo para o trabalho.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI \u00e9 uma ferramenta para testar e implantar c\u00f3digo. N\u00e3o gostaria de entrar nos detalhes da configura\u00e7\u00e3o do Travis CI, pois cada projeto \u00e9 \u00fanico e isso n\u00e3o traria muita vantagem. Mas vou falar sobre os fundamentos que permitir\u00e3o que voc\u00ea comece a trabalhar caso decida usar o Travis CI. Seja o que for que voc\u00ea escolha \u2014 Travis CI, CircleCI, Jenkins, ou algo diferente, m\u00e9todos semelhantes de configura\u00e7\u00e3o ser\u00e3o aplicados.<\/p>\n<p>Para come\u00e7ar a trabalhar com o Travis CI, acesse <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">o site do projeto<\/a><\/noindex> e crie uma conta. Em seguida, integre o Travis CI \u00e0 sua conta do GitHub. Durante a configura\u00e7\u00e3o do sistema, voc\u00ea precisar\u00e1 indicar o reposit\u00f3rio que deseja automatizar e permitir acesso a ele. (Estou usando o GitHub, mas tenho certeza de que o Travis CI pode se integrar ao BitBucket, GitLab, e outros servi\u00e7os semelhantes).<\/p>\n<p>Toda vez que o Travis CI come\u00e7a a trabalhar, um servidor \u00e9 ativado para executar os comandos especificados no arquivo de configura\u00e7\u00e3o, incluindo a implanta\u00e7\u00e3o dos ramos correspondentes do reposit\u00f3rio.<\/p>\n<h3>\u258dCiclo de vida da tarefa<\/h3>\n<p>\nO arquivo de configura\u00e7\u00e3o do Travis CI, chamado <code>.travis.yml<\/code> e armazenado no diret\u00f3rio raiz do projeto, suporta a concep\u00e7\u00e3o de eventos <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">do ciclo de vida<\/a><\/noindex> da tarefa. Aqui est\u00e3o esses eventos, apresentados na ordem em que ocorrem:<\/p>\n<ul>\n<li><code>apt addons<\/code><\/li>\n<li><code>components do cache<\/code><\/li>\n<li><code>before_install<\/code><\/li>\n<li><code>install<\/code><\/li>\n<li><code>before_script<\/code><\/li>\n<li><code>script<\/code><\/li>\n<li><code>before_cache<\/code><\/li>\n<li><code>after_success ou after_failure<\/code><\/li>\n<li><code>before_deploy<\/code><\/li>\n<li><code>deploy<\/code><\/li>\n<li><code>after_deploy<\/code><\/li>\n<li><code>after_script<\/code><\/li>\n<\/ul>\n<p><\/p>\n<h3>\u258dTestes<\/h3>\n<p>\nNo arquivo de configura\u00e7\u00e3o, irei configurar um servidor local do Travis CI. Escolhi a vers\u00e3o 12 do Node como linguagem e indiquei ao sistema que deve instalar as depend\u00eancias necess\u00e1rias para usar o Docker.<\/p>\n<p>Tudo o que est\u00e1 listado em <code>.travis.yml<\/code>, ser\u00e1 executado para todos os pull requests em todos os ramos do reposit\u00f3rio, a menos que indicado o contr\u00e1rio. Essa \u00e9 uma caracter\u00edstica \u00fatil, pois significa que podemos testar todo o c\u00f3digo que entra no reposit\u00f3rio. Isso permite saber se o c\u00f3digo est\u00e1 pronto para ser gravado no ramo <code>master<\/code>, e se ele n\u00e3o quebrar\u00e1 o processo de constru\u00e7\u00e3o do projeto. Nesta configura\u00e7\u00e3o global, estou instalando tudo localmente, executando o servidor de desenvolvimento do Webpack em segundo plano (essa \u00e9 uma caracter\u00edstica do meu fluxo de trabalho) e realizando os testes.<\/p>\n<p>Se voc\u00ea deseja que o seu reposit\u00f3rio exiba \u00edcones com informa\u00e7\u00f5es sobre a cobertura de c\u00f3digo dos testes, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">aqui<\/a><\/noindex> Voc\u00ea pode encontrar um guia breve sobre como usar o Jest, o Travis CI e o Coveralls para coletar e exibir essas informa\u00e7\u00f5es.<\/p>\n<p>Ent\u00e3o, aqui est\u00e1 o conte\u00fado do arquivo <code>.travis.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\"># \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u044f\u0437\u044b\u043a\nlanguage: node_js\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e Node.js\nnode_js:\n\u00a0\u00a0- '12'\n\nservices:\n\u00a0\u00a0# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 Docker\n\u00a0\u00a0- docker\n\ninstall:\n\u00a0\u00a0# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm ci\n\nbefore_script:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm run dev &amp;\n\nscript:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b\n\u00a0\u00a0- npm run test<\/code><\/pre>\n<p>\nAqui terminam as a\u00e7\u00f5es executadas para todos os ramos do reposit\u00f3rio e para pull requests.<\/p>\n<h3>\u258dImplanta\u00e7\u00e3o<\/h3>\n<p>\nPartindo da suposi\u00e7\u00e3o de que todos os testes automatizados foram conclu\u00eddos com sucesso, podemos, embora n\u00e3o seja obrigat\u00f3rio, implantar o c\u00f3digo no servidor de produ\u00e7\u00e3o. Como queremos fazer isso apenas para o c\u00f3digo do ramo <code>master<\/code>, damos instru\u00e7\u00f5es apropriadas ao sistema nas configura\u00e7\u00f5es de implanta\u00e7\u00e3o. Antes de tentar usar em seu projeto o c\u00f3digo que abordaremos a seguir, gostaria de avis\u00e1-lo de que voc\u00ea deve ter um script real chamado para a implanta\u00e7\u00e3o.<\/p>\n<pre><code class=\"plaintext\">deploy:\n  # Compilar o cont\u00eainer Docker e envi\u00e1-lo para o Docker Hub\n  provider: script\n  script: bash deploy.sh\n  on:\n    branch: master<\/code><\/pre>\n<p>\nO script de implanta\u00e7\u00e3o resolve duas tarefas:<\/p>\n<ul>\n<li>Compila\u00e7\u00e3o, tagging e envio da imagem para o Docker Hub usando a ferramenta CI (neste caso, o Travis CI).<\/li>\n<li>Carregamento da imagem no servidor, parada do cont\u00eainer antigo e in\u00edcio do novo (neste caso, o servidor est\u00e1 na plataforma DigitalOcean).<\/li>\n<\/ul>\n<p>\nPrimeiro, precisamos configurar o processo autom\u00e1tico de compila\u00e7\u00e3o, tagging e envio da imagem ao Docker Hub. Tudo isso \u00e9 muito parecido com o que j\u00e1 fizemos manualmente, exceto que aqui precisamos de uma estrat\u00e9gia para atribuir tags \u00fanicas \u00e0s imagens e automatizar o login. Tive dificuldades com alguns detalhes do script de implanta\u00e7\u00e3o, como a estrat\u00e9gia de tagging, login, codifica\u00e7\u00e3o de chaves SSH, e estabelecimento de conex\u00e3o SSH. Mas, felizmente, meu namorado \u00e9 muito bom com bash, assim como com muitas outras coisas. Ele me ajudou a escrever este script.<\/p>\n<p>Ent\u00e3o, a primeira parte do script \u00e9 enviar a imagem para o Docker Hub. Fazer isso \u00e9 bem simples. O esquema de tagging que usei implica em combinar o hash git e a tag git, se existir. Isso garante que uma tag \u00fanica seja criada e facilita a identifica\u00e7\u00e3o da compila\u00e7\u00e3o da qual ela se baseia. <code>DOCKER_USERNAME<\/code> e <code>DOCKER_PASSWORD<\/code> \u2014 s\u00e3o vari\u00e1veis de ambiente personalizadas que podem ser definidas atrav\u00e9s da interface do Travis CI. O Travis CI processar\u00e1 automaticamente os dados secretos para que n\u00e3o caiam em m\u00e3os erradas.<\/p>\n<p>Aqui est\u00e1 a primeira parte do script <code>deploy.sh<\/code>.<\/p>\n<pre><code class=\"plaintext\">#!\/bin\/sh\nset -e # \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u043e\u0448\u0438\u0431\u043e\u043a\n\nIMAGE=&quot;&lt;username&gt;\/&lt;repository&gt;&quot; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 # \u041e\u0431\u0440\u0430\u0437 Docker\nGIT_VERSION=$(git describe --always --abbrev --tags --long) # Git-\u0445\u044d\u0448 \u0438 \u0442\u0435\u0433\u0438\n\n# \u0421\u0431\u043e\u0440\u043a\u0430 \u0438 \u0442\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t ${IMAGE}:${GIT_VERSION} .\ndocker tag ${IMAGE}:${GIT_VERSION} ${IMAGE}:latest\n\n# \u0412\u0445\u043e\u0434 \u0432 Docker Hub \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\necho &quot;${DOCKER_PASSWORD}&quot; | docker login -u &quot;${DOCKER_USERNAME}&quot; --password-stdin\ndocker push ${IMAGE}:${GIT_VERSION}<\/code><\/pre>\n<p>\nA parte dois do script depende totalmente do tipo de host que voc\u00ea est\u00e1 usando e de como a conex\u00e3o est\u00e1 organizada. No meu caso, como estou usando a Digital Ocean, os comandos para se conectar ao servidor s\u00e3o <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>. Ao trabalhar com a AWS, usaremos a ferramenta <code>aws<\/code>, e assim por diante.<\/p>\n<p>Configurar o servidor n\u00e3o foi especialmente dif\u00edcil. Assim, configurei um droplet baseado na imagem b\u00e1sica. Vale notar que o sistema que escolhi requer a instala\u00e7\u00e3o manual do Docker uma \u00fanica vez e o in\u00edcio manual do Docker uma \u00fanica vez. Para instalar o Docker, usei o Ubuntu 18.04, portanto, se voc\u00ea tamb\u00e9m usar o Ubuntu, para fazer o mesmo, pode simplesmente seguir <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">este<\/a><\/noindex> guia simples.<\/p>\n<p>N\u00e3o estou falando aqui sobre comandos espec\u00edficos para o servi\u00e7o, pois esse aspecto pode variar bastante em diferentes casos. Apenas apresentarei um plano geral de a\u00e7\u00e3o a ser realizado ap\u00f3s a conex\u00e3o via SSH ao servidor onde o projeto ser\u00e1 implantado:<\/p>\n<ul>\n<li>\u00c9 preciso encontrar o cont\u00eainer que est\u00e1 em execu\u00e7\u00e3o e par\u00e1-lo.<\/li>\n<li>Em seguida, \u00e9 preciso iniciar um novo cont\u00eainer em segundo plano.<\/li>\n<li>Voc\u00ea precisar\u00e1 definir a porta local do servidor para <code>80<\/code> \u2014 isso permitir\u00e1 acessar o site em um endere\u00e7o como <code>example.com<\/code>, sem especificar a porta, em vez de usar um endere\u00e7o como <code>example.com:5000<\/code>.<\/li>\n<li>E, finalmente, \u00e9 preciso remover todos os cont\u00eaineres e imagens antigos.<\/li>\n<\/ul>\n<p>\nAqui est\u00e1 a continua\u00e7\u00e3o do script.<\/p>\n<pre><code class=\"plaintext\"># \u041d\u0430\u0439\u0442\u0438 ID \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\nCONTAINER_ID=$(docker ps | grep takenote | cut -d&quot; &quot; -f1)\n\n# \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439, \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u0443\ndocker stop ${CONTAINER_ID}\ndocker run --restart unless-stopped -d -p 80:5000 ${IMAGE}:${GIT_VERSION}\ndocker system prune -a -f<\/code><\/pre>\n<p><\/p>\n<h2>Algumas coisas a serem observadas<\/h2>\n<p>\nTalvez, ao se conectar ao servidor via SSH do Travis CI, voc\u00ea veja um aviso que impedir\u00e1 a continua\u00e7\u00e3o da instala\u00e7\u00e3o, pois o sistema estar\u00e1 esperando a resposta do usu\u00e1rio.<\/p>\n<pre><code class=\"plaintext\">A autenticidade do host '&lt;hostname&gt; (&lt;IP address&gt;)' n\u00e3o pode ser estabelecida.\nA impress\u00e3o digital da chave RSA \u00e9 &lt;key fingerprint&gt;.\nVoc\u00ea tem certeza de que deseja continuar a conex\u00e3o (sim\/n\u00e3o)?<\/code><\/pre>\n<p>\nDescobri que a chave de string pode ser codificada em base64 para que seja poss\u00edvel trabalhar com ela de forma conveniente e segura. Na fase de instala\u00e7\u00e3o, \u00e9 poss\u00edvel decodificar a chave p\u00fablica e registr\u00e1-la no arquivo <code>known_hosts<\/code> para evitar o erro mencionado acima.<\/p>\n<pre><code class=\"plaintext\">echo &lt;public key&gt; | base64 # exibe &lt;a chave p\u00fablica codificada em base64&gt;<\/code><\/pre>\n<p>\nNa pr\u00e1tica, esse comando pode parecer assim:<\/p>\n<pre><code class=\"plaintext\">echo \"123.45.67.89 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom\/BWDSU\nGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3\nPbv7kOdJ\/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK\/7XA\nt3FaoJoAsncM1Q9x5+3V0Ww68\/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw\/Pb0rwert\/En\nmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z\/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx\nNrRFi9wrf+M7Q== you@example.com\" | base64<\/code><\/pre>\n<p>\nE \u00e9 assim que fica o que ela gera \u2014 uma string codificada em base64:<\/p>\n<pre><code class=\"plaintext\">MTIzLjQ1LjY3Ljg5IHNzaC1yc2EgQUFBQUIzTnphQzF5YzJFQUFBQUJJd0FBQVFFQWtsT1Vwb0RIcmZIWTE3U2JybVRJcE5MVEdLOVRqb20vQldEU1UKR1BsK25hZnpsSERUWVc3aGRJNHlaNWV3MThKSDRKVzlqYmhVRnJ2aVF6TTd4bEVMRVZmNGg5bEZYNVFWa2JQcHBTd2cwY2RhMwpQYnY3a09kSi9NVHlCbFdYRkNSK0hBbzNGWFJpdEJxeGlYMW5LaFhwSEFac01jaUxxOFY2UmpzTkFRd2RzZE1GdlNsVksvN1hBCnQzRmFvSm9Bc25jTTFROXg1KzNWMFd3NjgvZUlGbWIxenVVRmxqUUpLcHJyWDg4WHlwTkR2allOYnk2dncvUGIwcndlcnQvRW4KbVorQVc0T1pQblRQSTg5WlBtVk1MdWF5ckQyY0U4NlovaWw4YitndzNyMysxbkthdG1Ja2puMnNvMWQwMVFyYVRsTXFWU3NieApOclJGaTl3cmYrTTdRPT0geW91QGV4YW1wbGUuY29tCg==<\/code><\/pre>\n<p>\nAqui est\u00e1 o comando mencionado anteriormente<\/p>\n<pre><code class=\"plaintext\">install:\n\u00a0\u00a0- echo  | base64 -d &gt;&gt; $HOME\/.ssh\/known_hosts<\/code><\/pre>\n<p>\nA mesma abordagem pode ser usada com a chave privada ao estabelecer uma conex\u00e3o, pois voc\u00ea pode precisar da chave privada para acessar o servidor. Ao trabalhar com a chave, voc\u00ea s\u00f3 precisa garantir seu armazenamento seguro em uma vari\u00e1vel de ambiente do Travis CI, e que ela n\u00e3o seja exibida em nenhum lugar.<\/p>\n<p>Outra coisa a se notar \u00e9 que pode ser necess\u00e1rio executar todo o script de implanta\u00e7\u00e3o apresentado como uma \u00fanica linha, por exemplo \u2014 usando <code>doctl<\/code>. Isso pode exigir alguns esfor\u00e7os adicionais.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh  --ssh-command \"todas as comandos estar\u00e3o aqui &amp;&amp; aqui\"<\/code><\/pre>\n<p><\/p>\n<h2>TLS\/SSL e balanceamento de carga<\/h2>\n<p>\nDepois que fiz tudo o que foi mencionado acima, o \u00faltimo problema que encontrei foi que o servidor n\u00e3o tinha SSL. Como estou usando um servidor Node.js, para fazer com que <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-secure-a-containerized-node-js-application-with-nginx-let-s-encrypt-and-docker-compose\">funcione<\/a><\/noindex> o proxy reverso Nginx e o Let\u2019s Encrypt, \u00e9 preciso passar um bom tempo arrumando.<\/p>\n<p>Eu realmente n\u00e3o queria realizar todas essas configura\u00e7\u00f5es de SSL manualmente, por isso apenas criei um balanceador de carga e registrei suas informa\u00e7\u00f5es no DNS. No caso da DigitalOcean, por exemplo, criar um certificado autoassinado que se atualiza automaticamente no balanceador de carga \u00e9 um procedimento simples, gratuito e r\u00e1pido. Essa abordagem tamb\u00e9m tem a vantagem adicional de permitir que seja muito f\u00e1cil configurar o SSL em v\u00e1rios servidores atr\u00e1s do balanceador de carga, permitindo que os pr\u00f3prios servidores n\u00e3o 'se preocupem' com o SSL, mas ainda assim utilizem, como de costume, a porta <code>80<\/code>. Portanto, configurar o SSL no balanceador de carga \u00e9 muito mais simples e conveniente do que m\u00e9todos alternativos de configura\u00e7\u00e3o de SSL.<\/p>\n<p>Agora, voc\u00ea pode fechar no servidor todas as portas que aceitam conex\u00f5es de entrada, exceto a porta <code>80<\/code>, usada para comunica\u00e7\u00e3o com o balanceador de carga, e a porta <code>22<\/code> para SSH. Como resultado, qualquer tentativa de acessar o servidor diretamente por quaisquer portas, exceto essas duas, falhar\u00e1.<\/p>\n<h2>Resultados<\/h2>\n<p>\nDepois que fiz tudo o que descrevi neste material, eu j\u00e1 n\u00e3o tinha medo da plataforma Docker ou dos conceitos de cadeias CI\/CD automatizadas. Eu consegui configurar uma cadeia de integra\u00e7\u00e3o cont\u00ednua, onde o c\u00f3digo \u00e9 testado antes de ser enviado para produ\u00e7\u00e3o e \u00e9 implantado automaticamente no servidor. Tudo isso ainda \u00e9 relativamente novo para mim, e estou certa de que h\u00e1 maneiras de melhorar meu fluxo de trabalho automatizado e torn\u00e1-lo mais eficiente. Portanto, se voc\u00ea tiver ideias a esse respeito, me deixe <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">saber. Espero que este artigo tenha ajudado voc\u00ea em seus neg\u00f3cios. Quero acreditar que, ao l\u00ea-lo, voc\u00ea aprendeu tanto quanto eu aprendi enquanto lidava com tudo o que descrevi.<\/a><\/noindex> No nosso<\/p>\n<p><b>P.S.<\/b> marketplace <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace#order\">, h\u00e1 uma imagem<\/a><\/noindex> , que \u00e9 instalada com um clique. Voc\u00ea pode verificar o funcionamento dos cont\u00eaineres em <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace\/docker#advantages\">Docker<\/a><\/noindex>. Todos os novos clientes recebem 3 dias gr\u00e1tis para testes. <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/\">VPS<\/a><\/noindex>Voc\u00ea est\u00e1 utilizando tecnologias CI\/CD em seus projetos?<\/p>\n<p><b>Prezados leitores!<\/b> RabbitMQ. Parte 1. Introdu\u00e7\u00e3o. Erlang, AMQP<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Cria\u00e7\u00e3o de uma cadeia CI\/CD e automa\u00e7\u00e3o do trabalho com Docker\" src=\"\/wp-content\/uploads\/2020\/02\/3fbfa90ea56a3d9b6b89ba945eb94570.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":69352,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-69351","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-02-18T11:44:06+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-03-03T13:15:00+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Cria\u00e7\u00e3o de uma cadeia CI\/CD e automa\u00e7\u00e3o do trabalho com Docker | ProHoster","description":"\ud83e\udd47 Cria\u00e7\u00e3o de uma cadeia CI\/CD e automa\u00e7\u00e3o do trabalho com Docker | ProHoster","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster","og:description":"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e.","og:url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-02-18T11:44:06+00:00","article:modified_time":"2020-03-03T13:15:00+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"69351","title":null,"description":null,"keywords":null,"keyphrases":{"focus":[],"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 19:24:45","updated":"2026-08-11 12:50:24","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/69351","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=69351"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/69351\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media\/69352"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=69351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=69351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=69351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}