저번에 방학 동안 프로젝트를 했을 때 github action과 CodeDeploy를 사용하여 CI/CD를 구축했었습니다.
그때는 AWS를 사용해서 AWS에 제일 최적화한 것이 CodeDeploy이기 때문에 codeDeploy를 선택했었습니다.
이번에 혼자 해보는 프로젝트에서는 docker를 사용해 보려고 합니다.
Docker란?
- Docker는 컨테이너화된 애플리케이션을 만들고 실행하기 위한 오픈 소스 플랫폼입니다. 컨테이너는 애플리케이션과 해당 애플리케이션을 실행하는 데 필요한 모든 종속성(라이브러리, 환경 설정 등)을 포함하는 완전한 실행 환경입니다.
- Docker를 사용하면 애플리케이션을 컨테이너로 패키징 할 수 있으며, 이 컨테이너는 호스트 시스템에서 독립적으로 실행될 수 있습니다. 컨테이너는 가볍고 빠르며, 동일한 환경에서 일관성 있게 실행될 수 있습니다. 또한, Docker는 호스트 시스템의 운영 체제와 독립적으로 동작하므로, 다양한 운영 체제에서 동일한 컨테이너를 실행할 수 있습니다.
이미지와 컨테이너란?
- 이미지 (Image)
- Docker 이미지는 애플리케이션과 그 애플리케이션을 실행하는 데 필요한 모든 것들을 포함하는 패키지입니다.
- 이미지는 읽기 전용이며, 컨테이너를 생성하는 데 사용됩니다.
- 일반적으로 Docker 이미지는 여러 개의 레이어로 구성되며, 각 레이어는 파일 시스템의 변경 사항을 나타냅니다.
- Docker 이미지는 다른 이미지를 기반으로 만들 수 있으며, Dockerfile이라는 텍스트 파일에 명령어를 기록하여 이미지를 정의합니다.
- Dockerfile에는 기본 이미지, 애플리케이션의 종속성 설치, 환경 설정 등이 포함된다.
- Docker 이미지는 버전 관리가 가능하며, 버전 태그를 통해 특정 버전의 이미지를 지정할 수 있습니다.
- Docker 이미지는 Docker Hub와 같은 온라인 이미지 레지스트리에 저장되어 공유할 수 있으며, 개발자들은 필요한 이미지를 가져와 사용하거나, 직접 이미지를 빌드하여 공유할 수 있습니다.
- 컨테이너 (Container)
- Docker 컨테이너는 이미지의 인스턴스입니다.
- 컨테이너는 이미지를 기반으로 생성되며, 실행 중인 애플리케이션을 포함합니다.
- 컨테이너는 격리된 프로세스로, 호스트 시스템과 독립적으로 실행됩니다.
Docker 이미지는 애플리케이션과 그 실행에 필요한 모든 것을 포함하는 패키지이고, 컨테이너는 이미지의 인스턴스로, 격리된 환경에서 애플리케이션을 실행합니다. Docker를 사용하면 이미지와 컨테이너를 통해 애플리케이션을 쉽게 패키징, 배포 및 실행할 수 있습니다.
Docker와 CodeDeploy의 차이
Docker의 장점
- 일관성 : Docker는 컨테이너를 사용하여 환경 일관성을 제공하므로, 개발, 테스트, 프로덕션 환경에서 동일한 환경을 유지할 수 있습니다.
- 이식성 : Docker 컨테이너는 운영 체제와 독립적으로 실행될 수 있어, 다양한 환경에서 쉽게 이식할 수 있습니다.
- 속도와 확장성 : 가볍고 빠르며, 여러 단계와 작업을 병렬로 실행할 수 있어 배포 속도와 효율성을 높일 수 있습니다.
- 격리와 안정성 : 각 컨테이너는 격리되어 있으므로, 한 컨테이너의 문제가 다른 컨테이너에 영향을 주지 않고 안정성이 높아집니다.
- 확장성과 유연성 : 각각의 컨테이너로 구성 요소를 관리할 수 있어, 도구, 라이브러리, 데이터베이스 등을 독립적으로 관리하고 업데이트할 수 있습니다.
AWS CodeDeploy의 장점
- 서비스 제공 : AWS CodeDeploy는 클라우드 기반의 서비스로 제공되어, 손쉽게 사용할 수 있습니다.
- 확장성 : AWS CodeDeploy는 AWS의 다른 서비스와 통합되어 확장성이 뛰어나며, 다양한 배포 옵션을 제공합니다.
- 배포 전략 : CodeDeploy는 다양한 배포 전략을 지원하여 롤백, 롤아웃, 블루-그린 배포 등을 쉽게 구현할 수 있습니다.
- 모니터링 : CodeDeploy는 배포 과정을 모니터링하고, 오류를 자동으로 처리하고 복구할 수 있는 기능을 제공합니다.
- 다양한 플랫폼 지원 : CodeDeploy는 다양한 플랫폼과 언어를 지원하여 다양한 애플리케이션에 대해 사용할 수 있습니다.
Github action 및 AWS CodeDeploy로 CI/CD 구축 보라 가기
두 개의 차이점
- 기술 스택 : Docker는 애플리케이션 컨테이너화 기술에 초점을 두고 있으며, CodeDeploy는 배포 자동화 및 관리에 특화되어 있습니다.
- 종속성 : Docker는 컨테이너 이미지에 애플리케이션과 종속성을 포함시키는 반면, CodeDeploy는 애플리케이션 배포를 위해 코드 저장소와 통합하여 사용합니다.
- 배포 환경 : Docker를 사용하는 경우에는 자체 인프라스트럭처에 Docker 환경을 구축해야 하지만, CodeDeploy는 AWS 인프라스트럭처에 직접 구축됩니다.
Docker의 CI/CD 과정
Docker의 CI/CD 과정을 그림으로 한번 표시해 봤습니다.
- 인텔리제이 프로젝트에 Docker file을 만든 후 git에 push 합니다.
- Dockerfile을 빌드해서 docker image 파일을 생성합니다.
- docker image 파일을 docker hub에 push 합니다.
- EC2에서 docker hub에 있는 docker image 파일을 pull 해서 받아옵니다.
- EC2에 있는 컨테이너가 image 파일을 실행시킵니다.
EC2에서 Docker 설치
CodeDeploy는 AWS에서 지원해 주는 방법이라 EC2에 따로 설치해 줄 필요가 없었습니다. 따라서 환경세팅을 할 게 없었습니다. 하지만 docker를 EC2에 설치해줘야 합니다.
참고 : https://insight.infograb.net/docs/aws/installing-docker-on-aws-ec2/
Repository 구성
sudo apt-get update
// apt가 HTTPS을 통해 repository를 사용할 수 있도록 필요한 패키지들을 설치한다.
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 apt-key add -
// Stable Repository(안정 버전 저장소)를 설정하기 위해 아래 명령어를 사용한다.
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) \
stable"
// 공식 Docker 저장소로부터 설치하는 것인지 확인한다.
sudo apt-cache policy docker-ce
Docker Engine 설치
sudo apt-get update
// Docker Engine과 containerd 최신 버전을 설치한다.
sudo apt-get install docker-ce docker-ce-cli containerd.io
//hello-world 이미지를 구동하여 Docker CE가 정상적으로 설치되었는지 확인한다.
sudo docker run hello-world
//docker가 잘 설치되었는지 확인한다
docker --version
트러블슈팅
나중에 github action으로 docker에 접근할 때 접근할 수 없다는 에러가 뜹니다. 이것은 외부에서 나의 EC2 도커에 접근할 수 없기 때문입니다. 위에서 보면 모두 sudo로 처리했습니다.
즉, sudo 없이 코드들을 처리하고 싶다.
sudo usermod -a -G docker ubuntu
이 명령어를 실행하면 됩니다. 하지만 에러가 뜹니다. Docker 데몬 소켓에 연결하는 동안 권한이 거부되었다는 에러가 발생합니다.
sudo chmod 666 /var/run/docker.sock
그래서 권한을 변경시켜 주면 됩니다.
Docker 파일 생성
Docker 파일은 root에 만들어줘야 합니다. 인텔리제이에서 제일 상단 폴더에 만들어주면 됩니다.
FROM openjdk:17-oracle
COPY ./build/libs/My_Spring_Project-0.0.1-SNAPSHOT.jar My_Spring_Project.jar
ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=prod","My_Spring_Project.jar"]
- FROM
- 내 프로젝트는 자바 17이므로 openjdk:17-oracle을 사용할 것입니다.
- COPY
- COPY는 왼쪽에 있는 파일을 오른쪽에 복사해 주는 명령어입니다.
- ENTRYPOINT
- EC2에서 실행할 명령어입니다.
docker-compose.yml 파일
docker-compose.yml은 컨테이너들을 관리해 주는 역할을 합니다.
이 프로젝트에는 컨테이너를 하나만 사용합니다. Redis 및 다른 것을 사용할 일이 지금은 없습니다.
그러므로 이 파일은 지금은 만들지 않습니다.
GithubAction의 docker-deploy.yml파일
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
- on 속성은 해당 워크플로우가 언제 실행되는지 알려줍니다.
- 나는 master에 push, pr이 올 때 실행하도록 설정했습니다.
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- 자바 17 버전을 사용한다고 설정했습니다.
# 서브 모듈 접근하기
- name: Checkout repo
uses: actions/checkout@v3
with:
token: ${{ secrets.TOKEN }}
submodules: true
# 서브 모듈 변경 점 있으면 update
- name: Git Sumbodule Update
run: |
git pull --recurse-submodules
git submodule update --remote --recursive
- 저는 프로젝트에서 submodule을 사용합니다. 그러므로 submodule 접근과 update를 해줘야 합니다.
# gradlew 권한 변경
- name: Grant execute permission for gradlew
run : chmod +x gradlew
# 빌드하기 (test부분은 빌드 안함)
- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: clean build -x test
- push나 pr을 보낼 때 현재 github에 올라간 파일을 build 합니다.
- 이때 만든 tdd는 빼고 build 하도록 설정했습니다.
- name: 도커 이미지 빌드
run: docker build -t parksewoong/test:latest .
- Docker에 이미지를 빌드합니다.
- 저는 repository이름을 이렇게 설정해 줬으므로 parksewoong/test의 제일 최근을 build 합니다.
- name: Docker - Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- DockerHub의 로그인하는 과정입니다,
- name: Docker Hub 퍼블리시
run: docker push parksewoong/test:latest
- 빌드한 내용을 parksewoong/test:latest에 push 합니다.
- name: Depoly in AWS ec2
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
sudo docker stop $(sudo docker ps -a -q)
sudo docker rm $(sudo docker ps -a -q)
sudo docker pull parksewoong/test:latest
sudo docker run -d -p 8080:8080 --name test-server parksewoong/test:latest
- 이제 위에서 만든 이미지를 EC2에 배포하는 과정입니다.
- host, username, key를 EC2에서 가져와 github의 secrets으로 설정해 줍니다.
- script가 실행되는 부분입니다
- 현재 실행하고 있는 것을 stop 하고 삭제합니다
- 그리고 지금 제일 최근 image를 dockerhub에서 가져옵니다.(pull)
- 그 후 EC2에 있는 Docker 컨테이너에서 실행을 해주면 됩니다.
전체코드
name: Java CI with Gradle
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
deploy:
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: Checkout repo
uses: actions/checkout@v3
with:
token: ${{ secrets.TOKEN }}
submodules: true
# 서브 모듈 변경 점 있으면 update
- name: Git Sumbodule Update
run: |
git pull --recurse-submodules
git submodule update --remote --recursive
# gradlew 권한 변경
- name: Grant execute permission for gradlew
run : chmod +x gradlew
# 빌드하기 (test부분은 빌드 안함)
- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: clean build -x test
# Docker
- name: 도커 이미지 빌드
run: docker build -t parksewoong/test:latest .
- name: Docker - Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Docker Hub 퍼블리시
run: docker push parksewoong/test:latest
- name: Depoly in AWS ec2
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
sudo docker stop $(sudo docker ps -a -q)
sudo docker rm $(sudo docker ps -a -q)
sudo docker pull parksewoong/test:latest
sudo docker run -d -p 8080:8080 --name test-server parksewoong/test:latest
어떻게 보면 CodeDeploy 보다 구현해야 할 코드는 적은 것 같습니다. 하지만 Redis 및 다른 요소들을 사용한다면 고려해야 할 요소들이 더 많아질 것 같습니다. 실제 프로젝트에서는 log 파일도 필요할 것이므로 이 부분도 설정해줘야 합니다.
지금은 제일 간단하게 EC2에 배포하는 것만 했는데 다른 지원해 주는 것들을 더 공부해 봐야겠습니다.
'토이프로젝트 > 나만의 프로젝트' 카테고리의 다른 글
스프링 시큐리티로 회원가입, 로그인 구현하기 (0) | 2023.06.27 |
---|---|
테스트 코드 공부 (0) | 2023.06.21 |
데이터베이스 정규화 하기 (2) | 2023.04.29 |
OAuth2.0 이란? (2) | 2023.02.07 |
SpringBoot를 이용한 이메일 인증 하기 (0) | 2023.02.06 |