Docker - Docker + Spring Boot + Github Action CICD
- -
개요
github push 이벤트 발생 시 github action을 사용해 Docker에 띄워놓은 spring boot 컨테이너에 이미지를 빌드하여 CICD 환경을 구축하는 작업
준비물
- 도메인으로 ssh 접속이 가능하고 docker, docker-compose가 정상 설치된 서버
- docker hub 계정
- spring boot 프로젝트 repository
서버 디렉토리 구조
~.ssh/
├── project
│ └── docker-compose.yml
│ └── nginx
│ └── default.conf
└── authorized_keys
~.ssh/project/docker-compose.yml 파일 작성
version: '3' # Docker Compose의 버전 지정
services: # 서비스 정의 시작
nginx: # nginx 서비스 정의
image: nginx:latest # 사용할 이미지 지정 (최신 버전)
volumes: # 볼륨 설정 - 호스트의 nginx/default.conf 파일을 컨테이너 내의 /etc/nginx/nginx.conf 파일로 마운트
- ./nginx/default.conf:/etc/nginx/nginx.conf
restart: always # 컨테이너가 비정상적으로 종료될 경우 항상 재시작
ports: # 포트 포워딩 설정 - 호스트의 80 포트와 443 포트를 컨테이너의 80 포트와 443 포트로 매핑
- 80:80
- 443:443
networks: # 네트워크 설정 - webnet 네트워크에 연결
- webnet
spring: # spring 서비스 정의
container_name: 프로젝트명 # 컨테이너 이름 설정
image: 도커허브사용자명/프로젝트명:latest # 사용할 이미지 지정 (도커 허브에 등록된 이미지)
ports: # 포트 포워딩 설정 - 호스트의 8080 포트를 컨테이너의 8080 포트로 매핑
- "8080:8080"
networks: # 네트워크 설정 - webnet 네트워크에 연결
- webnet
networks: # 네트워크 정의 시작
webnet: # webnet 네트워크 정의
# 서비스 간의 커뮤니케이션을 위한 도커 내장 네트워크, 별도 설정 없이 기본 값으로 사용
!!!!!!!!!주의!!!!!!!!! - yml 코드 내 도커허브사용자명, 프로젝트명 부분 꼭 수정할 것
~.ssh/project/nginx/default.conf 작성
events {
worker_connections 1024;
}
http {
server {
listen 80; # 80번 포트에서 요청 수신
server_name 도메인;
location / {
proxy_pass http://프로젝트명:8080; # 요청을 Spring 서비스로 프록시
proxy_set_header Host $host; # 요청의 호스트 헤더 설정
proxy_set_header X-Real-IP $remote_addr; # 클라이언트의 실제 IP 설정
}
}
}
!!!!!!!!!주의!!!!!!!!! - yml 코드 내 도메인, 프로젝트명 부분 꼭 수정할 것
Github Action workflow 등록
name: CI/CD Pipeline # GitHub Actions 워크플로우의 이름
on:
push: # master 브랜치로의 푸시 이벤트 발생 시 워크플로우 실행
branches:
- master
pull_request: # master 브랜치로의 풀 리퀘스트 이벤트 발생 시 워크플로우 실행
branches:
- master
jobs:
build: # build라는 이름의 job 정의
runs-on: ubuntu-latest # 최신 버전의 Ubuntu에서 실행
steps: # job을 구성하는 단계들 정의
- name: Checkout repository # Step 1: 코드 저장소 체크아웃
uses: actions/checkout@v3 # 최신 버전(v3)의 checkout 액션 사용
- name: JDK 21 설정 # Step 2: JDK 21 설정
uses: actions/setup-java@v4 # 버전 4의 setup-java 액션 사용
with:
java-version: '21' # JDK 21 버전 지정
distribution: 'temurin' # Temurin 배포판 사용
java-package: 'jdk' # JDK 패키지 설치
check-latest: false # 최신 버전 확인 안 함
server-id: 'github' # GitHub 서버 ID 설정
server-username: ${{ github.actor }} # GitHub 액터의 사용자 이름 사용
server-password: ${{ secrets.GITHUB_TOKEN }} # GitHub 토큰 사용
overwrite-settings: true # 기존 설정 덮어쓰기
- name: Cache Gradle packages # Step 3: Gradle 패키지 캐시
uses: actions/cache@v3 # 최신 버전(v3)의 캐싱 액션 사용
with:
path: ~/.gradle # Gradle 패키지의 캐시 경로
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} # Gradle 파일의 해시를 기반으로한 캐시 키
restore-keys: ${{ runner.os }}-gradle # 캐시 복원 키
- name: Build with Gradle # Step 4: Gradle로 프로젝트 빌드
run: ./gradlew clean build # Gradle의 clean 및 build 명령 실행
- name: Login to Docker Hub # Step 5: Docker Hub 로그인
run: docker login -u "${{ secrets.DOCKER_USERNAME }}" -p "${{ secrets.DOCKER_PASSWORD }}" # GitHub Secrets에서 저장한 Docker 자격 증명 사용
- name: Build Docker image # Step 6: 어플리케이션용 Docker 이미지 빌드
run: |
docker build . -t ${{ secrets.DOCKER_USERNAME }}/프로젝트명:${{ github.sha }} # GitHub SHA로 태그한 Docker 이미지 빌드
docker tag ${{ secrets.DOCKER_USERNAME }}/프로젝트명:${{ github.sha }} ${{ secrets.DOCKER_USERNAME }}/프로젝트명:latest # Docker 이미지를 'latest'로 태그
- name: Push Docker image to Docker Hub # Step 7: Docker 이미지를 Docker Hub에 푸시
run: |
docker push ${{ secrets.DOCKER_USERNAME }}/프로젝트명:${{ github.sha }} # GitHub SHA로 태그된 Docker 이미지 푸시
docker push ${{ secrets.DOCKER_USERNAME }}/프로젝트명:latest # 'latest' Docker 이미지 푸시
- name: Deploy to server # Step 8: 서버에 어플리케이션 배포
uses: appleboy/ssh-action@v1.0.3 # SSH 액션(v1.0.3)을 사용하여 SSH로 배포
with:
host: ${{ secrets.DEPLOY_SERVER }} # GitHub Secrets에서 저장한 서버 호스트명 또는 IP 주소 사용
username: ${{ secrets.SSH_USER }} # GitHub Secrets에서 저장한 SSH 사용자 이름 사용
key: ${{ secrets.SSH_PRIVATE_KEY }} # GitHub Secrets에서 저장한 SSH 개인 키 사용
port: 22 # SSH 포트
script: |
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/프로젝트명:${{ github.sha }} # 특정 태그의 Docker 이미지 풀
sudo docker stop 프로젝트명 || true # 실행 중인 Docker 컨테이너 중지 (오류 무시)
sudo docker rm 프로젝트명 || true # Docker 컨테이너 삭제 (오류 무시)
sudo docker run -d -p 8080:8080 --network 프로젝트명_webnet --name 프로젝트명 ${{ secrets.DOCKER_USERNAME }}/프로젝트명:${{ github.sha }} # 지정된 옵션으로 Docker 컨테이너 실행
!!!!!!!!!주의!!!!!!!!! - yml 코드 내 프로젝트명 부분 꼭 수정할 것
Github Repository Secrets 등록
repository -> Settings -> Secrets and Variables -> Actions
등록할 Secrets 목록
1. DEPLOY_SERVER - 서버 주소(ip, 도메인 중 택 1)
2. SSH_USER - SSH 사용자명
3. DOCKER_USERNAME - docker hub 유저명
4. DOCKER_PASSWORD - docker hub 비밀번호
5. SSH_PRIVATE_KEY - 서버 SSH 접속 시 사용하는 private key
서버 authorized_keys 설정
cd ~.ssh
vi authorized_keys ## 서버 SSH 접속 시 사용하는 public key 복붙 후 저장
chmod 600 authorized_keys
실행 및 마무리
위의 세팅을 마치고 ~.ssh/project 경로에서 docker compose up 명령어를 사용하면 spring 이미지 빌드 시 에러가 발생할 겁니다. 우선 git push 이벤트를 발생시켜 docker hub에 도커허브사용자명/프로젝트명:latest 이미지를 push 하고 그 후에 docker compose up 명령어를 사용해보세요. 물론 그렇게 하면 또 docker 에러가 발생할텐데 도커 컨테이너명이 겹쳐서 발생하는 오류니 이케이케 해결하시면 됩니다.
해결 하셨다면 docker compose up 명령어를 사용해보세요. 에러가 발생한다구요? 오타 나신거 아니에요? 브라우저에서 접속이 안 된다구요? 포트 안 열어놓은거 아니세요? 하하 화이팅입니다.
여차저차해서 nginx 컨테이너와 spring 컨테이너를 띄워 서버가 잘 돌아간다면 그 이후로는 master 브런치로 push 이벤트 발생 시 잘 빌드가 될 거예요.
간단 실행 순서
1. git push 이벤트 발생 시키기
2. 서버 ~.ssh/project 경로 접속
3. docker compose down 프로젝트명
4. docker rm 프로젝트명
5. docker compose up
6. git push 이벤트 발생 시키기 (cicd 최종 테스트)
소중한 공감 감사합니다