| name | webs-deployment |
| description | Deployment web — Vercel, Docker, CI/CD, monitoring, canary, rollback. Zero-downtime, tự động scale. |
webs-deployment — Deployment
Triển khai Vercel
{
"framework": "nextjs",
"regions": ["sin1", "iad1"],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
]
},
{
"source": "/(.*\\.(png|jpg|webp|svg|ico))",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
}
]
}
Docker (Next.js)
# Dockerfile
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]
services:
web:
build: .
ports: ["3000:3000"]
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/app
depends_on: [db]
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:16-alpine
volumes: [pgdata:/var/lib/postgresql/data]
environment:
- POSTGRES_PASSWORD=password
restart: unless-stopped
volumes:
pgdata:
CI/CD (GitHub Actions)
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID }}
vercel-args: "--prod"
Health Check Endpoint
export async function GET() {
const health = {
status: "healthy",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage().heapUsed,
};
try {
await prisma.$queryRaw`SELECT 1`;
} catch {
return NextResponse.json({ ...health, status: "degraded", db: "down" }, { status: 503 });
}
return NextResponse.json(health);
}
Giám sát (Sentry)
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1,
environment: process.env.NODE_ENV,
beforeSend(event) {
if (event.exception?.values?.[0]?.type === "NavigationAbort") return null;
return event;
},
});
Triển khai Canary
export async function getFeatureFlags(userId: string) {
const hash = hashCode(userId) % 100;
return {
newCheckout: hash < 10,
newDashboard: hash < 5,
};
}
function hashCode(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash |= 0;
}
return Math.abs(hash);
}