deployment-automation
npx skills add https://github.com/supercent-io/skills-template --skill deployment-automation
Agent 安装分布
Skill 文档
Deployment Automation
When to use this skill
- ì ê· íë¡ì í¸: ì²ìë¶í° ìë ë°°í¬ ì¤ì
- ìë ë°°í¬ ê°ì : ë°ë³µì ì¸ ìë ìì ìëí
- ë©í° íê²½: dev, staging, production íê²½ ë¶ë¦¬
- ì¤ì¼ì¼ë§: í¸ëí½ ì¦ê° ëë¹ Kubernetes ëì
Instructions
Step 1: Docker 컨í ì´ëí
ì í리ì¼ì´ì ì Docker ì´ë¯¸ì§ë¡ í¨í¤ì§í©ëë¤.
Dockerfile (Node.js ì±):
# Multi-stage build for smaller image size
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files and install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy source code
COPY . .
# Build application (if needed)
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
# Copy only necessary files from builder
COPY /app/node_modules ./node_modules
COPY /app/dist ./dist
COPY /app/package.json ./
# Create non-root user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK \
CMD node healthcheck.js
# Start application
CMD ["node", "dist/index.js"]
.dockerignore:
node_modules
npm-debug.log
.git
.env
.env.local
dist
build
coverage
.DS_Store
ë¹ë ë° ì¤í:
# Build image
docker build -t myapp:latest .
# Run container
docker run -d -p 3000:3000 --name myapp-container myapp:latest
# Check logs
docker logs myapp-container
# Stop and remove
docker stop myapp-container
docker rm myapp-container
Step 2: GitHub Actions CI/CD
ì½ë í¸ì ì ìëì¼ë¡ í ì¤í¸ ë° ë°°í¬í©ëë¤.
.github/workflows/deploy.yml:
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '18'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
build:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix={{branch}}-
type=semver,pattern={{version}}
latest
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to production
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.PROD_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
script: |
cd /app
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
docker-compose up -d --no-deps --build web
docker image prune -f
Step 3: Kubernetes ë°°í¬
íì¥ ê°ë¥í 컨í ì´ë ì¤ì¼ì¤í¸ë ì´ì ì 구íí©ëë¤.
k8s/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
labels:
app: myapp
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ghcr.io/username/myapp:latest
imagePullPolicy: Always
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: production
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
ë°°í¬ ì¤í¬ë¦½í¸ (deploy.sh):
#!/bin/bash
set -e
# Variables
NAMESPACE="production"
IMAGE_TAG="${1:-latest}"
echo "Deploying myapp:${IMAGE_TAG} to ${NAMESPACE}..."
# Apply Kubernetes manifests
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/secrets.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
# Update image
kubectl set image deployment/myapp myapp=ghcr.io/username/myapp:${IMAGE_TAG} -n ${NAMESPACE}
# Wait for rollout
kubectl rollout status deployment/myapp -n ${NAMESPACE} --timeout=5m
# Verify
kubectl get pods -n ${NAMESPACE} -l app=myapp
echo "Deployment completed successfully!"
Step 4: Vercel/Netlify (íë¡ í¸ìë)
ì ì ì¬ì´í¸ ë° Next.js ì±ì ê°ë¨í ë°°í¬í©ëë¤.
vercel.json:
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"env": {
"DATABASE_URL": "@database-url",
"API_KEY": "@api-key"
},
"regions": ["sin1", "icn1"],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-Content-Type-Options",
"value": "nosniff"
}
]
}
],
"redirects": [
{
"source": "/old-path",
"destination": "/new-path",
"permanent": true
}
]
}
CLI ë°°í¬:
# Install Vercel CLI
npm i -g vercel
# Login
vercel login
# Deploy to preview
vercel
# Deploy to production
vercel --prod
# Set environment variable
vercel env add DATABASE_URL
Step 5: 무ì¤ë¨ ë°°í¬ ì ëµ
ìë¹ì¤ ì¤ë¨ ìì´ ì ë²ì ì ë°°í¬í©ëë¤.
Blue-Green ë°°í¬ (docker-compose):
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app-blue
- app-green
app-blue:
image: myapp:blue
environment:
- NODE_ENV=production
- COLOR=blue
app-green:
image: myapp:green
environment:
- NODE_ENV=production
- COLOR=green
switch.sh (Blue/Green ì í):
#!/bin/bash
CURRENT_COLOR=$(cat current_color.txt)
NEW_COLOR=$([[ "$CURRENT_COLOR" == "blue" ]] && echo "green" || echo "blue")
# Deploy new version to inactive environment
docker-compose up -d app-${NEW_COLOR}
# Wait for health check
sleep 10
# Health check
if curl -f http://localhost:8080/health; then
# Update nginx to point to new environment
sed -i "s/${CURRENT_COLOR}/${NEW_COLOR}/g" nginx.conf
docker-compose exec nginx nginx -s reload
# Update current color
echo ${NEW_COLOR} > current_color.txt
# Stop old environment after 5 minutes (rollback window)
sleep 300
docker-compose stop app-${CURRENT_COLOR}
echo "Deployment successful! Switched to ${NEW_COLOR}"
else
echo "Health check failed! Keeping ${CURRENT_COLOR}"
docker-compose stop app-${NEW_COLOR}
exit 1
fi
Output format
ë°°í¬ ì²´í¬ë¦¬ì¤í¸
## Deployment Checklist
### Pre-Deployment
- [ ] All tests passing (unit, integration, E2E)
- [ ] Code review approved
- [ ] Environment variables configured
- [ ] Database migrations ready
- [ ] Rollback plan documented
### Deployment
- [ ] Docker image built and tagged
- [ ] Image pushed to container registry
- [ ] Kubernetes manifests applied
- [ ] Rolling update started
- [ ] Pods healthy and ready
### Post-Deployment
- [ ] Health check endpoint responding
- [ ] Metrics/logs monitoring active
- [ ] Performance baseline established
- [ ] Old pods terminated (after grace period)
- [ ] Deployment documented in changelog
Constraints
íì ê·ì¹ (MUST)
-
Health Checks: 모ë ìë¹ì¤ì health check ìëí¬ì¸í¸
app.get('/health', (req, res) => { res.status(200).json({ status: 'ok' }); }); -
Graceful Shutdown: SIGTERM ì í¸ ì²ë¦¬
process.on('SIGTERM', async () => { console.log('SIGTERM received, shutting down gracefully'); await server.close(); await db.close(); process.exit(0); }); -
íê²½ë³ì ë¶ë¦¬: íëì½ë© ê¸ì§, .env ì¬ì©
ê¸ì§ ì¬í (MUST NOT)
- Secrets ì»¤ë° ê¸ì§: API í¤, ë¹ë°ë²í¸ë¥¼ Gitì ì ë 커ë°íì§ ìì
- íë¡ëì
ìì ëë²ê·¸ 모ë:
NODE_ENV=productioníì - latest íê·¸ë§ ì¬ì©: ë²ì íê·¸ ì¬ì© (v1.0.0, sha-abc123)
Best practices
- Multi-stage Docker builds: ì´ë¯¸ì§ í¬ê¸° ìµìí
- Immutable infrastructure: ìë² ìì ëì ìë¡ ë°°í¬
- Blue-Green deployment: 무ì¤ë¨ ë°°í¬ ë° ì¬ì´ 롤백
- Monitoring íì: Prometheus, Grafana, Datadog
References
Metadata
ë²ì
- íì¬ ë²ì : 1.0.0
- ìµì¢ ì ë°ì´í¸: 2025-01-01
- í¸í íë«í¼: Claude, ChatGPT, Gemini
ê´ë ¨ ì¤í¬
- monitoring: ë°°í¬ í 모ëí°ë§
- security: ë°°í¬ ë³´ì
íê·¸
#deployment #CI/CD #Docker #Kubernetes #automation #infrastructure