Alisson Machado
21 December 2020

Capítulo 5: Deploy de uma aplicacao usando Docker

Este capítulo tem como objetivo explicar como instalar o Docker, criacao de containers, imagens, redes e o deploy de imagens em um cenario real.

 

Estrutura: 

Neste capítulo discutiremos os seguintes tópicos: 

  • Introdução ao Docker 
  • Instalando Docker e criando contêineres 
  • Criando imagens usando Dockerfile 
  • Publicando imagens e implantando-as



Introdução ao Docker 

 

Docker é uma ferramenta desnvolvida para criar contêineres, versioná-los e compartilhá-los de uma maneira smples. Para quem tem mais experiência com Linux, provavelmente já sabe o que é um Container, usando o comando chroot no Linux.

Explicando de uma maneira mais superficial um contêiner é um diretório em seu sistema de arquivos que permite instalar outro sistema operacional Linux, como o comando diz chroot (change root), o novo diretório raiz / , será o no qual você acabou de definir. No entanto, um sistema operacional precisa muito mais do que apenas os arquivos, então para a Parte de Rede, o Docker usa os iptables no seu background, gerenciando alguns redirecionamentos através de regras NAT, nos próximos parágrafos eu vou mostrar a quantidade de coisas que Docker gerencia para você executando apenas alguns comandos simples. 

 

Instalação 

 

Em primeiro lugar, precisamos instalar o Docker em nosso ambiente, na documentação oficial você encontrará todas as etapas e as instruções detalhadas, abaixo temos um resumo de tudo. 

apt clean
apt-get update
apt-get remove docker docker-engine docker.io containerd runc -y

 

Os comandos acima removerão qualquer instalação de visualizações do Docker se você tiver uma em sua máquina. 


 apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common -y

 

Agora estamos apenas instalando algumas dependências que são necessárias para a sua instalação do Docker, como o apt-transport-https, que é um requisito para baixar pacotes de repositórios https. 

 

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -

 

Este comando adicionará a chave GNU Privacy Guard para garantir a autenticidade do repositório Docker. 

 

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"


Estamos usando o Ubuntu; portanto, o último comando é especificamente para ambientes ubuntu. 

  

apt-get update -y
apt-get install docker-ce docker-ce-cli containerd.io -y

 

Para concluir, estes últimos comandos onde atualizar sua lista de repositórios locais e instalar a Edição Da Comunidade Docker.   

 

Criando containers 

 

O ambiente está pronto, para validar se o seu Docker ja esta em execucao, você pode executar o seguinte comando. 

 

root@devops:~# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2020-04-02 10:28:47 UTC; 1min 7s ago
Docs: https://docs.docker.com
Main PID: 6464 (dockerd)
Tasks: 9
CGroup: /system.slice/docker.service
└─6464 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

 

Ok, então sua instalação Docker está ativa e funcionando. Para criar um contêiner, basta digitar este comando: 

 

root@devops:~# docker run -ti centos /bin/bash
Unable to find image 'centos:latest' locally
latest: Pulling from library/centos
8a29a15cefae: Downloading [=================================================> ] 72.58MB/73.23MB   

 

O último comando significa que queremos executar um novo contêiner, com um TTY ( Terminal) que é representado pelo parâmetro -t e interativo, representado pelo parâmetro -i, assim, podemos executar comandos no terminal de contêineres e analisar a resposta. 

 

Quando seu contêiner é criado, você pode ver uma saída como esta: 

root@devops:~# docker run -ti centos /bin/bash
Unable to find image 'centos:latest' locally
latest: Pulling from library/centos
8a29a15cefae: Pull complete Digest: sha256:fe8d824220415eed5477b63addf40fb06c3b049404242b31982106ac204f6700
Status: Downloaded newer image for centos:latest
[root@595b42fceebc /]#

 

A última linha é a mais importante: 

 

[root@595b42fceebc /] #                                                                                                                                                                                               

 

Se você comparar com o nosso terminal antes de executar o comando: 


root@devops:~# docker run -ti centos /bin/bash 

 

Você pode ver claramente que o nome do host foi alterado, o que significa que agora estamos dentro do contêiner e todos os comandos que executamos a partir de agora, estarão funcionando dentro do contêiner e nada será instalado dentro da nossa VM. O nome do host dado ao contêiner 595b42fceebc, é o ID do contêiner, que é usado para gerenciar seu contêiner. Para sair do seu contêiner, basta digitar: exit ou CTRL+D. 

 

[root@595b42fceebc /]#
exit
root@devops:~#

 

Para ver seus contêineres em execução, digite o seguinte comando: 

root@devops:~# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

 

Podemos ver que nao temos nenhum contêiner em execução, isso acontece porque uma vez que você digita exit ou CTRL+D , o contêiner é parado, para ver os containers atualmente parados use o comando: 

root@devops:~# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
595b42fceebc centos "/bin/bash" 9 minutes ago Exited (0) 2 minutes ago hardcore_neumann

 

Se você der uma olhada, você pode ver que o valor na coluna CONTAINER ID é exatamente o mesmo que o hostname no contêiner quando o criamos. 

 

No entanto, por que Docker tem esse comportamento? Por que quando saímos de um contêiner o contêiner está parado? Acontece, por que no conceito de contêineres, um contêiner é criado para uma tarefa em específico, diferente das máquinas virtuais, que geralmente usamos para varias coisas, como por exemplo no capitulo em que subimos um servidor LAMP, portanto, não precisamos nos preocupar com o tempo de atividade ou com a manutenção de um contêiner, você pode simplesmente excluí-lo e criar um novo. Se você quiser fazer alterações em um container, você deve modificar a imagem, salvá-la, excluir o container atual e iniciar um novo com a nova versão da imagem. 

 

No nosso caso, criamos um contêiner baseado em uma imagem centos e nós apenas executamos o comando /bin/bash, que estava mantendo meu contêiner em execução, uma vez que o comando parou de executar o contêiner foi parado. Ele também se aplica às configurações do contêiner, se você quiser alterar um redirecionamento ou quaisquer outros parâmetros, você deve criar um novo contêiner com esses novos parâmetros, salvá-lo e executar um novo contêiner. 

 

Agora crie um novo contêiner baseado na imagem centos e dentro deste contêiner colocaremos uma aplicação Python que aprendemos no último capítulo. 

 

root@devops:~# docker run -ti --name python_app -p 5000:5000 centos /bin/bash
[root@ce7f74b0304a /]#                                                                               

 

A criação agora foi claramente mais rápida do que a primeira, isso acontece porque a imagem do CentOS já está em nosso repositório local, portanto, o Docker não precisa baixá-lo novamente. 

 

Instale as dependências no container. 

[root@ce7f74b0304a /]# yum clean all && yum install python3 python3-pip -y
Failed to set locale, defaulting to C.UTF-8
0 files removed
Failed to set locale, defaulting to C.UTF-8
CentOS-8 - AppStream 11% [======= ] 122 kB/s | 767 kB 00:49 ETA                                                                                                

 

Enquanto o download está sendo executado, temos um novo comando agora, o yum, este comando é o respectivo para APT no Ubuntu, você pode usá-lo para instalar pacotes de repositórios remotos. 

 

Após o acabamento do download instale os módulos Python para a aplicação. 


[root@ce7f74b0304a /]# python3 -m pip install flask
Successfully installed Jinja2-2.11.1 MarkupSafe-1.1.1 Werkzeug-1.0.1 click-7.1.1 flask-1.1.1 itsdangerous-1.1.0

 

Copie o código fonte usado no último capítulo para o contêiner. 

 

[root@ce7f74b0304a /]# cat <<EOF > /srv/app.py
> from flask import Flask
>
> app = Flask(__name__)
>
> @app.route("/")
> def index():
>   return "DevOps with Linux"
>
>
> if __name__ == "__main__":
>   app.run(debug=True,host="0.0.0.0")
> EOF


Execute a aplicação para ver se ela está funcionando. 

 

[root@ce7f74b0304a /]# python3 /srv/app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 123-083-749

 

Perfeito, então você pode testar se a partir do navegador web. 

 

 


Tudo está funcionando bem, então temos um contêiner funcionando, com um aplicativo dentro, as dependências foram instaladas e já sabemos o comando para executar a aplicação, agora precisamos criar uma nova imagem baseada nessa. Para criar a imagem precisamos sair do contêiner.

Então vamos lá.  

root@ce7f74b0304a /]# python3 /srv/app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 123-083-749
192.168.178.60 - - [02/Apr/2020 11:08:54] "GET / HTTP/1.1" 200 -
192.168.178.60 - - [02/Apr/2020 11:08:54] "GET /favicon.ico HTTP/1.1" 404 -
^C[root@ce7f74b0304a /]# exit
root@devops:~#                            

 

As linhas importantes são as últimas, onde eu digito CTRL+C para parar a aplicação e CTRL+D para sair do contêiner. 

 

root@devops:~# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce7f74b0304a centos "/bin/bash" 18 minutes ago Exited (0) 57 seconds ago python_app
595b42fceebc centos "/bin/bash" 38 minutes ago Exited (0) 30 minutes ago hardcore_neumann

 

Temos agora um contêiner chamado python_app, se você verificar a última coluna, esta é a referencia que vamos usar para criar uma imagem. 

 

root@devops:~# docker commit python_app my_first_image
sha256:866e933c059b90a098cad06a1989d24bf16870caea1d691e2c0d3f4599f1608c

 

O parâmetro commit, recebe como primeiro parâmetro um container, pode ser um container em execucao ou parado, não importa, o segundo parâmetro é o nome da imagem, portanto, estamos criando uma nova imagem chamada my_first_image. 

 

Você pode verificar as imagens que você tem executando o comando: 

 

root@devops:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
my_first_image latest 866e933c059b 2 minutes ago 279MB
centos latest 470671670cac 2 months ago 237MB

 

Podemos ver duas imagens, uma é a imagem do CentOS, que baixamos do Docker Hub, que é o repositório oficial, a outra é my_first_image, que criamos agora. 

 

Você pode criar agora quantas instâncias da sua aplicação que você desejar, apenas executando o seguinte comando: 

 

root@devops:~# docker run -ti -p 5000:5000 my_first_image python3 /srv/app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat

 

O parâmetro -p 5000:5000, está mapeando a porta 5000 da nossa máquina local para a porta 5000 do nosso contêiner. 

 

Se você quiser publicar sua imagem para baixar em qualquer outro servidor, você pode criar uma conta no Docker Hub https://hub.docker.com/ .

E envie sua imagem para lá, no meu caso eu já tenho uma conta, então eu vou te ensinar como você pode enviar sua própria imagem para o hub. 

 

root@devops:~# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: alissonmenezes
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

 

O nome de usuário que você define no momento em que você está criando a conta.

Você deve usar o docker login, para autenticar em seu repositório, o nome de usuário também é usado para especificar onde você armazenará essa imagem, por exemplo: 

 

root@devops:~# docker tag my_first_image alissonmenezes/my_first_image
root@devops:~# docker push alissonmenezes/my_first_image
The push refers to repository [docker.io/alissonmenezes/my_first_image]
ef02c4bc0109: Pushing [===========================> ] 22.67MB/41.69MB ef02c4bc0109: Pushed

 

Agora minha imagem esta publicada para o mundo inteiro, se eu verificar meu próprio perfil no Docker Hub, e possível vê-la listada. 

 

 

 

 

Minha imagem está salva e está preparada para ser executada em qualquer Docker instalado em qualquer lugar do mundo, assim, agora vou limpar meu ambiente, todas as imagens e contêineres e criar um novo contêiner baseado nessa imagem baixada diretamente do Hub. 

 

root@devops:~# docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache

Are you sure you want to continue? [y/N] y
Deleted Containers:
06035663ec0423a479cddb0c287637626641c79c93896c6566efb802dc3ea35f
4bbab42a400fc72a339977886cde2e061c9c1dce78305d5cb56336e6f36d5965
adc48c7c0be4d422891b9e44146018c175120aab202a338f88f4a1847b50ba67
ce7f74b0304a9bc08bf0ccdd2832d6907408c0f6a9c80c56a75d3bdbf6738b62
595b42fceebc9e4f9c6e2d23d54a8ecd7eaead266ef114c953b1715d0f58a7ee
Total reclaimed space: 41.69MB 


O comando docker system prune é usado para limpar seu ambiente, excluindo todos os containers parados. Eu executei o comando e agora vou validar se o ambiente está limpo. 

root@devops:~# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

 

Sem contêineres funcionando, agora vamos excluir todas as imagens. 

 

root@devops:~# docker image rm $(docker image ls)
Untagged: alissonmenezes/my_fist_image:latest
Untagged: alissonmenezes/my_fist_image@sha256:3c729fd3e1a595ff2bcf0937611732550ebde0c0d1945a87c01f979ca620b9fa
Untagged: my_first_image:latest
Deleted: sha256:866e933c059b90a098cad06a1989d24bf16870caea1d691e2c0d3f4599f1608c
Deleted: sha256:fbf13ca6b28b7113d750e70b57c59e2cfc90ae6b3c7436166161c92eef1dc219
Untagged: centos:latest

 

Verificando se ainda temos imagens. 

root@devops:~# docker image ls -a
REPOSITORY TAG IMAGE ID CREATED SIZE 


Sem imagens, então vamos criar um contêiner baseado na imagem que enviamos para o hub. 

 

root@devops:~# docker run -ti -p 5000:5000 alissonmenezes/my_fist_image:latest python3 /srv/app.py
Unable to find image 'alissonmenezes/my_fist_image:latest' locally
latest: Pulling from alissonmenezes/my_fist_image
8a29a15cefae: Pull complete f21402989f68: Pull complete Digest: sha256:3c729fd3e1a595ff2bcf0937611732550ebde0c0d1945a87c01f979ca620b9fa
Status: Downloaded newer image for alissonmenezes/my_fist_image:latest
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 249-372-527

 

Você pode ver agora o principal objetivo do Docker, que é criar imagens para com o ambiente completo para sua aplicação funcionar, acabamos de criar uma versão de um CentOS com Python3 instalamos uma aplicação dentro, se você quiser criar uma nova versão da sua aplicação, ou atualizar a versão CentoOS, ou mesmo alterar o S.O. base da imagem, como migrar do CentOS para Alpine, você pode fazer isso, fazer todos os testes, criar uma nova versão da imagem com o mesmo nome, enviá-la para o hub e baixá-la no seu ambiente de produção/qualidade/desenvolvimento. 

 

 Criando imagens com Dockerfile 

 

Agora, você já tem todos os passos de como criar uma imagem e como enviá-la para o hub, podemos automatizar essas etapas usando o Dockerfile, este arquivo ajuda você a rastrear as modificações feitas em uma imagem. Quando você apenas cria um contêiner instala tudo e cria uma imagem, é uma tarefa difícil de rastrear tudo o que foi instalado, portanto, se você quiser criar uma nova versão para a mesma aplicação, você tem que acompanhar todas as dependências e tudo, para garantir que todas as dependências antes, elas ainda estejam presentes na nova imagem. 

 

Crie agora um arquivo chamado Dockerfile e incluiremos todas as etapas em dentro do arquivo. 

 

root@devops:~# vim Dockerfile

from centos
maintainer alisson.copyleft@gmail.com

run yum clean all
run yum install python3 python3-pip -y
run python3 -m pip install flask
copy app.py /srv/app.py

expose 5000

cmd ["python3","/srv/app.py"]

 

Acima você tem o conteúdo do arquivo, que é exatamente todos os comandos que executamos dentro do contêiner. As declarações são autoexplicativas, assim, você pode usar: 

 

FROM, para especificar a imagem base 

MAINTAINER, para especificar quem está mantendo a imagem 

RUN, executará os comandos dentro do contêiner 

COPY,copiar um arquivo da máquina local para o recipiente 

EXPOSE, publica o porto de contêineres 

CMD, o comando que garantirá o contêiner funcionando 

 

Para criar a imagem baseada no Arquivo Docker, execute este comando: 

 

root@devops:~# docker build . -t my_new_image
Sending build context to Docker daemon 735.7kB
Step 1/8 : from centos
latest: Pulling from library/centos
8a29a15cefae: Pulling fs layer    

 

O comando docker build, procurará um Dockerfile dentro do diretório atual e o parâmetro -t, significa TAG, que definirá o nome da imagem. 

 

Successfully built b1b23966bb89
Successfully tagged my_new_image:latest

 

Você pode criar um novo contêiner executando um comando como este: 

 

root@devops:~# docker run my_new_image
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 130-050-890 


É fácil ver que agora não tivemos que passar nenhum parâmetro de adição, porque tudo isso já foi passado no Dockerfile. 

 

O Dockerfile basicamente fez os mesmos passos como nós antes, ele criou um novo contêiner, se você verificar a saída: 

 

Successfully built b1b23966bb89

 

Este é o ID do contêiner e, no final, o recipiente foi marcado com o nome especificado na compilação do docker. 

 

Successfully tagged my_new_image:latest

 

Você poderia colocar diretamente o nome do repositório e empurrá-lo para lá. Agora você já tem o básico do Docker e é o mínimo necessário para você começar a Dockerizar suas aplicações. 

 

Conclusão 

 

Para concluir, podemos ver que trabalhar com docker é uma tarefa fácil, só temos que aplicar nosso conhecimento prévio em uma forma diferente de trabalho, em que você pode criar uma versão do seu ambiente, incluindo a infraestrutura e o código, enviá-lo para todos os lugares que você quiser, mas geralmente em ambientes de produção utilizamos Kubernetes, e Docker é um requisito para trabalharmos com ele.