Curso Hands On gratuito de Docker

Neste treinamento hands on de Docker você irá aprender como trabalhar com o Docker de forma simples e objetiva, possibilitando assim ter um avanço em sua carreira seja como DevOps ou mesmo como developer.

Este conteúdo foi criado primeiramente para uso próprio então caso tenha alguma sugestão ou mesmo dúvida utilize os comentários desta publicação

Links importantes que estão relacionados a este conteúdo:

https://cloud.docker.com/
https://github.com/jaccon/curso-free-docker-handson
https://www.linkedin.com/in/jaccon/

Capítulo 1 – Introdução

a) O que é Docker
b) Por que o Docker
c) O que são containers ?
d) Diferenças entre imagens e containers
e) Arquitetura do Docker

Capítulo 2 – Instalalação

a) Instalação

Capítulo 3 – Mão na Massa

a) Criando o primeiro container
b) Reutilizando um container
c) Compartilhamentos de recursos no Docker
d) Mapeamento de um volume local para dentro do container
e) Iniciando containers como daemons
f) Vendo os logs de um container
g) Inspecionando um container

Capítulo 4 – Gerenciamento de imagens

a) Criando tags para imagens
b) Removendo uma determinada tag
c) Diferenças entre Docker Hub e Docker Registry
d) Fazendo nossa primeira build - Dockerfile
e) Inspecionando dados de uma determinada imagem
f) Copiando arquivos para dentro de uma imagem
g) Explorando o arquivo DockerFile
h) Enviando imagens para o Docker Hub

Capítulo 5 – Gerenciando as redes dos containers

a) Modelo padrão de rede no Docker
b) Listando as interfaces de rede e CLI de gerenciamento
c) Testando conectividade entre dois containers
d) Criando uma nova rede e testando conectividade

Capítulo 6 – Conhecendo o Docker Compose

a) Conhecendo o Docker Compose

Capítulo 1 – Introdução

a) O que é Docker
Docker é uma plataforma Open Source escrito em Go, que é uma linguagem de programação de alto desempenho desenvolvida dentro do Google, que facilita a criação e administração de ambientes isolados. Resumindo o Docker é uma engine de administração de containers. Falaremos mais sobre containers no ítem 3 deste capítulo.

b) Por que o Docker
O Docker possibilita um ambiente de infra-estrutura mais escalável, seguro e com melhor gerenciamento de recursos de hardware. Escalável pois ele já foi projetado para que você possa escalar pois aplica o conceito de aplicações com micro-serviços.

Seguro pois com ele é possível isolar cada aplicação de uma ambiente equivalente a um chroot jail pois ele é baseado na evolução do LXC.

E por último ele economiza recursos de hardware pois diferente do modelo de virtualização ele aproveita a libs e o core do sistema host como base de seus novos containers. Vamos exemplificar melhor a diferença aqui na imagem abaixo:

Do lado esquerdo você possui o modelo tradicional de virtualização com hypervisor. Do lado direito você possui uma ideia de aplicação por containers onde temos o hardware, o OS do host, as libs e por sequência as aplicações que estão dentro de cada um dos containers.

c) O que são Containers
é um conjunto de um ou mais processos organizados isoladamente do sistema. Todos os arquivos necessários à execução de tais processos são fornecidos por uma imagem distinta. Na prática, os containers Linux são portáteis e consistentes durante toda a migração entre os ambientes de desenvolvimento, teste e produção. Essas características os tornam uma opção muito mais rápida do que os pipelines de desenvolvimento, que dependem da replicação dos ambientes de teste tradicionais.

d) Diferenças entre imagens e containers
A imagem é um conjunto de metadados que representam a tecnologia/SO que iremos utilizar. Um container como descrito no ítem 3 é um conjunto de processos organizados isoladamente.

Capítulo 2 – Instalação

a) Instalação

A instalação do Docker é muito simple e para cada determinado de plataforma há uma ferramenta que auxilia a instalação. No caso do Windows/ MacOS você deve entrar no site do projeto em:
https://www.docker.com/products/docker-desktop

Feito isto a instalação é simples como o Next, Next + Enter

No Linux o processo é um pouco diferente mas não tão diferente. Para distribuições baseadas no Debian a instalação deve ser feita como :

apt-get install docker-io

Capítulo 3 – Mão na massa

a) Criando o primeiro container

O primeiro container não poderia ser diferente vamos testar a aplicação Hello World que na primeira vez irá baixar do Docker Hub a imagem do Hello World vai executar e vai parar o container. Abra o terminal e digite o comando:

docker run hello-world

O comando docker run é a concatenação de 4 comandos em um único comando. Com ele o Docker irá executar:

docker image pull * para baixar a imagem do registry para a máquina;
docker container create * faz a criação do container
docker container start * faz a inicialização do container
docker container exec * faz a execução do comando

b) Reutilizando um container

Para que seja possível reutilizarmos um container é importante também que nossos containers sejam nomeados corretamente. Exemplo

Primeiro criamos um container que no nosso caso terá o nome de jaccon.com.br-frontend

docker run -ti –name jaccon.com.br-frontend debian

jaccon.com.br-frontend em negrito é o nome que demos para nosso container

Podemos sair do container utilizando o comando exit dentro do container e depois para entrar no container novamente utilizamos o comando:

docker container start -ai jaccon.com.br-frontend

c) Compartilhamentos de recursos no Docker

O compartilhamento de recursos entre a máquina local e o container é necessário por diversos fatores. Compartilhamento de volumes, compartilhamento de portas e até mesmo compartilhamento de rede

Aqui nós iremos aprender como compartilhar estes recursos. Começaremos apontando uma determinada porta para nosso container. Subiremos um servidor web NGINX para ser o servidor web de nossa aplicação:

docker container run -p 8080:80 --name nginx nginx

Explicando as flag -p
-p 8080:80 : estamos dizendo que a porta 8080 no nosso servidor local irá expor a porta 80 de dentro do nosso container

d) Mapeamento de um volume local para dentro do container

Para fazermos o mapeamento de volumes locais para dentro de um volume vamos utilizar ainda o conceito de mapeamento de volume do frontend de nosso site. Aqui iremos criar uma pasta chamada website-frontend com um arquivo index.html e vamos expor para dentro do container sendo assim:

mkdir website-frontend
touch website-frontend/index.html
cd website-frontend
docker container run -p 8080:80 -v $(pwd):/usr/share/nginx/html nginx

Perceba que nós publicamos o index.html que está dentro da nossa pasta website-frontend para dentro do nosso NgInx

e) Iniciando containers como Daemons

Agora nós iremos utilizar o Docker como daemons ou seja vamos iniciar estes containers de forma que ele execute os processos mas não de forma interativa.

Vamos criar então um novo container usando o comando:

docker container run -d --name website-frontend -p 8080:80 -v $(pwd):/usr/share/nginx/html nginx

Execute o comando docker ps para verificar o funcionamento

Para pararmos este container basta executar

docker container stop website-frontend

f) Vendo os logs de um container

Imaginando que ainda temos o container website-frontend no ar nós vamos agora listar todos os logs deste container:

docker container logs website-frontend

g) Inspecionando um container

Através do comando inspect você pode ter todos os metadados correspondentes a um container. Nele você pode tirar informações como quantidade de memória, volume apontado, dados de rede, tempo de execução e muito mais.
Sendo assim vamos executar:

docker container inspect website-frontend
Saída do comando inspect com os metadados de nosso container

Capítulo 4 – Gerenciamento de imagens

a) Criando tags para imagens

As tags são ponteiros para imagens específicas. Você pode além de baixar a imagem para sua máquina você pode também criar uma tag para identificar suas modificações. Sendo assim você pode customizar suas imagens conforme sua necessidade. Primeiramente vamos listar as imagens que temos em nossa máquina

docker image ls

A saída do comando acima será algo como

Perfeito, agora vamos renomear a nossa imagem nginx para website-frontend:

docker image tag nginx website-frontend
Para confeir se a tag foi criada basta executar novamente o docker image ls

b) Removendo uma determinada tag

Agora que nós temos uma tag nossa criada vamos testar o comando de remoção. É extremamente simples

docker image rm website-frontend

Você pode passar uma lista de imagens a serem excluidas apenas dando o espaço entre os nomes de imagens

c) Diferenças entre Docker Hub e Docker Registry

De uma forma resumida o Docker Hub é um serviço em nuvem que possibilita que você faça o download de imagens e também registre suas imagens de forma pública.

O Docker Registry é um repositório server-side que possibilita que você registre suas imagens de forma privada, dentro de sua empresa

d) Fazendo nossa primeira build – Dockerfile

Agora vamos criar nossa primeira build. Quando falamos em gerar uma build já estamos entrando na customização do Docker para atender suas necessidades de customização. Primeiramente vamos criar uma pasta chamada por exemplo website-frontend e dentro dela crie um arquivo com o nome Dockerfile ( preste atenção no nome do arquivo pois qualquer nome diferente deste o comando não irá executar ). Dentro deste arquivo adicione o conteúdo

FROM nginx:latest
RUN echo ' Hello World' > /usr/share/nginx/html/index.html

Estamos dizendo que a build utilizará a última versão da imagem do NGinx e exibirá uma página com o resultado Hello World como página padrão

Depois execute o comando para gerar a build

docker image build -t website-frontend .
O resultado da build será algo como o screenshot acima

Para testar a nossa nova imagem basta executar:

docker container run -p 8080:80 website-frontend

O Dockerfile é um arquivo que pode receber argumentos que podem ser referenciadas através do ARGS. Vamos criar um exemplo:

FROM debian
LABEL maintainer 'Website Frontend'
ARG URL=minhaurl.com.br
ENV URL=${URL}

O argumento URL está sendo passado como parametro para dentro do container. Para testar se a variavel de ambiente foi enviada para dentro do container podemos utilizar:

docker container run website1 bash -c 'echo ${URL}'
A saída do comando será algo como o screenshot acima

Nós podemos alterar um argumento passando o valor para uma variavél em tempo de execução da build. Para isto vamos ao exemplo:

docker image build --build-arg URL=myappurl -t website1 .

perceba que o valor de URL agora foi setado para myappurl . O resultado você pode ver rodando novamente o comando:

docker container run website1 bash -c 'echo ${URL}'

e) Inspectionando dados de uma determinada imagem

A inspeção dos metadados de uma determinada imagem pode ser feita através do comando:

docker image inspect --format "{{index .Config.Labels \"maintainer\"}}" website1

No exemplo acima nós iremos retornar o mantenedor daquela imagem. Você pode obter qualquer dado da imagem através da flag inspect

f) Copiando arquivos para dentro da imagem

Nós podemos copiar arquivos para dentro de nossa imagem utilizando o comando COPY. Para isto vamos criar um novo exemplo de Dockerfile adicionando este comando.

Vamos criar uma pasta contendo alguns arquivos .html como exemplo

mkdir public_html
touch public_html/content.html
ls public_html

Feito isto vamos criar nosso Dockerfile

No arquivo Dockerfile estamos copiando todo o conteúdo *.html da pasta local public_html para dentro de /usr/share/nginx/html

Testando no browser você teria algo como a saída abaixo:

g) Explorando o arquivo DockerFile

Vamos criar um exemplo mais completo agora. Vamos servir uma página html utilizando um servidor web criado através do Python. Para isto vamos começar executando os seguintes comandos abaixo:

mkdir build-dev
mkdir build-dev/files

Você encontra os arquivos de apoio no repositório Git no Github neste link

Vamos agora criar um novo arquivo Dockerfile dentro de build-dev com os seguintes conteúdos:

FROM python:3.6
LABEL maintainer 'Andre Jaccon [email protected]'
RUN useradd www && \
mkdir /app && \
mkdir /log && \
chown www /log
USER www
VOLUME /log
WORKDIR /app
EXPOSE 8000
ENTRYPOINT ["/usr/local/bin/python"]
CMD ["run.py"]

Explicando:
FROM: estamos baixando a versão 3.6 do Python
LABEL: as informações do mantenedor
RUN: o Docker irá executar os seguintes comandos Bash dentro do nosso container
USER: usuário que será utilizado para rodar a aplicação
WORKDIR: diretório de trabalho, onde será executado os comandos
EXPOSE: 8000 a porta que será expostas para fora do container
ENTRYPOINT: diretório onde está localizado o binário do Python
CMD: o script que será executado pelo entrypoint

Vamos gerar a nossa nova build agora com o nome de python-server:

docker images build -t python-server .

Após ele ter gerado a nova build vamos iniciar o novo container

docker container run -it -v $(pwd)/files:/app -p 8080:8000 --name python-server-backend python-server

Perceba que nós fizemos duas ações importantes a primeira foi mapear o volume de ./files para dentro da pasta /app no container e mapeamos a porta local 8080 para requisições na 8000 do container que é a porta que está configurada a nossa aplicação Python

h) Enviando imagens para o Docker Hub

Depois de customizado a nossa imagem agora é hora de fazermos o upload dela para o Docker Hub para que outros usuários também possam utilizar.

Você deve entrar no site https://hub.docker.com criar uma conta e logar no Docker Hub

docker login --username=seu-usuario aqui

Depois de logado é necessário que você faça crie uma tag para submeter para o Docker Hub vamos utilizar o exemplo abaixo. Nós temos uma imagem do NGINX e vamos criar uma tag com o nome de http-server que é a imagem que vamos submeter para o Docker-Hub. Lembre-se que da mesma maneira você irá subir suas customizações para o DOcker Hub porém estas imagens são públicas.

docker image tag nginx jaccon/http-server:1.0

explicando os pontos em negrito:
nginx é o nome da imagem que iremos tagear
jaccon/http-server é o novo nome da tag que criamos
:1.0 é a versão da imagem

e depois vamos fazer o push:

docker image push jaccon/http-server:1.0

Capítulo 05 – Redes

a) Modelo padrão de rede no Docker

No Docker o modelo padrão que ele utiliza para o gerenciamento no networking dos containers é o modo bridge. Na imagem abaixo você pode conferir um gráfico explicando este funcionamento:

Existe um isolamento entre a rede do host com as redes de dentro dos containers. Você pode configurar diversos cenários e nós vamos explicar aqui alguns destes exemplos. Os modelos de networking suportados pelo Docker são:

  • None Network ( sem rede )
  • Bridge Network ( padrão )
  • Host Network
  • Overlay Network ( Docker Swarm )

b) Listando as interfaces de rede e CLI de gerenciamento

Para gerenciarmos o network no Docker nós temos o comando network. Com ele é possível configurar o ambiente ideal para suas aplicações.

docker network ls
Comando para listar os tipos de drivers de rede disponíveis no Docker

Com o comando acima você pode listar quais as redes ativas no momento. Veja que estes são os tipos de drivers disponíveis para você setar na execução dos seus containers. Digamos que você queira subir um container completamente isolado da rede externa. Para isto você pode executar:

docker container run -d --net none debian

O comando acima irá rodar o container do Debian em modo daemon e especificando a rede como none.

Para listarmos o endereço de IP que está setado dentro de nosso container podemos utilizar:

docker container run --rm debian bash -c "ip addr"

No exemplo da saída do comando podemos perceber que nosso container Debian foi criado sem a opção de networking.

c) Testando conectividade entre dois containers

Para testar a conectividade entre dois containers primeiramente vamos criar dois containers em modo bridge e vamos testar a conectividade entre eles através do comando ping

docker container run -d --name container1 alpine sleep 1000
docker container run -d --name container2 alpine sleep 1000
docker container ps

depois de criado os dois containers podemos verificar o ip dos containers com o comando:

docker container exec -it container1 ifconfig
docker container exec -it container2 ifconfig

agora com os endereços ip de cada container vamos executar um comando ping entre um e outro

docker container exec -it container1 ping 172.17.0.2

para testar a conectividade para fora da rede local podemos pingar por exemplo o site do jaccon.com.br

docker container exec -it container1 ping jaccon.com.br

d) Criando uma nova rede e testando conectividade

A nossa ideia aqui é criar um isolamento ainda maior entre nossos containers. Vamos imaginar o seguinte cenário, você está hospedando dentro do mesmo servidor/ambiente cloud vários sites de clientes distintos. Estes containers precisam estar isolados em ambientes diferentes e em redes diferentes. Então vamos criar nossa nova rede:

docker network create --driver bridge site1
O comando acima criou uma nova rede com o nome de site1

Agora nós vamos criar um novo container isolando a rede dele através do novo network driver site1

docker container run -d --name container1 --net site1 alpine sleep 10000

OK já temos o container1 utilizando a rede site1, agora vamos criar uma nova interface dentro do container1 utilizando a rede bridge

docker network connect bridge container1

Veja que agora nosso container1 tem duas interfaces de rede

Capítulo 6 – Docker Compose

a) Conhecendo o Docker Compose

O Docker Compose é um orquestrador de containers. Você gerenciar diversos containers de forma manual é quase insano. Vamos criar a seguinte estrutura

mkdir site1/public_html
mkdir site2/public_html

agora vamos criar um exemplo de arquivo docker-compose.yml. Lembrando que os arquivos neste formato precisam de que a identação esteja de acordo para que funciona correntamente.

version: '2'
 services:
  site1_front:
   image: nginx
   volumes:
 ./site1/public_html:/usr/share/nginx/html
 ports:
 8080:80
 site2_frontend:
 image: nginx
 volumes:
 ./site2/public_html:/usr/share/nginx/html
 ports:
 8081:80 

Você pode baixar o exemplo do arquivo docker-compose.yml no GitHub em https://raw.githubusercontent.com/jaccon/curso-free-docker-handson/master/capitulo-06/docker-compose.yml

depois basta executar o comando:

docker-compose up