• ABOUT
  • POSTS
  • GUESTBOOK

ยฉ 2025 BlueCool12 All rights reserved.

2025.09.17ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

๐Ÿž CI/CD ์˜ค๋ฅ˜: GHCR์„ ์ด์šฉํ•œ GitHub Actions ์ตœ์ ํ™” ๋ฐฐํฌ

[๋ฌธ์ œ ์š”์•ฝ] 

  • ์ฆ์ƒ: GitHub Actions๋ฅผ ํ†ตํ•œ ๋ฐฐํฌ ์ค‘ Connection timeout ๋ฐœ์ƒ
  • ์›์ธ: ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ ์ฆ๊ฐ€๋กœ ํ™ˆ์„œ๋ฒ„์—์„œ ์ง์ ‘ Docker ๋นŒ๋“œ๊ฐ€ ์˜ค๋ž˜ ๊ฑธ๋ ค SSH ์„ธ์…˜/์ž‘์—… ์ œํ•œ ์‹œ๊ฐ„์„ ์ดˆ๊ณผ
  • ํ•ด๊ฒฐ: GitHub Actions์—์„œ GHCR(GitHub Container Registry)๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œยทํ‘ธ์‹œ, ์„œ๋ฒ„๋Š” ์ด๋ฏธ์ง€ pull ํ›„ ์žฌ๋ฐฐํฌ 

 



๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ GitHub Actions ๊ตฌ์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค. 
 

plaintext
name: Deploy to Raspberry Pi 

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    name: Deploy via SSH
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          
      - name: Setup SSH
        uses: webfactory/ssh-agent@v0.7.0
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
          
      - name: SSH and deploy to Raspberry Pi
        run: |
          ssh -p ${{ secrets.RASPBERRY_PORT }} -o StrictHostKeyCheking=no ${{ secrets.RASPBERRY_USER }}@${{ secrets.RASPBERRY_HOST }} << 'EOF'
            set -euo pipefail
            
            cd /home/${{ secrets.RASPBERRY_USER }}/docker-compose/bluecool/blue
            git fetch --all --prune
            git reset --hard origin/main
            git config core.ignorecase false
            
            docker compose build --no-cache blue
            docker compose up -d --force-recreate blue
            
            docker image prune -f || true
          EOF


ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ž‘์„๋•Œ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ์ง€๋งŒ ์ ์  ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉด์„œ ์„œ๋ฒ„ ์ธก์—์„œ docker compose build --no-cache ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ์‹ ๋•Œ๋ฌธ์— ๋นŒ๋“œ ์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•˜์—ฌ ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ–ˆ๋‹ค. 

๋”ฐ๋ผ์„œ CI์—์„œ ์ด๋ฏธ์ง€๋ฅผ GHCR์— ๋นŒ๋“œยทํ‘ธ์‹œํ•˜๊ณ  ์„œ๋ฒ„๋Š” ๋นŒ๋“œ ์—†์ด pull๋งŒ ํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ๋‹ค. ๋ณ€๊ฒฝ๋œ ์„ค์ •์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค. 
 

plaintext
name: Build & Deploy to Raspberry Pi

on:
  push:
    branches: [ main ]
    
concurrency:
  group: blue-deploy-${{ github.ref }}
  cancel-in-progress: true
  
env:
  IMAGE_NAME: ghcr.io/bluecool12/blue
    
jobs:
  build:
    name: Build & Push (GHCR)
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      
      - uses: docker/setup-qemu-action@v3
      - uses: docker/setup-buildx-action@v3
      
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
           
      - name: Build & Push
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/arm64
          push: true
          tags: |
            ${{ env.IMAGE_NAME }}:latest
            ${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type-gha
          cache-to: type-gha,mode=max
          build-args: |
            NEXT_PUBLIC_API_BASE_URL=${{ vars.NEXT_PUBLIC_API_BASE_URL }}
            PUBLIC_API_BASE_URL=${{ vars.PUBLIC_API_BASE_URL }}
            
  deploy:
    name: Deploy via SSH (pull & up)
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Setup SSH
        uses: webfactory/ssh-agent@v0.7.0
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} 
          
      - name: Add host key (known_hosts)
        run: |
          mkdir -p ~/.ssh
          ssh-keyscan -p "${{ secrets.RASPBERRY_PORT }}" -H "${{ secrets.RASPBERRY_HOST }}" >> ~/.ssh/known_hosts
          chmod 700 ~/.ssh
          chmod 644 ~/.ssh/known_hosts
          
      - name: SSH and deploy to Raspberry Pi
        run: |
          ssh -p ${{ secrets.RASPBERRY_PORT }} \
              -o ServerAliveInterval-30 -o ServerAliveCountMax=10 \
              ${{ secrets.RASPBERRY_USER }}@${{ secrets.RASPBERRY_HOST }} << 'EOF'
            set -Eeuo pipefail
            
            cd /home/${{ secrets.RASPBERRY_USER }}/docker-compose/bluecool/blue
            
            git fetch --all --prune || true
            git reset --hard origin/main || true
            git config core.ignorecase false || true
            
            docker compose pull blue
            docker compose up -d blue
            doccker image prune -f || true
          EOF


๊ธฐ์กด์˜ Dockerfile์„ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋กœ ์˜ฎ๊ธฐ๊ณ  GitHub Actions์—์„œ GHCR๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œยทํ‘ธ์‹œํ•˜๋„๋ก ๊ตฌ์„ฑํ•˜์˜€๊ณ  Buildx ์บ์‹œ๋ฅผ ํ™œ์šฉํ•ด ๋นŒ๋“œ ์†๋„๊นŒ์ง€ ์ตœ์ ํ™” ํ•˜์˜€๋‹ค. 

ํ™ˆ์„œ๋ฒ„์—์„œ๋Š” docker-compose.yml์ด ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•˜๋„๋ก ์ˆ˜์ •ํ•˜๊ณ  ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋˜ํ•œ GitHub Variables๋ฅผ ํ†ตํ•ด ์ฃผ์ž…ํ•˜์˜€๋‹ค. 

๋˜ํ•œ SSH ์ ‘์† ์‹œ ํ˜ธ์ŠคํŠธ ์ธ์ฆ์„ ์ƒ๋žตํ•˜์ง€ ์•Š๊ณ  known_hosts์— ํ‚ค๋ฅผ ๋ฏธ๋ฆฌ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜์˜€๋‹ค. 
 

์ด์ „ ๊ธ€
๐Ÿ” chownยทchmod๋กœ ๋ฐฐ์šฐ๋Š” ๋ฆฌ๋ˆ…์Šค ํŒŒ์ผ ๊ถŒํ•œ ๊ด€๋ฆฌ
๋‹ค์Œ ๊ธ€
๐Ÿ“ก ์ธํ„ฐ๋„ท ํ†ต์‹ ์˜ ํ•ต์‹ฌ - TCP, IP ๊ทธ๋ฆฌ๊ณ  UDP ์ดํ•ดํ•˜๊ธฐ
์žฅ์‹์šฉ ๋กœ๊ณ