This commit is contained in:
387
scripts/ci/build-production.sh
Executable file
387
scripts/ci/build-production.sh
Executable file
@@ -0,0 +1,387 @@
|
||||
#!/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/"
|
||||
Reference in New Issue
Block a user