Exercícios Propostos para treinar a elaboração e contrução de conteiners, compose files e Dockerfiles
Exercícios Docker
🔹Execute um container usando a imagem do Nginx e acesse a página padrão no navegador.
Exemplo de aplicação: Use a landing page do TailwindCSS como site estático dentro do container.
🔹Inicie um container Ubuntu e interaja com o terminal dele.
Exemplo de aplicação: Teste um script Bash que imprime logs do sistema ou instala pacotes de forma interativa.
🔹Liste todos os containers em execução e parados, pare um container em execução e remova um container específico.
Exemplo de aplicação: Gerenciar containers de testes criados para verificar configurações ou dependências.
🔹Crie um Dockerfile para uma aplicação Flask que retorna uma mensagem ao acessar um endpoint.
Exemplo de aplicação: Use a API de exemplo Flask Restful API Starter para criar um endpoint de teste.
🔹Execute um container MySQL e configure um volume para armazenar os dados do banco de forma persistente.
Exemplo de aplicação: Use o sistema de login e cadastro do Laravel Breeze, que usa MySQL.
🔹Utilize um multi-stage build para otimizar uma aplicação Go, reduzindo o tamanho da imagem final.
Exemplo de aplicação: Compile e rode a API do Go Fiber Example dentro do container.
🔹Crie uma rede Docker personalizada e faça dois containers, um Node.js e um MongoDB, se comunicarem.
Exemplo de aplicação: Utilize o projeto MEAN Todos para criar um app de tarefas usando Node.js + MongoDB.
🔹Utilize Docker Compose para configurar uma aplicação Django com um banco de dados PostgreSQL.
Exemplo de aplicação: Use o projeto Django Polls App para criar uma pesquisa de opinião integrada ao banco.
🔹Construa uma imagem baseada no Nginx ou Apache, adicionando um site HTML/CSS estático.
Exemplo de aplicação: Utilize a landing page do Creative Tim para criar uma página moderna hospedada no container.
Esse exercício mesmo que muito básico e estár classificado como fácil ele trás um reforço de como se inicia um container, para quem não está familiarizado com a tecnologia é algo excelente para treinar o entendimento da sintaxe a acrescentar na familiarização com a tecnologia.
mkdir nginx-projeto
cd nginx-projeto
nano Dockerfile
Aqui nós configuramos o nosso container com a imagem nginx, na ultima versão e copiamos o arquivo index.html para o destino /usr/share/nginx/html/index.html e já de cara expomos a porta 80 para acesso via http e adicionamos o comando nginx -g daemon off para que o nginx rode a página em primeiro plano, não sendo executado como um daemon.
FROM nginx:latest
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx","-g", "daemon off;"]
A pagina html foi retirada do repositório Landing-Page.
Este exercício trouxe um pouco de criatividade na questão de se criar algo interativo dentro do container porém nada muito novo também. Porém, como eu no momento de fazer este exercício não havia estudado tanto acabei fazendo do " caminho mais longo " e criei o container com Ubuntu e criei um script de instalação interativa dentro dele.
docker pull ubuntu
docker run -ti --name ubuntu-projeto ubuntu /bin/sh
cd /
mkdir app
cd app
nano install-packages.sh
#!/bin/bash
# Cores para melhorar a visualização
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # Sem cor
# Função para verificar se o script está sendo executado como root
check_root() {
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Este script precisa ser executado como root (sudo).${NC}"
echo -e "Por favor, execute novamente com: ${YELLOW}sudo $0${NC}"
exit 1
fi
}
# Função para detectar o gerenciador de pacotes
detect_package_manager() {
if command -v apt &> /dev/null; then
PKG_MANAGER="apt"
INSTALL_CMD="apt install -y"
UPDATE_CMD="apt update"
elif command -v dnf &> /dev/null; then
PKG_MANAGER="dnf"
INSTALL_CMD="dnf install -y"
UPDATE_CMD="dnf check-update"
elif command -v yum &> /dev/null; then
PKG_MANAGER="yum"
INSTALL_CMD="yum install -y"
UPDATE_CMD="yum check-update"
elif command -v pacman &> /dev/null; then
PKG_MANAGER="pacman"
INSTALL_CMD="pacman -S --noconfirm"
UPDATE_CMD="pacman -Sy"
elif command -v zypper &> /dev/null; then
PKG_MANAGER="zypper"
INSTALL_CMD="zypper install -y"
UPDATE_CMD="zypper refresh"
else
echo -e "${RED}Não foi possível detectar um gerenciador de pacotes suportado.${NC}"
echo -e "Este script suporta: apt, dnf, yum, pacman e zypper."
exit 1
fi
echo -e "${BLUE}Gerenciador de pacotes detectado: ${GREEN}$PKG_MANAGER${NC}"
}
# Função para instalar pacotes
install_package() {
local package=$1
echo -e "\n${YELLOW}Instalando $package...${NC}"
if eval "$INSTALL_CMD $package"; then
echo -e "${GREEN}✓ $package instalado com sucesso!${NC}"
return 0
else
echo -e "${RED}✗ Falha ao instalar $package.${NC}"
return 1
fi
}
# Função para atualizar a lista de pacotes
update_packages() {
echo -e "\n${BLUE}Atualizando listas de pacotes...${NC}"
if eval "$UPDATE_CMD"; then
echo -e "${GREEN}✓ Listas de pacotes atualizadas com sucesso!${NC}"
else
echo -e "${YELLOW}⚠ Houve um problema ao atualizar as listas de pacotes.${NC}"
echo -e "${YELLOW}⚠ Continuando com a versão atual das listas.${NC}"
fi
}
# Função para exibir o menu de pacotes
show_menu() {
clear
echo -e "${BLUE}=============================================${NC}"
echo -e "${BLUE} INSTALADOR INTERATIVO DE PACOTES ${NC}"
echo -e "${BLUE}=============================================${NC}"
echo -e "${YELLOW}Gerenciador de pacotes: $PKG_MANAGER${NC}\n"
echo -e "${GREEN}1.${NC} Utilitários básicos (htop, neofetch, curl, wget)"
echo -e "${GREEN}2.${NC} Ferramentas de desenvolvimento (git, build-essential/gcc, make)"
echo -e "${GREEN}3.${NC} Servidores web (nginx, apache2)"
echo -e "${GREEN}4.${NC} Bancos de dados (postgresql, mariadb/mysql)"
echo -e "${GREEN}5.${NC} Editores de texto (vim, nano, emacs)"
echo -e "${GREEN}6.${NC} Instalação personalizada (informe o pacote)"
echo -e "${GREEN}7.${NC} Atualizar lista de pacotes"
echo -e "${GREEN}8.${NC} Sair\n"
echo -n -e "${YELLOW}Selecione uma opção [1-8]:${NC} "
}
# Função principal
main() {
check_root
detect_package_manager
while true; do
show_menu
read -r option
case $option in
1)
echo -e "\n${BLUE}Instalando utilitários básicos...${NC}"
update_packages
if [ "$PKG_MANAGER" = "pacman" ]; then
install_package "htop neofetch curl wget"
else
install_package "htop"
install_package "neofetch"
install_package "curl"
install_package "wget"
fi
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
2)
echo -e "\n${BLUE}Instalando ferramentas de desenvolvimento...${NC}"
update_packages
install_package "git"
# Instalar pacotes específicos de compilação baseados na distribuição
if [ "$PKG_MANAGER" = "apt" ]; then
install_package "build-essential"
elif [ "$PKG_MANAGER" = "dnf" ] || [ "$PKG_MANAGER" = "yum" ]; then
install_package "gcc gcc-c++ make"
elif [ "$PKG_MANAGER" = "pacman" ]; then
install_package "base-devel"
elif [ "$PKG_MANAGER" = "zypper" ]; then
install_package "gcc gcc-c++ make"
fi
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
3)
echo -e "\n${BLUE}Instalando servidores web...${NC}"
update_packages
echo -e "\n${YELLOW}Qual servidor web você deseja instalar?${NC}"
echo -e "${GREEN}1.${NC} Nginx"
echo -e "${GREEN}2.${NC} Apache"
echo -e "${GREEN}3.${NC} Ambos"
echo -e "${GREEN}4.${NC} Voltar"
echo -n -e "${YELLOW}Selecione uma opção [1-4]:${NC} "
read -r web_option
case $web_option in
1) install_package "nginx" ;;
2)
if [ "$PKG_MANAGER" = "apt" ]; then
install_package "apache2"
else
install_package "httpd"
fi
;;
3)
install_package "nginx"
if [ "$PKG_MANAGER" = "apt" ]; then
install_package "apache2"
else
install_package "httpd"
fi
;;
4) continue ;;
*) echo -e "${RED}Opção inválida!${NC}" ;;
esac
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
4)
echo -e "\n${BLUE}Instalando bancos de dados...${NC}"
update_packages
echo -e "\n${YELLOW}Qual banco de dados você deseja instalar?${NC}"
echo -e "${GREEN}1.${NC} PostgreSQL"
echo -e "${GREEN}2.${NC} MariaDB/MySQL"
echo -e "${GREEN}3.${NC} Ambos"
echo -e "${GREEN}4.${NC} Voltar"
echo -n -e "${YELLOW}Selecione uma opção [1-4]:${NC} "
read -r db_option
case $db_option in
1) install_package "postgresql postgresql-client" ;;
2)
if [ "$PKG_MANAGER" = "apt" ] || [ "$PKG_MANAGER" = "pacman" ]; then
install_package "mariadb-server"
else
install_package "mysql-server"
fi
;;
3)
install_package "postgresql postgresql-client"
if [ "$PKG_MANAGER" = "apt" ] || [ "$PKG_MANAGER" = "pacman" ]; then
install_package "mariadb-server"
else
install_package "mysql-server"
fi
;;
4) continue ;;
*) echo -e "${RED}Opção inválida!${NC}" ;;
esac
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
5)
echo -e "\n${BLUE}Instalando editores de texto...${NC}"
update_packages
echo -e "\n${YELLOW}Qual editor você deseja instalar?${NC}"
echo -e "${GREEN}1.${NC} Vim"
echo -e "${GREEN}2.${NC} Nano"
echo -e "${GREEN}3.${NC} Emacs"
echo -e "${GREEN}4.${NC} Todos"
echo -e "${GREEN}5.${NC} Voltar"
echo -n -e "${YELLOW}Selecione uma opção [1-5]:${NC} "
read -r editor_option
case $editor_option in
1) install_package "vim" ;;
2) install_package "nano" ;;
3) install_package "emacs" ;;
4)
install_package "vim"
install_package "nano"
install_package "emacs"
;;
5) continue ;;
*) echo -e "${RED}Opção inválida!${NC}" ;;
esac
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
6)
echo -e "\n${BLUE}Instalação personalizada${NC}"
echo -n -e "${YELLOW}Digite o nome do pacote que deseja instalar:${NC} "
read -r custom_package
if [ -n "$custom_package" ]; then
update_packages
install_package "$custom_package"
else
echo -e "${RED}Nome de pacote inválido!${NC}"
fi
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
7)
update_packages
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
8)
echo -e "\n${GREEN}Saindo do instalador. Até mais!${NC}"
exit 0
;;
*)
echo -e "\n${RED}Opção inválida! Por favor, tente novamente.${NC}"
echo -e "\n${GREEN}Pressione Enter para continuar...${NC}"
read -r
;;
esac
done
}
# Iniciar o script
main
chmod +x install-packages.sh
sudo ./install-packages.sh
Neste exercício os comandos deveriam ser executados durante a execução de um container, logo há duas formas de o faze-lo, sendo executar o container com -d para configurar como detached ou separado e a outra forma é abrir um novo terminal e executar o comando de para do container.
docker ps -a
docker stop {id do container}
docker rm {id do container}
Este exercício requisitou bastante de mim em comparação aos outros, mas mesmo assim a resolução era relativamente simples, somente tive dificuldade com a API pois a documentação dela estava descontinuada mas ainda funcionava. Em primeiro momento tive que criar um arquivo python para chamar a API, também criei um arquivo requirements.txt para colocar os arquivos necessários no momentos da instalação do python no container e tudo isso foi configurado no Dockerfile
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello World!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Mesmo que foi apenas um pacote informado no requirements pesquisei e vi que era a forma mais segura e garantida de que fosse baixar a versão correta
flask==2.3.3
Este Dockerfile configura toda a parte da conexão da API além de expor a porta 5000 para acesso, copia o arquivo requirements para dentro do container e faz as devidas configurações
# Use a imagem oficial do Python como base
FROM python:3.9-slim
# Define o diretório de trabalho dentro do container
WORKDIR /app
# Copia os arquivos de requisitos primeiro para aproveitar o cache de camad>COPY requirements.txt .
# Instala as dependências
RUN pip install --no-cache-dir -r requirements.txt
# Copia o resto do código da aplicação
COPY . .
# Expõe a porta que a aplicação vai usar
EXPOSE 5000
# Define variáveis de ambiente
ENV FLASK_APP=app.py
ENV FLASK_ENV=development
# Comando para executar a aplicação
CMD ["flask","run","--host=0.0.0.0"]
Por fim a criação da imagem do container e feita atráves do comando docker build -t {nome da imagem} . que cria a imagem com base no Dockerfile (deve ser executado dentro da mesma pasta que se encontra o Dockerfile) e para executar o container com esta imagem utilize o comando docker run -p 5000:5000 {nome da imagem} ápos isso será necessário acessar o endereço http://localhost:5000 para ter a visualização da pagina com o Hello World!.
Aqui começamos os exercícios de nível médio, neste exercício eu tive muita dificuldade pois não entendia muito bem como funcionava a tecnologia do Laravel Breeze, e por tanto eu acabei tendo muita dificuldade, tanto que nem sei si consegui atingir o resultado esperado ao finalizar tal exercício:
mkdir laravel-breeze-mysql
cd laravel-breeze-mysql
touch docker-compose.yml Dockerfile default.conf
version: '3'
services:
# Aplicação Laravel
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel_app
restart: unless-stopped
volumes:
- ./src:/var/www/html
depends_on:
- mysql
networks:
- laravel-network
# Servidor Web Nginx
nginx:
image: nginx:alpine
container_name: laravel_nginx
restart: unless-stopped
ports:
- "8000:80"
volumes:
- ./src:/var/www/html
- ./nginx/conf.d:/etc/nginx/conf.d
depends_on:
- app
networks:
- laravel-network
# Banco de dados MySQL
mysql:
image: mysql:8.0
container_name: laravel_mysql
restart: unless-stopped
environment:
MYSQL_DATABASE: laravel
MYSQL_USER: laravel
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: secret_root
SERVICE_TAGS: dev
SERVICE_NAME: mysql
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- laravel-network
networks:
laravel-network:
driver: bridge
volumes:
mysql_data:
driver: local
FROM php:8.2-fpm
# Instala dependências do sistema
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
zip \
unzip \
libpq-dev \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath intl zip \
&& docker-php-ext-configure gd \
&& docker-php-ext-install gd
# Limpa cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Instala o Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Define o diretório de trabalho
WORKDIR /var/www/html
# Copia o projeto Laravel
COPY ./src /var/www/html
# Ajusta permissões
RUN chown -R www-data:www-data /var/www/html
# Expõe a porta 9000 para o PHP-FPM
EXPOSE 9000
# Inicia o PHP-FPM
CMD ["php-fpm"]
server {
listen 80;
index index.php index.html;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /var/www/html/public;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
gzip_static on;
}
}
Após a configuração de cada um dos arquivos criasse a estrutura de diretórios onde utilizamos o comando mkdir -p src nginx/conf.d e movemos o arquivo default.conf para este diretório. Após isso inicializamos o projeto Laravel com o comando docker-compose up -d e executamos mais 2 comandos, o comando docker exec -it laravel_app bash para entrarmos no container e termos acesso ao bash dele e o comando composer create-project laravel/laravel . que irá criar um novo projeto Laravel. Ainda dentro do container fazemos a sequência de comandos para instalar o Laravel Breeze e suas depêndencias:
composer require laravel/breeze --dev
php artisan breeze:install blade
npm install
npm run build
aproveitamos e configuramos o arquivo .env dentro do Laravel para termos acesso e usarmos o MySQL:
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret
Executamos a migração para a criação da tabela de usuários php artisan migrate e por final para testar fazemos o acesso via http://localhost:8000
Como dito anteriormente, a idéia era utilizar o Laravel Breeze como um sistema de registro e o MySQL faria o armazenamento destes dados porém não consegui chegar num resultado que aparentava estabelecer esta solução mas em decorrência disto notasse que foi usado ambos os serviços tanto quanto o Laravel quanto o MySQL unicamente a parte de registro e feita, entretanto em demasia de tentativas não fora encontrada uma solução para tal ocorrência.
Este exercício não foi tão complicado quanto o anterior pelo fato de eu já ter visto uma aplicabilidade semelhante a requisitada, portanto foi mais tranquilo e sem muitas dificuldades aplicar os conceitos vistos anteriormente. Neste exercício como todos os demais em que envolve-se uma aplicação fora criada um diretório para a organização da aplicação.
mkdir go
cd go
Já adianto que o repositório do GoFiber está descontinuado porém aparentemente ainda é possivel utilizar de algumas tecnologias dele, então eu fiz a clonagem do repositorio para visualizar e utilizar do código fornecido e apenas configurar a compilação e execução dentro do container. Criando o arquivo app.go nano app.go
package main
import (
"log"
"github.com/gofiber/fiber/v2" // Importing the fiber package for handling HTTP requests
"github.com/gofiber/fiber/v2/middleware/cors" // Middleware for handling Cross-Origin Resource Sharing (CORS)
"github.com/gofiber/fiber/v2/middleware/favicon" // Middleware for serving favicon
"github.com/gofiber/fiber/v2/middleware/logger" // Middleware for logging HTTP requests
// Package for logging errors
)
func main() {
app := fiber.New() // Initialize a new Fiber instance
// register middlewares
app.Use(favicon.New()) // Use favicon middleware to serve favicon
app.Use(cors.New()) // Use CORS middleware to allow cross-origin requests
app.Use(logger.New()) // Use logger middleware to log HTTP requests
// Define a GET route for the path '/hello'
app.Get("/hello", func(c *fiber.Ctx) error {
return c.SendString("World!") // Send a response when the route is accessed
})
log.Fatal(app.Listen(":5000")) // Start the server on port 5000 and log any errors
}
module yendoapi
go 1.20
require github.com/gofiber/fiber/v2 v2.52.5
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/sys v0.15.0 // indirect
)
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
Após a criação dos arquivos .go e .sum e .mod faremos a criação e configuração dos arquivos Dockerfile e docker-compose.yml
FROM golang:alpine AS build
RUN adduser -D -g '' appuser
RUN mkdir /app
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o app .
FROM alpine:latest
WORKDIR /app
RUN adduser -D -g '' appuser
COPY --from=build /app/app /app/
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 5000
CMD ["/app/app"]
services:
api:
build: "."
ports:
- "5000"
restart: "always"
networks:
- "api.network"
deploy:
replicas: 5
nginx:
image: nginx:stable-alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/api.conf:ro
depends_on:
- api
ports:
- "8080:8080"
networks:
- "api.network"
networks:
api.network:
além de claro necessitar da criação de um diretório nginx e dentro dele o nginx.conf codo apresenta no repositório da Fiber:
mkdir -p nginx
mkdir -p ./nginx/nginx.conf
Gostaria de salientar também que fiz algumas alterações no código do repositório da Fiber para que a execução fosse feita com sucesso. Ao final utilizasse do comando docker-compose up --build e assim tendo uma página sendo acessada atráves do http://localhost:8080 e as 5 instâncias do serviço operam sem conflitos de porta e fazendo o redirecionamento semelhantemente a um load balance
Neste exercício onde o reposítório do MEAN todo também encontrava-se forá do ar neste caso optei por executar o exercício de manieira a utilizar as tecnologias porém ao invés de uma aplicação de anotação de tarefas fora uma aplicação de registro de livros, podendo retira-los, atualiza-los e até busrca unitariamente atráves do ID. Para tal criei primeiramente o arquivo docker-compose.yml:
version: '3.8'
services:
mongodb:
image: mongo:6.0
container_name: meu_mongodb
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
nodejs:
build: .
container_name: meu_nodejs
environment:
MONGODB_URL: mongodb://admin:admin@mongodb:27017/meu_db?authSource=admin
ports:
- "3000:3000"
depends_on:
- mongodb
volumes:
- .:/app
- /app/node_modules
volumes:
mongo-data:
{
"name": "meu-projeto-node-mongo",
"version": "1.0.0",
"description": "Projeto Node.js com MongoDB em Docker",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^6.13.0"
}
}
const express = require('express');
const mongoose = require('mongoose');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware para analisar corpos de requisição JSON
app.use(express.json());
// Conexão com o MongoDB
const MONGODB_URL = process.env.MONGODB_URL;
mongoose.connect(MONGODB_URL)
.then(() => console.log('Conectado ao MongoDB com sucesso!'))
.catch(err => console.error('Erro ao conectar ao MongoDB:', err));
const { Schema, model } = mongoose;
const LivroSchema = new Schema({
titulo: { type: String, required: true },
autor: { type: String, required: true },
ano: { type: Number, required: true }
});
const Livro = model('Livro', LivroSchema);
// Middleware de tratamento de erros centralizado
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Ocorreu um erro interno!' });
});
// Validação de dados de entrada
const validarLivro = (req, res, next) => {
const { titulo, autor, ano } = req.body;
if (!titulo || !autor || !ano) {
return res.status(400).json({ error: 'Dados do livro incompletos!' });
}
next();
};
app.post('/livros', validarLivro, async (req, res) => {
try {
const livro = new Livro(req.body);
await livro.save();
res.status(201).json(livro);
} catch (err) {
next(err);
}
});
app.get('/livros', async (req, res) => {
try {
const livros = await Livro.find();
res.json(livros);
} catch (err) {
next(err);
}
});
app.get('/livros/:id', async (req, res) => {
try {
const livro = await Livro.findById(req.params.id);
if (!livro) return res.status(404).json({ error: 'Livro não encontrado' });
res.json(livro);
} catch (err) {
next(err);
}
});
app.put('/livros/:id', validarLivro, async (req, res) => {
try {
const livro = await Livro.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true });
if (!livro) return res.status(404).json({ error: 'Livro não encontrado' });
res.json(livro);
} catch (err) {
next(err);
}
});
app.delete('/livros/:id', async (req, res) => {
try {
const livro = await Livro.findByIdAndDelete(req.params.id);
if (!livro) return res.status(404).json({ error: 'Livro não encontrado' });
res.json({ message: 'Livro deletado com sucesso' });
} catch (err) {
next(err);
}
});
// Definir um modelo simples
const TestSchema = new Schema({ name: String });
const TestModel = model('Test', TestSchema);
// Rota para testar a conexão
app.get('/', async (req, res) => {
try {
const testDoc = new TestModel({ name: 'Teste Docker' });
await testDoc.save();
res.send('Documento salvo no MongoDB com sucesso!');
} catch (err) {
next(err);
}
});
// Iniciar o servidor
app.listen(PORT, () => {
console.log(`Servidor rodando na porta ${PORT}`);
});
FROM node:18.19.1-buster-slim
EXPOSE 3000
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm","start"]
Referente ao exercício eu mudei bastante a ideia de resolução porém utilizei as tecnologias como haviam sido empregadas e objetificadas, único porém é que para utilizar do cadastro, da listagem, busca e deletagem de livros tive que fazer uso de um gerenciador de API para colocar em uso o endereço http://localhost:3000/livros e fazendo POST, GET e DELETE gerenciado de API, funcionou porém creio que não foi com está ideia que foi proposto o exercício.
Este exercício em comparação aos outros foi até que tranquilo porém exigiu bastante entendimento sobre as demais aplicações utilizadas fora o Docker.
mkdir Django
cd Django
touch docker-compose.yml Dockerfile requirements.txt settings.py
Finalizada a pré configuração dos arquivos começasse a inserção das configurações de cada arquivo especificamente
services:
postgresql:
image: postgres:latest
container_name: PostgresSQL_container
environment:
- POSTGRES_USER=root
- POSTGRES_PASSWORD=root
- POSTGRES_DB=django_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data/
healthcheck:
test: ["CMD-SHELL","pg_isready -U root -d django_db"]
interval: 5s
timeout: 5s
retries: 5
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
environment:
- DATABASE_URL=postgres://root:root@db:5432/django_db
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
postgresql:
condition: service_healthy
volumes:
postgres_data:
FROM python:3.9-slim-buster
WORKDIR /app
RUN apt-get update && apt-get install -y\
libpq-dev\
gcc
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PYTHONUNBUFFERED=1
ENV DATABASE_URL=postgres://root:root@postgresql:5432/django_db
CMD ["python","manage.py","runserver","0.0.0.0:8000"]
Django==4.2
psycopg2-binary==2.9.6
dj-database-url==1.3.0
import os
import dj_database_url
DATABASES = {
'default': dj_database_url.config(default=os.environ.get('DATABASE_URL'))
}
E para finalizar a configuração dos arquivos executamos o comando django-admin startproject core . que fará a inserção dos arquivos restantes do django. E por fim utilizamos o comando docker-compose up --build e acessamos a aplicação via http://localhost:8000
Finalizando a lista de exercícios, este exercício mesmo sendo classificado como díficil fora mais fácil do que outros no nível médio que tinha a documentação descontinuada.
mkdir estatic
cd estatic
touch Dockerfile docker-compose.yml
mkdir site
Neste exercício foi requisitado o uso de uma Landing Page do Creative Tim, logo utilizei do repositório deles para fazer isto.
FROM nginx:alpine
COPY site/ /usr/share/nginx/html/
EXPOSE 80
version: '3.8'
services:
web:
build: .
ports:
- "8080:80"
Sim, os arquivos foram demasiadamente menores do que em comparação com o restante dos exercícios. Finalizando o exercício utilizamos o comando docker-compose build e logo em seguida docker-compose up, podendo se fazer o acesso ao container através do comando docker exec -it {nome do projeto} /bin/sh e verificar a integridade dos arquivos e ver o error.log em caso de algum problema. Além e claro da utilização do acesso via http://localhost:8080 para verificar a pagina estatica.
Mesmo tendo vários repositórios descontinuados foi muito desafiador buscar alternativas para contemplar o objetivo dos exercícios, admito que tamanho desafio me deixou mais entusiasmado com a tecnologia de conteineirização e suas várias possibilidades de aplicação.