# ð Deploy Web Application
Table of Contents
āđāļāļāđāļāđāļĨāļĒāļĩāļāļĩāđāđāļāđ
āđāļāļāļŠāļēāļĢāļāļĩāđāļāļ°āđāļāļ°āļāļģāļāļēāļĢ deploy web application āđāļāļĒāđāļāđāđāļāļāđāļāđāļĨāļĒāļĩāļāđāļāđāļāļāļĩāđ
Cloudflare: DNS Management & CDN - āļāļąāļāļāļēāļĢ domain name āđāļĨāļ°āđāļāļīāđāļĄāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļāđāļ§āđāļāđāļāļāđDigital Ocean: Cloud Server Provider - āđāļāļīāļĢāđāļāđāļ§āļāļĢāđāļŠāļģāļŦāļĢāļąāļ hosting applicationDocker: Containerization - āļāļąāļāļāļēāļĢāđāļĨāļ° package application āđāļŦāđāļāļĢāđāļāļĄ deployNginx: Web Server & Reverse Proxy - āļĢāļąāļ request āļāļēāļ user āđāļĨāļ°āļŠāđāļāļāđāļāđāļāļĒāļąāļ applicationSSL Certbot: SSL Certificate Management - āļāļąāļāļāļēāļĢāđāļāļĢāļąāļāļĢāļāļ SSL/TLS āļāļąāļāđāļāļĄāļąāļāļī
āļ āļēāļāļĢāļ§āļĄāļŠāļāļēāļāļąāļāļĒāļāļĢāļĢāļĄ
Container Architecture
āļĢāļ°āļāļāļāļ°āđāļĒāļ service āļāļāļāđāļāđāļ 3 containers āļŦāļĨāļąāļ
âââââââââââââââââââ âââââââââââââââââââ ââââââââââââââââââââ Nginx â â Web App â â Certbot ââ Container â â Container â â Container ââââââââââââââââââââĪ âââââââââââââââââââĪ âââââââââââââââââââĪâ âĒ Reverse Proxy â â âĒ React App â â âĒ SSL Certs ââ âĒ SSL Handling â â âĒ Built Assets â â âĒ Auto Renewal ââ âĒ Port 80/443 â â âĒ Static Files â â âĒ Background ââââââââââââââââââââ âââââââââââââââââââ âââââââââââââââââââNetwork Flow
Internet â Cloudflare â Nginx Container â Web App Container â Certbot Container (SSL Certs)āđāļāļĢāļāļŠāļĢāđāļēāļāđāļāļĨāđ
web-app/âââ .deployment/ # āđāļāļĨāđāļŠāļģāļŦāļĢāļąāļ deploymentâ âââ nginx/â â âââ web-app.conf # Nginx config āļŠāļģāļŦāļĢāļąāļ container āļ āļēāļĒāđāļâ â âââ web-app-nginx.conf # Nginx config āļŠāļģāļŦāļĢāļąāļ main serverâ âââ certbot-init.sh # Script āļŠāļģāļŦāļĢāļąāļāļāļ SSL āļāļĢāļąāđāļāđāļĢāļâ âââ certbot-renew.sh # Script āļŠāļģāļŦāļĢāļąāļāļāđāļāļāļēāļĒāļļ SSL (Manual)âââ docker-compose.yml # āļāļģāļŦāļāļāļāđāļē containersâââ Dockerfile # āļŠāļĢāđāļēāļ Docker imageâââ .gitlab-ci.yml # CI/CD pipeline configurationāļāļāļīāļāļēāļĒāđāļāļĨāđāļŠāļģāļāļąāļ:
web-app.conf: āļāļģāļŦāļāļāļāđāļē Nginx āļŠāļģāļŦāļĢāļąāļ serve static files āļ āļēāļĒāđāļ containerweb-app-nginx.conf: āļāļģāļŦāļāļāļāđāļē Nginx āļŦāļĨāļąāļāļŠāļģāļŦāļĢāļąāļāļĢāļąāļ traffic āļāļēāļ internetcertbot-init.sh: Script āļŠāļģāļŦāļĢāļąāļāļāļāđāļāļĢāļąāļāļĢāļāļ SSL āļāļĢāļąāđāļāđāļĢāļāļāļēāļ Letâs Encryptcertbot-renew.sh: Script āļŠāļģāļŦāļĢāļąāļāļāđāļāļāļēāļĒāļļāđāļāļĢāļąāļāļĢāļāļ SSL āļāļąāļāđāļāļĄāļąāļāļī
āļāļąāđāļāļāļāļāļāļēāļĢāļāļīāļāļāļąāđāļ
1ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 1: āļŠāļĢāđāļēāļ Dockerfile
āļŠāļĢāđāļēāļāđāļāļĨāđ Dockerfile āđāļāļ·āđāļ build Docker image āļāļāļ web application (React)
# Build Stage - āļŠāļĢāđāļēāļ applicationFROM node:18-alpine as build
# āļāļīāļāļāļąāđāļ pnpm package managerRUN npm install -g pnpm
WORKDIR /app
# āļāļģāļŦāļāļ environment variable āļŠāļģāļŦāļĢāļąāļ API URLARG VITE_API_URLENV VITE_API_URL=${VITE_API_URL:-http://localhost/api}
# Copy dependency files āđāļĨāļ°āļāļīāļāļāļąāđāļ packagesCOPY package.json pnpm-lock.yaml ./RUN pnpm install --frozen-lockfile
# Copy source code āđāļĨāļ° build applicationCOPY . .RUN pnpm build
# Production Stage - āļŠāļĢāđāļēāļ production containerFROM nginx:alpine
# Copy built files āđāļāļĒāļąāļ nginx directoryCOPY --from=build /app/dist /usr/share/nginx/html
# Copy nginx configurationCOPY .deployment/nginx/web-app.conf /etc/nginx/conf.d/default.conf
EXPOSE 80CMD ["nginx", "-g", "daemon off;"]āļāļģāļāļāļīāļāļēāļĒ:
- Multi-stage build: āđāļĒāļ build stage āđāļĨāļ° production stage āđāļāļ·āđāļāļĨāļāļāļāļēāļ image
- VITE_API_URL: āļāļąāļ§āđāļāļĢāļŠāļģāļŦāļĢāļąāļāļāļģāļŦāļāļ URL āļāļāļ API backend
- nginx:alpine : āđāļāđ Nginx āļāļ Alpine Linux āđāļāļ·āđāļāļāļāļēāļāđāļĨāđāļāđāļĨāļ°āļĢāļ§āļāđāļĢāđāļ§
2ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 2: āļŠāļĢāđāļēāļ Docker Compose Configuration
āļŠāļĢāđāļēāļāđāļāļĨāđ docker-compose.yml āđāļāļ·āđāļāļāļģāļŦāļāļāļāđāļē services āļāļąāđāļāļŦāļĄāļ:
version: '3.8'
services: # Main Nginx Server - āļĢāļąāļ traffic āļāļēāļ internet nginx: image: nginx:latest container_name: web-app-nginx ports: - '80:80' # HTTP - '443:443' # HTTPS volumes: - ./.deployment/nginx/web-app-nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./.deployment/ssl:/etc/nginx/ssl:ro - certbot-webroot:/var/www/certbot:ro networks: - web-app-network restart: unless-stopped depends_on: - web-app - certbot
# Web Application Container web-app: image: ${IMAGE_TAG:-your-registry/web-app:latest} container_name: web-app networks: - web-app-network environment: - VITE_API_URL=${VITE_API_URL} restart: unless-stopped depends_on: - certbot
# SSL Certificate Management certbot: image: certbot/certbot:latest container_name: web-app-certbot volumes: - ./ssl:/etc/letsencrypt - certbot-webroot:/var/www/certbot - ./certbot-logs:/var/log/letsencrypt entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet; sleep 12h & wait $${!}; done;'" networks: - web-app-network restart: unless-stopped
volumes: certbot-webroot: driver: local
networks: web-app-network: driver: bridgeāļāļģāļāļāļīāļāļēāļĒ Services:
- nginx: Main reverse proxy server āļāļĩāđāļĢāļąāļ traffic āļāļēāļ internet
- web-app: Container āļāļĩāđ run React application
- certbot: āļāļąāļāļāļēāļĢāđāļāļĢāļąāļāļĢāļāļ SSL āđāļĨāļ°āļāđāļāļāļēāļĒāļļāļāļąāļāđāļāļĄāļąāļāļīāļāļļāļ 12 āļāļąāđāļ§āđāļĄāļāļāđāļēāļ cron job
3ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 3: āļāļģāļŦāļāļāļāđāļē Nginx āļŠāļģāļŦāļĢāļąāļ Web App Container
āļŠāļĢāđāļēāļāđāļāļĨāđ .deployment/nginx/web-app.conf:
server { listen 80; server_name _;
# āļāļģāļŦāļāļāļāļģāđāļŦāļāđāļāđāļāļĨāđ static root /usr/share/nginx/html; index index.html index.htm;
# āļāļģāļŦāļāļāļāļēāļĢāļāļąāļāļāļēāļĢ routing āļŠāļģāļŦāļĢāļąāļ SPA location / { # āļāļĒāļēāļĒāļēāļĄāļŦāļēāđāļāļĨāđāļāļēāļĄāļĨāļģāļāļąāļ āļāđāļēāđāļĄāđāļĄāļĩāđāļŦāđ fallback āđāļāļāļĩāđ index.html try_files $uri $uri/ /index.html; }}āļāļģāļāļāļīāļāļēāļĒ:
- try_files: āļŠāļģāļāļąāļāļŠāļģāļŦāļĢāļąāļ Single Page Application āđāļāļ·āđāļāđāļŦāđ routing āļāļāļ React āļāļģāļāļēāļāđāļāđāļāļđāļāļāđāļāļ
- server_name _: āļĢāļąāļ request āļāļēāļāļāļļāļ hostname
4ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 4: āļāļģāļŦāļāļāļāđāļē Nginx āļŦāļĨāļąāļāļāļĢāđāļāļĄ SSL
āļŠāļĢāđāļēāļāđāļāļĨāđ .deployment/nginx/web-app-nginx.conf:
# HTTP Server - Redirect āđāļ HTTPSserver { listen 80; server_name your-domain.com;
# āđāļŦāđ Certbot āđāļāđāļŠāļģāļŦāļĢāļąāļ verification location /.well-known/acme-challenge/ { root /var/www/certbot; }
# Redirect HTTP traffic āđāļ HTTPS location / { return 301 https://$host$request_uri; }}
# HTTPS Server - Main Applicationserver { listen 443 ssl http2; server_name your-domain.com;
# SSL Certificate Configuration ssl_certificate /etc/nginx/ssl/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/live/your-domain.com/privkey.pem;
# SSL Security Settings ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off;
# Proxy Headers āļŠāļģāļŦāļĢāļąāļ backend communication proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
# Forward requests āđāļāļĒāļąāļ web-app container location / { proxy_pass http://web-app; }
# Error Page Handling error_page 404 /index.html; error_page 500 502 503 504 /index.html;
# Security Headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;}āļāļģāļāļāļīāļāļēāļĒāļāļēāļĢāļāļģāļŦāļāļāļāđāļē:
- HTTP to HTTPS redirect: āļāļąāļāļāļąāļāđāļŦāđāđāļāđ HTTPS āđāļŠāļĄāļ
- SSL Security: āļāļģāļŦāļāļāļāđāļēāļāļ§āļēāļĄāļāļĨāļāļāļ āļąāļĒāļāļēāļĄ best practices
- Proxy Pass: āļŠāđāļ request āđāļāļĒāļąāļ web-app container
- Security Headers: āđāļāļīāđāļĄāļāļ§āļēāļĄāļāļĨāļāļāļ āļąāļĒāđāļŦāđāđāļ§āđāļāđāļāļāđ
5ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 5: āļŠāļĢāđāļēāļ Script āļŠāļģāļŦāļĢāļąāļāļāļ SSL Certificate āļāļĢāļąāđāļāđāļĢāļ
āļŠāļĢāđāļēāļāđāļāļĨāđ .deployment/certbot-init.sh:
#!/bin/sh
# āļĢāļąāļ parameters āļāļēāļ command lineDOMAIN=$1EMAIL=$2
# â
āļāļĢāļ§āļāļŠāļāļāļ§āđāļēāļĄāļĩ parameters āļāļĢāļāļŦāļĢāļ·āļāđāļĄāđif [ -z "$DOMAIN" ] || [ -z "$EMAIL" ]; then echo "Usage: ./certbot-init.sh <domain> <email>" exit 1fi
echo "ð Obtaining SSL certificate for $DOMAIN..."echo "ð§ Notification email: $EMAIL"
echo "â ïļ Make sure to stop any services using port 80 first"echo " Run: docker compose down"echo ""
# ð āļĢāļąāļ Certbot āđāļāļ·āđāļāļāļāđāļāļĢāļąāļāļĢāļāļ SSLdocker run -it --rm --name certbot \ -v "$(pwd)/ssl:/etc/letsencrypt" \ -v "$(pwd)/ssl-var:/var/lib/letsencrypt" \ -p 80:80 \ certbot/certbot certonly \ --standalone \ --email $EMAIL \ --agree-tos \ --no-eff-email \ -d $DOMAIN
# â
āļāļĢāļ§āļāļŠāļāļāļāļĨāļāļēāļĢāļāļģāļāļēāļif [ $? -eq 0 ]; then echo "â
SSL certificate obtained successfully!" echo "ð Certificate location: ./ssl/live/$DOMAIN/" echo "" echo "ð Now you can start the services:" echo " docker compose up -d"else echo "â Failed to obtain SSL certificate" echo "ðĄ Make sure:" echo " - Domain $DOMAIN points to this server" echo " - Port 80 is accessible from internet" echo " - No other service is using port 80" exit 1fiāļāļģāļāļāļīāļāļēāļĒ Script:
- standalone mode: āđāļāđ Certbot āđāļāđāļŦāļĄāļāļāļĩāđāđāļĄāđāļāđāļāļāļāļķāđāļ web server āļāļ·āđāļ
- port 80: Certbot āļāđāļāļāļāļēāļĢāđāļāđ port 80 āđāļāļ·āđāļ verify domain ownership
- volume mapping: āđāļāđāļāđāļāļĢāļąāļāļĢāļāļāđāļāđāļāļĨāđāļāļāļĢāđ local āđāļāļ·āđāļāđāļāđāļāļēāļāļāđāļāđāļ
6ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 6: āļŠāļĢāđāļēāļ Script āļŠāļģāļŦāļĢāļąāļāļāđāļāļāļēāļĒāļļ SSL Certificate (āļāļĢāļāļĩ Manual)
āļŠāļĢāđāļēāļāđāļāļĨāđ .deployment/certbot-renew.sh:
#!/bin/sh
echo "ð Manually renewing SSL certificates..."
# ð āļŠāļąāđāļāļāđāļāļāļēāļĒāļļāđāļāļĢāļąāļāļĢāļāļ SSLdocker compose run --rm certbot renew
# â
āļāļĢāļ§āļāļŠāļāļāļāļĨāļāļēāļĢāļāļģāļāļēāļif [ $? -eq 0 ]; then echo "â
Certificate renewal successful!" echo "ð Restarting nginx to apply changes..." # āļĢāļĩāļŠāļāļēāļĢāđāļ nginx āđāļāļ·āđāļāđāļāđāđāļāļĢāļąāļāļĢāļāļāđāļŦāļĄāđ docker compose restart bacc-web-uielse echo "â Certificate renewal failed" exit 1fiāļŦāļĄāļēāļĒāđāļŦāļāļļ:
- Certbot āļāļ°āļāđāļāļāļēāļĒāļļāđāļŦāđāļāļąāļāđāļāļĄāļąāļāļīāđāļāļĒāđāļĄāđāļāđāļāļāļāļąāđāļāļāđāļēāđāļāļīāđāļĄāđāļāļīāļĄ
- Script āļāļĩāđāđāļāđāļŠāļģāļŦāļĢāļąāļāļāđāļāļāļēāļĒāļļāļāđāļ§āļĒāļāļāđāļāļ (manual renewal)
7ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 7: āđāļāļĢāļĩāļĒāļĄāļāļ§āļēāļĄāļāļĢāđāļāļĄāļŠāļģāļŦāļĢāļąāļ GitLab CI/CD
āļāđāļāļāļŠāļĢāđāļēāļāđāļāļĨāđ .gitlab-ci.yml āļāđāļāļāđāļāļĢāļĩāļĒāļĄāļāļēāļĢāļāļąāļāļāļĩāđ:
āļāļąāļ§āđāļāļĢ CI/CD āđāļ GitLab (Settings > CI/CD > Variables):
CI_REGISTRY_USER: Username āļŠāļģāļŦāļĢāļąāļ Docker RegistryCI_REGISTRY_PASSWORD: Password āļŠāļģāļŦāļĢāļąāļ Docker RegistrySSH_PRIVATE_KEY: Private Key āļŠāļģāļŦāļĢāļąāļāđāļāļ·āđāļāļĄāļāđāļ serverDEPLOY_HOST: IP Address āļŦāļĢāļ·āļ hostname āļāļāļ serverDEPLOY_USER: Username āļŠāļģāļŦāļĢāļąāļ SSH āđāļāđāļē serverDEPLOY_PATH: Path āļāļ server āļŠāļģāļŦāļĢāļąāļ deploy app
āļāļēāļĢāļŠāļĢāđāļēāļ SSH Key:
# āđāļāļ·āđāļāļĄāļāđāļāđāļāđāļē serverssh DEPLOY_USER@DEPLOYHOSE
# āđāļāđāļēāđāļāļāļĩāđ .sshcd .ssh
# runssh-keygen
#1 Copy public key āđāļāđāļŠāđāđāļ authorized_keys#2 Copy private key āđāļāđāļŠāđāđāļ GitLab CI/CD VariablesāļŠāļĢāđāļēāļāđāļāļĨāđ .gitlab-ci.yml:
stages: - build - deploy
variables: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: '/certs' DOCKER_IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA DOCKER_IMAGE_LATEST: $CI_REGISTRY_IMAGE:latest
# ðĻ Build Stage - āļŠāļĢāđāļēāļ Docker Imagebuild:docker: stage: build image: docker:24.0.5 services: - docker:24.0.5-dind before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - | echo "Building Docker image: $DOCKER_IMAGE_TAG" docker build --tag $DOCKER_IMAGE_TAG --tag $DOCKER_IMAGE_LATEST . echo "Pushing Docker image..." docker push $DOCKER_IMAGE_TAG if [ "$CI_COMMIT_BRANCH" == "main" ]; then echo "Pushing latest tag..." docker push $DOCKER_IMAGE_LATEST fi after_script: - docker logout only: - main - tags
# ð Deploy Stage - Deploy āđāļāļĒāļąāļ Productiondeploy:production: stage: deploy image: docker:24.0.5 services: - docker:24.0.5-dind environment: name: production url: $PRODUCTION_URL before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - | # āļāļīāļāļāļąāđāļ SSH client apk add --no-cache openssh-client mkdir -p ~/.ssh echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H $DEPLOY_HOST >> ~/.ssh/known_hosts script: - | echo "Deploying $DOCKER_IMAGE_TAG to production..." echo "Image: $DOCKER_IMAGE_TAG"
# Copy docker-compose.yml āđāļāļĒāļąāļ server echo "Copying docker-compose.yml to $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH..." scp docker-compose.yml $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/docker-compose.yml
# Copy .deployment directory āđāļāļĒāļąāļ server if [ -d ".deployment" ]; then echo "Copying .deployment directory to $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/.deployment..." ssh $DEPLOY_USER@$DEPLOY_HOST "mkdir -p $DEPLOY_PATH/.deployment" scp -r .deployment/* $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/.deployment/ fi
# āļāļąāđāļāļāđāļē permissions āļŠāļģāļŦāļĢāļąāļāđāļāļĨāđ deployment echo "Setting permissions for .deployment files..." ssh $DEPLOY_USER@$DEPLOY_HOST " cd $DEPLOY_PATH && \ chmod +x .deployment/certbot-init.sh .deployment/certbot-renew.sh && \ for file in .deployment/certbot-init.sh .deployment/nginx/web-app-nginx.conf .deployment/nginx/web-app.conf; do if [ ! -f \"\$file\" ]; then echo \"ERROR: \$file is not a regular file!\" exit 1 fi done && \ echo 'â All .deployment files verified as regular files' "
# Deploy application āļāļ server ssh $DEPLOY_USER@$DEPLOY_HOST " cd $DEPLOY_PATH && \ echo 'Logging into Docker registry...' && \ echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY && \ export IMAGE_TAG=$DOCKER_IMAGE_TAG && \ export DOMAIN=${DOMAIN:-localhost} && \ echo 'Pulling Docker images...' && \ docker compose -f docker-compose.yml pull && \ echo 'Starting containers...' && \ docker compose -f docker-compose.yml up -d && \ docker compose -f docker-compose.yml restart nginx && \ docker compose -f docker-compose.yml ps && \ docker logout $CI_REGISTRY "
echo "â Deployment completed successfully" after_script: - docker logout only: - main - tags when: manual # āļāđāļāļāļāļāļāļļāđāļĄ Deploy āļāđāļ§āļĒāļāļāđāļāļāļāļģāļāļāļīāļāļēāļĒ Pipeline:
- Build Stage: āļŠāļĢāđāļēāļ Docker image āđāļĨāļ° push āđāļāļĒāļąāļ registry
- Deploy Stage: Download image āđāļĨāļ°āļĢāļąāļ containers āļāļ production server
- Manual Deployment: āļāđāļāļāļāļąāļāļāļēāļĢ deploy āļāļąāļāđāļāļĄāļąāļāļīāđāļāļĒāđāļĄāđāļāļąāđāļāđāļ
8ïļâĢ āļāļąāđāļāļāļāļāļāļĩāđ 8: Initial Deployment
āđāļĄāļ·āđāļāļāļļāļāļāļĒāđāļēāļāđāļāļĢāļĩāļĒāļĄāļāļĢāđāļāļĄāđāļĨāđāļ§ āđāļŦāđāļāļģāļāļēāļĢ ð deploy āļāļĢāļąāđāļāđāļĢāļāļāļąāļāļāļĩāđ:
āđāļāļĢāļĩāļĒāļĄ Server Environment:
# āđāļāļ·āđāļāļĄāļāđāļāđāļāđāļē serverssh DEPLOY_USER@DEPLOYHOSE
# āļŠāļĢāđāļēāļāđāļāļĨāđāļāļāļĢāđāļŠāļģāļŦāļĢāļąāļ application /var/www/web-appmkdir -p DEPLOY_PATHāļāļ SSL Certificate āļāļĢāļąāđāļāđāļĢāļ āļāļģāđāļāđ 2 āđāļāļ:
āđāļāļāļāļĩāđ 1
-
āļāļģāđāļāļĨāđ
.deploymentāļĄāļēāļāļĩāđ serverDEPLOY_PATHāļāđāļāļ (āļāđāļēāļscpāļŦāļĢāļ·āļgit clone) -
āļāļąāđāļāļāđāļē permissions:
Terminal window chmod +x .deployment/certbot-init.sh -
āļĢāļąāļ script
certbot:Terminal window -
āļāļĢāļ§āļāļŠāļāļāļ§āđāļēāđāļāļĨāđāļāļāļĢāđ
sslāđāļĨāļ°ssl-varāļāļđāļāļŠāļĢāđāļēāļāļāļķāđāļāļŦāļĢāļ·āļāđāļĄāđ:Terminal window ls -
āļĢāļąāļ pipeline
deploy:productionāļāļĩāđ GitLab -
āđāļŠāļĢāđāļāļŠāļīāđāļ â
āđāļāļāļāļĩāđ 2 (āđāļāđ GitLab CI/CD pipeline)
- āļĢāļąāļ pipeline
deploy:productionāļāļĩāđ GitLab āļāđāļāļ - āđāļāđāļēāđāļāđāļ server:
Terminal window cd DEPLOY_PATH/.deployment - āļĢāļąāļ script
certbot:Terminal window - āļĢāļąāļ pipeline
deploy:productionāļāļĩāđ GitLab āļāļĩāļāļāļĢāļąāđāļ - āđāļŠāļĢāđāļāļŠāļīāđāļ â
āļŦāļĄāļēāļĒāđāļŦāļāļļ: āļāļēāļĢāļāļ SSL Certificate āđāļāļāļāļĩāđ 2 āļāļ°āđāļāđāļĢāļąāļāļŠāļīāļāļāļīāđ execute āļāļąāļāđāļāļĄāļąāļāļīāļāļēāļ CI/CD
â Deploy āļŠāļģāđāļĢāđāļāđāļĨāđāļ§āļ§āļ§āļ§āļ§ âĪïļðŧ
- āđāļāđāļē website
â ïļ āļŦāļĄāļēāļĒāđāļŦāļāļļāļŠāļģāļāļąāļ:
āļāđāļāļāļģāļŦāļāļāļāđāļāļ Deploy:
-
â Server Setup:
Terminal window # āļāļąāļāđāļāļāļĢāļ°āļāļāđāļŦāđāđāļāđāļāđāļ§āļāļĢāđāļāļąāđāļāļĨāđāļēāļŠāļļāļ on Debiansudo apt updatesudo apt upgrade -y# āļāļīāļāļāļąāđāļ packages āļāļ·āđāļāļāļēāļāļāļĩāđāļāļģāđāļāđāļsudo apt install -y curl wget git vim nano htop unzip software-properties-common apt-transport-https ca-certificates gnupg lsb-release# āļĨāļ Docker āđāļ§āļāļĢāđāļāļąāļāđāļāđāļē (āļāđāļēāļĄāļĩ)sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-doc podman-docker containerd runc | cut -f1)# āđāļāļīāđāļĄ GPG Key āļāļāļ Dockersudo mkdir -p /etc/apt/keyringscurl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpgsudo chmod a+r /etc/apt/keyrings/docker.gpg# āļāļąāļāđāļāļ indexsudo apt update# āļāļīāļāļāļąāđāļāđāļāđāļāđāļāļ Docker āļĨāđāļēāļŠāļļāļsudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin# āļāļđāđāļ§āļāļĢāđāļāļąāļdocker --versiondocker compose version# start dockersudo systemctl start docker -
â Domain Name: āļāđāļāļāļāļĩāđ A record āđāļāļĒāļąāļ IP āļāļāļ server
-
â Port Access: āđāļāļīāļ port 80 āđāļĨāļ° 443 āđāļ firewall
-
â Docker Installation: āļāļīāļāļāļąāđāļ Docker āđāļĨāļ° Docker Compose āļāļ server (āļāļđāļāļąāđāļāļāļāļāļāđāļēāļāļāļ)
-
â SSL Certificate: āļāļāđāļāļĢāļąāļāļĢāļāļāļāđāļāļ enable HTTPS
āļāļģāļŠāļąāđāļāļāļĩāđāļĄāļĩāļāļĢāļ°āđāļĒāļāļāđ
# āļāļđāđāļāļĨāđāđāļ containersdocker compose exec -it nginx shdocker compose exec -it web-app shdocker compose exec -it certbot sh
# āļāļđ logs āļāļāļ containersdocker compose logs -f nginxdocker compose logs -f web-appdocker compose logs -f certbotdocker compose logs
# āļĢāļĩāļŠāļāļēāļĢāđāļ specific containerdocker compose restart nginxdocker compose restart web-app
# āļāļąāļāđāļāļ containers āļāđāļ§āļĒ image āđāļŦāļĄāđdocker compose pulldocker compose up -d
# āļāļĢāļ§āļāļŠāļāļ SSL certificateopenssl x509 -in ssl/live/your-domain.com/fullchain.pem -text -noout
# āļāļāļŠāļāļāļāļēāļĢāļāđāļāļāļēāļĒāļļ SSL (dry run)docker compose run --rm certbot renew --dry-runāļāļēāļĢ Monitor āđāļĨāļ° Maintenance
1. Log Monitoring
# āļāļīāļāļāļēāļĄ logs āđāļāļ real-timedocker compose logs -f
# āļāļđ logs āļāļāļ nginx accessdocker compose exec nginx tail -f /var/log/nginx/access.log
# āļāļĢāļ§āļāļŠāļāļ disk usagedf -hdu -sh ssl/2. SSL Certificate Management
# āļāļĢāļ§āļāļŠāļāļāļ§āļąāļāļŦāļĄāļāļāļēāļĒāļļdocker compose run --rm certbot certificates
# āļāđāļāļāļēāļĒāļļāļāđāļ§āļĒāļāļāđāļāļ./.deployment/certbot-renew.sh
# āļāļāļŠāļāļāļāļēāļĢāļāđāļāļāļēāļĒāļļdocker compose run --rm certbot renew --dry-run3. Backup Strategy
# āļŠāļģāļĢāļāļāļāđāļāļĄāļđāļĨāļŠāļģāļāļąāļtar -czf backup-$(date +%Y%m%d).tar.gz ssl/ docker-compose.yml .deployment/
# Restoretar -xzf backup-20241126.tar.gzDocument
- Docker Documentation
- Nginx Configuration Guide
- Letâs Encrypt Documentation
- GitLab CI/CD Documentation
āļāļēāļĢāļāļīāļāļāđāļāđāļĨāļ°āļŠāļāļąāļāļŠāļāļļāļ
āļŦāļēāļāļāļāļāļąāļāļŦāļēāđāļāļāļēāļĢ deploy āļŠāļēāļĄāļēāļĢāļāļāļĢāļ§āļāļŠāļāļāđāļāđāļāļēāļ:
- Container Logs:
docker compose logs - Nginx Logs: āđāļ container āļŦāļĢāļ·āļ host system
- SSL Status:
docker compose run --rm certbot certificates - Network Connectivity:
curl -I https://your-domain.com
āļ§āļąāļāļāļĩāđāļāļąāļāđāļāļāļĨāđāļēāļŠāļļāļ: 26 āļāļĪāļĻāļāļīāļāļēāļĒāļ 2025