EC2 + Docker 기반 Jenkins, Nginx, Certbot, Unity CLI, Firebase App Distribution, Slack 연동 완전 자동화 구성

✅ 전체 아키텍처 흐름

GitHub → Webhook → Nginx (443/80) → Jenkins (내부 8080) → Unity Build & Test → APK 생성 → Firebase 배포 → Slack 알림 + 테스터 관리

📁 디렉토리 구조

infra/
├── docker-compose.yml
├── .env
├── jenkins/
│   └── jenkins_home/
├── nginx/
│   ├── nginx.conf
│   ├── ssl/
│   └── healthcheck.conf
├── certbot/
│   └── init-letsencrypt.sh
scripts/
├── build.sh
├── test.sh
├── deploy.sh
├── notify.sh
├── manage-testers.sh
unity-project/
└── BuildScript.cs

✅ docker-compose.yml (보안 반영)

version: '3.8'

services:
  jenkins:
    image: jenkins/jenkins:2.504.2-lts
    container_name: jenkins
    restart: unless-stopped
    expose:
      - "8080"
    networks:
      - backend
    volumes:
      - ./jenkins/jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock

  nginx:
    image: nginx:stable
    container_name: nginx
    restart: unless-stopped
    depends_on:
      - jenkins
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/healthcheck.conf:/etc/nginx/conf.d/healthcheck.conf
      - ./nginx/ssl:/etc/letsencrypt
      - ./nginx/ssl:/var/www/certbot
    healthcheck:
      test: ["CMD", "curl", "-f", "<http://localhost/health>"]
      interval: 10s
      retries: 5
    networks:
      - backend

  certbot:
    image: certbot/certbot
    container_name: certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do sleep 6h & wait $${!}; certbot renew --webroot -w /var/www/certbot --quiet; nginx -s reload; done'"
    volumes:
      - ./nginx/ssl:/etc/letsencrypt
      - ./nginx/ssl:/var/www/certbot
    networks:
      - backend

networks:
  backend:
    driver: bridge

🔐 Jenkins Credentials 등록 항목

ID 설명
firebase\_app\_id Firebase 프로젝트 App ID
firebase\_token Firebase CLI 인증 토큰
slack\_webhook Slack Webhook URL

🧩 Jenkinsfile 예시

pipeline {
  agent {
    docker {
      image 'unityci/editor:2022.3.12f1-android'
      args '-v /var/run/docker.sock:/var/run/docker.sock'
    }
  }
  environment {
    UNITY_PROJECT_PATH = 'unity-project'
    BUILD_OUTPUT_PATH = 'build/output.apk'
    FIREBASE_APP_ID = credentials('firebase_app_id')
    FIREBASE_TOKEN = credentials('firebase_token')
    SLACK_WEBHOOK = credentials('slack_webhook')
    TESTER_GROUP = 'testers'
  }
  triggers {
    githubPush()
  }
  stages {
    stage('Checkout') {
      steps {
        git url: '<https://github.com/your-org/unity-game-repo.git>', branch: 'main'
      }
    }
    stage('Build') {
      steps {
        sh './scripts/build.sh $UNITY_PROJECT_PATH'
        archiveArtifacts artifacts: 'build/output.apk', fingerprint: true
      }
    }
    stage('Deploy') {
      steps {
        sh './scripts/deploy.sh $FIREBASE_APP_ID $BUILD_OUTPUT_PATH $FIREBASE_TOKEN $TESTER_GROUP'
        sh './scripts/manage-testers.sh $FIREBASE_APP_ID $FIREBASE_TOKEN $TESTER_GROUP'
      }
    }
  }
  post {
    success { sh './scripts/notify.sh success' }
    failure { sh './scripts/notify.sh failure' }
  }
}

🛠️ 스크립트 예시

build.sh

#!/bin/bash
UNITY_PROJECT_PATH=$1
BUILD_PATH="build/output.apk"
/opt/unity/Editor/Unity \\
  -batchmode \\
  -nographics \\
  -silent-crashes \\
  -logFile /dev/stdout \\
  -projectPath "$UNITY_PROJECT_PATH" \\
  -executeMethod "BuildScript.BuildAndroid" \\
  -buildTarget Android \\
  -quit

deploy.sh

#!/bin/bash
firebase appdistribution:distribute "$2" --app "$1" --token "$3" --groups "$4"

manage-testers.sh

#!/bin/bash
# 예: CSV에서 이메일 읽어 등록

notify.sh

#!/bin/bash
curl -X POST -H 'Content-type: application/json' \\
--data '{"text":"Build $1!"}' "$SLACK_WEBHOOK"