Funcionamento
Containers vs VMs
Docker é uma plataforma que permite a execução de containers baseado no LXC. Um container contém um ou mais processo em execução.
Ao contrário de máquinas virtuais no qual roda um sistema operacional completo, com Docker os processo são executados de maneira isolado mas compartilham os recursos do Kernel Linux. Isso pode ser visto na ilustração abaixo:
Nessa imagem temos três aplicações, as caixas com App. No caso de VMs, a esquerda, cada uma dessas aplicações tem seus binários e bibliotecas e cada uma roda em cima de uma sistema operacional completo (Guest OS).
No caso de containers com Docker, na imagem a direita, cada aplicação tem seus binários e bibliotecas, mas as três são executadas pelo mesmo sistema operacional (Host OS). Neste caso essas aplicações compartilham os recursos do Kernel desse sistema operacional. Uma vantagem é que não tem recursos duplicados do sistema operacional rodando para cada aplicação economizando assim recursos computacionais. Outra vantagem é que em caso de manutenção com upgrade do sistema operacional, somente um a atualizar.
Um ponto importante é que mesmo compartilhando o Kernel, os processos executam em um contexto isolado. Cada container tem seu próprio sistema de arquivos, interfaces de rede e o uso de recurso como memória e CPU pode ser limitado.
Cliente docker e dockerd
o Docker é constituído de duas partes:
-
O dockerd, um processo persistente que gerencia os containers.
-
O cliente docker é a principal maneira de interagir com o Docker
O Cliente se conecta e envia comandos ao Daemon que os executa
Rede
Introdução
Como vimos no item Containers vs VMs, cada container pode ter sua própria interface de rede. Isso quer dizer que cada container tem seu(s) próprio(s) endereço(s) IP.
Essa rede permite conectar os containers:
-
Entre eles
-
Com mundo externo
O subsistema de rede no Docker é customizável por meio de drivers. Os principais drivers são:
-
Bridge
-
Host
-
Overlay
-
None
Redes Bridge
O tipo de rede utilizado por padrão por Docker é o Bridge. Esse tipo de rede permite aos containers conectados na mesma Bridge se comunicarem permitindo a isolação de containers que estão conectados em outras Bridges. Em um ambiente local é o tipo de rede mais comumente utilizado.
Segue uma imagem ilustrando uma rede no Docker:
Nesta imagem um container está conectado em uma rede de tipo Bridge chamada docker0, rede criada por padrão na instalação do Docker. Esse container possui o endereço IP 172.17.0.2 em uma interface virtual veth.
As interfaces virtuais criadas pelo Docker par os containers são visíveis no Linux quando é executado o comando ifconfig.
|
Segue um diagrama com outro exemplo de rede Bridge no Docker:
Nesse diagrama podemos ver duas redes de tipo Bridge: docker0 e my_bridge. O container web possui duas interfaces virtuais e está conectado nas duas redes docker0 e my_bridge. Esse container possui dois endereços IP: 172.17.0.2 e 10.0.0.2. O container db está conectado somente a uma rede: my_bridge e possui o endereço IP 10.0.0.254. Dessa maneira os containers web e db conseguem se comunicar utilizando a rede my_bridge.
Outros drivers de rede
No caso do driver de rede de tipo Host, o container não recebe endereço IP particular e não é isolado da rede da máquina que executa o dockerd, o container utiliza diretamente o endereço de rede do Host.
O driver Overlay é utilizado para criar uma rede distribuída entre vários hosts Docker. Isso permite que containers hospedados em hosts distintos consigam se comunicar.
Quanto ao driver None, ele serve a desativar a rede em um container.
Mapeamento de portas
Por padrão os containers conseguem se conectar em máquinas externas, mas máquinas externas não conseguem se conectar nos containers.
Para poder acessar os serviços executados dentro dos containers de fora da rede, pode ser efetuado um mapeamento de portas do Host com portas dentro de containers. Esse mapeamento deve ser realizado na hora de criar um container, se um novo mapeamento deve ser efetuado o container deverá ser recriado.
Volumes e Armazenamento
É possível armazenar dados dentro da camada em escrita de um container mas isso traz várias desvantagens:
-
Os dados não são mais persistidos quando o container para de ser executado.
-
A camada em escrita de um container está vinculada ao um host em particular dificultando mover um container para outro host
-
Para poder escrever nessa camada deve ser utilizado um driver de sistema de arquivos de tipo union cuja performance é inferior aos outros tipos de sistemas de arquivo.
Docker oferece três maneiras diferentes de montar dados em um container a partir do Host Docker:
-
Volumes
-
Bind Mounts (apontamento em uma pasta do host)
-
Sistema tmpfs
Segue um diagrama apresentando essas três opções:
Os Dados dos Volumes são armazenados como parte do sistema de arquivo do Host Docker (/var/lib/docker/volumes/ por padrão no Linux). Os processos não Docker não deveriam acessar esses dados. Essa é a maneira recomendada de persistir dados com Docker.
Os Bind Mounts podem ser armazenados em qualquer lugar no Host. Os processos não Docker rodando no Host podem modificar esse arquivos a qualquer momento.
Os dados em tmpfs são armazenados na memória do Host e nunca são escritos no sistema de arquivos. Por exemplo esse tipo de montagem pode ser utilizado para disponibilizar segredos como senhas para o container sem poder ser acessível no sistema de arquivos.
Imagens
As imagens:
-
Servem para executar os containers
-
Contém camadas com os binários necessários para a aplicação
-
São criadas a partir de instruções em um Dockerfile
-
São armazenadas em um registro
-
São versionadas
FROM openjdk:8-jre-alpine
RUN apk --no-cache add msttcorefonts-installer fontconfig && update-ms-fonts && fc-cache -f
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS JHIPSTER_SLEEP=0 JAVA_OPTS="" HOME=/home/jhipster
# Add a jhipster user to run our application so that it doesn't need to run as root
RUN adduser -D -s /bin/sh jhipster
WORKDIR /home/jhipster
ADD entrypoint.sh entrypoint.sh
RUN chmod 755 entrypoint.sh && chown jhipster:jhipster entrypoint.sh
USER jhipster
ENTRYPOINT ["./entrypoint.sh"]
EXPOSE 8081 5701/udp
ADD *.war app.war
Instalação do Docker
Instalação no Linux
Uma das maneiras para instalar o Docker em uma distribuição Linux é executando um script shell:
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh
|
Para poder executar os comandos do cliente docker com o seu usuário, sem precisar de
|
Verificar a instalação
Para visualizar informações sobre a instalação do Docker, executar o seguinte comando:
docker info
Para verificar se o Docker foi instalado corretamente, executar o seguinte comando:
docker container run hello-world
O resultado da execução desse comando é:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:c3b4ada4687bbaa170745b3e4dd8ac3f194ca95b2d0518b417fb47e5879d9b5f
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Como a imagem não existia localmente ela foi recuperada do Docker Hub que é o repositório padrão para imagens: library/hello-world.
Depois de imprimir a mensagem o container não está mais em execução, com Docker se o processo principal para a execução o container é parado. Isso pode ser verificado executando o comando: docker container ls -a. Para remover o container, executar o comando: docker container rm {id/name}. Onde {id/name} representa o nome ou identificador do container (primeira coluna na execução do docker container ls -a)
Docker na prática
As Registries Docker
Uma registry Docker é um sistema de armazenamento e de distribuição para imagens Docker. Uma registry Docker é organizada em repositórios Docker, onde um repositório contém todas as versões (tags) de uma imagem específica.
Por padrão, a registry docker.io já vem configurado como podemos ver quando executamos docker info (ver: Verificar a instalação):
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/ (1)
...
| 1 | Endereço da registry padrão |
Para buscar novas imagens nos Registries configurados, usa-se:
docker search nginx
O resultado da execução desse comando é:
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 12695 [OK] (1)
jwilder/nginx-proxy Automated Nginx reverse pro… 1745 [OK]
richarvey/nginx-php-fpm Container running Nginx + P… 757 [OK]
...
| 1 | As imagens oficiais Docker são imagens revisadas e publicadas por uma equipe dedicada de Docker, Inc. Essa equipe trabalha em conjunto com os mantenedores dos softwares contidos na imagem e com uma equipe de especialistas em segurança. |
A Registry padrão docker está acessível via navegador web no endereço: https://hub.docker.com/
O comando:
docker image pull nginx
é equivalente ao comando:
docker image pull docker.io/nginx:latest
Execução de um nginx
Agora vamos executar um container com o servidor web nginx. Esse serviço escuta na porta 80 dentro do container, para poder conectar nessa porta a partir do navegador na máquina local, precisa mapear essa porta utilizando a opção -p, lembrando que esse mapeamento de portas é feito assim: Host → Container. Executar o seguinte comando.
docker container run -p 8000:80 nginx
INFO: executamos o nosso container em primeiro plano, com isso o log está aparecendo diretamente no nosso console.
Acessando a url http://localhost:8000/ podemos verificar que o servidor nginx está rodando corretamente e que a página padrão do mesmo está sendo exibida. Vamos agora personalizar a página padrão do nginx, para isso vamos montar uma pasta no nosso computador dentro do container. Isso é feito com a opção -v. Similar ao mapeamento de portas, o mapeamento de volumes é feito nesse sentido: Host → Container. Vamos parar o container atualmente em execução com o comando: Ctrl+C.
docker container run -p 8000:80 -v $HOME/nginx:/usr/share/nginx/html/ nginx
Se criarmos um arquivo $HOME/nginx/index.html, navegando no endereço http://localhost:8000/ veremos o arquivo servido pelo nginx. Editando esse arquivo e efetuando refresh da página, veremos as alterações no navegador.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bem-vindo no Docker!</title>
</head>
<body>
<h1>Bem-vindo no nginx no Docker!</h1>
</body>
</html>
Criação de imagens - Parte 1
A criação de imagens personalizadas nos permite entregar uma aplicação pronta para ser executada. No exemplo que vimos acima utilizamos o nginx para criar um site estático. Agora que nosso site está pronto, vamos criar uma imagem contendo esse site pronta para distribuição.
Para criar nossa imagem precisamos criar um arquivo Dockerfile que contém as instruções para criar nossa imagem. Um arquivo Dockerfile tem a seguinte estrutura:
# Comment
INSTRUCTION arguments
As principais instruções em um Dockerfile são:
-
FROM: Continuar a construção a partir de uma imagem existente -
COPY: Copiar arquivo dentro da imagem -
RUN: Executar comandos dentro da imagem -
ENTRYPOINT: Ponto de entrada da imagem (executado na inicialização) -
CMD: Comando adicional
Nós vimos um exemplo de Dockerfile em Dockerfile exemplo.
Na criação de uma imagem sempre colocar no final as instruções que podem mudar ou adicionam conteúdo que muda, por exemplo o arquivo war de uma aplicação. Dessa maneira baixar uma nova versão da imagem é menos custoso.
|
Para mais informações sobre essas instruções ver a documentação do Docker
O Dockerfile para criar nossa imagem customizado como nosso site estático tem a seguinte estrutura:
FROM nginx (1)
COPY index.html /usr/share/nginx/html/index.html (2)
| 1 | Com a instrução FROM utilizamos a imagem nginx como base, isso significa que nossa imagem herdará todo o conteúdo dessa imagem base. |
| 2 | A instrução COPY permite copiar um arquivo local dentro da imagem. |
Por padrão, por motivos de segurança, somente os arquivos na pasta e subpstas onde se encontra o Dockerfile estão disponíveis para serem copiados dentro da imagem.
|
Uma vez o Dockerfile criado, a criação da imagem pode ser efetuada com o comando:
docker image build -t meu-nginx:1.0 .
Sending build context to Docker daemon 3.072kB Step 1/2 : FROM nginx ---> 2073e0bcb60e Step 2/2 : COPY index.html /usr/share/nginx/html/index.html ---> caa00c1cc01f Successfully built caa00c1cc01f Successfully tagged meu-nginx:1.0
O paramêtro -t indica a tag da imagem criada, e o . no final representa o diretório onde encontrar o Dockerfile.
Depois da criação da imagem, a mesma estará disponível no runtime Docker local e poderá ser listada com o comando:
docker image ls
Criação de imagens - Parte 2
Nessa segunda parte vamos criar uma nova imagem onde rodaremos uma aplicação em Python com a framework Flask.
Primeiramente vamos criar a pasta onde efetuaremos nossos trabalhos:
mkdir -p ~/treinamento-docker/python/src && cd ~/treinamento-docker/python/src
No subdiretório ~/treinamento-docker/python/src, vamos adicionar dois arquivos, app.py e requirements.txt, com os seguintes conteúdos:
#! /usr/bin/env python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello, world!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
click==6.7
Flask==0.12.2
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==1.0
Para agilizar a criação dos arquivos, basta executar os comandos abaixo:
cat <<EOF > ~/treinamento-docker/python/src/app.py
#! /usr/bin/env python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello, world!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
EOF
e
cat <<EOF > ~/treinamento-docker/python/src/requirements.txt
click==6.7
Flask==0.12.2
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==1.0
Werkzeug==0.12.2
EOF
Na raiz do diretório ~/treinamento-docker/python/, vamos adicionar o Dockerfile com o seguinte conteúdo:
FROM fedora:27
LABEL maintainer="do-not-reply@redhat.com"
LABEL version="1.0"
RUN mkdir /var/www
ADD src/. /var/www
RUN pip3 install -r /var/www/requirements.txt
EXPOSE 8080
USER 12345
CMD ["python3", "/var/www/app.py"]
Para agilizar a criação do arquivo, podemos utilizar:
cat <<EOF > ~/treinamento-docker/python/Dockerfile
FROM fedora:27
LABEL maintainer="do-not-reply@redhat.com"
LABEL version="1.0"
RUN mkdir /var/www
ADD src/. /var/www
RUN pip3 install -r /var/www/requirements.txt
EXPOSE 8080
USER 12345
CMD ["python3", "/var/www/app.py"]
EOF
Para iniciarmos o processo de construção da nova imagem, usa-se:
cd ~/treinamento-docker/python
docker image build -t app-python .
Valide se sua imagem está funcionando corretamente por meio do comando:
docker container run --rm -p 8080:8080 app-python
Pode testar a aplicação com curl:
curl http://localhost:8080/
Que deve retornar o famoso: Hello, world!.
Verifique a sua imagem nova no registro local:
docker image ls | grep app-python
Podemos ver que é a nossa imagem que foi construída recentemente conforme a listagem resultando do comando acima.
Trabalhando com registries privadas
As registries privadas permitem armazenar imagens customizadas e compartilhá-las dentro de uma organização. Essas registries privadas são geralmente protegidas e não podem ser acessadas sem antes efetuar o login. Iremos utilizar a registry test-registry.basis.com.br para o treinamento. Para logar executar o comando:
docker login test-registry.basis.com.br
O docker solicitará login e senha para se conectar na registry:
Username: fulano.detal
Password: *****
Login Succeeded
Caso não tenha acesso ou erre a senha a seguinte mensagem será exibida:
Error response from daemon: login attempt to https://test-registry.basis.com.br/v2/ failed with status: 401 Unauthorized
Uma vez logado podemos efetuar o push da nossa imagem customizada para a registry test-registry.basis.com.br. O primeiro passo é renomear a imagem que criamos e prefixar ela com o endereço da registry:
docker image tag meu-nginx:1.0 test-registry.basis.com.br/meu-nginx:1.0
Isso criará uma referência test-registry.basis.com.br/meu-nginx:1.0 apontando para a imagem meu-nginx:1.0 criada na seção anterior. Agora podemos publicar essa imagem na nossa registry privada com o comando:
docker image push test-registry.basis.com.br/meu-nginx:1.0
A imagem será então armazenada na nossa registry e será disponível para todas as pessoas com acesso na test-registry.basis.com.br.
A imagem local pode ser removida com o comando:
docker image rm meu-nginx:1.0
docker image rm test-registry.basis.com.br/meu-nginx:1.0
| Remover a imagem local, mesmo com o prefixo da registry não remove a imagem armazenada lá. |
Gerenciar os containers
O Docker possuí comandos para gerenciar e monitorar os containers em execução.
ls/ps e top
Para listar os containers em execução em uma máquina executar o comando:
docker container ls
Que tem por saída:
CONTAINER ID IMAGE COMMAND f1e8ee464217 basisti/ruby-guard:2.5-1 "bundle exec guard" CREATED STATUS PORTS NAMES About an hour ago Up About an hour 0.0.0.0:35729->35729/tcp cool_kapitsa
Na lista de containers podemos ver a imagem que permitiu criar o container na coluna IMAGE. Podemos ver também com a coluna PORTS quais portas estão expostas e quais foram mapeadas: 35729→35729 no nosso exemplo, ou seja, a porta 35729 mapeia a porta 35729 no container. A coluna STATUS nos indica se o container está rodando (Up) ou parado (Exited ou Created).
Para efetuar operações nos containers podemos usar o id (coluna CONTAINER ID) ou o nome (coluna NAMES) reportado na listagem.
O comando top <Id/Name> deixa visualizar os processos em execução nos containers:
docker container top f1e8ee464217
O retorno do comando é similar ao comando top do Linux:
PID USER TIME COMMAND
4269 root 0:00 {bundle} /usr/local/bundle/bin/guard
4319 root 0:08 /usr/local/bin/ruby /usr/local/bundle/gems/guard-2.15.0/bin/_guard-core
inspect
Também podemos inspecionar os metadados do container através de:
docker container inspect f1e8ee464217
A saída do comando contendo informações sobre o estado do container, a configuração, rede, volumes dentre outros. Essa saída está no formato json, e portanto podemos usar o jq para filtrar os dados.
Para visualizar a rede do container:
docker container inspect f1e8ee464217 | jq '.[].NetworkSettings.Networks'
Que tem por resultado:
{
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "9289c7908e2f980adbf9392d6ae805bf0977fe6b8abc400a3a2cb9ae88ff44f3",
"EndpointID": "de60656b5c3ff54e4eefbfb1939ace386908285a9163f824ae1d2a764bdce3f6",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
O status do container pode ser obtido com:
docker container inspect f1e8ee464217 | jq '.[].State.Status'
Que tem por saída:
"running"
Emfim, para listar as variáveis de ambiente, usar o comando:
docker container inspect f1e8ee464217 | jq '.[].Config.Env'
Que terá por resultado:
[ "LANG=en_US.UTF-8", "LANGUAGE=en_US.UTF-8", "LC_ALL=en_US.UTF-8", "PATH=/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "RUBY_MAJOR=2.5", "RUBY_VERSION=2.5.5", "RUBY_DOWNLOAD_SHA256=9bf6370aaa82c284f193264cc7ca56f202171c32367deceb3599a4f354175d7d", "RUBYGEMS_VERSION=3.0.3", "GEM_HOME=/usr/local/bundle", "BUNDLE_PATH=/usr/local/bundle", "BUNDLE_SILENCE_ROOT_WARNING=1", "BUNDLE_APP_CONFIG=/usr/local/bundle" ]
exec e attach
Para executar comandos dentro de um container utiliza-se o comando exec -it <Id/Name> comando. Por exemplo para listar os arquivos em um determinado pasta, pode rodar o seguinte:
docker exec -ti f1e8ee464217 ls -al /
Isso permite executar um shell dentro do container:
docker exec -ti f1e8ee464217 bash
Para sair desse shell basta executar exit.
O comando attach permite se conectar no processo principal em execução no container.
docker attach f1e8ee464217
Se o comando prinicipal possuir um prompt, como é o caso com os containers que têm como ponto de entrada um shell ou uma console de interação de uma linguagem de programação, os dados digitados são repassados para o prompt.
Docker Compose
O Docker Compose é uma ferramenta para executar aplicações multi-containers, por exemplo uma aplicação web e um banco de dados. Ele permite você definir quais são os serviços que compõe uma aplicação e executar containers para cada um desses componentes. Adicionalmente o Docker Compose cria uma rede para interligar esses containers e permite a esses containers se referenciarem utilizando o nome do serviço, sem precisar conhecer o endereço IP de cada um.
Definição dos serviços
Os serviços são definidos em um arquivo no formato yaml, chamado docker-compose.yml.
| o formato yaml é sensível à identação do arquivo e não aceita tabs. |
Se o arquivo yaml contendo as definições não tem por nome docker-compose.yml a opção -f permite especificar o arquivo.
|
No nosso exemplo iremos executar uma aplicação Spring Boot conectado a um banco de dados PostgreSQL.
Criar uma pasta para o nosso exemplo:
mkdir -p ~/treinamento-docker/compose && cd ~/treinamento-docker/compose
No subdiretório ~/treinamento-docker/compose, criar o arquivo docker-compose.yml com o seguinte conteúdo:
version: '2'
services: (1)
app: (2)
image: basisti/example-springboot-pgsql:latest (3)
ports: (4)
- "8080:8080"
depends_on: (5)
- mypostgres
mypostgres: (2)
image: postgres (3)
ports: (4)
- "5432:5432"
environment: (6)
- POSTGRES_PASSWORD=password
- POSTGRES_USER=postgres
- POSTGRES_DB=mydb
| 1 | Os serviços são definidos dentro de uma entrada services |
| 2 | Cada serviço tem um nome (app e mypostgres no exemplo) dentro do qual será definido o container e as opções de execução. |
| 3 | A opção image define a imagem a utilizar para criar os containers. |
| 4 | A seção ports permite mapear portas do Host com o container da mesma maneira da opção p do comando docker container run. |
| 5 | A entrada depends_on declara dependências entre serviço, no caso app depende de mypostgres. |
| 6 | A seção environment permite definir variáveis de ambientes. |
| O depends_on não significa que o container que depende de outro esperará que o serviço executado no container do qual ele depende esteja pronto para receber requisições, define apenás uma ordem de criação. |
Execução com Docker Compose
O comando up do Docker Compose cria os containers e os execute. Depois de executar:
docker-compose up
Um log de execução parecido com o seguinte deve aparecer no prompt:
Pulling app (basisti/example-springboot-pgsql:latest)... (1) latest: Pulling from basisti/example-springboot-pgsql Digest: sha256:208151018160ec1eebb26c8b40f9229af442f8b86ea802b1303b2469e96a7826 Status: Downloaded newer image for basisti/example-springboot-pgsql:latest Starting nginx_mypostgres_1 ... done (2) Starting nginx_app_1 ... done Attaching to nginx_mypostgres_1, nginx_app_1 mypostgres_1 | 2020-02-24 17:40:25.506 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 mypostgres_1 | 2020-02-24 17:40:25.506 UTC [1] LOG: listening on IPv6 address "::", port 5432 mypostgres_1 | 2020-02-24 17:40:25.509 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" mypostgres_1 | 2020-02-24 17:40:25.552 UTC [23] LOG: database system was shut down at 2019-09-26 21:01:40 UTC mypostgres_1 | 2020-02-24 17:40:25.585 UTC [1] LOG: database system is ready to accept connections app_1 | wait-for-it.sh: waiting 15 seconds for mypostgres:5432 mypostgres_1 | 2020-02-24 17:40:26.091 UTC [30] LOG: incomplete startup packet app_1 | wait-for-it.sh: mypostgres:5432 is available after 0 seconds app_1 | app_1 | . ____ _ __ _ _ app_1 | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ app_1 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ app_1 | \\/ ___)| |_)| | | | | || (_| | ) ) ) ) app_1 | ' |____| .__|_| |_|_| |_\__, | / / / / app_1 | =========|_|==============|___/=/_/_/_/ app_1 | :: Spring Boot :: (v1.5.2.RELEASE) app_1 | app_1 | 2020-02-24 17:40:27.768 INFO 14 --- [ main] com.example.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT on faa1df91c11e with PID 14 (/app/app.jar started by root in /app)
| 1 | Nesse ponto a imagem basisti/example-springboot-pgsql:latest não existe e o Docker Compose a baixa localmente. |
| 2 | Depois das imagens serem baixadas o Docker Compose inicializa os containers |
Quando aparacer a mensagem abaixo, a aplicação está pronta para ser utilizada:
app_1 | 2020-02-24 17:40:34.148 INFO 14 --- [ main] com.example.DemoApplication : Started DemoApplication in 6.876 seconds (JVM running for 7.957)
Para verificar que aplicação esteja rodando corretamente, executar o curl:
curl http://localhost:8080
[{"id":1,"name":"A"},{"id":2,"name":"B"},{"id":3,"name":"C"}]
A aplicação trouxe os dados armazenados no banco de dados PostgreSQL.
Se executar o comando docker container ls os dois containers criados aparecerão com os nomes compose_app_1 e compose_mypostgres_1. Os comandos que vimos nas seções anteriores se aplicam a esses containers.
Digitando a combinação Ctrl+C a aplicação é parada:
Gracefully stopping... (press Ctrl+C again to force) Stopping nginx_app_1 ... done Stopping nginx_mypostgres_1 ... done
Os containers são parados e não destruídos, a próxima execução de docker-compose up reiniciará os containers existentes.
para deixar os containers criados pelo Docker Compose rodando em plano de fundo, utilizar o parâmetro -d: docker-compose up -d
|
O comando docker-compose down, quando executado na pasta contendo o docker-compose.yml para e remove os containers completamente.
O comando docker-compose logs permite ver os logs dos containers em execução.s