Adiciona estrutura inicial do projeto NestJS com Docker, incluindo Dockerfile, docker-compose, scripts de deploy e configuração do Nginx.
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Nunca sobe projetos NestJS — ficam só no VPS
|
||||||
|
projects/
|
||||||
|
node_modules/
|
||||||
|
*.log
|
||||||
|
.env
|
||||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
# Install system deps
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
nginx \
|
||||||
|
git \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
openssl
|
||||||
|
|
||||||
|
# Install PM2 globally
|
||||||
|
RUN npm install -g pm2
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
RUN mkdir -p /home/deploy/projects \
|
||||||
|
/home/deploy/scripts \
|
||||||
|
/etc/nginx/sites-dynamic \
|
||||||
|
/var/log/nginx \
|
||||||
|
/run/nginx
|
||||||
|
|
||||||
|
# Copy scripts
|
||||||
|
COPY scripts/ /home/deploy/scripts/
|
||||||
|
RUN chmod +x /home/deploy/scripts/*.sh
|
||||||
|
|
||||||
|
# Copy nginx base config
|
||||||
|
COPY nginx/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
WORKDIR /home/deploy
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["/home/deploy/scripts/start.sh"]
|
||||||
135
README.md
135
README.md
@@ -1,2 +1,135 @@
|
|||||||
# docker-nest-js
|
# NestJS Multi-Projeto Stack — Portainer
|
||||||
|
|
||||||
|
## Estrutura no Host (VPS)
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/deploy/
|
||||||
|
├── nest-stack/ ← este repositório
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── docker-compose.yml
|
||||||
|
│ ├── nginx/nginx.conf
|
||||||
|
│ ├── scripts/
|
||||||
|
│ │ ├── start.sh ← entrypoint do container
|
||||||
|
│ │ └── deploy.sh ← deploy dentro do container
|
||||||
|
│ └── host-deploy.sh ← atalho para rodar do host
|
||||||
|
│
|
||||||
|
└── projects/ ← SEUS PROJETOS (volume montado)
|
||||||
|
├── projeto1/ ← git clone aqui
|
||||||
|
└── projeto2/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup inicial
|
||||||
|
|
||||||
|
### 1. No servidor (VPS), clone este repo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <este-repo> /home/deploy/nest-stack
|
||||||
|
mkdir -p /home/deploy/projects
|
||||||
|
chmod +x /home/deploy/nest-stack/host-deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. No Portainer → Stacks → Add Stack
|
||||||
|
|
||||||
|
- **Name:** `nestjs-stack`
|
||||||
|
- **Build method:** Repository (aponta para este repo)
|
||||||
|
— OU —
|
||||||
|
- **Build method:** Upload → sobe o `docker-compose.yml`
|
||||||
|
|
||||||
|
### 3. Clique em **Deploy the stack**
|
||||||
|
|
||||||
|
O container vai:
|
||||||
|
- Buildar a imagem (Nginx + Node + PM2)
|
||||||
|
- Montar `/home/deploy/projects` como volume
|
||||||
|
- Subir projetos que já tiverem build em `dist/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adicionando um novo projeto
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# No HOST (fora do container)
|
||||||
|
cd /home/deploy/projects
|
||||||
|
git clone https://github.com/seu-usuario/meu-projeto.git projeto1
|
||||||
|
|
||||||
|
# Roda o deploy
|
||||||
|
./home/deploy/nest-stack/host-deploy.sh projeto1
|
||||||
|
|
||||||
|
# Pronto! Acessível em:
|
||||||
|
# http://nest.juancjc.com.br/projeto1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Atualizando um projeto existente
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Atualiza o código
|
||||||
|
cd /home/deploy/projects/projeto1
|
||||||
|
git pull
|
||||||
|
|
||||||
|
# Redeploya
|
||||||
|
/home/deploy/nest-stack/host-deploy.sh projeto1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reset do container (via Portainer)
|
||||||
|
|
||||||
|
1. Portainer → Containers → `nestjs-stack` → **Restart** ou **Reset**
|
||||||
|
2. O container sobe e **automaticamente relança todos os projetos** que tiverem `dist/main.js`
|
||||||
|
3. Os arquivos dos projetos continuam intactos (estão no volume do host)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comandos úteis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ver logs do container
|
||||||
|
docker logs nestjs-stack -f
|
||||||
|
|
||||||
|
# Ver processos PM2 rodando
|
||||||
|
docker exec nestjs-stack pm2 list
|
||||||
|
|
||||||
|
# Ver logs de um projeto específico
|
||||||
|
docker exec nestjs-stack pm2 logs projeto1
|
||||||
|
|
||||||
|
# Entrar no container
|
||||||
|
docker exec -it nestjs-stack bash
|
||||||
|
|
||||||
|
# Ver status do nginx
|
||||||
|
docker exec nestjs-stack nginx -t
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Regras de porta
|
||||||
|
|
||||||
|
| Projeto | Porta interna |
|
||||||
|
|---------------|---------------|
|
||||||
|
| projeto1 | 3001 |
|
||||||
|
| projeto2 | 3002 |
|
||||||
|
| projeto3 | 3003 |
|
||||||
|
| projetoN | 300N |
|
||||||
|
|
||||||
|
A porta é determinada pela **ordem alfabética das pastas** em `/home/deploy/projects`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Seu projeto NestJS não precisa de nenhuma config especial
|
||||||
|
|
||||||
|
O Nginx remove o prefixo `/projeto1` antes de repassar ao NestJS.
|
||||||
|
Seu app recebe as rotas normalmente como `/`.
|
||||||
|
|
||||||
|
**Único requisito:** seu `main.ts` deve aceitar a porta via variável de ambiente ou argumento:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// main.ts
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
const port = process.argv.find((_, i, a) => a[i-1] === '--port') || process.env.PORT || 3000;
|
||||||
|
await app.listen(port);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
|
```
|
||||||
|
|||||||
25
docker-compose.yml
Normal file
25
docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
nest-stack:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: nestjs-stack
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "2442:80"
|
||||||
|
volumes:
|
||||||
|
# Projetos ficam no host — persistem no reset do container
|
||||||
|
- /home/deploy/projects:/home/deploy/projects
|
||||||
|
# Scripts acessíveis para executar deploy de fora do container
|
||||||
|
- ./scripts:/home/deploy/scripts
|
||||||
|
environment:
|
||||||
|
- PROJECTS_DIR=/home/deploy/projects
|
||||||
|
- BASE_PORT=3000
|
||||||
|
- DOMAIN=nest.juancjc.com.br
|
||||||
|
labels:
|
||||||
|
- "com.neststack.managed=true"
|
||||||
|
|
||||||
|
# Nota: sem volume nomeado para projects — pasta no host é intencional
|
||||||
|
# Isso permite git clone direto no host sem entrar no container
|
||||||
14
host-deploy.sh
Normal file
14
host-deploy.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# host-deploy.sh — rode isso NO HOST para deployar um projeto
|
||||||
|
# Uso: ./host-deploy.sh meu-projeto
|
||||||
|
# Ou: ./host-deploy.sh (deploya todos)
|
||||||
|
|
||||||
|
TARGET="${1:-}"
|
||||||
|
|
||||||
|
if [ -n "$TARGET" ]; then
|
||||||
|
echo ">>> Deployando $TARGET via container..."
|
||||||
|
docker exec nestjs-stack /home/deploy/scripts/deploy.sh "$TARGET"
|
||||||
|
else
|
||||||
|
echo ">>> Deployando todos os projetos via container..."
|
||||||
|
docker exec nestjs-stack /home/deploy/scripts/deploy.sh
|
||||||
|
fi
|
||||||
36
nginx/nginx.conf
Normal file
36
nginx/nginx.conf
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /run/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent"';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
client_max_body_size 50M;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Rota raiz — lista projetos disponíveis
|
||||||
|
location = / {
|
||||||
|
return 200 'NestJS Stack — use /nome-do-projeto para acessar seus projetos';
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rotas dinâmicas dos projetos (geradas pelo gen-nginx.sh)
|
||||||
|
include /etc/nginx/sites-dynamic/*.conf;
|
||||||
|
}
|
||||||
|
}
|
||||||
115
scripts/deploy.sh
Normal file
115
scripts/deploy.sh
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# deploy.sh — deploya um projeto ou todos
|
||||||
|
# Uso dentro do container:
|
||||||
|
# /home/deploy/scripts/deploy.sh → deploya todos
|
||||||
|
# /home/deploy/scripts/deploy.sh meu-projeto → deploya só um
|
||||||
|
|
||||||
|
PROJECTS_DIR="${PROJECTS_DIR:-/home/deploy/projects}"
|
||||||
|
BASE_PORT="${BASE_PORT:-3000}"
|
||||||
|
TARGET="${1:-}"
|
||||||
|
|
||||||
|
deploy_project() {
|
||||||
|
local project="$1"
|
||||||
|
local port="$2"
|
||||||
|
local project_dir="$PROJECTS_DIR/$project"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo ">>> Deployando: $project → porta $port"
|
||||||
|
|
||||||
|
if [ ! -d "$project_dir" ]; then
|
||||||
|
echo "✗ Pasta $project_dir não encontrada"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$project_dir"
|
||||||
|
|
||||||
|
# Instala dependências
|
||||||
|
echo " → npm install..."
|
||||||
|
npm install --prefer-offline 2>&1 | tail -3
|
||||||
|
|
||||||
|
# Build
|
||||||
|
echo " → npm run build..."
|
||||||
|
npm run build
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo " ✗ Build falhou para $project"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Salva a porta usada
|
||||||
|
echo "$port" > "$project_dir/.port"
|
||||||
|
|
||||||
|
# PM2: reinicia se já existe, senão cria
|
||||||
|
if pm2 describe "$project" > /dev/null 2>&1; then
|
||||||
|
echo " → Reiniciando processo PM2..."
|
||||||
|
pm2 restart "$project"
|
||||||
|
else
|
||||||
|
echo " → Criando processo PM2..."
|
||||||
|
pm2 start dist/main.js \
|
||||||
|
--name "$project" \
|
||||||
|
--env production \
|
||||||
|
-- --port "$port"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gera/atualiza config nginx
|
||||||
|
cat > "/etc/nginx/sites-dynamic/${project}.conf" <<EOF
|
||||||
|
location /${project}/ {
|
||||||
|
proxy_pass http://localhost:${port}/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade \$http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
proxy_cache_bypass \$http_upgrade;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo " ✓ $project deployado em /$project"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------
|
||||||
|
# Lógica principal
|
||||||
|
# -------------------------------------------
|
||||||
|
|
||||||
|
if [ -n "$TARGET" ]; then
|
||||||
|
# Deploya projeto específico
|
||||||
|
# Descobre qual porta estava reservada ou pega a próxima livre
|
||||||
|
port_index=1
|
||||||
|
for d in "$PROJECTS_DIR"/*/; do
|
||||||
|
[ -d "$d" ] || continue
|
||||||
|
name=$(basename "$d")
|
||||||
|
if [ "$name" = "$TARGET" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
port_index=$((port_index + 1))
|
||||||
|
done
|
||||||
|
port=$((BASE_PORT + port_index))
|
||||||
|
deploy_project "$TARGET" "$port"
|
||||||
|
else
|
||||||
|
# Deploya todos os projetos
|
||||||
|
port_index=1
|
||||||
|
for project_dir in "$PROJECTS_DIR"/*/; do
|
||||||
|
[ -d "$project_dir" ] || continue
|
||||||
|
project=$(basename "$project_dir")
|
||||||
|
port=$((BASE_PORT + port_index))
|
||||||
|
deploy_project "$project" "$port"
|
||||||
|
port_index=$((port_index + 1))
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Recarrega nginx
|
||||||
|
echo ""
|
||||||
|
echo "→ Recarregando Nginx..."
|
||||||
|
nginx -s reload
|
||||||
|
pm2 save
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "================================================"
|
||||||
|
echo " Deploy concluído!"
|
||||||
|
for project_dir in "$PROJECTS_DIR"/*/; do
|
||||||
|
[ -d "$project_dir" ] || continue
|
||||||
|
project=$(basename "$project_dir")
|
||||||
|
port=$(cat "$project_dir/.port" 2>/dev/null || echo "?")
|
||||||
|
echo " ✓ /$project → localhost:$port"
|
||||||
|
done
|
||||||
|
echo "================================================"
|
||||||
76
scripts/start.sh
Normal file
76
scripts/start.sh
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# start.sh — entrypoint do container
|
||||||
|
# Roda ao iniciar/resetar o container
|
||||||
|
|
||||||
|
PROJECTS_DIR="${PROJECTS_DIR:-/home/deploy/projects}"
|
||||||
|
BASE_PORT="${BASE_PORT:-3000}"
|
||||||
|
|
||||||
|
echo "================================================"
|
||||||
|
echo " NestJS Stack — Iniciando..."
|
||||||
|
echo "================================================"
|
||||||
|
|
||||||
|
# Garante que a pasta de projetos existe
|
||||||
|
mkdir -p "$PROJECTS_DIR"
|
||||||
|
mkdir -p /etc/nginx/sites-dynamic
|
||||||
|
|
||||||
|
# Limpa configs antigas do nginx (container pode ter sido resetado)
|
||||||
|
rm -f /etc/nginx/sites-dynamic/*.conf
|
||||||
|
|
||||||
|
# Inicia nginx
|
||||||
|
echo "→ Iniciando Nginx..."
|
||||||
|
nginx -t && nginx
|
||||||
|
echo "✓ Nginx rodando"
|
||||||
|
|
||||||
|
# Se houver projetos com build existente, sobe eles via PM2
|
||||||
|
echo "→ Verificando projetos existentes..."
|
||||||
|
port_index=1
|
||||||
|
|
||||||
|
for project_dir in "$PROJECTS_DIR"/*/; do
|
||||||
|
[ -d "$project_dir" ] || continue
|
||||||
|
project=$(basename "$project_dir")
|
||||||
|
port=$((BASE_PORT + port_index))
|
||||||
|
|
||||||
|
# Só sobe se já tiver build
|
||||||
|
if [ -f "$project_dir/dist/main.js" ]; then
|
||||||
|
echo " → Subindo $project na porta $port"
|
||||||
|
|
||||||
|
pm2 start "$project_dir/dist/main.js" \
|
||||||
|
--name "$project" \
|
||||||
|
--env production \
|
||||||
|
-- --port "$port" 2>/dev/null
|
||||||
|
|
||||||
|
# Gera config nginx para esse projeto
|
||||||
|
cat > "/etc/nginx/sites-dynamic/${project}.conf" <<EOF
|
||||||
|
location /${project}/ {
|
||||||
|
proxy_pass http://localhost:${port}/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade \$http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
proxy_cache_bypass \$http_upgrade;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
echo "$port" > "$project_dir/.port"
|
||||||
|
else
|
||||||
|
echo " ⚠ $project sem build — rode: docker exec nestjs-stack /home/deploy/scripts/deploy.sh $project"
|
||||||
|
fi
|
||||||
|
|
||||||
|
port_index=$((port_index + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Recarrega nginx com as configs dos projetos
|
||||||
|
nginx -s reload 2>/dev/null || true
|
||||||
|
echo "✓ Nginx atualizado com rotas dos projetos"
|
||||||
|
|
||||||
|
pm2 save 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "================================================"
|
||||||
|
echo " Stack pronta!"
|
||||||
|
echo " Acesse: http://\${DOMAIN:-localhost}/<projeto>"
|
||||||
|
echo "================================================"
|
||||||
|
|
||||||
|
# Mantém o container vivo e faz log do PM2
|
||||||
|
pm2 logs --raw
|
||||||
Reference in New Issue
Block a user