[AWS, Git] AWS 인스턴스에 CI/CD 파이프라인 구축 (spring, react, nginx, java application)

Sungjin Cho·2024년 9월 4일

AWS

목록 보기
7/7
post-thumbnail

서론

언젠가 CI/CD 구축을 해야지...하다가 계속 하지 않고 코드의 변경 사항이 생길 때 마다 docker-compose build로 이미지를 빌드하고 하나하나 docker tag ~~ 이런식으로 태그 지정하고, docker push하고 ssh 서버에서 docker pull 까지 반복 작업을 하려니 너무 번거로워서 하루 날 잡고 CI/CD 구축을 하기로 하였다.
이미 두번 정도 시도를 했다가 실패했기 때문에 이번에는 꼭 끝내보자 하고 끝장을 봤다.

CI CD 전략

master 브랜치 제외,

개발 브랜치: cebu.citymart.kr_dev

배포 브랜치: cebu.citymart.kr

개발 브랜치는 gajapos Organization의 Gajapos 레포지토리의 cebu.citymart.kr_dev 브랜치이다. 기본적으로 코드 통합은 개인 레포지토리에서 Pull Request 를 통한 merge로 cebu.citymart.kr_dev 브랜치로 통합되고, 문제가 없는 경우 cebu.citymart.kr 브랜치로 통합된다.

cebu.citymart.kr 브랜치로의 푸시를 성공하면 자동으로 SSH 서버로 푸시된 코드를 배포하기로 한다.

GitHub Actions Workflow 생성

  1. GitHub 저장소에서 Actions 탭으로 이동한다.
  2. New workflow 버튼을 클릭한다.

  1. Set up a workflow yourself 옵션을 선택한다.

  1. 파일 이름을 ci-cd.yml로 입력한다.

2. Secrets 설정

  1. 저장소의 Settings 탭으로 이동한다.

  2. 왼쪽 사이드바에서 Secrets and variables > Actions를 선택한다.

  3. New repository secret 버튼을 클릭하여 다음 secrets를 추가합니다:

    • DOCKERHUB_USERNAME: Docker Hub 사용자 이름
    • DOCKERHUB_TOKEN: Docker Hub 액세스 토큰
    • AWS_HOST: AWS EC2 인스턴스의 공개 IP 또는 도메인
    • AWS_USERNAME: EC2 인스턴스 접속 사용자 이름 (예: ubuntu)
    • AWS_PRIVATE_KEY: EC2 인스턴스 접속용 SSH 개인 키

    등 나머지 기타 변수들 추가.

3. ci-cd.yml 파일 설명

# workflow name
name: CI/CD Pipeline

# workflow를 발생시킬 event
on:
  push:
    branches:
      - cebu.citymart.kr  # cebu.citymart.kr 브랜치에 push가 발생할 때 실행

# 실행할 작업들을 정의
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest  # Ubuntu 최신 버전에서 작업 실행
    steps:
      # 저장소의 코드를 체크아웃
      - name: Checkout code
        uses: actions/checkout@v2

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

      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      # Spring Docker 이미지 빌드, 푸시
      - name: Build and push Spring image
        uses: docker/build-push-action@v2
        with:
          context: ./gazapos_db
          push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cebu.citymart.kr' || github.event.inputs.deploy == 'true' }}
          tags: sungnij/gazapos-spring:latest

      # React를 위한 환경 변수 파일을 생성 (.env 파일)
      - name: Create .env file
        run: |
          echo "REACT_APP_ENVIRONMENT=${{ secrets.REACT_APP_ENVIRONMENT }}" > ./pos4phill-web/.env
          echo "REACT_APP_JEPETTO_PROD_API_URL=${{ secrets.REACT_APP_JEPETTO_PROD_API_URL }}" >> ./pos4phill-web/.env
          echo "REACT_APP_JEPETTO_DEV_API_URL=${{ secrets.REACT_APP_JEPETTO_DEV_API_URL }}" >> ./pos4phill-web/.env
          echo "REACT_APP_SPRING_PROD_API_URL=${{ secrets.REACT_APP_SPRING_PROD_API_URL }}" >> ./pos4phill-web/.env
          echo "REACT_APP_SPRING_DEV_API_URL=${{ secrets.REACT_APP_SPRING_DEV_API_URL }}" >> ./pos4phill-web/.env

      # React 애플리케이션의 Docker 이미지 빌드, 푸시
      - name: Build and push React image
        uses: docker/build-push-action@v2
        with:
          context: ./pos4phill-web
          push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cebu.citymart.kr' || github.event.inputs.deploy == 'true' }}
          tags: sungnij/gazapos-react:latest

      # Nginx의 Docker 이미지 빌드, 푸시
      - name: Build and push Nginx image
        uses: docker/build-push-action@v2
        with:
          context: ./nginx
          push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cebu.citymart.kr' || github.event.inputs.deploy == 'true' }}
          tags: sungnij/gazapos-nginx:latest

      # Jepetto 설정 파일을 업데이트
      - name: Update jepetto.properties
        run: |
          sed -i '/^BASE64_ENCODED_KEY=/d' ./gazapos/tomcat/conf/jepetto.properties
          sed -i '/^CITYMART_BOT_TOKEN=/d' ./gazapos/tomcat/conf/jepetto.properties
          sed -i '/^CITYMART_CHAT_ID=/d' ./gazapos/tomcat/conf/jepetto.properties
          echo "BASE64_ENCODED_KEY=${{ secrets.BASE64_ENCODED_KEY }}" >> ./gazapos/tomcat/conf/jepetto.properties
          echo "CITYMART_BOT_TOKEN=${{ secrets.CITYMART_BOT_TOKEN }}" >> ./gazapos/tomcat/conf/jepetto.properties
          echo "CITYMART_CHAT_ID=${{ secrets.CITYMART_CHAT_ID }}" >> ./gazapos/tomcat/conf/jepetto.properties

      # Jepetto Docker 이미지 빌드, 푸시
      - name: Build and push Jepetto image
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./gazapos/Dockerfile
          push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cebu.citymart.kr' || github.event.inputs.deploy == 'true' }}
          tags: sungnij/gazapos-jepetto:latest

      # AWS EC2 인스턴스에 SSH로 접속하여 배포 작업 수행
      - name: Deploy to AWS EC2
        if: github.event_name == 'push' && github.ref == 'refs/heads/cebu.citymart.kr' || github.event.inputs.deploy == 'true'
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.AWS_HOST }}
          username: ${{ secrets.AWS_USERNAME }}
          key: ${{ secrets.AWS_PRIVATE_KEY }}
          script: |
            # Git 저장소 안전 디렉토리 설정
            git config --global --add safe.directory /home/***/Gajapos
            # 현재 사용자를 docker 그룹에 추가
            sudo usermod -aG docker $USER
            # docker 그룹 권한 즉시 적용
            newgrp docker
            # 프로젝트 디렉토리로 이동
            cd ~/Gajapos
            # 최신 코드 pull
            git pull origin cebu.citymart.kr
            # 기존 Docker 컨테이너, 네트워크 정리
            docker-compose down --remove-orphans
            docker rm -f $(docker ps -aq)
            docker network prune -f
            # 최신 Docker 이미지 pull
            docker pull sungnij/gazapos-react:latest
            docker pull sungnij/gazapos-spring:latest
            docker pull sungnij/gazapos-jepetto:latest
            docker pull sungnij/gazapos-nginx:latest
            docker pull certbot/certbot
            # Docker Compose를 사용하여 서비스 시작
            docker-compose up -d

트러블 슈팅

docker image를 build 하고 tag를 지정 후 docker-compose pull 했을 때 ssh 서버에서 최신화된 이미지를 pull 하지 못하는 문제가 있었다. 그래서 workflow는 success 했음에도 불구하고 변경된 코드가 반영이 안되었다.

확인 결과 docker-compose pull 에서 제대로 최신화된 이미지를 가져오지 못하는것을 알게 되었고 docker-compose 를 사용하지 않고 직접 이미지를 모두 지정해서 docker pull 하도록 변경하였다.

		docker pull sungnij/gazapos-react:latest
		docker pull sungnij/gazapos-spring:latest
		docker pull sungnij/gazapos-jepetto:latest
		docker pull sungnij/gazapos-nginx:latest
		docker pull certbot/certbot

CI/CD 아키텍처

0개의 댓글