Skip to content

TIL -2025-06-02 [EC2 + Docker + GitHub Actions + GHCR + Slack Notification을 통한 CI/CD 구축] #79

@soheeGit

Description

@soheeGit

📌 EC2 + Docker + GitHub Actions + GHCR + Slack Notification을 통한 CI/CD 구축

1. EC2 인스턴스 생성 및 환경 설정

  • OS: Ubuntu 22.04 LTS
    • 보안 그룹: 포트 22(SSH), 80(HTTP) 열기
Image Image Image Image
  • SSH 접속
chmod 400 "cicd.pem"
ssh -i "cicd.pem" [email protected]
  • Docker 설치
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • 권한 및 테스트
docker --version
docker compose version
sudo docker run hello-world

sudo gpasswd -a $USER docker
sudo service docker restart
sudo su - 
su - ubuntu # 다시 일반 계정으로 돌아가기

2. GHCR 이미지 수동 실행 (테스트)

echo <GHCR_TOKEN> | docker login ghcr.io -u soheegit --password-stdin
docker run -d -p 80:8080 --name app ghcr.io/soheegit/boot-test-cicd:main

3. GitHub Actions Workflow (.github/workflows/docker-image-ci.yml)

  • 3-1. CI: 빌드 및 GHCR 이미지 푸시
name: docker-image-ci
on:
  push:
    branches: [ main ]

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

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

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 17
          cache: gradle

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3
        with:
          cache-read-only: true

      - run: chmod +x ./gradlew
      - run: ./gradlew test --no-daemon

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker Metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build and Push
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Slack notify (CI)
        if: always()
        uses: act10ns/[email protected]
        with:
          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
          status: ${{ job.status }}
          message: |
            *CI Build* `${{ github.repository }}`
            • *Status:* `${{ job.status }}`
            • *Branch:* `${{ github.ref_name }}`
            • *Image:* `${{ steps.meta.outputs.tags }}`
            • <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Logs>
  • 3-2. CD: EC2로 SSH 접속 후 자동 배포
  deploy-prod:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Redeploy via SSH
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            set -e
            LOWER_IMAGE_NAME=$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')
            IMAGE=${{ env.REGISTRY }}/$LOWER_IMAGE_NAME:main
            echo "${{ secrets.SOHEETOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
            docker pull $IMAGE
            docker rm -f app || true
            docker run -d --name app -p 80:8080 --restart unless-stopped $IMAGE

      - name: Slack notify (deploy)
        if: always()
        uses: act10ns/[email protected]
        with:
          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
          status: ${{ job.status }}
          message: |
            *Prod Deploy* `${{ github.repository }}`
            • *Status:* `${{ job.status }}`
            • *Server:* `${{ secrets.SSH_HOST }}`
            • *Image:* `${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main`
            • <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Logs>

4. GitHub Secrets 설정

Image

5. 결과 확인

  • EC2 서버: 자동 pull → 기존 컨테이너 종료 → 새 이미지 run
  • Slack: CI/CD 성공 여부 및 로그 링크 자동 전송
  • GitHub Actions: main 브랜치 push 시 전체 CI/CD 실행
Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions