Como criar sua primeira aplicação fullstack com Docker.
Organizar uma aplicação completa do zero, pode ser uma tarefa bastante complicada no começo, devido a alta quantidade de maneiras de resolver o mesmo problema. Muitos programadores ficam perdidos por onde começar, principalmente os iniciantes, focam em tecnologia, quando oque é uma das coisas mais legais, é colocar a mão na massa.
Porque utilizar Docker?
Com docker Ć© possivel abstrair todo o ambiente e acoplar somente em um local, sendo facilmente utilizado
para coloca-lo em produção, ou colocar para funcionar em um projeto do amigo dev. Além disso não precisamos
instalar diversos pacotes dentro da nossa maquina, jĆ” que o próprio emula esse ambiente pra gente. Alguns benefĆcios:
- fÔcil instalação
 - fƔcil deploy
 - versionamento de build com docker hub
 - pode ser integrado com pipelines
 - evita instalar N Versões da aplicação na própria mÔquina
 - fƔcil reciprocidade da comunidade
 - receita de bolo
 
*Como organizar a aplicação: *
Primeiro de tudo iremos escrever nossa receita docker onde a mesma irÔ se encarregar de praticamente tudo, optei por escolher o banco de dados Postgres, devido ser bastante fÔcil de mexer, o código é aberto, e tem muito conteudo na web. O projeto estÔ divido em 4 camadas. Sendo elas, client onde serÔ usado um framework React (pode ser qualquer um, como vue, angular etc..), server com node + express, redis, e banco de dados. *Nginx para chavear as portas da aplicação e servir o frontend e o backend em portas dinamicas. *
Na raiz do projeto.
version: '3'
services:
  postgres:
    image: "postgres:latest"
    environment:
      - POSTGRES_PASSWORD=postgres_password
  redis:
    image: "redis:latest"
  nginx:
    restart: always
    build:
      dockerfile: Dockerfile.dev
      context: ./nginx
    ports:
      - "3050:80"
    depends_on:
      - api
      - client       
  api:
    build:
      dockerfile: Dockerfile.dev
      context : ./server
    volumes:
      - /app/node_modules 
      - ./server:/app  
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - PGUSER=postgres
      - PGHOST=postgres
      - PGDATABASE=postgres
      - PGPASSWORD=postgres_password
      - PGPORT=5432  
  client:
    build:
      dockerfile: Dockerfile.dev
      context: ./client
    volumes:
      - /app/node_modules
      - ./client:/app 
    environment:
      - WDS_SOCKET=0   
Note que na receita Dockerfile acima estamos fazendo a referencia para seus devidos arquivos Dockerfile.dev
de cada serviço. Estou optando pelo arquivo .dev, pois assim apos o desenvolvedor buildar, podemos fazer versionamento e subir o build com uma esteira de deploy para produção.
Configuração do servidor:
š ./server/Dockerfile.dev
FROM node:14.14.0-alpine
WORKDIR '/app'
COPY package.json .
RUN npm install 
COPY . .
CMD ["npm", "run", "dev"]
š ./server/index.js
const keys = require('./keys')
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const { Pool } = require('pg')
const redis = require('redis')
const app = express()
app.use(cors())
app.use(bodyParser.json())
const pgClient = new Pool({
  user: keys.pgUser,
  port: keys.pgPort,
  host: keys.pgHost,
  database: keys.pgDataBase,
  password: keys.pgPassword,
})
pgClient.on("connect", (client) => {
  client
    .query("CREATE TABLE IF NOT EXISTS values (number INT)")
    .catch((err) => console.error(err));
});
const redisClient = redis.createClient({
  host: keys.redisHost,
  port: keys.redisPort,
  retry_strategy: () => 1000
})
const redisPublisher = redisClient.duplicate()
// Express route handlers
app.get('/', (req, res) => {
  res.send('Hi')
})
app.get('/values/all', async (req, res) => {
  const values = await pgClient.query('SELECT * FROM values')
  res.send(values.rows)
})
app.get('/values/current', async (req, res) => {
  redisClient.hgetall('values', (err, values) => {
    res.send(values)
  })
})
app.post('/values', async (req, res) => {
  const index = req.body.index
  if (parseInt(index) > 40) {
    return res.status(422).send({ message: 'Index to high' })
  }
  redisClient.hset('values', index, 'Nothing yet!')
  redisPublisher.publish('insert', index)
  pgClient.query('INSERT INTO values(number) VALUES($1)', [index])
  res.send({ working: true })
})
app.listen(5000, () => {
  console.info('app on listening on port 5000!')
})
O script acima Ʃ responsƔvel por iniciar o backend, como trata-se de somente um tutorial
optei por deixar todo o código em um arquivo para fÔcil entendimento, fique a vontade para quebra-lo camadas e arquivos separados, como service, e controllers (recomendado).
š ./server/keys.js
module.exports = {
    redisHost: process.env.REDIS_HOST,
    redisPort: process.env.REDIS_PORT,
    pgUser: process.env.PGUSER,
    pgHost: process.env.PGHOST,
    pgDataBase: process.env.PGDATABASE,
    pgPassword: process.env.PGPASSWORD,
    pgPort: process.env.PGPORT
}
Apos a instalação de todos os pacotes, ainda não sera possivel visualizar a aplicação no browser, pois teriamos que fazer uma configuração do nosso proxy nginx, onde o mesmo fica responsÔvel por expor as portas da aplicação
para o mundo externo.
š ./nginx/default.conf
upstream client {
  server client:3000;
}
upstream api {
  server api:5000;
}
server {
  listen 80;
  location / {
    proxy_pass http://client;
  }
  location /api {
    rewrite /api/(.*) /$1 break;
    proxy_pass http://api;
  }
  location /ws {
    proxy_pass http://client;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
  }
}
A configuração acima, acredito e julgo que seja muito ideal pois o frontend não irÔ precisar bater um endpoint separada pois tudo que começa com [api] ja temos a referencia que é a nosso serviço, logo oque vier depois disso trata-se somente do backend. Assim facilitando caso optar por deploy da aplicação em ambiente de produção, poupando tempo em troca de ip ou URI.
Disclaimer, como trata-se de um tutorial deixarei a parte do frontend e escolha do programador, onde somente mostrarei apenas o arquivo de proxy que ficarÔ dentro da pasta do client. Pode ser usado qualquer tipo de framework frontend, até mesmo js puro com webpack. Desde que seja adicionado o arquivo de configuração do nginx, também fazendo as devidas correções para ouvir o arquivo de html do projeto.
š ./client/nginx/default.conf
server {
    listen 3000;
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
}
Como rodar o projeto š
O comando docker abaixo serƔ responsƔvel por subir todo o nosso projeto, sendo possivel acessƔ-lo 
na porta 3050, para o frontend, e 5000 (/api/)para o backend.
docker compose -f docker-compose-dev.yml up
Link do repositório Gitlab
šØāš» Volte sempre...
              
    
Top comments (1)
ConteĆŗdo true!!!