본문 바로가기
📓 Cloud/CI-CD

[CI-CD 구축] AWS EC2, Docker 를 이용한 Spring Boot 자동 배포

by GroovyArea 2023. 6. 18.
인프라 구축은 참 쉽지 않다. 인프라 공부는 더더욱 어려운 것 같다.
무엇보다 실습을 하려면 비용이 참 많이 든다.

하지만, 원활한 개발을 위해서는 자동 배포를 구축해야지만, 서비스 개발에만 집중할 수 있다.
그래서 백엔드 개발자는 서버 개발 능력 이외에, 어느 정도 인프라 구축 및 환경에 대한 개념을 기본적으로 탑재해야 된다고 생각한다.

이번에 사이드 프로젝트 기획 및 인프라 구축을 해야될 일이 생겼다.
어떻게 최대한 쉽고 빠르게 구축할지 고민을 해뵜다.

이 전에는 jar 파일을 그대로 실행하기 위해 AWS EC2 인스턴스에 고대로 로컬과 비슷한 환경을 구성해 놓고, yaml 도 그대로 넣어놓고, 무중단 배포를 시도했다. Ubuntu 서버에서 할게 참 많았다.

도커를 공부하고, 사용해보면서 EC2 에 직접적인 환경 구축 대신 이 도커를 써보면 어떨까 하는 생각이 들었다.

그래서 고민 과정 및 결과를 나열해보겠다.

 

생각한 플로우

출처 : 위와 같음.

  • Github actions 로 CI 진행
  • Github actions 로 Docker image (Spring boot application) 생성 후 Docker hub 에 푸시,
  • Docker hub 에 최신 image 가 푸시될 경우, EC2 에서 이를 감지, Docker hub 에서 pull 받아서 docker image run 

이게 끝이다.

 

하지만, 최신 이미지가 허브에 올라갔다는 것을 EC2 에서 어떻게 감지하지?

다 Shell Script 로 짜야 하나 (도커 허브에 접속해서 최신 이미지가 있으면 찾고, pull 받아서 run) 

생각만 해도 막막하다.

 

도움을 얻고자, 나보다 경력 있는 주변 지인들께 내가 생각한 자동 배포 플로우를 설명 드렸다.

 

아니..??

Github actions 로 충분히 다 가능하단다.

역시나 누군가 만들어 놓은 Github Actions 라이브러리가 있다.

참 세상 편하다..

 

https://github.com/appleboy/ssh-action

 

GitHub - appleboy/ssh-action: GitHub Actions for executing remote ssh commands.

GitHub Actions for executing remote ssh commands. Contribute to appleboy/ssh-action development by creating an account on GitHub.

github.com

이 apple boy 를 이용하면, 충분히 가능하다.

굳이 최신 docker image 자동 감지니 뭐니, shell 을 짤 필요가 없다.

 

Github actions 작성하기

필독!!

- AWS EC2 인스턴스는 이미 만들어서 (나는 Ubuntu 22.4) 실행중이라고 가정하고 진행하겠다.

- spring boot application 도 이미 만들어졌다고 생각하면 된다.

- docker hub 에 repository 도 이미 만들었다고 생각하면 된다.

 

name: deploy

on:
  push:
    branches: [ "master" ]

jobs:
  deploy:
    name: spring boot server ci-cd
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
        
      - name: Build
        run: ./gradlew build -x test

      - name: Test
        #run: ./gradlew asciidoctor
        run: echo 'empty test'
        
        # 도커를 허브에 로그인 하기, docker hub username 과 token 이 필요하다.
      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
          
        # Jib 라이브러리 사용, docker file, docker compose 를 직접 작성할 필요가 없이 자동으로 만들어 주고, docker hub 에 push 까지 해준다..
      - name: Jib
        env:
          DOCKERHUB_REPOSITORY_PATH: ${{ secrets.DOCKERHUB_REPOSITORY_PATH }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: ./gradlew jib -x test
          -Djib.to.image=$DOCKERHUB_REPOSITORY_PATH
          -Djib.to.tags=latest
          -Djib.container.creationTime=USE_CURRENT_TIMESTAMP
          -Djib.container.environment=SLACK_WEB_HOOK_URL=$SLACK_WEBHOOK_URL
          -Djib.container.jvmFlags=-XX:MaxRAMPercentage=30.0,-XX:MinRAMPercentage=30.0,-Duser.timezone=Asia/Seoul,-Dspring.profiles.active=dev
          
        # 지금 실행중인 github actions 환경의 ip 를 따온다.
      - name: Get Github action IP
        id: ip
        uses: haythem/public-ip@v1.2              
        
        # 따온 github actions ip 를 배포할 EC2 의 보안 그룹에 추가하는 AWS CLI 실행.
      - name: Add Github Actions IP to Security group
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
        run: |
          aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_Security_Group_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32

		# apple boy 라이브러리 이용, EC2 에 직접 접속해서, docker hub 에 push 된 spring boot image 를 pull 받고 작성된 docker compose 를 기반으로 실행한다.
      - name: spring boot server deploy
        uses: appleboy/ssh-action@master
        env:
          DOCKERHUB_REPOSITORY_PATH: ${{ secrets.DOCKERHUB_REPOSITORY_PATH }}
        with:
          host: ${{ secrets.REMOTE_IP }}
          username: ${{ secrets.REMOTE_EC2_NAME }}
          key: ${{ secrets.REMOTE_SSH_KEY }}
          script: |
            cd ~/app
            sudo docker-compose down --rmi all
            sudo docker-compose up -d
          
        # docker 컨테이너가 정상 실행되면, 보안 그룹에 추가했던 github actions ip 를 삭제한다.
      - name: Remove Github Actions IP from security group
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
        run: |
          aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_Security_Group_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 > /dev/null
        if: always()

		# 이건 자유지만, slack 알림도 배포 성공 여부에 따라 오게 할 수 있다.
      - name: Slack notification
        uses: 8398a7/action-slack@v3
        with:
          username: github action
          status: ${{ job.status }}
          author_name: Github Action
          fields: repo,message,commit # repo,message,commit,author,action,eventName,ref,workflow,job,took
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        if: always()

이걸 .github 폴더 안에 넣어주자.

안에 ${{secrets.~~~}} 로 되어 있는 건 github secret 환경 변수에 넣어주면 된다.

 

  • AWS_ACCESS_KEY :  AWS iam 사용자 access key
  • AWS_SECRET_ACCESS_KEY : iam 사용자 secret key
  • REMOTE_IP : EC2 퍼블릭 ip
  • REMOTE_EC2_NAME : 나는 ubuntu 이므로, ubuntu 라 적으면 됨. (ssh 접속 할때, ~~@퍼블릭ip 이렇게 적을 때, 이 물결 이름임.)
  • REMOTE_SSH_KEY : EC2 생성할 때, 보통 pem 키 발급 받는 그거임.
  • 나머지 snake 케이스 아닌 변수들은, github actions 에서 제공하는 변수들이다.

 

EC2 에서 해야할 것.

일단 EC2 에 접속해보자잉.

 

그리고, app 이라는 디렉터리를 만들어라.

필요하면 아래대로, log 디렉터리도 만들어라.

 

그리고 docker-compose.yml 을 생성하자.

### docker-compose.yml

version: "3.7"
services:
  attieadconfig:
    container_name: springbootapp
    image: {이미지 경로 적기}:latest
    ports:
      - 80:8080
    deploy:
      resources:
        limits:
          cpus: '0.30'
          memory: 700m
    logging:
      driver: "json-file"
      options:
        max-file: "3"
        max-size: "10m"
    volumes:
      - ~/app/logs:/logs

 

그리고 실행

github actions 가 실행되고, 

배포가 생공했다면 자동 배포가 종료되었다.

 

보통 에러는 apple boy 스텝에서 터질 것이다.

그때 위에 올려두었던, apple boy github README 를 잘 읽어보면, 해결 방법이 상세히 적혀있을 것이다.

 

나의 경우는, authorized keys 에서 권한을 주어 해결했던 기억이 있다.

 

그럼 구축된 자동 배포가 든든히 뒷받침 되었으니,
즐거운 개발을 진행해보자~~!

반응형