Backend/Docker

깃허브 액션, 도커, EC2, RDS를 통해 배포하기

olsohee 2024. 4. 26. 15:52

도커를 처음 사용해보며 3일 간 많이 헤맸지만 그만큼 많이 배운 거 같다. 다음에 같은 실수를 반복하지 않기 위해 이번에 도커를 사용한 과정을 기록하고자 한다.

배포 과정

1. RDS 생성, EC2 서버 구축 및 설정

우선 AWS의 RDS와 EC2를 생성해야 한다.

 

그리고 EC2 서버에 도커 컨테이너를 통해 자바 프로그램을 실행시킬 것이기 때문에 EC2 서버에 도커를 설치해야 한다. ssh를 통해 EC2 서버에 접속한 다음, 이 블로그를 따라 서버에 도커를 설치하면 된다. 설치 후에는 docker --version을 통해 설치된 도커 버전을 확인할 수 있다.

 

이어서 자바 프로그램 외에도 redis도 도커 컨테이너로 띄워야 하므로, docker pull redis 명령어를 통해 redis 이미지를 pull 받아야 한다. 참고로 이때 permissiondenied 오류가 발생하면 sudo chmod 666 /var/run/docker.sock 명령어를 통해 권한을 허용하면 된다.

2. 도커 파일 생성

이후에 깃허브 액션을 통해 도커 이미지 생성 명령어가 실행될 것이다. 그런데 도커 이미지 생성을 위해서는 도커 파일이 있어야 한다. 도커 파일은 프로젝트 최상단에 Dockerfile이라는 이름이어야 한다. 내가 작성한 도커 파일은 다음과 같다. 

# 자바 버전
FROM openjdk:17 

# 빌드 결과로 생성된 jar 파일의 위치를 변수에 저장
ARG JAR_FILE=/build/libs/*.jar

# 변수 경로에 있는 모든 jar 파일을 도커 이미지 내의 루트 디렉토리로 복사 (이때 해당 파일 이름은 community.jar)
COPY ${JAR_FILE} community.jar

# 컨테이너 실행 시 반드시 실행될 명령어 (자바는 jar 파일을 실행할 때 java -jar <파일명> 을 실행)
ENTRYPOINT ["java","-jar","community.jar"]

 

이후에 빌드된 자바 프로젝트가 있는 위치에서 docker build 명령어를 실행하면 현재 디렉토리에 있는 도커 파일을 사용하여 도커 이미지가 생성된다. 

 

3. 깃허브 액션을 통한 CI/CD 설정

이어서 깃허브 액션을 통해 CI/CD 환경을 구성해보자. CI/CD가 동작하기 위한 파일을 생성해줘야 하는데, 프로젝트 최상단 아래 .github/workflows 디렉토리에 yml 파일을 생성하면 된다. 내가 작성한 파일은 다음과 같다.

name: Build and Deploy

on: # 깃허브액션이 트리거되는 순간을 지정
  push:
    branches: [ "main" ] 
  pull_request:
    branches: [ "main" ]

jobs:
  build-and-deploy: # job 이름
  
    runs-on: ubuntu-latest

    steps:
      # 1. 자바 소스 코드를 가져옴
      - name: checkout 
        uses: actions/checkout@v3

      # 2. jdk 설정
      - name: set up jdk 17 
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      # 3. application.yml 파일의 환경 변수 값을 채움
      - name: set yml
        uses: microsoft/variable-substitution@v1
        with:
          files: ./src/main/resources/application.yml # yml 파일 경로 지정
        env: # 깃허브 secrets로 설정한 값들
          spring.datasource.url: ${{ secrets.DATASOURCE_URL }}
          spring.datasource.username: ${{ secrets.DATASOURCE_USERNAME }}
          spring.datasource.password: ${{ secrets.DATASOURCE_PASSWORD }}
          jwt.secret: ${{ secrets.JWT_SECRET }}

      # 4. 빌드
      - name: build with gradle
        run: |
          chmod +x ./gradlew # 빌드 권한 부여
          ./gradlew build -x test # 테스트를 제외하고 빌드

      # 5. 도커 로그인
      - name: docker login
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      # 6. 도커 이미지 빌드 후 도커 허브에 푸시
      - name: docker image build
        run: |
          docker build -t ${{ secrets.USERNAME }}/funfit_community .
          docker push ${{ secrets.USERNAME }}/funfit_community

      # 7. 도커 허브에서 이미지 풀 받아온 후 컨테이너로 실행
      - name: deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_HOST }} # EC2 공인 ip 주소
          username: ubuntu # EC2 호스트 이름
          key: ${{ secrets.EC2_PRIVATE_KEY }} # EC2 pem 값
          script: |
            sudo docker pull ${{ secrets.DOCKER_USERNAME }}/funfit_community
            docker rm -f $(docker ps -qa) # 실행 중이던 모든 컨테이너 종료
            docker run -d --name redis -p 6379:6379 redis # redis 실행
            docker run -d --name funfit_community -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/funfit_community:latest # 자바 프로그램 실행

 

이때 주의할 점은 다음과 같다.

  • 깃허브에 올라가서는 안되는 주요 정보들은 application.yml 파일에 ${DATASOURCE_URL}과 같이 환경 변수로 설정하고, 이를 깃허브 secrets에 등록해야 한다.
  • application.yml 파일의 spring.datasource.url 값은 rds의 엔드포인트, 포트 번호, DB 이름을 기반으로 구성된다.
    • jdbc:mysql://{앤드포인트}:{포트번호}/{DB 이름}?useSSL=false&allowPublicKeyRetrieval=true
    • 이때 DB 이름은 rds를 생성할 때 추가 구성에서 초기 데이터베이스 이름으로 설정한 값이다.
  • 도커 이미지를 빌드하고 도커 허브에 push하기 위해서, 다음과 같이 이미지를 빌드해줘야 한다.
    • docker build -t ${{ secrets.USERNAME }}/funfit_community .
  • 그리고 다음과 같이 push 하면, {도커 허브 유저 이름}/funfit_community라는 이름의 레포지토리에 기본 값인 latest라는 태그명으로 올라가게 된다.
    • docker push ${{ secrets.USERNAME }}/funfit_community
  • 도커 허브에서 이미지를 pull 받아와서 실행시킬 때는, 기존에 실행 중이던 모든 컨테이너를 종료시켜야 한다.
  • redis와 자바 프로그램, 총 2개의 컨테이너를 실행시킬 건데, 이때 redis를 먼저 띄워야 한다.
  • 컨테이너 실행 명령어는 다음과 같다.
    •  docker run -d --name {컨테이너 이름} -p {호스트 포트}:{컨테이너 포트} {이미지 이름}
    • 즉, docker run -d --name redis -p 6379:6379 redis 라는 명령어는 redis라는 이름의 이미지를 redis라는 이름의 컨테이너로 실행시키고, 호스트 포트의 6379를 컨테이너 포트인 6379와 연결한다는 의미이다. 

4. ssh에서 EC2 서버 확인

깃허브 액션이 성공적으로 완료되었으면 ssh로 EC2 서버에 접속하여 확인해보자.

  • dokcer images 명령어를 통해 pull 받아온 이미지들을 확인할 수 있다.
  • docker ps 명령어를 통해 현재 실행 중인 컨테이너들을 확인할 수 있다.
  • docker logs {컨테이너 이름} 명령어를 통해 실행 중인 프로그램 로그를 볼 수 있다.

5. 웹으로 EC2 서버 접속

ssh로 EC2 서버에 정상적으로 컨테이너가 구동 중임을 확인했으면, 이제 웹으로 접속해보자. 이때 주의할 점은 인바운드 규칙 설정을 통해 포트를 열어주어야 한다는 것이다. 내 EC2 서버에는 자바 프로그램을 담은 컨테이너가 구동 중인데, 이 컨테이너의 포트는 8080이며, 호스트 포트의 8080을 통해 이 컨테이너에 접속할 수 있다. 따라서 EC2 서버의 8080 포트를 열어주어야 한다.

 

EC2 서버의 공인 IP 주소:8080를 웹브라우저에 치면 정상적으로 자바 프로그램에 접속이 가능하다.

마주한 문제

참고로 처음에는 EC2 서버에서 redis와 자바 프로그램을 컨테이너로 띄울 때, 하나씩 run 명령어를 치지 않고 도커 컴포즈를 사용하려고 했었다. 그리고 로컬 서버에서는 docker-compose 명령어를 통해 docker-compose.yml 파일에 설정한 대로 여러 개의 컨테이너가 순차적으로 잘 실행되었다.

 

그런데 문제는 EC2 서버에서는 docker-compose 명령어를 실행하면 docker-compose.yml 파일을 인식하지 못하고 있었다. 그러던 중 나와 같은 문제를 겪었던 이 블로그를 찾았다. 블로그 말에 따르면, 도커 허브로부터 이미지를 pull 받은 후 도커 컴포즈를 실행시키는 상황에서, 이미 도커 컴포즈 파일이 이미지화된 상태이기 때문에 컴포즈 파일을 찾지 못하는 거라고 한다. 따라서 도커 컴포즈를 사용하지 않고 하나씩 run 명령어를 통해 실행시켜주었다. 이 문제로 이틀을 고생했는데.. 위 블로그 글을 발견하게 되어서 참 다행이다.

'Backend > Docker' 카테고리의 다른 글

도커 네트워크  (0) 2024.06.25
도커 알아보기  (0) 2024.06.25
도커(Docker) 사용하기  (1) 2024.04.24