□ Dockerfile
# 1. Gradle을 이용해 애플리케이션 빌드
FROM gradle:8.12-jdk17 AS builder
WORKDIR /app
COPY . .
RUN gradle build --no-daemon
# 2. 실행 환경
FROM openjdk:17-slim
WORKDIR /app
ENV DB_USERNAME=${DB_USERNAME}
ENV DB_PASSWORD=${DB_PASSWORD}
ENV jwt_secret_key=${jwt_secret_key}
ENV origin_ip=${origin_ip}
ENV aes_secret_key=${aes_secret_key}
COPY --from=builder /app/build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
EXPOSE 8080
□ docker-compose
services:
mysql:
image: mysql:latest
container_name: mysql
restart: always
environment:
- MYSQL_ROOT_PASSWORD=${ROOT_PASSWORD}
- MYSQL_DATABASE=cointrading
- MYSQL_USER=${DB_USERNAME}
- MYSQL_PASSWORD=${DB_PASSWORD}
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
networks:
- app-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:latest
command: ["redis-server"]
container_name: redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
cointrading:
image: ${DOCKER_USERNAME}/cointrading:latest
container_name: cointrading
ports:
- "8080:8080"
environment:
- TZ=Asia/Seoul
- DB_HOST=mysql
- SPRING_REDIS_HOST=redis
- REDIS_HOST=${REDIS_HOST}
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
- AES_SECRET_KEY=${AES_SECRET_KEY}
- DB_URL=${DB_URL}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
restart: always
volumes:
redis-data:
mysql-data:
networks:
app-network:
driver: bridge
- mysql을 예시로
- mysql: → mysql이라는 이름의 서비스를 정의
- image: mysql:latest → 해당 이미지를 사용하여 MySQL 컨테이너를 생성, latest는 최신을 의미
- container_name: → 컨테이너의 이름을 지정
- restart: always → 컨테이너가 중지되거나 실패할 경우 자동으로 다시 시작
- environment: → 컨테이너의 환경변수를 설정
- ports: → MySQL 컨테이너의 포트를 호스트 머신과 연결
- volumes: → 데이터가 호스트 머신에 저장되도록 설정
- networks: → 컨테이너가 연결될 네트워크를 설정
- healthcheck: → 컨테이너의 상태를 체크
- test:["CMD", "mysqladmin", "ping", "-h", "localhost"] MySQL서버가 정상적으로 실행 중인지 확인
- interval: → 실행간격
- timeout: → 체크 명령의 타임아웃
- retries: → 체크 실패가 n번 발생하면 비정상으로 간주되어 재실행
□ Github Actions - workflow
name: CI/CD with Docker & Gradle
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
- name: Build with Gradle Wrapper
run: ./gradlew build
- name: Docker 로그인
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Docker 이미지 빌드
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/cointrading:latest .
- name: Docker Hub에 Push
run: docker push ${{ secrets.DOCKER_USERNAME }}/cointrading:latest
- name: Set up SSH Key
run: |
echo "${{ secrets.EC2_SSH_KEY }}" > private_key.pem
chmod 600 private_key.pem
- name: Transfer docker-compose.yml to EC2
run: |
scp -i private_key.pem -o StrictHostKeyChecking=no docker-compose.yml ubuntu@${{ secrets.EC2_HOST }}:/home/ubuntu/
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: EC2 서버에 배포 (SSH 접속)
uses: appleboy/ssh-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
# Docker Compose가 설치되어 있는지 확인
if ! docker compose version &> /dev/null && ! docker-compose version &> /dev/null; then
echo "Docker Compose could not be found, installing..."
sudo apt-get update
sudo apt-get install -y docker-compose-plugin
sudo ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose || true
else
echo "Docker Compose is already installed"
fi
# 환경 변수 파일 생성 (.env)
echo "REDIS_HOST=redis" > ~/.env
echo "DB_URL=jdbc:mysql://mysql:3306/cointrading" >> ~/.env
echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> ~/.env
echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> ~/.env
echo "AES_SECRET_KEY=${{ secrets.AES_SECRET_KEY }}" >> ~/.env
echo "JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }}" >> ~/.env
echo "ROOT_PASSWORD=${{ secrets.ROOT_PASSWORD }}" >> ~/.env
echo "DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}" >> ~/.env
# 최신 Docker 이미지 풀
docker stop cointrading || true
docker rm -f cointrading || true
docker rmi -f ${{ secrets.DOCKER_USERNAME }}/cointrading:latest || true
docker pull ${{ secrets.DOCKER_USERNAME }}/cointrading:latest
# Docker Compose 실행 (자동으로 .env 파일 로드)
cd ~/cointrading
docker-compose down
docker-compose up -d
rm -f ~/.env