From 56bd2c44d83ec4dcca162d4e36426637390d3290 Mon Sep 17 00:00:00 2001 From: donghyun Date: Sat, 10 Jan 2026 00:59:40 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[FIX]=20CI/CD,=20application,=20docker-comp?= =?UTF-8?q?ose=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 66 +++++++++++++++++++++++------ .github/workflows/ci.yml | 22 +++++++++- docker-compose.yaml | 50 ++++++++++++++-------- src/main/resources/application.yaml | 8 ++-- 4 files changed, 109 insertions(+), 37 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 8e16056..99cd0ed 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,23 +1,48 @@ -name: CD - Deploy to Server +name: CD on: push: branches: [ "main" ] +env: + DEPLOYMENT_NAME: team5-app + REGISTRY: ghcr.io + REMOTE_TARGET_DIR: k8s + jobs: - deploy: + build-and-deploy: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'gradle' + + - name: Build with Gradle + run: | + chmod +x gradlew + ./gradlew clean build -x test + + - name: Lowercase Image Name + run: | + echo "IMAGE_NAME=${REGISTRY,,}/${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV + env: + REGISTRY: ${{ env.REGISTRY }} + GITHUB_REPOSITORY: ${{ github.repository }} - name: Login to GHCR uses: docker/login-action@v3 with: - registry: ghcr.io + registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -26,7 +51,19 @@ jobs: with: context: . push: true - tags: ghcr.io/${{ github.repository }}:latest + tags: | + ${{ env.IMAGE_NAME }}:latest + ${{ env.IMAGE_NAME }}:${{ github.sha }} + + - name: Copy k8s to Server + uses: appleboy/scp-action@master + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USER }} + key: ${{ secrets.SERVER_SSH_KEY }} + port: 22 + source: "k8s/*" + target: "/home/${{ secrets.SERVER_USER }}/k8s" - name: Deploy to Server via SSH uses: appleboy/ssh-action@v1.0.3 @@ -34,13 +71,14 @@ jobs: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} + port: 22 script: | - # 1. 최신 이미지 받아오기 - docker pull ghcr.io/${{ github.repository }}:latest - - # 2. 기존 컨테이너 끄고 새 컨테이너 띄우기 (docker-compose 사용) - cd ~/app # 서버 내 프로젝트 폴더 위치 - docker-compose up -d - - # 3. 안 쓰는 이미지 정리 (용량 확보) - docker image prune -f \ No newline at end of file + DEPLOYMENT=${{ env.DEPLOYMENT_NAME }} + IMAGE=${{ env.IMAGE_NAME }}:latest + K8S_DIR="/home/${{ secrets.SERVER_USER }}/k8s" + + sudo k3s crictl pull $IMAGE + sudo k3s kubectl apply -f $K8S_DIR/ + sudo k3s kubectl rollout restart deployment $DEPLOYMENT + sudo k3s kubectl rollout status deployment $DEPLOYMENT + sudo k3s crictl rmi --prune \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdf8bf5..6e2c743 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Run Tests +name: CI on: push: @@ -21,6 +21,21 @@ jobs: --health-timeout 5s --health-retries 20 + mysql: + image: mysql:8.0 + ports: + - 3306:3306 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: toyproject-team5 + MYSQL_USER: waffle + MYSQL_PASSWORD: somepassword + options: >- + --health-cmd "mysqladmin ping -h localhost -uroot -proot" + --health-interval 10s + --health-timeout 5s + --health-retries 10 + steps: - uses: actions/checkout@v4 @@ -30,8 +45,11 @@ jobs: java-version: '17' distribution: 'temurin' + - name: Execute permission for gradlew + run: chmod +x gradlew + - name: Run ktlint run: ./gradlew ktlintCheck - name: Run Tests - run: ./gradlew test + run: ./gradlew test \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index c823cee..9ab6e30 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,26 +1,42 @@ -version: '3.8' - services: - # MySQL: JDBC와 연결될 DB - db: + app: + container_name: toyproject-team5 + build: . + ports: + - "8080:8080" + depends_on: + - mysql + - redis + environment: + # 1. DB 설정 + DB_HOST: mysql + DB_PORT: 3306 + DB_NAME: toyproject-team5 + DB_USER: waffle + DB_PASSWORD: somepassword + + # 2. Redis 설정 + REDIS_HOST: redis + REDIS_PORT: 6379 + + # 3. 기타 설정 + JWT_SECRET: "secret-key-for-local-development-only" + S3_BUCKET_NAME: "dummy-bucket" + + mysql: image: mysql:8.0 - container_name: mysql + container_name: mysql-db ports: - "3306:3306" environment: - MYSQL_DATABASE: ${DB_NAME} - MYSQL_USER: ${DB_USER} - MYSQL_PASSWORD: ${DB_PASSWORD} - MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} - volumes: - - ./mysql_data:/var/lib/mysql - command: - - --character-set-server=utf8mb4 - - --collation-server=utf8mb4_unicode_ci + MYSQL_DATABASE: toyproject-team5 + MYSQL_ROOT_PASSWORD: root + + MYSQL_USER: waffle + MYSQL_PASSWORD: somepassword - # Redis: JWT 블랙리스트나 캐싱용 redis: - image: redis:alpine # 가장 가벼운 버전 - container_name: redis + image: redis:alpine + container_name: redis-cache ports: - "6379:6379" \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a944437..44f0da4 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,8 +1,8 @@ spring: # DB 설정 (JDBC) datasource: - url: jdbc:mysql://localhost:3306/${DB_NAME:seminar2025}?allowPublicKeyRetrieval=true&useSSL=false - username: ${DB_USER:user} + url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:toyproject-team5}?allowPublicKeyRetrieval=true&useSSL=false + username: ${DB_USER:waffle} password: ${DB_PASSWORD:somepassword} driver-class-name: com.mysql.cj.jdbc.Driver @@ -19,7 +19,7 @@ spring: cloud: aws: s3: - bucket: ${S3_BUCKET_NAME} + bucket: ${S3_BUCKET_NAME:dummy-bucket} region: static: ap-northeast-2 stack: @@ -27,7 +27,7 @@ spring: # JWT 설정 jwt: - secret: ${JWT_SECRET} + secret: ${JWT_SECRET:secret-key-for-local-development-only} expiration-in-ms: 86400000 # Swagger From 1a6f07179dc7d436b3ca43edcfc45bb559295227 Mon Sep 17 00:00:00 2001 From: donghyun Date: Sat, 10 Jan 2026 01:57:00 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[FEAT]=20k8s=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- k8s/deployment.yaml | 48 ++++++++++++++++++++++++++++++++++ k8s/mysql.yaml | 63 +++++++++++++++++++++++++++++++++++++++++++++ k8s/redis.yaml | 28 ++++++++++++++++++++ k8s/service.yaml | 12 +++++++++ 4 files changed, 151 insertions(+) create mode 100644 k8s/deployment.yaml create mode 100644 k8s/mysql.yaml create mode 100644 k8s/redis.yaml create mode 100644 k8s/service.yaml diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml new file mode 100644 index 0000000..1e241e9 --- /dev/null +++ b/k8s/deployment.yaml @@ -0,0 +1,48 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: team5-app + labels: + app: team5-app +spec: + replicas: 2 + selector: + matchLabels: + app: team5-app + template: + metadata: + labels: + app: team5-app + spec: + containers: + - name: team5-app + image: ghcr.io/wafflestudio/23-5-team5-server:latest + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + # --- DB 접속 정보 --- + - name: DB_HOST + value: "mysql" + - name: DB_PORT + value: "3306" + - name: DB_USER + value: "waffle" + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: team5-secrets + key: mysql-user-password + + # --- Redis 접속 정보 --- + - name: REDIS_HOST + value: "redis" + - name: REDIS_PORT + value: "6379" + + # --- 보안 키 (JWT) --- + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: team5-secrets + key: jwt-secret \ No newline at end of file diff --git a/k8s/mysql.yaml b/k8s/mysql.yaml new file mode 100644 index 0000000..6b31986 --- /dev/null +++ b/k8s/mysql.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysql-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysql +spec: + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app: mysql + spec: + containers: + - name: mysql + image: mysql:8.0 + ports: + - containerPort: 3306 + env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: team5-secrets + key: mysql-root-password + - name: MYSQL_USER + value: waffle + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: team5-secrets + key: mysql-user-password + - name: MYSQL_DATABASE + value: toyproject-team5 + volumeMounts: + - name: mysql-persistent-storage + mountPath: /var/lib/mysql + volumes: + - name: mysql-persistent-storage + persistentVolumeClaim: + claimName: mysql-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: mysql +spec: + ports: + - port: 3306 + selector: + app: mysql \ No newline at end of file diff --git a/k8s/redis.yaml b/k8s/redis.yaml new file mode 100644 index 0000000..3205fa4 --- /dev/null +++ b/k8s/redis.yaml @@ -0,0 +1,28 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis +spec: + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis:7-alpine + ports: + - containerPort: 6379 +--- +apiVersion: v1 +kind: Service +metadata: + name: redis +spec: + ports: + - port: 6379 + selector: + app: redis \ No newline at end of file diff --git a/k8s/service.yaml b/k8s/service.yaml new file mode 100644 index 0000000..5cc6e48 --- /dev/null +++ b/k8s/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: team5-app-service +spec: + type: LoadBalancer + selector: + app: team5-app + ports: + - protocol: TCP + port: 8080 + targetPort: 8080 \ No newline at end of file