본문 바로가기

배운내용 정리

Github Action CI/CD + Docker

새로운 프로젝트에 앞서서 배포를 진행하기에 Github Actions + Docker를 통해 배포를 진행해보려고 한다.

 

순서는 다음과 같다.

1. Docker-compose.yml 작성

- 서비스에 필요한 인프라 이미지 작성 

- ex) postgis, memcached 작성 서비스할 Dockerfile에 관련된 설정 작성

- ※주의 사항 host는 Docker compose 파일에서 정의한 서비스 이름으로 사용

  •  application.yml의 각 인프라의 url 호스트명은 docker 컨테이너 명으로 설정

- 기술적 의사결정

  • Docker network 사용
    •  Docker network 사용하여  관리 및 유지보수를 용이하게 함
version: "3"
services:
  postgresql:
    image: postgis/postgis:16-3.4
    restart: always
    container_name: postgres
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_NAME}
    volumes:
      - ./data/postgres/:/var/lib/postgresql/data
    networks:
      - app-network

  memcached:
    image: memcached:1.6.25
    container_name: memcached
    ports:
      - "11211:11211"
    command: memcached -m 64 -o modern -v
    networks:
      - app-network

  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    restart: always
    depends_on:
      - postgresql
      - memcached
    networks:
      - app-network
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DATABASE_HOST=postgresql
      - MEMCACHED_HOST=memcached
      - MEMCACHED_PORT=11211

networks:
  app-network:
    driver: bridge

 

2. Dockerfile 작성

- docker를 통해 이미지 생성 및 빌드
- openjdk  + jar 파일을 빌드할 코드 작성

# 런타임 스테이지
FROM openjdk:21-jdk-slim

RUN mkdir /app

# EC2 인스턴스에 이미 존재하는 JAR 파일을 이미지에 복사
COPY service-back.jar /app/service-back.jar

# 컨테이너 실행 시 JAR 파일 실행
ENTRYPOINT ["java", "-jar", "/app/service-back.jar"]

 

3. github Actions gradle.yml 작성

- GithubActions를 통해 배포를 진행할 workflow 작성 (git 프로젝트 리포지토리 -> Actions -> new workflow) 

- CI/CD 코드작성

- CI

  • CI부분 github Action은 git vm을 통해 빌드 및 테스트 진행
  • github artifact를 사용하여 파일 업로드

트러블 슈팅

  • artifact 사용시 저장할 이름명과 Path명이 같으면  안됨
name: Upload build artifact
  uses: actions/upload-artifact@v2
  with:
    name: service-back
    path: build/libs/service-back-0.0.1-SNAPSHOT.jar

 

  • Github Actions 빌드 테스트 할때 postgis jdbc connection 문제 
    • github Actions는 자체적으로 github vm에서 build 및 테스트를 진행
    • 따라서 서비스 resources 파일에 있는 application-test.yml을 통해 테스트 즉 로컬 환경의 테스트와 같다
    • application-prod.yml과 다르게 postgis, memcached 호스트를 로컬로 변경하여 문제 해결

 

- CD

  • CI과정에서 업로드한 파일들을 scp와 ssh로 EC2 인스턴스에 전송
  • 인스턴스에서 Docker-compose.yml 빌드를 통해 서버 빌드 및 업데이트
# 워크 플로우 이름
name: Java CI with Gradle


# 워크 플로우가 언제 실행 될지를 정한다.
on:
  pull_request:
    types:
      - closed
    branches:
      - dev


# 워크 플로우가 깃 레포에 대한 권한을 읽기 만 가능하게 설정한다.
	@@ -15,40 +18,81 @@ permissions:

# 워크플로우에서 할 작업 정의한다.
jobs:
  # 작업 환경 = 우분투 최신 버전
  build:
    runs-on: ubuntu-latest
    if: (github.event_name == 'pull_request' && github.event.pull_request.merged == true)
    services:
      postgres:
        image: postgis/postgis:16-3.4
        env:
          POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}
          POSTGRES_DB: ${{ secrets.DB_NAME }}
          POSTGRES_USER: ${{ secrets.DB_USER }}
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      memcached:
        image: memcached:1.6.25
        ports:
          - 11211:11211

          # 깃허브에서 제공하는 checkout 엑션 사용
    steps:
      - uses: actions/checkout@v4

      # JDK 21 설정한당
      # temurin = Adoptium에서 제공하는 JDK
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'

      # gradle wrapper 파일에 실행 권한을 부여
      # gradle wrapper = 개발자가 특정 버전의 Gradle을 미리 설치하지 않고도 Gradle 빌드를 실행할 수 있게 해주는 편리한 도구
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      # 데이터베이스 서비스 준비 대기
      - name: Wait for Postgres to become ready
        run: |
          until docker exec $(docker ps -q -f "ancestor=postgis/postgis:16-3.4") pg_isready -h localhost -p 5432 -U postgres; do
            echo "Waiting for Postgres to become ready..."
            sleep 5
          done
          echo "Postgres is ready."
      # Gradle 빌드 엑션을 이용해서 프로젝트 빌드
      - name: Build with Gradle
        uses: gradle/gradle-build-action@v2
        with:
          gradle-version: '8.4'
          arguments: build test

      # 빌드해서 생긴 JAR 파일을 깃허브 아티팩트로 업로드!!
      - name: Upload build artifact
        uses: actions/upload-artifact@v2
        with:
          name: service-back
          path: build/libs/service-0.0.1-SNAPSHOT.jar

      - name: Upload Dockerfile
        uses: actions/upload-artifact@v2
        with:
          name: service-dockerfile
          path: Dockerfile

      - name: Upload docker-compose
        uses: actions/upload-artifact@v2
        with:
          name: service-docker-compose
          path: docker/docker-compose.yml


  # 배포 **
  deploy:
	@@ -57,21 +101,40 @@ jobs:

    # 위의 빌드작업한 JAR 파일 = 아티팩트를 다운로드
    steps:
      - name: Download build artifact
        uses: actions/download-artifact@v2
        with:
          name: service-back
          path: build/libs/

      - name: Download Dockerfile
        uses: actions/download-artifact@v2
        with:
          name: service-dockerfile
          path: ./

      - name: Download docker-compose
        uses: actions/download-artifact@v2
        with:
          name: service-docker-compose
          path: docker/

      - name: Deploy to EC2 and Build/Run Docker Containers
        run: |
          echo "${{ secrets.SSH_PEM_KEY }}" > ssh_key.pem
          chmod 600 ssh_key.pem
          scp -i ssh_key.pem -o StrictHostKeyChecking=no build/libs/service-0.0.1-SNAPSHOT.jar ${{ secrets.USER }}@${{ secrets.HOST }}:/home/${{ secrets.USER }}/service-back.jar
          ssh -i ssh_key.pem -o StrictHostKeyChecking=no ${{ secrets.USER }}@${{ secrets.HOST }} "pgrep java | xargs kill -9; nohup java -jar /home/${{ secrets.USER }}/service-back.jar > app.log 2>&1 &"
          scp -i ssh_key.pem -o StrictHostKeyChecking=no Dockerfile ${{ secrets.USER }}@${{ secrets.HOST }}:/home/${{ secrets.USER }}/Dockerfile
          scp -i ssh_key.pem -o StrictHostKeyChecking=no docker/docker-compose.yml ${{ secrets.USER }}@${{ secrets.HOST }}:/home/${{ secrets.USER }}/docker-compose.yml
          ssh -i ssh_key.pem -o StrictHostKeyChecking=no ${{ secrets.USER }}@${{ secrets.HOST }} << EOF
            cd /home/${{ secrets.USER }}
               docker compose build
               docker compose up -d
          EOF

4. EC2 배포할 서버의 인프라 구성

- Docker 다운로드(Docker-compose 포함)

- 이슈사항

EC2 Docker 다운로드(공식문서 참고)

https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository

 

Install Docker Engine on Ubuntu

Jumpstart your client-side server applications with Docker Engine on Ubuntu. This guide details prerequisites and multiple methods to install Docker Engine on Ubuntu.

docs.docker.com

 

궁금한것 

docker compose 빌드는 변경사항이 생긴다. 이렇게 되면 버전이 업데이트 될수록 Docker 이미지 관리는 어떻게 할것인가?

'배운내용 정리' 카테고리의 다른 글

Github Actions + Docker compose 버전관리 + nginx  (0) 2024.04.20
Nginx + Certbot을 통한 Https 및 TimeZone 적용  (0) 2024.04.15
JDBC  (0) 2023.11.13
Spring IoC와 DI  (0) 2023.11.10
Spring MVC  (0) 2023.11.09