[CICD] Docker + Github Actions + EC2 + RDS + SpringBoot + Flask 배포 (1)

easyone·2024년 5월 11일
0

SEVER

목록 보기
1/2
post-thumbnail

위의 구조대로 cicd 배포를 진행했다. Springboot와 flask는 인스턴스를 각각 만들어 별도로 실행해주었다. 해당 프로젝트는 추천시스템을 구현해야 했기에 flask와 Springboot를 둘다 사용하였다. 해당 게시글에서는 SpringBoot 배포만 다룰 예정이다.

1. EC2 인스턴스 생성 및 인바운드 규칙 설정


Mac os나 wsl를 주로 사용하면 .pem, 윈도우를 사용하면 .ppk를 선택해서 키 페어를 생성해준다.

SpringBoot는 8080포트를 사용하고, Redis와 MySQL을 사용하기 때문에 6379, 3306 포트와 HTTP, HTTPS, SSH 포트를 열어주었다.

이제 RDS를 생성해준다. MYSQL를 선택해줬다.

스토리지의 경우 프리티어 계정은 20을 초과해서 설정하면 과금이 될 수 있기 때문에 20으로 해주고, 과금 방지를 위해 스토리지 자동 조정 활성화도 체크 해제해준다.

EC2와 바로 연결하면 퍼블릭 엑세스가 안되기 때문에, 연결 안 함을 선택하고 추후 보안그룹으로 연결하도록 한다.
인바운드 규칙은 다음과 같이 설정했다.
1. EC2 보안그룹 ID, 포트 3306
2. 0.0.0.0/0, 포트 3306

2. Github Actions 파일 설정

이제 cicd 파이프라인을 구성하기 위해, 배포하려는 Github 레포지토리의 Actions 메뉴로 들어간다.

java를 검색해서 java with Gradle를 선택하면 yml 파일이 생성된다.

name: Java CI with Gradle

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

permissions:
  contents: read

jobs:
  CI-CD:
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:

    ## jdk setting
    - uses: actions/checkout@v4
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'


    ## gradle caching
    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
      with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-
        
    ## create application.yml
    - name: make application.yml
      if : contains(github.ref,'develop')
      run : | 
        cd ./src/main/resources
        touch ./application.yml
        echo "${{secrets.YML_DEV}}" > ./application.yml
      shell: bash
      
    - name: Grant Execute Permission For Gradlew
      run: chmod +x gradlew

    ## docker build & push
    - name: docker build
      run: |
        docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
        docker build -t ${{ secrets.DOCKER_USERNAME }}/imagename .
        docker push ${{ secrets.DOCKER_USERNAME }}/imagename:latest  # 이미지 푸시
    ## docker deploy
    - name: Deploy to dev
      uses: appleboy/ssh-action@master
      id: deploy-dev
      if: contains(github.ref, 'develop')
      with: 
        key: ${{ secrets.PRIVATE_KEY }}
        host: ${{ secrets.HOST_DEV }}
        username: ${{ secrets.USERNAME }}
        port: 22
        script: |
          docker rm -f $(docker ps -qa)
          docker pull ${{ secrets.DOCKER_USERNAME }}/imagename
          docker-compose up -d
          docker image prune -f

## time
  current-time:
    needs: CI-CD
    runs-on: ubuntu-latest
    steps:
      - name: Get Current Time
        uses: 1466587594/get-current-time@v2
        id: current-time
        with:
          format: YYYY-MM-DDTHH:mm:ss
          utcOffset: "+09:00"
      - name: Print Current Time
        run: echo "Current Time=${{steps.current-time.outputs.formattedTime}}"
        shell: bash

전체 단계는 다음과 같다.
1. 트리거 이벤트,배포될 브랜치 설정
2. jdk 설정 및 캐싱 설정
3. application.yml 파일 생성 및 github secrets 에서 내용 가져오기
4. gradlew 파일에 실행 권한 부여
5. docker 이미지 build, push 설정
6. docker 이미지를 docker hub에서 pull해오고 ec2 인스턴스에 존재하는 docker compose 파일 실행

applicaion.yml 파일에는 데이터베이스 관련 내용이 들어있기 때문에 레포지토리에서는 파일을 삭제하고 내용 전체를 복사해서 시크릿에 저장해준다. YML_DEV라는 변수로 저장해주었다. springboot에서는 /src/main/resources 이 경로에 있는 application.yml 파일만을 인식하기 때문에, touch 명령어로 해당 경로에 파일을 생성하고 시크릿의 내용을 넣어준다.

    - name: make application.yml
      if : contains(github.ref,'develop')
      run : | 
        cd ./src/main/resources
        touch ./application.yml
        echo "${{secrets.YML_DEV}}" > ./application.yml
      shell: bash

docker 이미지를 build하고 push하는 부분에서는 docker에 로그인해야 하기 때문에 가입했던 이메일이 아닌 username, password를 시크릿에 저장해준다.

    - name: docker build
      run: |
        docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
        docker build -t ${{ secrets.DOCKER_USERNAME }}/imagename .
        docker push ${{ secrets.DOCKER_USERNAME }}/imagename:latest  # 이미지 푸시

Deploy 하는 부분은 password로 하는 방법이 있고, privatekey로 하는 방법이 있는데 privatekey로 하는 방법을 선택했다.

- name: Deploy to dev
  uses: appleboy/ssh-action@master
  id: deploy-dev
  if: contains(github.ref, 'develop')
  with: 
    key: ${{ secrets.PRIVATE_KEY }}
    host: ${{ secrets.HOST_DEV }}
    username: ${{ secrets.USERNAME }}
    port: 22
    script: |
      docker rm -f $(docker ps -qa)
      docker pull ${{ secrets.DOCKER_USERNAME }}/imagename
      docker-compose up -d
      docker image prune -f

이 부분에서는 특히 Github secrets에 변수를 저장해줘야 한다.

${{ secrets.PRIVATE_KEY }} : ec2 인스턴스 생성 시 생성했던 .pem 파일의 내용을 복사해서 저장한다.(cat 명령어 사용) BEGIN PRIVATE KEY, END PRIVATE KEY 이 부분까지 다 복사해서 저장해준다.

${{ secrets.HOST_DEV }} : 생성한 인스턴스의 퍼블릭 IPv4 DNS를 복사해서 저장해준다.

${{ secrets.USERNAME }} : ec2 user를 넣어 주면 되는데, 처음에 선택한 운영체제에 따라 저장해주면 된다. ubuntu를 선택했기 때문에 ubuntu로 저장해준다.

이렇게 변수를 설정해 주고, ssh 접속을 해야하기 때문에 port를 22로 설정해준다. docker에서 image를 pull 하고, docker-compose up -d를 통해 docker-compose.yml 파일을 실행해준다. 실행이 끝나면 image prune을 통해 이미지를 제거해준다.

3. Docker compose 설정

이제 ec2 인스턴스에 docker 관련 설정을 해줘야 한다.
1. docker 설치
2. docker를 sudo 권한 없이 실행가능하도록 설정
3. docker-compose 설치
4. docker-compose.yml 파일 작성

docker 설치

다음 명령어를 실행해준다.

  • 시스템 업데이트

sudo apt-get update

  • 의존성 패키지 설치

sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

  • docker GPG 키 추가

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

  • docker 저장소 설치

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

  • 패키지 업데이트
sudo apt update
  • 도커 설치
sudo apt install docker-ce
  • 도커 버전 확인
sudo docker --version

docker sudo 없이 실행하도록 설정

위의 스크립트를 실행하려면 sudo 없이 docker 명령어를 사용 가능해야 한다.

docker 그룹에 사용자 추가

sudo usermod -aG docker $USER

그룹 변경사항 활성화

newgrp docker

도커 재시작

sudo service docker restart

docker-compose 설치

docker compose 설치

sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/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

설치 확인

docker-compose -v

docker-compose.yml 파일 작성

ec2 인스턴스에 docker-compose.yml 파일을 홈에 생성해줬다.

version: '3'
services:
  spring-boot:
    container_name: spring-boot
    image: [이미지이름]
    depends_on:
      - redis
    ports:
      - "8080:8080"
    restart: always
    networks:
      - this_network
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://[rds엔드포인트]/[초기 db 이름]?characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Seoul&autoReconnect=true
      SPRING_DATASOURCE_USERNAME: [사용자]
      SPRING_DATASOURCE_PASSWORD: [비밀번호]
  redis:
    image: redis:alpine
    container_name: redis_boot
    volumes:
      - redis:/var/lib/redis
    ports:
      - "6379:6379"
    networks:
      - this_network
  
      
networks:
  this_network:

volumes:
  redis:

코드는 다음과 같이 작성했고, redis와 springboot 컨테이너 관련 코드를 작성해줬다. springboot 부분에는 데이터베이스 설정을 해줘야 한다. redis와 springboot는 같은 네트워크로 연결해줘야 하기 때문에 this_network를 밑에 정의해줬다. 컨테이너 이름은 githuba actions 파일에서 설정해주지 않았으니 해당 파일에서 설정해준다.

모두 설정완료하고, push 또는 pull request를 merge 하게되면 배포 파이프라인이 시작된다.

배포가 잘 되는것을 알 수 있다.

profile
백엔드 개발자 지망 대학생

0개의 댓글