Docker Deployment
Docker provides consistent, reproducible deployments for your APSO backend.
Dockerfile
TypeScript (NestJS)
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine AS production
WORKDIR /app
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nestjs -u 1001
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
USER nestjs
EXPOSE 3001
ENV NODE_ENV=production
ENV PORT=3001
CMD ["node", "dist/main.js"]Python (FastAPI)
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Create non-root user
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 3001
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "3001"]Go (Gin)
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server
# Production stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /app/server .
RUN adduser -D appuser
USER appuser
EXPOSE 3001
CMD ["./server"]Docker Compose
Development
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "3001:3001"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:postgres@db:5432/apso
- JWT_SECRET=dev-secret
depends_on:
db:
condition: service_healthy
volumes:
- .:/app
- /app/node_modules
db:
image: postgres:15-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: apso
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:Production
# docker-compose.prod.yml
version: '3.8'
services:
api:
image: your-registry/apso-api:latest
ports:
- "3001:3001"
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
- JWT_SECRET=${JWT_SECRET}
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
interval: 30s
timeout: 10s
retries: 3
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- api.dockerignore
node_modules
npm-debug.log
Dockerfile*
docker-compose*
.git
.gitignore
.env*
*.md
.vscode
coverage
distBuilding Images
# Build image
docker build -t my-api:latest .
# Build with specific tag
docker build -t my-api:v1.0.0 .
# Build for different platforms
docker buildx build --platform linux/amd64,linux/arm64 -t my-api:latest .Running Containers
# Run with environment file
docker run -p 3001:3001 --env-file .env.production my-api:latest
# Run with specific environment variables
docker run -p 3001:3001 \
-e DATABASE_URL="postgresql://..." \
-e JWT_SECRET="secret" \
my-api:latest
# Run in background
docker run -d --name my-api -p 3001:3001 --env-file .env.production my-api:latest
# View logs
docker logs -f my-apiContainer Registry
Docker Hub
# Login
docker login
# Tag and push
docker tag my-api:latest username/my-api:latest
docker push username/my-api:latestGitHub Container Registry
# Login
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Tag and push
docker tag my-api:latest ghcr.io/username/my-api:latest
docker push ghcr.io/username/my-api:latestAWS ECR
# Login
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
# Tag and push
docker tag my-api:latest 123456789.dkr.ecr.us-east-1.amazonaws.com/my-api:latest
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/my-api:latestKubernetes
Deployment
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: apso-api
spec:
replicas: 3
selector:
matchLabels:
app: apso-api
template:
metadata:
labels:
app: apso-api
spec:
containers:
- name: api
image: your-registry/apso-api:latest
ports:
- containerPort: 3001
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: apso-secrets
key: database-url
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: apso-secrets
key: jwt-secret
livenessProbe:
httpGet:
path: /health
port: 3001
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 3001
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"Service
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: apso-api
spec:
selector:
app: apso-api
ports:
- port: 80
targetPort: 3001
type: ClusterIPIngress
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: apso-api
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- api.yourdomain.com
secretName: apso-api-tls
rules:
- host: api.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: apso-api
port:
number: 80Health Checks
// Docker health check endpoint
@Get('/health')
health() {
return { status: 'ok', timestamp: Date.now() };
}
@Get('/health/ready')
async ready() {
// Check database connection
await this.dataSource.query('SELECT 1');
return { status: 'ready' };
}Related
Last updated on