fbpx

TECH TALKS – Docker

Compartilhe esta publicação

Docker

Por Luiz Mineo

Neste mês convidamos o Luiz Mineo pra compartilhar conosco um tema do interesse de muitos, Docker.

Introdução

Lançado inicialmente como um projeto open source pela startup dotCloud em 2013, o Docker rapidamente se consolidou como a principal ferramenta de implantação e gestão de serviços do mercado, sendo adotado em larga escala por empresas como Google, IBM, RedHat, Microsoft e Amazon. 

Até então, os principais processos e ferramentas de implantação de serviços utilizavam máquinas virtuais, que oferecem benefícios como contextos isolados e replicabilidade, mas a um custo computacional alto, uma vez que é necessário emular uma camada de hardware para executar um segundo sistema operacional.

Docker não foi a primeira ferramenta a fazer uso do conceito de containers, sendo que o próprio kernel Linux já oferecia um conjunto de serviços para este fim. No entanto, Docker foi a primeira ferramenta a criar um conjunto de convenções simples, porém versáteis, que abrangem desde a criação e distribuição, até a implantação e gerenciamento de serviços.

Em resumo, o Docker oferece as principais vantagens de uma máquina virtual, mas sem o custo computacional elevado, o que permite seu uso em diversos cenários: Servidores em nuvem, ambientes de desenvolvimento e até mesmo dispositivos embarcados. No entanto, como o Docker é construído sobre o kernel linux, ele não pode ser executado diretamente em (ou executar serviços de) outros sistemas operacionais.

Arquitetura

O Docker é composto por três principais componentes:

Docker Daemon: É o serviço responsável por gerenciar imagens e containers. Possui uma API que é utilizada pelo Docker cli para envio de comandos, e que também pode ser utilizada por outras aplicações e serviços.

  • Imagens: Uma imagem pode ser vista como um disco virtual, que contém uma aplicação ou serviço pré-instalado, e que pode ser distribuída através de um registry.
  • Containers: São “instâncias” de uma imagem, equivalente a uma máquina virtual.

Docker cli: É a ferramenta de linha de comando que pode ser utilizada para enviar comandos para o daemon.

Docker Registry: É o repositório de imagens, de onde o docker pode fazer download (pull) e upload (push). O principal registry ativo hoje é o Docker Hub (https://hub.docker.com/), onde podemos encontrar imagens dos principais serviços e projetos open source. Também é possível manter uma instalação própria do registry, para distribuição de imagens privadas.

Instalação

Existem várias formas de instalar o docker. Para o Ubuntu e distribuições derivadas, como o Linux Mint, é recomendado instalar do repositório oficial com o apt

    $ sudo apt-get update && sudo apt-get install docker.io

Para demais distribuições, pode ser utilizado o instalador do Docker, que irá verificar o melhor repositório para fazer a instalação:

    $ curl -fsSL https://get.docker.com -o get-docker.sh

    $ sh get-docker.sh

Para Windows, é possível instalar o WSL2 e o Docker para Windows, que irá executar o docker em um ambiente virtualizado.

Criação de containers

Para demonstrar o uso do Docker, vamos configurar uma instância do banco de dados MySQL. Para tal, vamos utilizar a imagem oficial do MySQL no Docker Hub:

https://hub.docker.com/_/mysql

Para criar um container, o primeiro passo é verificar na documentação da imagem os seus parâmetros de configuração. Para configurar qualquer container Docker, são necessários três tipos de parâmetros. São eles:

  • Mapeamento de portas: Especifica quais portas de rede do container estarão disponíveis para acesso via host (servidor ou máquina onde o serviço do Docker está instalado). A configuração é feita informando a porta do host e a do container. No caso do MySQL, nós iremos expôr a porta 3306 do container, na porta 3306 do host.
  • Mapeamento de volumes: Volumes são diretórios do host que poderão ser acessados pelo container. A configuração é feita informando o diretório do host, e o diretório do container em que ele estará acessível. Caso a imagem especifique quais diretórios do container devem ser volumes, existe a opção de não criar um mapeamento explícito na criação do container. Neste caso, o Docker se encarregará de criar um diretório no host para o volume.
    Os volumes possuem duas principais funções. A primeira, é a persistência de arquivos: Todo arquivo criado pelo container é excluído quando o container é recriado, a não ser que o arquivo seja salvo em um volume. A segunda, é permitir transferência de arquivos entre host e container.
  • Variáveis de ambiente: Permite definir configurações específicas para o serviço. No caso do mysql, podemos utilizar as seguintes variáveis:
    • MYSQL_ROOT_PASSWORD: Define a senha do usuário root.
    • MYSQL_DATABASE: Nome da base de dados que será criada automaticamente, na primeira execução do container.
    • MYSQL_USER e MYSQL_PASSWORD: Caso especificados, um usuário com esses dados será criado na primeira execução do container.

O segundo passo, é utilizar o comando docker run para criar o container. Esse comando permite realizar em uma única etapa o download da imagem, criação e execução do container.

  • Para específicar o mapeamento de portas, use o parâmetro -p porta-host:porta-container. Também é possível utilizar o parâmetro -P, que irá alocar uma porta aleatório do host, para cada porta exposta pelo container.

    Exemplo: -p 3306:3306
  • Para especificar o mapeamento de volumes, use o parâmetro -v diretorio-host:diretorio-container.

    Exemplo: -v /home/usuario/mysql:/var/lib/mysql
  • Para especificar variáveis de ambiente, use o parâmetro -e NOME_VARIAVEL=valor.

    Exemplo: -e MYSQL_USER=usuario

Além dos parâmetros da imagem, o ‘docker run’ aceita outros argumentos para configurar o container, entre eles:

  • –name nome-container: Especifica um nome para o container. Caso não informado, o Docker irá escolher um nome aleatório
  •  -d: Permite que o container rode em background. Por padrão, ele é executado no shell do usuário.
  •  –restart always: Define a política de reinicialização do container, caso o container ou o processo do docker seja encerrado. Por padrão, o container nunca é reiniciado automaticamente. Além de ‘always’, é possível informar os valores ‘no’, ‘on-failure’ e ‘unless-stopped’.

Por fim, o último argumento informado ao docker run é o nome da imagem que será utilizada. Caso a imagem informada não exista localmente, e não seja informado um registry, ela será baixada do Docker Hub. Note que é possível especificar também a tag da imagem, que é uma versão específica.

     $ docker run -p 3306:3306 -v /home/usuario/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=meuprojeto -e MYSQL_USER=usuario -e MYSQL_PASSWD=123456 –name mysql –restart always -d mysql:latest

Após a execução do comando, o mysql deverá estar acessível na porta 3306 do host.

Principais comandos do Docker cli

Além do docker run, o Docker cli oferece diversos comandos para gerenciar containers e imagens. 

Por exemplo, para verificar se a nossa instância do MySQL está em execução, podemos usar o comando ‘docker ps’, que lista todos os containers em execução:

       $ docker ps

       CONTAINER ID   IMAGE          COMMAND                  CREATED       STATUS         PORTS                               NAMES

       ff85277753c5   mysql:latest   “docker-entrypoint.s…”   4 weeks ago   Up 7 seconds   0.0.0.0:3306->3306/tcp, 33060/tcp   mysql

    Para ver todos os containers, incluindo os que não estão em execução, pode ser usado o parâmetro ‘-a’

    Para parar ou iniciar um container, use docker stop e docker start

      $ docker stop mysql

      $ docker start mysql

    Para baixar uma imagem de um repositório, use docker pull

      $ docker pull mysql:latest

    Para visualizar o log de um container, use docker logs. O argumento –tail permite trazer apenas uma quantidade de linhas a partir do final do log, e o -f permite visualizar o log em tempo real.

      $ docker logs –tail=150 -f mysql

    Para visualizar o consumo de cpu, memória, transferência de disco e rede por container, use o ‘docker stats’. Esse comando também é atualizado em tempo real:

      $ docker stats

      CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O       BLOCK I/O         PIDS

      ff85277753c5   mysql     1.08%     360.7MiB / 31.23GiB   1.13%     15.9kB / 0B   79.4MB / 14.3MB   38

    Para executar um comando em um container em execução, use o docker exec. Esse comando é utilizado principalmente para ter acesso ao shell de um container.

      $ docker exec -it mysql /bin/bash

    E para excluir um container que não está em execução, use o comando docker rm.

      $ docker rm mysql

    Existem vários outros comandos disponibilizados pelo Docker Cli, que podem ser consultados na documentação do Docker:

https://docs.docker.com/engine/reference/run/

Criando imagens

Como vimos anteriormente, uma imagem do Docker é equivalente a um disco virtual, que contém um serviço pré-instalado. Para construir uma imagem, o Docker oferece uma linguagem própria de script, que permite definir a sequência de passos necessários para construí-la. Como exemplo, vamos criar uma imagem para um serviço Java empacotado como um jar executável (que é o artefato gerado na configuração padrão do Spring Boot, assim como outros frameworks).

O primeiro passo, é criar um diretório com os arquivos que devem ser incluídos no container (como o jar do nosso serviço), assim como o arquivo Dockerfile, que define as instruções a serem executadas para a criação da imagem. No nosso caso, o Dockerfile pode ter o seguinte conteúdo:

#FROM define uma imagem base

FROM ubuntu:bionic

#RUN executa comandos

RUN apt-get update && apt-get install openjdk-8-jdk -y

#COPY copia arquivos da pasta de contexto para dentro da imagem

COPY service.jar /app/

#VOLUME define diretórios que devem ser tratados como volume

VOLUME /dados

#ENV define variáveis de ambiente, que podem ser sobrescrita na criação do container

ENV JAVA_OPTS=”-Dfile.encoding=UTF-8 -Duser.timezone=America/Sao_Paulo -Duser.country=BR”

#EXPOSE define uma porta que poderá ser exposta parao host

EXPOSE 8080

#Os comandos executados após esta instrução, serão executados a partir deste diretório

WORKDIR /app

#CMD define o comando inicial do container

CMD java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar service.jar

Este exemplo já demonstra as principais instruções aceitas pelo Dockerfile: FROM, RUN, COPY, VOLUME, EXPOSE, ENV, WORKDIR e CMD.

Para construir e distribuir uma imagem, podemos usar os comandos docker build, docker tag e docker push. Exemplo:

        $ docker build -t meu-servico .              # cria uma imagem com nome ‘meu-servico’, a partir do diretório corrente, que deve conter um Dockerfile

        $ docker tag meu-servico meu-servico:1.0     # cria uma tag para a imagem

        $ docker push meu-servico:1.0                # publica a imagem

Note que para fazer o push, isto é, publicar a imagem, é necessário estar autenticado em um registry, como o Docker Hub.

Quando o Docker constrói uma imagem a partir de um Dockerfile, cada comando executado gera uma nova camada (layer), que pode ser distribuída independentemente pelo registry. Ou seja, se imagens distintas possuem uma mesma imagem base, ou se uma nova versão de uma imagem é disponibilizada, as camadas já existentes não precisam ser duplicadas no host ou no registry.

Gerenciando múltiplos containers

Em um cenário real, nossa aplicação terá múltiplos containers, como por exemplo, um banco de dados e um serviço. Nesta situação, é comum surgirem dois problemas. O primeiro, é a comunicação entre containers. Para que o container de um serviço acesse o container de uma base de dados, o segundo container precisa expor uma porta de acesso ao host, e o primeiro container deve acessá-la através do IP do host.

Para solucionar este problema, o Docker permite a criação de redes virtuais, de forma que containers associados a uma mesma rede, possam comunicar-se diretamente, sem depender do host.

Para criar uma rede, basta usar o comando abaixo:

      $ docker network create minharede

    Com a rede criada, podemos criar containers associados a ela:

       $ docker run –net=minharede –name mysql –restart always -d mysql:latest

Quando um container é associado a uma rede, ele pode ser acessado por outros containers da mesma rede através do seu nome. Por exemplo, um outro container poderá acessar o banco de dados do exemplo anterior através do endereço mysql:3306. Note que o nome do container é usado como hostname na rede, e que a porta do banco não precisa ser exposta ao host. 

Outro problema é a gestão desses containers. Se uma aplicação contém muitos serviços, gerenciar a implantação utilizando o Docker cli pode ser trabalhoso e propenso a falhas.

Para estes casos, o ecossistema do Docker oferece diversas ferramentas, entre elas estão o docker-compose e o Kubernetes.

O docker-compose é um ferramenta bastante simples, que trabalha diretamente sobre a API do Docker daemon, e pode ser usada para gerenciar múltiplos containers em um mesmo host. Para instalá-la, basta fazer o download do seu executável:

       $ sudo curl -L “https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)” -o /usr/local/bin/docker-compose

       $ sudo chmod +x /usr/local/bin/docker-compose

Uma vez instalado, podemos criar um arquivo docker-compose.yml, que descreve a nossa implantação, conforme exemplo abaixo:

version: ‘3’

services:

    web:

        container_name: web

        build:

            context: ./ui

            dockerfile: Dockerfile

        volumes:

            – “./ui/webui:/app”

        ports:

            – “80:80”

        restart: always

        depends_on:

            – api

            – mysqldb

    api:

        container_name: api

        build:

            context: ./api

            dockerfile: Dockerfile

        restart: always

        volumes:

            – “./api:/app”

        depends_on:

            – mysqldb

    mysqldb:

        container_name: mysqldb

        image: mysql:8

        restart: always

        environment:

            – MYSQL_DATABASE=meudb

            – MYSQL_ROOT_PASSWORD=123456

            – MYSQL_USER=meudb

            – MYSQL_PASSWORD=meudb

        ports:

            – “3306:3306”

        volumes:

            – “./data/db/mysql:/var/lib/mysql”

            – “./data/db/mysqlconf:/etc/mysql/conf.d”

Nesta configuração, estão sendo criados três containers: web, api e mysqldb. Os parâmetros de cada container são os mesmos do comando ‘docker run’, com algumas exceções. Por exemplo, note que os containers web e api são construídos diretamente de um Dockerfile, ao invés de uma imagem publicada em um registry, como é o caso do mysql. Note também que é possível definir a ordem de criação dos containers através do parâmetro depends_on.

Outro detalhe importante, é que o docker-compose cria por padrão uma rede para os containers, de forma que eles são acessíveis através do seu nome, conforme explicado anteriormente.

Uma vez que a configuração esteja criada, podemos usar o docker-compose para criar e iniciar os containers:

     $ docker-compose up -d

Para parar e remover os containers, podemos usar o comando:

     $ docker-compose down

Já o Kubernetes, por sua vez, é uma ferramenta voltada para gerenciamento de containers em um cluster de servidores. Ele é voltado para aplicações que demandam escalabilidade e alta disponibilidade.

Entre os recursos implementados pelo Kubernetes, estão:

  – Load balancer

  – Service discovery

  – Centralização de logs

  – Monitoramento

  – Quotas de recursos (uso de cpu, memória)

  – Políticas de atualização de containers

O Kubernetes tem dois componentes principais: O serviço de administração (apiserver) e os agentes, que são executados em cada nó do cluster. Assim como o docker, o Kubernetes também possui uma ferramenta de linha de comando (kubectl) para comunicação com o api server.

Para executar o kubernetes em ambiente de desenvolvimento, existem implementações da especificação do kubernetes que automatizam a criação de um cluster virtualizado, como o Minikube, Kind e Microk8s

O site do Kubernetes possui uma extensa documentação, que cobre os conceitos básicos para criação e gerenciamento de clusters, assim como a execução em ambiente de desenvolvimento:

https://kubernetes.io/pt-br/docs/setup/

Inscreva-se na nossa newsletter

Esteja atualizado das novidades do setor público e da Sonner

Veja outras publicações

Saiba mais como impulsionar a sua gestão

Conheça mais as soluções sonner.

sonner_modules

Vamos conversar?

Contate-nos e Descubra Como Otimizar sua Gestão, Priorizando o que Verdadeiramente Importa

Receba nossos conteúdos!

Preencha os campos.

Quer receber materiais exclusivos?

Preencha os campos.