Docker를 사용한 배포를 직접 해보면서 뭔가 이미지 파일들과 도커 허브를 잘 이용하면 자동화된 배포가 가능할 것 같았습니다.
기존에는 프로젝트를 진행하면서, 배포에 많은 시간을 사용했습니다. Main Branch에 Merge를 하면 WAS가 올려진 서버에 직접 SSH 접속해 이를 다시 받아오고, DB 변경 사항이나 배포용 환경설정에서의 Conflict 등을 직접 다룬다는 게, 프로젝트의 핵심이 되는 비지니스 로직에만 집중하지 못하고, 자꾸 밖으로 힘이 샌다는 느낌이 강하게 들었습니다.
변경 사항을 직접 테스트하고 빌드하고 이를 EC2 서버에 Deploy하는 과정을 한번에 해준다... 정말 멋진 자동화 프로세스입니다. 개발자는 항상 효율을 따지는 사람들 인 것 같습니다.
결과적으로 전부터 쭉 궁금했던 Git Action을 이용한 CI/CD를 개인 프로젝트에 적용하는 경험을 이번에 했습니다.
참고
CI 지속적인 통합 : 코드 변경이 GitHub에 푸시되면, GitHub Actions가 자동으로 테스트를 실행하고, 빌드를 생성합니다.
CD 지속적 배포 : 빌드가 성공하면 자동으로 애플리케이션이 프로덕션 서버에 배포됩니다.
그럼 본격적으로 딸깍 배포를 위한 Devops 환경 설정을 진행 해보겠습니다.
0. 기초적 구상
Git Action은 하나의 서버를 빌린다고 생각하면 됩니다.
즉, 이 원격 서버에서 제 프로젝트의 도커 이미지들을 (저의 경우 nginx, springboot) 새롭게 빌드하고
이 이미지를 개인 도커 허브에 Push합니다.
이후 ssh를 이용해 EC2에 원격으로 접속한 뒤 (원격 서버에서 원격 서버에 접속하는..)
Docker hub에서 이미지를 pull 받은 뒤 기존 컨테이너들을 내리고 새로운 이미지의 컨테이너를 실행하는 방식입니다.
1. Docker image 생성을 위한 Dockerfile과 docker-compose.yml 파일 작성
Dockerfile은 이전 글과 동일합니다.
변경점은 Https 설정을 위한 Volume 설정 추가
version: "2"
services:
nginx:
image: seogwoojin1/springboot-nginx:0.1
ports:
- 80:80
depends_on:
- springboot-app
networks:
- mynetwork
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
springboot-app:
image: seogwoojin1/demo-docker-springboot:0.1 # 올바른 이미지 이름을 사용합니다.
container_name: demo-docker-springboot
environment:
- DATABASE_URL=${DATABASE_URL}
- DATABASE_USER=${DATABASE_USER}
- DATABASE_PASSWORD=${DATABASE_PASSWORD}
expose:
- 8080
networks:
- mynetwork
networks:
mynetwork:
2. Git Action Workflow 파일 작성하기
사실상. 이 글의 알파이자 오메가
비지니스 관점에서 본다고 가정하면 이 워크플로우 파일이 핵심 로직이라고 생각하면 될 것 같습니다.
name: Build and Push Docker Images
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up environment variables
run: |
echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> $GITHUB_ENV
echo "DATABASE_USER=${{ secrets.DATABASE_USER }}" >> $GITHUB_ENV
echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> $GITHUB_ENV
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -x test
- name: Build and push springboot-nginx image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/springboot-nginx:0.1 --file ./nginx/nginx_Dockerfile ./nginx
docker push ${{ secrets.DOCKER_USERNAME }}/springboot-nginx:0.1
- name: Build and push demo-docker-springboot image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/demo-docker-springboot:0.1 .
docker push ${{ secrets.DOCKER_USERNAME }}/demo-docker-springboot:0.1
- name: Run scripts on server
uses: appleboy/ssh-action@master
with:
key: ${{ secrets.EC2_SSH_KEY }}
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/springboot-nginx:0.1
docker pull ${{ secrets.DOCKER_USERNAME }}/demo-docker-springboot:0.1
docker-compose down
docker container prune -f
docker-compose up -d
docker image prune -a -f
이 워크 플로우 yml 파일은 반드시 이 경로에 위치해야 합니다.
하나 씩 뜯어 보도록 하겠습니다.
name: Build and Push Docker Images
on:
push:
branches:
- main
메인 브랜치에 Push가 들어왔을 때 실행
jobs:
build:
runs-on: ubuntu-latest
원격 서버 운영체제 : ubuntu
steps:
- uses: actions/checkout@v3
기존 깃액션 라이브러리를 이용해 내 Git 폴더 전체 복붙
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
스프링을 빌드해야 하니 버전에 맞는 JDK 다운로드 (역시 라이브러리)
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
도커 빌드와 도커 업로드를 위한 도커 및 도커 허브 다운로드 + 로그인
- name: Set up environment variables
run: |
echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> $GITHUB_ENV
echo "DATABASE_USER=${{ secrets.DATABASE_USER }}" >> $GITHUB_ENV
echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> $GITHUB_ENV
DB 접속 정보를 위한 환경 변수 설정
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -x test
gradlew를 이용해 테스트를 제외한 jar 파일 새롭게 빌드
- name: Build and push springboot-nginx image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/springboot-nginx:0.1 --file ./nginx/nginx_Dockerfile ./nginx
docker push ${{ secrets.DOCKER_USERNAME }}/springboot-nginx:0.1
nginx 이미지 생성 후 hub에 push
- name: Build and push demo-docker-springboot image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/demo-docker-springboot:0.1 .
docker push ${{ secrets.DOCKER_USERNAME }}/demo-docker-springboot:0.1
앞서 새롭게 빌드한 jar 파일로 springboot 이미지 생성후 hub에 push
- name: Run scripts on server
uses: appleboy/ssh-action@master
with:
key: ${{ secrets.EC2_SSH_KEY }}
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/springboot-nginx:0.1
docker pull ${{ secrets.DOCKER_USERNAME }}/demo-docker-springboot:0.1
docker-compose down
docker container prune -f
docker-compose up -d
docker image prune -a -f
깃액션 라이브러리를 이용해 EC2 SSH 접속
접속한 후 새로 만든 이미지들 Pull 받고 컨테이너 새롭게 실행
참고로 ${{secrets}}는
Secrets and variables에서 등록할 수 있습니다.
3. push 후, 전체 과정과 결과 확인하기
이렇게 yml 파일을 등록 후, main에 새롭게 push가 들어가게 된다면..!
앞서 작성한 yml 설명서에 맞춰 원격서버가 순차적으로 실행시킵니다.
이후 제 EC2 서버가 띄우고 있는 사이트에 접속하면
다음과 같이 새로운 버전으로 업데이트되어 잘 돌아가고 있음을 확인할 수 있습니다.
느낀점
말은 이렇게 쉽게 했지만, 너무나 많은 에러와 마주했습니다.
(씨름한 시간으로 따지면 일주일이 넘지만, 빌드는 고작 2분,,🤣)
기본적으로 이 모든 배포 프로세스 동작 방식을 단 하나도 빠짐없이 이해를 하고 있어야 workflow 파일 작성이 가능한 수준이고, 부족한 지식으로 인해 이후 파생되는 에러는 덤이죠.
일단 workflow의 모든 흐름을 직접 볼 수 없는게 힘들었습니다. Build 과정에서 실패하면, 에러 로그말고는 참고할 수단이 없습니다. 원격에서 돌아가니깐요.
기존에 EC2 SSH로 접속해 직접하는 배포는 한 단계 한 단계 진행하며 에러를 딱 마주하면 그 command가 문제임을 직감할 수 있지만 여긴 그렇지 않았습니다.
그래도 이런 시행착오 속에서 스프링의 빌드 실행 과정, 도커와 도커 컴포즈에 대한 더 깊이 있는 이해, 원격 커맨드 실행 등에 대해 부담없이 배울 수 있었습니다.
그냥 EC2에 접근해서 진행하는 배포도 불과 반년 전에 어려웠했다면, 이젠 간단하게 해낼 것 같습니다.
'인프라' 카테고리의 다른 글
개인 프로젝트 AWS RDS + EC2 (Nginx + SpringBoot) docker-compose 배포 (0) | 2024.07.09 |
---|