본문 바로가기
프로젝트

git actions로 gcp에 프로젝트 배포하기

by jucheol 2024. 12. 1.

프로젝트에서 redis와 mysql을 같이 사용하는 상황이라 jar, redis, mysql을 하나의 도커 이미지로 만들어서 gcp에 올리기로 하였다.

 

1. Dockerfile

# Base image
FROM openjdk:21-jdk-slim

# Build arguments
ARG JAR_FILE=build/libs/*.jar

# Copy the application JAR
COPY ${JAR_FILE} app.jar

# Expose the application port
EXPOSE 8080

# Run the application
ENTRYPOINT ["java", "-jar", "/app.jar"]

 

2. docker-compose.yml

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: ${DOCKER_USERNAME}/app:latest
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
      DB_URL: ${DB_URL}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    env_file:
      - .env
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - /home:/data
    restart: always

  mysql:
    image: mysql:8.0
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
    env_file:
      - .env
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${DB_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 3
    restart: always

  redis:
    image: redis:6.2-alpine
    container_name: redis
    ports:
      - "6379:6379"
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
    env_file:
      - .env
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3
    restart: always

volumes:
  mysql-data:

docker-compose.yml을 이용해 mysql, redis, app.jar을 한번에 이미지로 만들어 줄 수 있도록 하였다.

이때 gcp에서 env 파일의 위치를 보고 참조할 수 있도록  volumes:   - mysql-data:/var/lib/mysql를 이용해 위치를 표시해주었다.

 

3. git actioins

name: Deploy to Compute Engine

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

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

      - name: Log in to Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin

      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          java-version: '21'
          distribution: 'temurin'

      - name: Add execute permission to Gradle Wrapper
        run: chmod +x ./gradlew

      - name: Build project without running tests
        run: ./gradlew build -x test

      - name: Install Docker
        run: |
          sudo apt-get update
          sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
          curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
          sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
          sudo apt-get update
          sudo apt-get install -y docker-ce
      - name: Install standalone Docker Compose
        run: |
          sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
          sudo chmod +x /usr/local/bin/docker-compose
          sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
      - name: Verify Docker Compose installation
        run: docker-compose --version

      - name: Generate .env file
        run: |
          cat <<EOF > .env
          DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}
          SPRING_PROFILES_ACTIVE=prod
          DB_URL=jdbc:mysql://mysql:3306/${{ secrets.MYSQL_DATABASE }}
          DB_USER=root
          DB_PASSWORD=${{ secrets.DB_PASSWORD }}
          MYSQL_DATABASE=${{ secrets.MYSQL_DATABASE }}
          REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }}
          JWT_EXPIRATION=${{ secrets.JWT_EXPIRATION }}
          JWT_SECRET=${{ secrets.JWT_SECRET }}
          YOUTUBE_KEY=${{ secrets.YOUTUBE_KEY }}
          EOF
        shell: bash

      - name: Build and push Docker images
        run: |
          docker-compose -f ${{ github.workspace }}/docker-compose.yml build
          docker-compose -f ${{ github.workspace }}/docker-compose.yml push
      - name: Authenticate to GCP
        uses: google-github-actions/auth@v1
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Set up GCP configuration
        run: |
          gcloud config set project ${{ secrets.GCP_PROJECT_ID }}
          gcloud config set compute/zone ${{ secrets.GCP_ZONE }}
      - name: Copy configuration files to VM
        run: |
          # Copy docker-compose.yml
          gcloud compute scp ${{ github.workspace }}/docker-compose.yml ${{ secrets.VM_NAME }}:/tmp/docker-compose.yml --zone ${{ secrets.GCP_ZONE }}
          
          # Copy .env file
          gcloud compute scp .env ${{ secrets.VM_NAME }}:/tmp/.env --zone ${{ secrets.GCP_ZONE }}
      - name: Move configuration files to appropriate directory
        run: |
          gcloud compute ssh ${{ secrets.VM_NAME }} --zone ${{ secrets.GCP_ZONE }} --command "
            sudo mv /tmp/docker-compose.yml /home/${{ secrets.VM_USER }}/docker-compose.yml &&
            sudo mv /tmp/.env /home/${{ secrets.VM_USER }}/.env &&
            sudo chown $USER:$USER /home/${{ secrets.VM_USER }}/docker-compose.yml &&
            sudo chown $USER:$USER /home/${{ secrets.VM_USER }}/.env
          "
      - name: Pull Docker images and start services on VM
        run: |
          gcloud compute ssh ${{ secrets.VM_NAME }} --zone ${{ secrets.GCP_ZONE }} --command "
            cd /home/${{ secrets.VM_USER }} &&
            sudo docker login -u '${{ secrets.DOCKER_USERNAME }}' -p '${{ secrets.DOCKER_PASSWORD }}' &&
            sudo docker-compose --env-file .env -f docker-compose.yml pull &&
            sudo docker-compose --env-file .env -f docker-compose.yml up -d
          ".

 git actioins  코드에서 ${{ secrets.VM_NAME }}와 같이 secrets.~ 인 값은 github에 있는 Secrets기능을 사용해 보안상 중요한 코드들을 숨겨둔 것이다.

https://every-word.tistory.com/16

 

git-secret 사용하기

배포를 진행하기 위해 환경변수, .env 파일처럼 보안상 중요한 코드를 숨기는 방법을 찾아보았다.  git-secret를 등록할 레포지토리에서 Settings -> Secrets and variables -> Actions로 이동한다 Repository secre

every-word.tistory.com

에 git secret를 사용하는 방법을 정리해 놨다.

 

git actioins 코드를 설명하면 다음과 같다.

 

1. 워크플로우 이름 및 트리거 조건

name: Deploy to Compute Engine

on:
  push:
    branches:
      - main
  • 워크플로우 이름: Deploy to Compute Engine
  • 트리거: main 브랜치에 코드가 푸시될 때 실행됩니다.

2. 작업(Job): build-and-deploy

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
  • 실행 환경: ubuntu-latest (최신 우분투 OS 사용)

(1) 코드 체크아웃

steps:
      - name: Checkout code
        uses: actions/checkout@v3
  • GitHub 저장소의 코드를 가져옵니다.

(2) Docker Hub 로그인

 - name: Log in to Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
  • Docker 이미지를 빌드하고 푸시하기 위해 Docker Hub에 로그인합니다.
  • secrets 사용: GitHub Secrets에서 보안 정보를 읽어옵니다

(3) Java 환경 설정

- name: Set up JDK
        uses: actions/setup-java@v3
        with:
          java-version: '21'
          distribution: 'temurin'
  • Java 21 버전을 설치하고 Gradle 빌드를 준비합니다.

(4) Gradle 빌드 준비 및 실행

 - name: Add execute permission to Gradle Wrapper
        run: chmod +x ./gradlew

 - name: Build project without running tests
        run: ./gradlew build -x test
  • Gradle Wrapper에 실행 권한을 부여하고, 테스트를 제외한 빌드를 실행합니다.

 

(5) Docker 및 Docker Compose 설치

- name: Install Docker
        run: |
          sudo apt-get update
          sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
          curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
          sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
          sudo apt-get update
          sudo apt-get install -y docker-ce
- name: Install standalone Docker Compose
        run: |
          sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
          sudo chmod +x /usr/local/bin/docker-compose
          sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
- name: Verify Docker Compose installation
        run: docker-compose --version
  • Docker: 애플리케이션 컨테이너 이미지를 빌드 및 배포하기 위해 설치.
  • Docker Compose: 여러 컨테이너 서비스를 정의하고 실행하기 위해 사용.
  • 설치 후, Docker Compose가 제대로 설치되었는지 확인.

(6) 환경 변수 파일(.env) 생성

  - name: Generate .env file
        run: |
          cat <<EOF > .env
          DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}
          SPRING_PROFILES_ACTIVE=prod
          DB_URL=jdbc:mysql://mysql:3306/${{ secrets.MYSQL_DATABASE }}
          DB_USER=root
          DB_PASSWORD=${{ secrets.DB_PASSWORD }}
          MYSQL_DATABASE=${{ secrets.MYSQL_DATABASE }}
          REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }}
          JWT_EXPIRATION=${{ secrets.JWT_EXPIRATION }}
          JWT_SECRET=${{ secrets.JWT_SECRET }}
          YOUTUBE_KEY=${{ secrets.YOUTUBE_KEY }}
          EOF
        shell: bash
  • 배포 과정에서 필요한 민감한 정보를 포함한 .env 파일을 생성.
  • GitHub Secrets에서 읽은 값을 활용.

(7) Docker 이미지 빌드 및 푸시

- name: Build and push Docker images
        run: |
          docker-compose -f ${{ github.workspace }}/docker-compose.yml build
          docker-compose -f ${{ github.workspace }}/docker-compose.yml push
 
  • docker-compose.yml 파일을 기반으로 이미지를 빌드한 뒤, Docker Hub에 푸시.

(8) Google Cloud Platform(GCP) 인증

- name: Authenticate to GCP
        uses: google-github-actions/auth@v1
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

- name: Set up GCP configuration
        run: |
          gcloud config set project ${{ secrets.GCP_PROJECT_ID }}
          gcloud config set compute/zone ${{ secrets.GCP_ZONE }}
  • GCP 인증: GitHub Secrets에서 서비스 계정 키(JSON)를 읽어 인증.
  • 구성 설정: GCP 프로젝트 및 리전을 설정.

(9) GCE로 파일 복사

- name: Copy configuration files to VM
        run: |
          # Copy docker-compose.yml
          gcloud compute scp ${{ github.workspace }}/docker-compose.yml ${{ secrets.VM_NAME }}:/tmp/docker-compose.yml --zone ${{ secrets.GCP_ZONE }}
          
          # Copy .env file
          gcloud compute scp .env ${{ secrets.VM_NAME }}:/tmp/.env --zone ${{ secrets.GCP_ZONE }}
- name: Move configuration files to appropriate directory
        run: |
          gcloud compute ssh ${{ secrets.VM_NAME }} --zone ${{ secrets.GCP_ZONE }} --command "
            sudo mv /tmp/docker-compose.yml /home/${{ secrets.VM_USER }}/docker-compose.yml &&
            sudo mv /tmp/.env /home/${{ secrets.VM_USER }}/.env &&
            sudo chown $USER:$USER /home/${{ secrets.VM_USER }}/docker-compose.yml &&
            sudo chown $USER:$USER /home/${{ secrets.VM_USER }}/.env
          "
  • gcloud compute scp 명령을 사용해 구성 파일을 GCE 가상머신(VM)으로 복사.

(10) VM에서 Docker 컨테이너 실행

 - name: Pull Docker images and start services on VM
        run: |
          gcloud compute ssh ${{ secrets.VM_NAME }} --zone ${{ secrets.GCP_ZONE }} --command "
            cd /home/${{ secrets.VM_USER }} &&
            sudo docker login -u '${{ secrets.DOCKER_USERNAME }}' -p '${{ secrets.DOCKER_PASSWORD }}' &&
            sudo docker-compose --env-file .env -f docker-compose.yml pull &&
            sudo docker-compose --env-file .env -f docker-compose.yml up -d
          "
  • VM에 접속한 후, Docker 이미지를 가져와 컨테이너 서비스를 시작합니다.
  • .env 파일을 읽어 Docker Compose를 실행합니다.