Docker의 공식 홈페이지에서는 다음과 같이 도커를 소개하고 있다.
Docker is a platform designed to help developers build, share, and run modern applications. We handle the tedious setup, so you can focus on the code.
도커는 컨테이너를 기반으로 어플리케이션을 쉽게 빌드하여 배포할 수 있도록 돕는 가상화 플랫폼이다. 다양한 언어, 프레임워크, 아키텍처 등이 등장하면서 서버마다 설정된 방식이 조금만 달라도 문제가 발생하는 경우가 많았는데, 이를 해결하기 위해 등장하였다.
도커 이미지를 올리기 위해 도커 레포지토리를 생성해야 한다. 도커 허브에 접속하여 회원가입 후 Create repository 클릭.
레포지토리의 이름을 작성하고 public으로 설정한 뒤레포지토리를 생성한다.
Dockerfile은 도커 커스텀 이미지를 생성할 수 있도록 돕는 이미지 설정파일이다. 이를 작성하여 빌드를 진행하면 docker가 자동으로 dockerfile의 명령어들을 인식해서 조건에 맞는 이미지를 생성하게 된다.
FROM openjdk:11-jre-slim
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
해당 포스트에서는 EC2 인스턴스를 생성하여 진행하였다.
EC2 인스턴스를 생성하는 방법은 다음 포스트에서..
참고로 아래 명령어는 Amazon Linux 2를 기준으로 한다.
서버에 접속하여 docker를 설치한다.
sudo yum install docker
docker를 실행하자. (sudo 명령어를 사용해서 설치했기 때문에 이후 명령어에서도 계속 sudo를 앞에 붙여야 한다)
sudo systemctl start docker
아래 명령어를 통해 docker에서 실행되는 컨테이너를 확인할 수 있다.
sudo docker ps
이제 Github Actions에서 자동 배포를 설정해보자.
참고로, Github Actions는 CI/CD를 위해 github에서 제공하는 서비스로, 코드 빌드, 테스트, 배포 등과 같은 반복적인 과정을 자동화하는 데에 도움을 준다. 특정 이벤트가 발생할 때 작업을 실행하거나 주기적으로 반복적인 작업을 하도록 실행할 수 있다.
깃허브의 프로젝트 레포지토리에 들어가 Actions 탭을 클릭한다.
검색창에 java with gradle이라고 치면 세번째에 Java with Gradle이라는 workflow가 있다.
configure를 누르면 아래와 같은 gradle.yml 파일이 나타난다.
프로젝트에 맞기 해당 부분을 변경하는 작업을 진행한다.
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Java CI with Gradle # workflow 이름
on:
push:
branches: [ "main" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: make application.yml
run: |
# create application.yml
cd ./src/main
mkdir resources
cd ./resources
# application.yml 파일 생성하기
touch ./application.yml
# Secrets에 저장한 값을 application.yml 파일에 쓰기
echo "${{ secrets.DATABASE }}" >> ./application.yml
shell: bash
- name: Build with Gradle
run: ./gradlew bootJar
## 도커 이미지 빌드 후 도커허브에 push하기
- name: web docker build and push
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_REPO }} .
docker push ${{ secrets.DOCKER_REPO }}
## 서버에 접속하여 도커 이미지를 pull 받고 실행하기
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v0.1.6
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.KEY }}
port: 22
script: |
sudo docker stop prod-server
sudo docker rm prod-server
sudo docker image rm ${{ secrets.DOCKER_REPO }}
sudo docker pull ${{ secrets.DOCKER_REPO }}
sudo docker run -d -p 80:8080 ${{ secrets.DOCKER_REPO }}
위 코드를 하나씩 뜯어보자.
먼저 main 브랜치에 push 될 때에만 이 workflow가 돌아가도록 하기 위해 아래와 같이 pull_request에 대한 부분을 없앤다.(아래에서는 제거해야 하는 코드를 보여주기 위해 주석 처리 형태로 진행한다)
on:
push:
branches: [ "main" ]
# pull_request:
# branches: [ "main" ]
jobs>build>steps의 하위에 있는 아이들은 이 workflow를 실행했을 때 자동적으로 진행되는 과정이 담긴 부분이다. 먼저 아래 부분은 jdk를 설정하는 구간이다.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
그다음 .gitignore가 적용되었던 application.yml 파일을 작성한다. 이때 ${{ secrets.[이름] }}
은 secrets에 저장된 값의 키를 의미한다.
- name: make application.yml
run: |
# create application.yml
cd ./src/main
mkdir resources
cd ./resources
# application.yml 파일 생성하기
touch ./application.yml
# Secrets에 저장한 값을 application.yml 파일에 쓰기
echo "${{ secrets.DATABASE }}" >> ./application.yml
shell: bash
secrets는 깃허브 레포지토리의 settings에 들어가면 찾을 수 있다.
New repository secret을 누르면 아래와 같은 화면이 나오는데 Name에는 말그대로 secrets의 이름을 입력하고, Secret에는 이름에 해당하는 비밀 정보들을 넣으면 된다. application.yml 파일을 넣고 싶을 때는 파일 전체 코드를 복붙해서 넣으면 된다.
위와 같이 설정을 해두면 workflow가 실행될 때 깃헙에서 자동적으로 해당 정보를 넣어서 작업을 진행한다.
다음은 도커 이미지를 빌드하여 도커 레포지토리에 푸시하는 구간이다. 먼저 도커 허브의 ID와 Password를 입력하여 도커에 로그인한다. 그후 docker로 빌드를 진행하는데 DOCKER_REPO는 [도커허브ID]/[레포지토리명]의 형태로 작성하면 된다. 그다음 도커 레포지토리에 push하면 이미지가 도커 허브에 올라간다.
- name: Build with Gradle
run: ./gradlew bootJar
## 도커 이미지 빌드 후 도커허브에 push하기
- name: web docker build and push
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_REPO }} .
docker push ${{ secrets.DOCKER_REPO }}
그 다음은 서버에 접속하여 도커 이미지를 pull 받고 실행하는 작업이다. HOST에 인스턴스 주소를, KEY에 pem키를 넣는다. 참고로 pem키는 터미널에 드래그 앤 드랍을 하면 확인할 수 있는데 ---으로 시작하는 부분부터 ---로 끝나는 부분까지 전부 복사해서 secrets에 저장하면 된다.
## 서버에 접속하여 도커 이미지를 pull 받고 실행하기
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v0.1.6
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.KEY }}
port: 22
script: |
sudo docker stop prod-server
sudo docker rm prod-server
sudo docker image rm ${{ secrets.DOCKER_REPO }}
sudo docker pull ${{ secrets.DOCKER_REPO }}
sudo docker run -d -p 80:8080 ${{ secrets.DOCKER_REPO }}
scripts에서는 서버에 접속 후 시행할 명령어들이 한 줄씩 적혀있다. 현재 돌아가고 있는 컨테이너를 멈추고 삭제한 뒤 앞에서 빌드한 이미지를 pull 받고 컨테이너를 실행하는 과정이다. 마지막 명령어에서 -p 80:8080
은 -p(포트) 호스트에서 사용하는 포트:컨테이너에서 사용하는 포트
를 의미하고, -d
는 무중단 배포를 위해 넣은 명령어이다.
모든 변경이 끝난 후 이 파일을 메인 브랜치에 push하게 되면 자동 배포가 진행되고 Actions에서 아래와 같이 성공적으로 배포되었음을 확인할 수 있다.
참고 사이트