Docker Command Not Found no Mac: Solucione Problemas e Configure Seu Ambiente

DockerBeginner
Pratique Agora

Introdução

O Docker revolucionou o desenvolvimento de aplicações, permitindo que os desenvolvedores criem, implementem e executem aplicações em ambientes isolados, chamados de containers. Usuários de Mac podem, por vezes, encontrar o erro "docker command not found" (comando docker não encontrado), o que pode ser frustrante ao iniciar com a containerização. Este laboratório irá guiá-lo através da compreensão dos conceitos do Docker, da verificação da sua instalação do Docker, da resolução de problemas comuns e da configuração de um ambiente Docker adequado para o seu trabalho de desenvolvimento.

Compreendendo os Fundamentos do Docker e Verificando a Instalação

O Docker fornece uma maneira padronizada de empacotar aplicações e suas dependências em containers, tornando-as portáteis em diferentes ambientes. Antes de solucionar quaisquer problemas com o Docker, vamos garantir que entendemos os fundamentos e verificar nossa instalação.

O que é Docker?

Docker é uma plataforma que usa a tecnologia de containerização para facilitar a criação, implantação e execução de aplicações. Ao contrário das máquinas virtuais, os containers Docker compartilham o kernel do sistema host, mas são executados em ambientes isolados, tornando-os leves e eficientes.

Componentes-chave do Docker incluem:

  • Docker Engine: O runtime que constrói e executa containers
  • Docker Images: Templates somente leitura usados para criar containers
  • Docker Containers: Instâncias em execução de Docker images
  • Docker Registry: Um repositório para armazenar e compartilhar Docker images
  • Dockerfile: Um arquivo de texto contendo instruções para construir um Docker image

Verificando a Instalação do Docker

Nosso ambiente de laboratório já tem o Docker instalado. Vamos verificar isso checando a versão do Docker:

docker --version

Você deve ver uma saída semelhante a:

Docker version 20.10.21, build 20.10.21-0ubuntu1~22.04.3

Agora, vamos verificar se o daemon do Docker está em execução:

sudo systemctl status docker

Você deve ver uma saída indicando que o Docker está ativo (em execução). A saída será semelhante a:

● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since ...

Pressione q para sair da visualização de status.

Se, por qualquer motivo, o Docker não estiver em execução, você pode iniciá-lo com:

sudo systemctl start docker

Executando seu Primeiro Container

Vamos verificar se o Docker está funcionando corretamente executando um container simples "hello-world":

docker run hello-world

Este comando baixa a imagem hello-world se ela ainda não estiver disponível localmente e a executa em um container. Você deve ver uma saída semelhante a:

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

A saída explica o que o Docker fez para executar este container, fornecendo uma boa introdução a como o Docker funciona.

Verificando Containers em Execução

Para ver todos os containers atualmente em execução, use:

docker ps

Como o container hello-world sai imediatamente após exibir sua mensagem, você provavelmente não o verá nesta lista. Para ver todos os containers, incluindo aqueles que foram parados, use:

docker ps -a

Isso mostra todos os containers, seus IDs, as imagens de onde foram criados, quando foram criados e seu status atual.

Agora você verificou que o Docker está instalado e funcionando corretamente em seu ambiente, e executou seu primeiro container!

Trabalhando com Docker Images e Containers

Agora que você verificou que o Docker está funcionando corretamente, vamos aprender como trabalhar com Docker images e containers com mais detalhes.

Compreendendo Docker Images

Docker images são os blueprints (plantas) para containers. Elas contêm o código da aplicação, bibliotecas, dependências, ferramentas e outros arquivos necessários para que uma aplicação seja executada.

Vamos explorar Docker images usando alguns comandos básicos:

Listando Imagens Disponíveis

Para ver todas as Docker images disponíveis em seu sistema:

docker images

Você deve ver uma saída semelhante a:

REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    feb5d9fea6a5   X months ago    13.3kB

Obtendo Imagens do Docker Hub

Docker Hub é um serviço de registro baseado em nuvem onde você pode encontrar e compartilhar Docker images. Vamos obter uma imagem popular:

docker pull nginx

Este comando baixa a imagem mais recente do servidor web nginx. Você verá a saída do progresso à medida que várias camadas da imagem são baixadas:

Using default tag: latest
latest: Pulling from library/nginx
...
Digest: sha256:...
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

Execute docker images novamente para ver a imagem nginx recém-baixada em sua lista.

Trabalhando com Containers

Agora que temos algumas imagens, vamos aprender como criar e gerenciar containers.

Executando um Container

Vamos executar um container nginx que servirá uma página web:

docker run --name my-nginx -p 8080:80 -d nginx

Este comando faz várias coisas:

  • --name my-nginx: Nomeia o container "my-nginx"
  • -p 8080:80: Mapeia a porta 8080 em seu host para a porta 80 no container
  • -d: Executa o container em modo detached (em segundo plano)
  • nginx: Especifica a imagem a ser usada

Verificando se o Container está em Execução

Verifique se seu container está em execução:

docker ps

Você deve ver seu container nginx na lista de containers em execução:

CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                  NAMES
a1b2c3d4e5f6   nginx     "/docker-entrypoint.…"   X seconds ago    Up X seconds    0.0.0.0:8080->80/tcp   my-nginx

Acessando o Servidor Web

Você pode acessar o servidor web nginx abrindo um navegador web em seu ambiente de VM LabEx e navegando para:

http://localhost:8080

Alternativamente, você pode usar curl do terminal:

curl http://localhost:8080

Você deve ver a página HTML de boas-vindas padrão do nginx.

Visualizando Logs do Container

Para ver os logs do seu container:

docker logs my-nginx

Isso mostra os logs de acesso para o servidor nginx.

Parando e Removendo Containers

Para parar um container em execução:

docker stop my-nginx

Para remover um container (ele deve ser parado primeiro):

docker rm my-nginx

Verifique se o container foi removido:

docker ps -a

O container chamado "my-nginx" não deve mais aparecer na lista.

Agora você entende os fundamentos de como trabalhar com Docker images e containers. Você obteve imagens do Docker Hub, executou containers, mapeou portas, visualizou logs e gerenciou ciclos de vida de containers.

Criando suas Próprias Docker Images com Dockerfiles

Até agora, usamos Docker images pré-construídas do Docker Hub. Agora, vamos aprender como criar nossas próprias Docker images personalizadas usando Dockerfiles.

O que é um Dockerfile?

Um Dockerfile é um arquivo de texto que contém instruções para construir uma Docker image. Ele especifica a imagem base, adiciona arquivos, instala software, define variáveis de ambiente e configura o container que será criado a partir da imagem.

Criando seu Primeiro Dockerfile

Vamos criar uma aplicação web simples usando Node.js e empacotá-la como uma Docker image.

Primeiro, crie um novo diretório para seu projeto:

mkdir -p ~/project/node-app
cd ~/project/node-app

Agora, crie uma aplicação Node.js simples. Primeiro, crie um arquivo chamado app.js:

nano app.js

Adicione o seguinte código ao arquivo:

const http = require("http");

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/plain");
  res.end("Hello World from Docker!\n");
});

const port = 3000;
server.listen(port, () => {
  console.log(`Server running at http://localhost:${port}/`);
});

Salve o arquivo pressionando Ctrl+O, depois Enter e saia do nano com Ctrl+X.

Em seguida, crie um arquivo package.json para definir sua aplicação Node.js:

nano package.json

Adicione o seguinte conteúdo:

{
  "name": "docker-node-app",
  "version": "1.0.0",
  "description": "A simple Node.js app for Docker",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "author": "",
  "license": "ISC"
}

Salve e saia do nano.

Agora, crie um Dockerfile:

nano Dockerfile

Adicione o seguinte conteúdo:

## Use an official Node.js runtime as the base image
FROM node:14-alpine

## Set the working directory in the container
WORKDIR /usr/src/app

## Copy package.json and package-lock.json
COPY package.json ./

## Install dependencies
RUN npm install

## Copy the application code
COPY app.js ./

## Expose the port the app runs on
EXPOSE 3000

## Command to run the application
CMD ["npm", "start"]

Salve e saia do nano.

Construindo sua Docker Image

Agora que você tem um Dockerfile, você pode construir sua Docker image:

docker build -t my-node-app .

Este comando constrói uma imagem a partir do seu Dockerfile:

  • -t my-node-app: Marca a imagem com o nome "my-node-app"
  • .: Especifica que o Dockerfile está no diretório atual

Você verá a saída mostrando o progresso da construção:

Sending build context to Docker daemon  X.XXkB
Step 1/7 : FROM node:14-alpine
 ---> XXXXXXXXXX
Step 2/7 : WORKDIR /usr/src/app
 ---> XXXXXXXXXX
...
Successfully built XXXXXXXXXX
Successfully tagged my-node-app:latest

Executando sua Docker Image Personalizada

Agora, execute um container usando sua imagem recém-construída:

docker run --name node-app-container -p 3000:3000 -d my-node-app

Verifique se o container está em execução:

docker ps

Você deve ver seu container na lista:

CONTAINER ID   IMAGE         COMMAND         CREATED          STATUS          PORTS                    NAMES
XXXXXXXXXX     my-node-app   "npm start"     X seconds ago    Up X seconds    0.0.0.0:3000->3000/tcp   node-app-container

Testando sua Aplicação

Teste a aplicação fazendo uma requisição HTTP para ela:

curl http://localhost:3000

Você deve ver:

Hello World from Docker!

Compreendendo o Dockerfile

Vamos revisar os componentes-chave do nosso Dockerfile:

  1. FROM node:14-alpine: Especifica a imagem base a ser usada
  2. WORKDIR /usr/src/app: Define o diretório de trabalho dentro do container
  3. COPY package.json ./: Copia arquivos do host para o container
  4. RUN npm install: Executa um comando dentro do container durante o processo de construção
  5. EXPOSE 3000: Documenta que o container escuta na porta 3000
  6. CMD ["npm", "start"]: Especifica o comando a ser executado quando o container inicia

Limpeza

Vamos limpar parando e removendo o container:

docker stop node-app-container
docker rm node-app-container

Você agora aprendeu como criar suas próprias Docker images usando Dockerfiles, construir essas imagens e executar containers com base nelas. Esta é uma habilidade fundamental para o desenvolvimento com Docker.

Gerenciando Dados com Docker Volumes

Um desafio ao trabalhar com containers Docker é a persistência de dados. Containers são efêmeros, o que significa que qualquer dado criado dentro de um container é perdido quando o container é removido. Docker volumes resolvem esse problema, fornecendo uma maneira de persistir dados fora dos containers.

Compreendendo Docker Volumes

Docker volumes são o mecanismo preferido para persistir dados gerados e usados por containers Docker. Eles são completamente gerenciados pelo Docker e são isolados da estrutura de diretórios do sistema de arquivos do host.

Benefícios de usar volumes incluem:

  • Volumes são mais fáceis de fazer backup ou migrar do que bind mounts
  • Você pode gerenciar volumes usando comandos da Docker CLI
  • Volumes funcionam em containers Linux e Windows
  • Volumes podem ser compartilhados com mais segurança entre vários containers
  • Drivers de volume permitem que você armazene volumes em hosts remotos, provedores de nuvem ou criptografe o conteúdo dos volumes

Criando e Usando Docker Volumes

Vamos criar um container de banco de dados MySQL simples que usa um volume para persistir seus dados.

Criando um Volume

Primeiro, crie um Docker volume:

docker volume create mysql-data

Você pode listar todos os volumes com:

docker volume ls

Você deve ver seu novo volume na lista:

DRIVER    VOLUME NAME
local     mysql-data

Executando um Container com um Volume

Agora, vamos executar um container MySQL que usa este volume:

docker run --name mysql-db -e MYSQL_ROOT_PASSWORD=mysecretpassword -v mysql-data:/var/lib/mysql -p 3306:3306 -d mysql:5.7

Este comando:

  • --name mysql-db: Nomeia o container "mysql-db"
  • -e MYSQL_ROOT_PASSWORD=mysecretpassword: Define uma variável de ambiente para configurar o MySQL
  • -v mysql-data:/var/lib/mysql: Monta o volume "mysql-data" no diretório onde o MySQL armazena seus dados
  • -p 3306:3306: Mapeia a porta 3306 no host para a porta 3306 no container
  • -d: Executa o container em modo detached
  • mysql:5.7: Especifica a imagem a ser usada

Aguarde um momento para o container iniciar e, em seguida, verifique se ele está em execução:

docker ps

Interagindo com o Banco de Dados

Vamos criar um banco de dados e uma tabela para demonstrar a persistência de dados. Primeiro, conecte-se ao container MySQL:

docker exec -it mysql-db bash

Dentro do container, conecte-se ao servidor MySQL:

mysql -u root -pmysecretpassword

Crie um novo banco de dados e tabela:

CREATE DATABASE testdb;
USE testdb;
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255));
INSERT INTO users (name) VALUES ('John'), ('Jane'), ('Bob');
SELECT * FROM users;

Você deve ver os dados inseridos:

+----+------+
| id | name |
+----+------+
|  1 | John |
|  2 | Jane |
|  3 | Bob  |
+----+------+

Saia do prompt do MySQL e do container:

exit
exit

Testando a Persistência do Volume

Agora, vamos parar e remover o container e, em seguida, criar um novo usando o mesmo volume:

docker stop mysql-db
docker rm mysql-db

Crie um novo container usando o mesmo volume:

docker run --name mysql-db-new -e MYSQL_ROOT_PASSWORD=mysecretpassword -v mysql-data:/var/lib/mysql -p 3306:3306 -d mysql:5.7

Agora, conecte-se ao novo container e verifique se nossos dados persistiram:

docker exec -it mysql-db-new bash
mysql -u root -pmysecretpassword
USE testdb
SELECT * FROM users

Você deve ver os mesmos dados que inserimos anteriormente:

+----+------+
| id | name |
+----+------+
|  1 | John |
|  2 | Jane |
|  3 | Bob  |
+----+------+

Saia do prompt do MySQL e do container:

exit
exit

Isso demonstra que os dados persistiram mesmo após a remoção do container original, porque foram armazenados em um Docker volume.

Inspecionando e Gerenciando Volumes

Você pode inspecionar um volume para obter mais informações sobre ele:

docker volume inspect mysql-data

Isso mostrará detalhes como o ponto de montagem e o driver usado:

[
  {
    "CreatedAt": "YYYY-MM-DDTHH:MM:SS+00:00",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/mysql-data/_data",
    "Name": "mysql-data",
    "Options": {},
    "Scope": "local"
  }
]

Para limpar, vamos parar e remover o container:

docker stop mysql-db-new
docker rm mysql-db-new

Se você também quiser remover o volume:

docker volume rm mysql-data

Você agora aprendeu como usar Docker volumes para persistir dados entre os ciclos de vida dos containers, o que é essencial para aplicações com estado (stateful applications) como bancos de dados.

Explorando o Docker Networking

O Docker networking permite que os containers se comuniquem entre si e com o mundo exterior. Compreender as capacidades de networking do Docker é crucial para construir aplicações multi-container.

Tipos de Rede Docker

O Docker fornece vários drivers de rede prontos para uso:

  • bridge: O driver de rede padrão. Containers na mesma rede bridge podem se comunicar.
  • host: Remove o isolamento de rede entre o container e o host.
  • none: Desabilita todo o networking para um container.
  • overlay: Conecta múltiplos daemons Docker e permite que os serviços Swarm se comuniquem.
  • macvlan: Atribui um endereço MAC a um container, fazendo com que ele apareça como um dispositivo físico na rede.

Explorando a Rede Bridge Padrão

Quando você instala o Docker, ele cria automaticamente uma rede bridge padrão. Vamos explorá-la:

docker network ls

Você deve ver uma saída semelhante a:

NETWORK ID     NAME      DRIVER    SCOPE
XXXXXXXXXXXX   bridge    bridge    local
XXXXXXXXXXXX   host      host      local
XXXXXXXXXXXX   none      null      local

Você pode inspecionar a rede bridge padrão:

docker network inspect bridge

Este comando fornece informações detalhadas sobre a rede, incluindo os containers conectados a ela, o intervalo de endereços IP e o gateway.

Criando e Usando Redes Bridge Personalizadas

Vamos criar uma rede bridge personalizada para um melhor isolamento de containers:

docker network create my-network

Verifique se a rede foi criada:

docker network ls

Você deve ver sua nova rede na lista:

NETWORK ID     NAME           DRIVER    SCOPE
XXXXXXXXXXXX   bridge         bridge    local
XXXXXXXXXXXX   host           host      local
XXXXXXXXXXXX   my-network     bridge    local
XXXXXXXXXXXX   none           null      local

Agora, vamos executar dois containers nesta rede e demonstrar a comunicação entre eles.

Primeiro, inicie um container NGINX na rede personalizada:

docker run --name web-server --network my-network -d nginx

Em seguida, vamos executar um container Alpine Linux e usá-lo para testar a conectividade com o container NGINX:

docker run --name alpine --network my-network -it alpine sh

Dentro do container Alpine, instale o curl e teste a conectividade com o container NGINX:

apk add --update curl
curl web-server

A saída deve ser o HTML da página de boas-vindas do NGINX. Isso funciona porque o Docker fornece DNS embutido para containers em redes personalizadas, permitindo que eles resolvam nomes de containers para endereços IP.

Digite exit para sair do container Alpine:

exit

Conectando Containers a Múltiplas Redes

Containers podem ser conectados a múltiplas redes. Vamos criar outra rede:

docker network create another-network

Conecte o container web-server existente a esta nova rede:

docker network connect another-network web-server

Verifique se o container agora está conectado a ambas as redes:

docker inspect web-server -f '{{json .NetworkSettings.Networks}}' | json_pp

Você deve ver que o container está conectado a my-network e another-network.

Executando Containers com Publicação de Portas

Quando você deseja tornar o serviço de um container acessível de fora do host Docker, você precisa publicar suas portas:

docker run --name public-web -p 8080:80 -d nginx

Este comando mapeia a porta 8080 no host para a porta 80 no container. Você pode acessar o servidor web NGINX usando:

curl http://localhost:8080

Você deve ver a página de boas-vindas do NGINX.

Limpeza

Vamos limpar os containers e redes que criamos:

docker stop web-server alpine public-web
docker rm web-server alpine public-web
docker network rm my-network another-network

Verifique se os containers e redes foram removidos:

docker ps -a
docker network ls

Compreendendo a Comunicação entre Containers

Este passo demonstrou como o Docker networking permite:

  1. Comunicação container-para-container usando redes personalizadas
  2. Resolução de DNS usando nomes de containers
  3. Conexão de containers a múltiplas redes
  4. Exposição de serviços de container para o mundo exterior usando publicação de portas

Essas capacidades de networking são essenciais para construir aplicações complexas, multi-container, onde os componentes precisam se comunicar entre si e com serviços externos.

Resumo

Parabéns por concluir este laboratório Docker! Você aprendeu conceitos e habilidades essenciais do Docker que formam a base do desenvolvimento e implantação baseados em containers.

Neste laboratório, você:

  • Verificou a instalação do Docker e executou seu primeiro container
  • Trabalhou com imagens e containers Docker, incluindo a busca de imagens do Docker Hub e o gerenciamento do ciclo de vida dos containers
  • Criou sua própria imagem Docker personalizada usando um Dockerfile
  • Usou Docker volumes para persistir dados entre os ciclos de vida dos containers
  • Explorou o Docker networking para habilitar a comunicação container-para-container

Essas habilidades permitirão que você:

  • Empacote aplicações e suas dependências em containers portáteis
  • Crie ambientes padronizados para desenvolvimento, teste e produção
  • Implemente arquiteturas de microsserviços onde cada componente é executado em seu próprio container
  • Garanta a persistência de dados para aplicações com estado (stateful applications)
  • Construa aplicações complexas multi-container com isolamento e comunicação adequados

O Docker se tornou uma ferramenta essencial no desenvolvimento moderno de software, e o conhecimento que você adquiriu será valioso em uma ampla gama de cenários de desenvolvimento e operações.