SEPEHR.SYS

Starting SEPEHR.SYS…

Loading

سپهر محسنی

مهندس نرم‌افزار فول‌استک و هوش مصنوعی

SEPEHR.SYS۲۶ بازی، ۲۶ ابزار، یک رزومه
داکر برای Production: راهنمای کامل کانتینری‌سازی اپلیکیشن‌های وب — NOTES ×
AddressC:\DOCS\NOTES\داکر-برای-production-راهنمای-کامل-کانتینریسازی-اپلیکیشنهای-وب

داکر برای Production: راهنمای کامل کانتینری‌سازی اپلیکیشن‌های وب

۰۴/۰۱/۲۰۲۶۵ دقیقه مطالعهSepehr Mohseni

داکر شیوهٔ deploy کردن اپلیکیشن‌ها را متحول کرده است. این راهنمای جامع همه‌چیز را پوشش می‌دهد؛ از...

داکر شیوهٔ deploy کردن اپلیکیشن‌ها را متحول کرده است. این راهنمای جامع همه‌چیز را پوشش می‌دهد؛ از کانتینری‌سازی مقدماتی گرفته تا استقرارهای آمادهٔ production، همراه با بهترین شیوه‌های امنیتی و استراتژی‌های ارکستراسیون.

آشنایی با معماری داکر

پیش از پرداختن به پیکربندی‌های production، لازم است اجزای اصلی داکر را بشناسید:

  • Docker Engine - محیط اجرایی (runtime) که کانتینرها را می‌سازد و اجرا می‌کند
  • Images - قالب‌های فقط‌خواندنی که کد اپلیکیشن و وابستگی‌های آن را در بر دارند
  • Containers - نمونه‌های در حال اجرای ایمیج‌ها
  • Volumes - ذخیره‌سازی ماندگار داده‌ها
  • Networks - ارتباط میان کانتینرها

<Callout type="info"> کانتینرها کرنل سیستم‌عامل میزبان را به‌اشتراک می‌گذارند؛ به همین دلیل در مقایسه با ماشین‌های مجازی بسیار سبک‌ترند و در عین حال ایزوله‌سازی فرایندها را نیز فراهم می‌کنند. </Callout>

بیلدهای چندمرحله‌ای برای Production

بیلدهای چندمرحله‌ای (multi-stage) ایمیج‌های production کوچک‌تر و امن‌تری تولید می‌کنند:

اپلیکیشن Node.js

# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# Stage 2: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 3: Production
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production

# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy only necessary files
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./

USER nextjs

EXPOSE 3000

CMD ["node", "dist/main.js"]

اپلیکیشن PHP/Laravel

# Stage 1: Composer dependencies
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install \
    --no-dev \
    --no-scripts \
    --no-autoloader \
    --prefer-dist

COPY . .
RUN composer dump-autoload --optimize

# Stage 2: Frontend assets
FROM node:20-alpine AS frontend
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 3: Production image
FROM php:8.3-fpm-alpine AS production

# Install PHP extensions
RUN apk add --no-cache \
    libpng-dev \
    libzip-dev \
    && docker-php-ext-install \
    pdo_mysql \
    gd \
    zip \
    opcache

# Configure OPcache for production
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini \
    && echo "opcache.memory_consumption=256" >> /usr/local/etc/php/conf.d/opcache.ini \
    && echo "opcache.max_accelerated_files=20000" >> /usr/local/etc/php/conf.d/opcache.ini \
    && echo "opcache.validate_timestamps=0" >> /usr/local/etc/php/conf.d/opcache.ini

WORKDIR /var/www/html

# Copy application
COPY --from=vendor /app/vendor ./vendor
COPY --from=frontend /app/public/build ./public/build
COPY . .

# Set permissions
RUN chown -R www-data:www-data storage bootstrap/cache

USER www-data

EXPOSE 9000

CMD ["php-fpm"]

<Note title="نکتهٔ امنیتی"> در production همیشه کانتینرها را با کاربری غیر از root اجرا کنید. این کار آسیب احتمالی ناشی از container escape را محدود می‌کند. </Note>

Docker Compose برای محیط توسعه

یک محیط توسعهٔ کامل بسازید:

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myapp
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"

  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "80:80"
    depends_on:
      - app

volumes:
  postgres_data:
  redis_data:

Docker Compose برای Production

# docker-compose.prod.yml
version: '3.8'

services:
  app:
    image: ${REGISTRY}/myapp:${TAG:-latest}
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - app-network
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.prod.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    networks:
      - app-network

networks:
  app-network:
    driver: overlay

بهترین شیوه‌های امنیتی

اسکن ایمیج‌ها برای یافتن آسیب‌پذیری‌ها

# Using Docker Scout
docker scout cves myapp:latest

# Using Trivy
trivy image myapp:latest

# Using Snyk
snyk container test myapp:latest

شیوه‌های امن در نوشتن Dockerfile

# Use specific version tags, not 'latest'
FROM node:20.10.0-alpine3.18

# Don't store secrets in images
# Use build arguments for build-time secrets
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc \
    && npm ci \
    && rm .npmrc

# Use COPY instead of ADD (ADD can fetch remote URLs)
COPY package*.json ./

# Set proper file permissions
RUN chmod -R 755 /app

# Use read-only root filesystem where possible
# (configure in docker-compose or runtime)

# Don't run as root
USER node

# Use HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:3000/health || exit 1

امنیت در زمان اجرا

# docker-compose.secure.yml
services:
  app:
    image: myapp:latest
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
      - /var/run
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

شبکه‌بندی کانتینرها

شبکه‌های Bridge سفارشی

version: '3.8'

services:
  frontend:
    networks:
      - frontend-network
      - backend-network

  api:
    networks:
      - backend-network
      - database-network

  database:
    networks:
      - database-network

networks:
  frontend-network:
    driver: bridge
  backend-network:
    driver: bridge
    internal: true  # No external access
  database-network:
    driver: bridge
    internal: true

لاگ‌گیری و مانیتورینگ

لاگ‌گیری متمرکز با ELK Stack

version: '3.8'

services:
  app:
    logging:
      driver: gelf
      options:
        gelf-address: "udp://logstash:12201"
        tag: "myapp"

  elasticsearch:
    image: elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data

  logstash:
    image: logstash:8.11.0
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    ports:
      - "12201:12201/udp"

  kibana:
    image: kibana:8.11.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200

volumes:
  elasticsearch_data:

متریک‌های Prometheus

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'docker'
    static_configs:
      - targets: ['cadvisor:8080']

  - job_name: 'app'
    static_configs:
      - targets: ['app:3000']
    metrics_path: '/metrics'

یکپارچه‌سازی با CI/CD

پایپ‌لاین GitHub Actions

# .github/workflows/docker.yml
name: Build and Deploy

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    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=
            type=ref,event=branch
            type=semver,pattern={{version}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Scan for vulnerabilities
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          format: 'sarif'
          output: 'trivy-results.sarif'

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Deploy to production
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.PROD_HOST }}
          username: ${{ secrets.PROD_USER }}
          key: ${{ secrets.PROD_SSH_KEY }}
          script: |
            cd /opt/myapp
            docker compose pull
            docker compose up -d --remove-orphans
            docker image prune -f

بهینه‌سازی عملکرد

بهینه‌سازی cache در بیلد

# Order layers from least to most frequently changed
FROM node:20-alpine

WORKDIR /app

# System dependencies (rarely change)
RUN apk add --no-cache dumb-init

# Package files (change occasionally)
COPY package*.json ./
RUN npm ci --only=production

# Application code (changes frequently)
COPY . .

ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/main.js"]

محدودیت‌های منابع

services:
  app:
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M

جمع‌بندی

داکر امکان استقرار یکنواخت و تکرارپذیر را در محیط‌های مختلف فراهم می‌کند. با رعایت این بهترین شیوه‌های production — بیلدهای چندمرحله‌ای، سخت‌سازی امنیتی، شبکه‌بندی درست و یکپارچه‌سازی با CI/CD — می‌توانید اپلیکیشن‌های کانتینری مقاوم و آمادهٔ مقیاس‌پذیری بسازید.

نکات کلیدی:

  • برای داشتن ایمیج‌های کوچک‌تر و امن‌تر از بیلدهای چندمرحله‌ای استفاده کنید
  • در production هرگز کانتینرها را با کاربر root اجرا نکنید
  • health check و محدودیت‌های منابع را پیاده‌سازی کنید
  • ایمیج‌ها را به‌طور منظم از نظر آسیب‌پذیری اسکن کنید
  • از لاگ‌گیری و مانیتورینگ مناسب استفاده کنید

این یادداشت ترجمهٔ فارسی نوشتهٔ خودم است — نسخهٔ اصلی (انگلیسی) در dev.to

‹ برگشت به یادداشت‌ها

SEPEHR.SYS — همه‌چیز در همین مرورگر اجرا می‌شود.