위의 구조대로 cicd 배포를 진행했다. Springboot와 flask는 인스턴스를 각각 만들어 별도로 실행해주었다. 해당 프로젝트는 추천시스템을 구현해야 했기에 flask와 Springboot를 둘다 사용하였다. 해당 게시글에서는 SpringBoot 배포만 다룰 예정이다.
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
이제 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을 통해 이미지를 제거해준다.
이제 ec2 인스턴스에 docker 관련 설정을 해줘야 한다.
1. docker 설치
2. docker를 sudo 권한 없이 실행가능하도록 설정
3. docker-compose 설치
4. docker-compose.yml 파일 작성
다음 명령어를 실행해준다.
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
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
위의 스크립트를 실행하려면 sudo 없이 docker 명령어를 사용 가능해야 한다.
docker 그룹에 사용자 추가
sudo usermod -aG docker $USER
그룹 변경사항 활성화
newgrp docker
도커 재시작
sudo service docker restart
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
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 하게되면 배포 파이프라인이 시작된다.
배포가 잘 되는것을 알 수 있다.