[홈서버 구축] Github Actions 설정 & Docker Compose 설정 수정

오젼·2025년 1월 29일
0

[홈서버 구축]

목록 보기
4/5

정리

github actions를 실행하면 코드 변경이 있을 때 백엔드 이미지를 github actions 러너에서 새로 빌드해서 ghcr에 푸시하고

ssh로 라즈베리파이에 접속한 다음 새로운 이미지를 pull 해와서 백엔드 컨테이너만 재시작 시킴.

(초기에 라즈베리파이에서 $ docker-compose up -d --build로 nginx, mariadb, backend 컨테이너를 미리 띄워놔야 함)

Github Actinos 설정: deploy.yml

name: Deploy to Development Server

on:
  push:
    branches: [develop]
  pull_request:
    branches: [develop]

jobs:
  build-and-deploy:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-latest
    environment: development

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          submodules: true
          token: ${{ secrets.GH_PAT }}
      
      # Gradle 캐시 설정
      - name: Setup Gradle cache
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: ${{ runner.os }}-gradle-

      # 서브모듈 설정
      - name: Setup submodule
        run: |
          cd src/main/resources/config
          git checkout develop
          cp data-dev.sql ../data-dev.sql
          cd ../../../../

      # GitHub Container Registry 로그인
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GH_PAT }}
      
      # 도커 빌더 설정
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # Backend 이미지 빌드 및 푸시
      - name: Build and push Backend image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker-files/backend/Dockerfile
          push: true
          tags: ${{ secrets.BACKEND_IMAGE }}
          platforms: linux/arm64
          cache-from: type=gha
          cache-to: type=gha,mode=max

      # 필요한 파일만 서버로 전송
      - name: Copy deployment files
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.HOST }}
          port: ${{ secrets.PORT }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          source: "docker-compose.yml,docker-files/nginx/"
          target: "${{ secrets.DEPLOY_PATH }}"
          strip_components: 0

      # 백엔드 배포
      - name: Deploy backend
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.HOST }}
          port: ${{ secrets.PORT }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd ${{ secrets.DEPLOY_PATH }}

            # GitHub Container Registry 로그인 추가
            echo ${{ secrets.GH_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
            
            # 환경 변수 파일 생성
            cat > .env << EOL
            DB_ROOT_PASSWORD=${{ secrets.DB_ROOT_PASSWORD }}
            DB_NAME=${{ secrets.DB_NAME }}
            DB_USER=${{ secrets.DB_USER }}
            DB_PASSWORD=${{ secrets.DB_PASSWORD }}
            DOMAIN_NAME=${{ secrets.DOMAIN_NAME }}
            BACKEND_PORT=${{ secrets.BACKEND_PORT }}
            NGINX_PORT=${{ secrets.NGINX_PORT }}
            DB_PORT=${{ secrets.DB_PORT }}
            BACKEND_IMAGE=${{ secrets.BACKEND_IMAGE }}
            EOL
            
            # 백엔드만 재배포
            docker-compose pull backend
            docker-compose up -d --no-deps backend
            
            # 상태 확인
            docker-compose ps
            
            # 미사용 이미지 정리
            docker image prune -f --filter "until=168h"

Github Actions 설정을 위해선 .github 디렉토리에 yml 파일을 생성해줘야 한다.

+) 파일을 설명하기 전에 헤맸던 내용을 적어보면..
처음엔 GitHub Actions 워크플로우에서 라즈베리파이에 ssh 접속을 하고 레파지토리의 파일들을 SCP(Secure Copy Protocol)를 사용해 전송해준 다음, 라즈베리파이에서 백엔드 도커 이미지를 빌드하고 컨테이너를 띄우는 방식을 사용했었다.
그런데 배포 시간이 너무 오래 걸렸다. 라즈베리파이의 성능이 좋지 않아 이미지 빌드에 시간이 오래 걸리는 것이 문제였다.
그래서 백엔드 이미지 빌드는 GitHub Actions에서 수행하여 GHCR(GitHub Container Registry)에 올리고, 라즈베리파이에서는 필요한 설정 파일(docker-compose.yml, nginx 설정)만 SCP로 전송받은 후 GHCR에서 이미지를 pull 받아 실행하는 방식으로 변경했다.
이외에도 Github Actions 러너에서 Gradle 빌드 캐시를 활용하여 이전 빌드의 의존성 다운로드와 컴파일 결과물을 재사용함으로써 빌드 속도를 개선시켰다.

단계별 설명

1. 트리거 설정

on:
  push:
    branches: [develop]
  pull_request:
    branches: [develop]
  • develop 브랜치에 push하거나 pull request가 생성될 때 워크플로우가 실행된다.

2. 작업 환경 설정

jobs:
  build-and-deploy:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-latest
    environment: development
  • Ubuntu 환경에서 실행
  • GitHub 패키지에 대한 쓰기 권한 필요
  • development 환경으로 설정

권한 설정을 해준 이유는 백엔드 도커 이미지를 GHCR에 push 해야하기 때문이다.

이때 github settings(Actions->General)에서 workflow permissions를 수정해줘야 한다. repository의 settings에서 저 설정이 막혀 있다면 organization의 settings에서 설정을 해줘야 한다!

배포에 필요한 변수값들을 깃허브의 development environment에 github secrets로 등록해두었기 때문에 development 환경으로 설정해준다.
환경별로 변수를 나눠서 관리할 수 있어 편하다!

3. 주요 단계들

a. 코드 체크아웃

- name: Checkout code
  uses: actions/checkout@v4
  with:
    submodules: true
    token: ${{ secrets.GH_PAT }}
  • 워크플로우가 실행되는 러너에 레포지토리의 코드를 클론
  • 서브모듈을 포함한 코드를 가져옴
  • GitHub Personal Access Token을 사용하여 인증

백엔드 스프링의 application.properties와 같은 파일들을 보안상 이유로 private repository에 올리고 서브모듈로 관리하고 있기 때문에 서브모듈을 포함한 코드를 가져오도록 설정해줬다.

이때 private 레파지토리에 접근하기 위해 토큰이 필요하기 때문에 깃허브에서 토큰을 생성해주고 github secrets에 등록해줬다.

b. Gradle 캐시 설정

- name: Setup Gradle cache
  uses: actions/cache@v3
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
    restore-keys: ${{ runner.os }}-gradle-
  • Gradle 빌드 속도 향상을 위한 캐시 설정
  • 이전 빌드의 의존성을 재사용
  • path에서 캐시할 디렉토리를 지정:
    ~/.gradle/caches: Gradle이 다운로드한 의존성 파일들이 저장되는 위치
    ~/.gradle/wrapper: Gradle Wrapper 파일이 저장되는 위치
  • key는 캐시를 식별하는 고유한 키:
    runner.os: 운영체제 정보
    hashFiles(): gradle 설정 파일들의 해시값
  • 캐시 키가 일치하지 않을 경우 restore-keys에 지정된 이전 캐시를 사용
    이를 통해 의존성 다운로드 시간을 줄여 전체적인 빌드 속도를 향상시킬 수 있음

속도 향상을 위해 추가했던 설정이다.

GitHub Actions에서 Gradle 빌드 시 매번 모든 의존성을 다운로드하면 시간이 오래 걸린다. 그래서 actions/cache 액션을 사용하여 Gradle의 의존성 파일들을 캐시하고 재사용하도록 했다.

c. 서브모듈 설정

- name: Setup submodule
  run: |
    cd src/main/resources/config
    git checkout develop
    cp data-dev.sql ../data-dev.sql
  • 설정 파일이 있는 서브모듈을 develop 브랜치로 설정
  • 개발 환경용 SQL 파일을 복사

submodule도 develop 브랜치로 설정해준다. 그리고 resources/config에 두었던 테스트용 sql을 resoures/ 아래로 복사해준다.

d. Docker 관련 설정

# Backend 이미지 빌드 및 푸시
- name: Build and push Backend image
  uses: docker/build-push-action@v5
  with:
    context: .
    file: docker-files/backend/Dockerfile
    push: true
    tags: ${{ secrets.BACKEND_IMAGE }}
    platforms: linux/arm64
    cache-from: type=gha
    cache-to: type=gha,mode=max
  • GitHub Container Registry 로그인
  • Docker Buildx 설정 (멀티 플랫폼 빌드 지원)
  • 백엔드 이미지 빌드 및 레지스트리에 푸시
  • ARM64 플랫폼 타겟팅

GitHub Container Registry는 github에서 제공하는 컨테이너 레지스트리 서비스다. 도커 이미지를 Github Actions 러너에서 빌드하고 GHCR에 push하고 라즈베리파이에선 pull하는 방식으로 사용했다.

그런데 Github Actions의 러너는 linux/amd64 기반이다. 라즈베리파이3B는 64-bit ARM 프로세서를 탑재하고 있고 내가 설치한 OS가 64bit라 linux/arm64 플랫폼으로 타겟팅을 해줘야했다. 크로스 플랫폼 빌드라 시간이 훨씬 더 걸리게 되는데ㅠ.. 이건 어떻게 시간을 더 줄일 방법이 없는 것 같다.

e. 배포 단계

# 필요한 파일만 서버로 전송
- name: Copy deployment files
  uses: appleboy/scp-action@v0.1.7
  with:
    source: "docker-compose.yml,docker-files/nginx/"
    target: "${{ secrets.DEPLOY_PATH }}"
    strip_components: 0

# 백엔드 배포
- name: Deploy backend
  uses: appleboy/ssh-action@v1.0.3
  with:
    host: ${{ secrets.HOST }}
    port: ${{ secrets.PORT }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    script: |
       cd ${{ secrets.DEPLOY_PATH }}

       # GitHub Container Registry 로그인 추가
       echo ${{ secrets.GH_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
            
       # 환경 변수 파일 생성
       cat > .env << EOL
       DB_ROOT_PASSWORD=${{ secrets.DB_ROOT_PASSWORD }}
       DB_NAME=${{ secrets.DB_NAME }}
              ...
       EOL
            
       # 백엔드만 재배포
       docker-compose pull backend
       docker-compose up -d --no-deps backend
            
       # 상태 확인
       docker-compose ps
            
       # 미사용 이미지 정리
       docker image prune -f --filter "until=168h"

배포 과정은 크게 두 단계로 나눠진다.

먼저 SCP(Secure Copy Protocol)를 통해 배포에 필요한 파일들을 라즈베리파이로 전송한다. 백엔드 도커 이미지는 GHCR에서 pull해서 사용하기 때문에 Docker Compose 설정 파일과 Nginx 설정 파일만 전송하면 된다.

그 다음 라즈베리파이에 SSH 접속을 해 실제 배포를 수행한다:
1. GHCR에서 이미지를 받아오기 위해 GitHub Personal Access Token으로 로그인
2. GitHub Secrets의 변수들을 .env 파일로 생성
3. 백엔드 컨테이너만 새로운 이미지로 업데이트. --no-deps 옵션으로 의존성 컨테이너(예: 데이터베이스)는 재시작하지 않는다.
4. docker-compose ps 명령어로 컨테이너들이 정상적으로 실행되고 있는지 확인한다.
5. docker image prune 명령어로 7일 이상 된 사용하지 않는 이미지들을 정리하여 디스크 공간을 관리한다.

이제 이렇게 github actions 설정은 끝!

6분 정도 걸린다

Docker Compose 설정 수정: docker-compose.yml

services:
 nginx:
   image: nginx:alpine@arm64
   container_name: cr-nginx
   depends_on:
     backend:
       condition: service_started
   ports:
     - "${NGINX_PORT}:80"
   volumes:
     - ./docker-files/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
   networks:
     - app-network
   restart: unless-stopped

 mariadb:
   image: mariadb:latest@arm64
   container_name: cr-mariadb
   healthcheck:
     test: ["CMD", "mariadb", "-h", "localhost", "-u${DB_USER}", "-p${DB_PASSWORD}", "-e", "SELECT 1"]
     start_period: 30s
     interval: 10s
     timeout: 5s
     retries: 5
   environment:
     MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
     MARIADB_DATABASE: ${DB_NAME}
     MARIADB_USER: ${DB_USER}
     MARIADB_PASSWORD: ${DB_PASSWORD}
   ports:
     - "${DB_PORT}:3306"
   volumes:
     - mariadb-data:/var/lib/mysql
   networks:
     - app-network
   restart: unless-stopped

 backend:
   image: ${BACKEND_IMAGE}
   platform: linux/arm64
   container_name: cr-backend
   depends_on:
     mariadb:
       condition: service_healthy
   environment:
     - DB_NAME=${DB_NAME}
     - DB_USER=${DB_USER}
     - DB_PASSWORD=${DB_PASSWORD}
   ports:
     - "${BACKEND_PORT}:8080"
   networks:
     - app-network
   restart: unless-stopped

networks:
 app-network:
   driver: bridge

volumes:
 mariadb-data:
  1. 백엔드 서비스 설정 변경

    • build 설정을 제거하고 image: ${BACKEND_IMAGE}로 변경하여 GHCR에서 이미지를 가져오도록 수정
    • platform: linux/arm64를 명시하여 라즈베리파이에서 실행할 ARM64 아키텍처 지정
    • 볼륨 마운트 제거 (개발 환경에서만 필요한 설정이었음)
    • MariaDB 서비스가 정상적으로 시작된 후에 실행되도록 depends_on 조건 추가
  2. MariaDB 설정 개선

    • image: mariadb:latest@arm64로 변경하여 ARM64 아키텍처 지정
    • 데이터베이스 연결 상태를 확인하는 헬스체크 추가
    • 환경 변수들을 백엔드 서비스로 이동 (실제 필요한 서비스에만 환경 변수 지정)
  3. Nginx 설정 수정

    • image: nginx:alpine@arm64로 변경하여 ARM64 아키텍처 지정
    • depends_on 조건을 더 명확하게 수정 (condition: service_started 추가)

빌드 전략의 변경(로컬 빌드 → GHCR 활용)에 따라 Docker Compose 설정도 함께 수정했다.

일단 라즈베리파이에서 실행하기 위해 모든 이미지를 ARM64 아키텍처용으로 지정했다.

주요 변경사항은 mariadb에 healthcheck를 추가하고 backend가 의존하도록(depends_on) 하게 한 것과 nginx가 backend에 의존하도록 하게 한 것, 그리고 backend에서 db 관련 설정값들을 환경변수를 사용할 수 있도록 지정해준 것이다.

healthcheck는 컨테이너가 특정 조건을 만족하는지 확인하여 정상적으로 실행 중인지 판단할 때 사용된다. mariadb에 접속이 가능할 때 backend가 실행돼야 db 접속 오류로 종료되지 않기 때문에 해당 내용을 추가해줬다.

또 nginx.conf에서 proxy_pass http://backend:8080;와 같이 백엔드 컨테이너의 도메인명을 사용하기 때문에, backend 컨테이너가 시작된 후에 nginx 컨테이너가 시작되도록 의존성을 추가해줬다. 그렇지 않으면 해당하는 도메인을 찾을 수 없어 nginx에서 오류가 나게 됐었다.

0개의 댓글

관련 채용 정보