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!!!