#!/bin/bash # scripts/ci/build-production.sh - Сборка продакшен образов set -e echo "🚀 Building production images..." # Переменные REGISTRY=${DOCKER_REGISTRY:-"registry.hub.docker.com"} PROJECT_NAME="catlink" VERSION=${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}} BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") VCS_REF=${DRONE_COMMIT_SHA:-$(git rev-parse HEAD)} echo "📋 Build information:" echo " • Registry: $REGISTRY" echo " • Project: $PROJECT_NAME" echo " • Version: $VERSION" echo " • Build Date: $BUILD_DATE" echo " • VCS Ref: $VCS_REF" # Создание production .env echo "⚙️ Creating production environment configuration..." cat > .env.production << EOF # Production Environment Configuration NODE_ENV=production DJANGO_ENV=production DEBUG=False # Security SECRET_KEY=\${SECRET_KEY} ALLOWED_HOSTS=\${ALLOWED_HOSTS} CORS_ALLOWED_ORIGINS=\${CORS_ALLOWED_ORIGINS} # Database DATABASE_URL=\${DATABASE_URL} # Media and Static STATIC_URL=/static/ MEDIA_URL=/media/ STATIC_ROOT=/app/staticfiles MEDIA_ROOT=/app/media # Build info BUILD_VERSION=$VERSION BUILD_DATE=$BUILD_DATE VCS_REF=$VCS_REF EOF # Создание production docker-compose файла echo "🐳 Creating production Docker Compose configuration..." cat > docker-compose.production.yml << EOF version: '3.8' services: web: build: context: ./backend dockerfile: Dockerfile args: BUILD_VERSION: $VERSION BUILD_DATE: $BUILD_DATE VCS_REF: $VCS_REF image: $REGISTRY/$PROJECT_NAME-backend:$VERSION environment: - DJANGO_ENV=production - DEBUG=False restart: unless-stopped labels: - "traefik.enable=true" - "traefik.http.routers.catlink-api.rule=Host(\`api.catlink.dev\`)" - "traefik.http.services.catlink-api.loadbalancer.server.port=8000" frontend: build: context: ./frontend/linktree-frontend dockerfile: Dockerfile args: BUILD_VERSION: $VERSION BUILD_DATE: $BUILD_DATE VCS_REF: $VCS_REF NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL} image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION environment: - NODE_ENV=production restart: unless-stopped labels: - "traefik.enable=true" - "traefik.http.routers.catlink.rule=Host(\`catlink.dev\`)" - "traefik.http.services.catlink.loadbalancer.server.port=3000" networks: default: external: name: traefik_default EOF # Обновление Dockerfile для production echo "📝 Updating Dockerfiles for production..." # Backend Dockerfile cat > backend/Dockerfile.production << EOF FROM python:3.11-slim as builder # Build arguments ARG BUILD_VERSION=latest ARG BUILD_DATE ARG VCS_REF # Metadata LABEL org.opencontainers.image.title="CatLink Backend" LABEL org.opencontainers.image.description="CatLink Django API" LABEL org.opencontainers.image.version="\$BUILD_VERSION" LABEL org.opencontainers.image.created="\$BUILD_DATE" LABEL org.opencontainers.image.revision="\$VCS_REF" LABEL org.opencontainers.image.source="https://github.com/smartsoltech/links" # Install system dependencies RUN apt-get update && apt-get install -y \\ gcc \\ postgresql-client \\ && rm -rf /var/lib/apt/lists/* # Set work directory WORKDIR /app # Install Python dependencies COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy project COPY . . # Create directories RUN mkdir -p staticfiles media # Collect static files RUN python manage.py collectstatic --noinput # Create non-root user RUN useradd --create-home --shell /bin/bash app \\ && chown -R app:app /app USER app # Health check HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \\ CMD curl -f http://localhost:8000/api/ || exit 1 EXPOSE 8000 CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "backend.wsgi:application"] EOF # Frontend Dockerfile cat > frontend/linktree-frontend/Dockerfile.production << EOF FROM node:20-alpine as builder # Build arguments ARG BUILD_VERSION=latest ARG BUILD_DATE ARG VCS_REF ARG NEXT_PUBLIC_API_URL # Metadata LABEL org.opencontainers.image.title="CatLink Frontend" LABEL org.opencontainers.image.description="CatLink Next.js Application" LABEL org.opencontainers.image.version="\$BUILD_VERSION" LABEL org.opencontainers.image.created="\$BUILD_DATE" LABEL org.opencontainers.image.revision="\$VCS_REF" LABEL org.opencontainers.image.source="https://github.com/smartsoltech/links" WORKDIR /app # Copy package files COPY package*.json ./ # Install dependencies RUN npm ci --only=production # Copy source code COPY . . # Set build environment ENV NODE_ENV=production ENV NEXT_PUBLIC_API_URL=\$NEXT_PUBLIC_API_URL ENV BUILD_VERSION=\$BUILD_VERSION # Build application RUN npm run build # Production stage FROM node:20-alpine as runner WORKDIR /app # Create non-root user RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs # Copy built application 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 # Health check HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \\ CMD curl -f http://localhost:3000 || exit 1 EXPOSE 3000 ENV PORT 3000 ENV HOSTNAME "0.0.0.0" CMD ["node", "server.js"] EOF # Сборка продакшен образов echo "🔨 Building production Docker images..." # Backend echo " • Building backend production image..." docker build -f backend/Dockerfile.production \\ --build-arg BUILD_VERSION="$VERSION" \\ --build-arg BUILD_DATE="$BUILD_DATE" \\ --build-arg VCS_REF="$VCS_REF" \\ -t "$REGISTRY/$PROJECT_NAME-backend:$VERSION" \\ -t "$REGISTRY/$PROJECT_NAME-backend:latest" \\ backend/ # Frontend echo " • Building frontend production image..." docker build -f frontend/linktree-frontend/Dockerfile.production \\ --build-arg BUILD_VERSION="$VERSION" \\ --build-arg BUILD_DATE="$BUILD_DATE" \\ --build-arg VCS_REF="$VCS_REF" \\ --build-arg NEXT_PUBLIC_API_URL="$NEXT_PUBLIC_API_URL" \\ -t "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" \\ -t "$REGISTRY/$PROJECT_NAME-frontend:latest" \\ frontend/linktree-frontend/ # Проверка размеров образов echo "📊 Production image sizes:" docker images | grep "$PROJECT_NAME" | grep -E "($VERSION|latest)" # Сканирование образов на уязвимости (если доступно) echo "🔍 Scanning production images..." for image in "$PROJECT_NAME-backend:$VERSION" "$PROJECT_NAME-frontend:$VERSION"; do echo " • Scanning $image..." docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \\ aquasec/trivy image --exit-code 0 --severity HIGH,CRITICAL "$REGISTRY/$image" || \\ echo " ⚠️ Vulnerability scan completed with findings" done # Тестирование продакшен образов echo "🧪 Testing production images..." # Создание тестового docker-compose для продакшен образов cat > docker-compose.test-prod.yml << EOF version: '3.8' services: web-prod: image: $REGISTRY/$PROJECT_NAME-backend:$VERSION environment: - DJANGO_ENV=production - DEBUG=False - SECRET_KEY=test-secret-key - DATABASE_URL=sqlite:///db.sqlite3 - ALLOWED_HOSTS=localhost,127.0.0.1 ports: - "8001:8000" frontend-prod: image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION environment: - NODE_ENV=production - NEXT_PUBLIC_API_URL=http://localhost:8001 ports: - "3001:3000" depends_on: - web-prod EOF # Запуск тестов продакшен образов echo " • Starting production containers for testing..." docker-compose -f docker-compose.test-prod.yml up -d sleep 30 # Проверка здоровья backend_health=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8001/api/ || echo "000") frontend_health=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3001 || echo "000") echo " • Backend health: $backend_health" echo " • Frontend health: $frontend_health" # Очистка тестовых контейнеров docker-compose -f docker-compose.test-prod.yml down if [ "$backend_health" = "200" ] && [ "$frontend_health" = "200" ]; then echo "✅ Production images tested successfully!" else echo "❌ Production image testing failed" exit 1 fi # Создание манифестов для деплоя echo "📄 Creating deployment manifests..." mkdir -p /tmp/deploy-manifests # Kubernetes манифесты cat > /tmp/deploy-manifests/catlink-k8s.yml << EOF apiVersion: apps/v1 kind: Deployment metadata: name: catlink-backend labels: app: catlink-backend version: $VERSION spec: replicas: 2 selector: matchLabels: app: catlink-backend template: metadata: labels: app: catlink-backend version: $VERSION spec: containers: - name: backend image: $REGISTRY/$PROJECT_NAME-backend:$VERSION ports: - containerPort: 8000 env: - name: DJANGO_ENV value: "production" resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" --- apiVersion: apps/v1 kind: Deployment metadata: name: catlink-frontend labels: app: catlink-frontend version: $VERSION spec: replicas: 2 selector: matchLabels: app: catlink-frontend template: metadata: labels: app: catlink-frontend version: $VERSION spec: containers: - name: frontend image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION ports: - containerPort: 3000 env: - name: NODE_ENV value: "production" resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" EOF echo "✅ Production build completed successfully!" echo "📦 Images built:" echo " • $REGISTRY/$PROJECT_NAME-backend:$VERSION" echo " • $REGISTRY/$PROJECT_NAME-frontend:$VERSION" echo "📁 Deployment manifests: /tmp/deploy-manifests/"