diff --git a/.github/workflows/.DS_Store b/.github/workflows/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.github/workflows/.DS_Store differ diff --git a/.github/workflows/develop_build_deploy.yml b/.github/workflows/develop_build_deploy.yml index 67a7d2a..86eb7a1 100644 --- a/.github/workflows/develop_build_deploy.yml +++ b/.github/workflows/develop_build_deploy.yml @@ -1,140 +1,119 @@ -#name: develop push Build and Deploy -# -#on: -# push: -# branches: [ "develop" ] -# -#env: -# DOCKERHUB_USERNAME: fittheman -# DOCKERHUB_IMAGE_NAME: fittheman-server -# -#jobs: -# build-deploy: -# runs-on: ubuntu-latest -# environment: DEV -# -# steps: -# # 체크아웃 -# - name: Checkout -# uses: actions/checkout@v4 -# -# # JDK 17 세팅 -# - name: Set up JDK 17 -# uses: actions/setup-java@v4 -# with: -# java-version: '17' -# distribution: 'temurin' -# -# # 테스트 환경에서 필요한 컨테이너 실행 (redis, postgres) -# - name: Run Containers -# run: docker compose -f ./docker-compose-test.yml up -d -# -# # 테스트 환경에서 필요한 스키마, 데이터 등록 -# - name: Apply Schema and Data -# env: -# TEST_POSTGRES_CONTAINER_NAME: "${{ github.event.repository.name }}-postgres-1" -# TEST_POSTGRES_USER: test -# TEST_POSTGRES_DB: ftm_test_db -# run: | -# echo "⏳ Waiting for postgres to be ready..." -# until docker exec -i $TEST_POSTGRES_CONTAINER_NAME pg_isready -U $TEST_POSTGRES_USER -d $TEST_POSTGRES_DB; do -# echo "postgres is not ready yet. Retrying in 3 seconds..." -# sleep 3 -# done -# echo "✅ postgres is ready!" -# -# echo "${{ secrets.SCHEMA_SQL }}" | docker exec -i $TEST_POSTGRES_CONTAINER_NAME psql -U $TEST_POSTGRES_USER -d $TEST_POSTGRES_DB -# echo "${{ secrets.DATA_SQL }}" | docker exec -i $TEST_POSTGRES_CONTAINER_NAME psql -U $TEST_POSTGRES_USER -d $TEST_POSTGRES_DB -# -# # Gradlew 실행 권한 허용 -# - name: Grant Execute Permission for Gradlew -# run: chmod +x ./gradlew -# -# # .env 파일 생성 -# - name: Load secrets into .env file -# run: | -# echo "${{ secrets.ENV }}" >> .env -# -# # Swagger API 문서화 task 실행 -# - name: Apply Swagger API Document Task -# run: ./gradlew copyOasToSwagger -# -# # Rest Docs API 문서화 task 실행 -# - name: Apply Rest Docs API Document Task -# run: ./gradlew copyDocument -# -# # Gradle 빌드 -# - name: Build with Gradle -# id: gradle -# uses: gradle/gradle-build-action@v2 -# with: -# arguments: | -# bootJar -# --scan -# cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} -# -# # Dockerhub 로그인 -# - name: Login to Dockerhub -# uses: docker/login-action@v3 -# with: -# username: ${{ env.DOCKERHUB_USERNAME }} -# password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} -# -# # Docker 메타데이터 -# - name: Extract Docker metadata -# id: metadata -# uses: docker/metadata-action@v5.5.0 -# env: -# DOCKERHUB_IMAGE_FULL_NAME: ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_IMAGE_NAME }} -# with: -# images: ${{ env.DOCKERHUB_IMAGE_FULL_NAME }} -# tags: | -# type=sha,prefix= -# -# # Docker 이미지 빌드, 도커허브 푸시 -# - name: Build and Push Docker image -# uses: docker/build-push-action@v3 -# with: -# context: . -# push: true -# tags: ${{ steps.metadata.outputs.tags }} # 추출된 도커 메타데이터 tags -> "${DOCKERHUB_USERNAME}/${DOCKERHUB_IMAGE_NAME}:{TAG} -# -# # EC2 서버로 docker-compose.yml 파일 복사 -# - name: Copy docker-compose file to EC2 -# uses: burnett01/rsync-deployments@7.0.1 -# with: -# switches: -avzr --delete -# path: docker-compose.yml -# remote_host: ${{ secrets.EC2_HOST }} -# remote_user: ${{ secrets.EC2_USER }} -# remote_key: ${{ secrets.SSH_PRIVATE_KEY }} -# remote_path: /home/ubuntu/ -# -# # EC2 서버로 nginx 파일 복사 -# # docker-compose.yml 에서 nginx 컨테이너 실행 시 파일을 마운트하기 위함 -# - name: Copy default.conf file to EC2 -# uses: burnett01/rsync-deployments@7.0.1 -# with: -# switches: -avzr --delete -# path: ./nginx -# remote_host: ${{ secrets.EC2_HOST }} -# remote_user: ${{ secrets.EC2_USER }} -# remote_key: ${{ secrets.SSH_PRIVATE_KEY }} -# remote_path: /home/ubuntu -# -# # EC2 배포 -# - name: Deploy to EC2 Server -# uses: appleboy/ssh-action@v1.0.3 -# env: -# IMAGE_FULL_PATH: ${{ steps.metadata.outputs.tags }} -# DOCKERHUB_IMAGE_NAME: ${{ env.DOCKERHUB_IMAGE_NAME }} -# with: -# host: ${{ secrets.EC2_HOST }} -# username: ${{ secrets.EC2_USER }} -# key: ${{ secrets.SSH_PRIVATE_KEY }} -# envs: IMAGE_FULL_PATH, DOCKERHUB_IMAGE_NAME # docker-compose.yml 에서 사용할 환경 변수 -# debug: true -# script: | -# echo "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin -# docker compose up -d -# docker image prune -a -f \ No newline at end of file +name: develop push Build and Deploy + +on: + push: + branches: [ "develop" ] + +env: + DOCKERHUB_USERNAME: fittheman + DOCKERHUB_IMAGE_NAME: fittheman-server + +jobs: + build-deploy: + runs-on: ubuntu-latest + environment: DEV + + steps: + # 체크아웃 + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + + # JDK 17 세팅 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # 테스트 환경에서 필요한 컨테이너 실행 (redis, postgres) + - name: Run Containers + run: docker compose -f ./docker-compose-test.yml up -d + + # 테스트 환경에서 필요한 스키마, 데이터 등록 + - name: Apply Schema and Data + env: + TEST_POSTGRES_CONTAINER_NAME: "${{ github.event.repository.name }}-postgres-1" + TEST_POSTGRES_USER: test + TEST_POSTGRES_DB: ftm_test_db + run: | + echo "⏳ Waiting for postgres to be ready..." + until docker exec -i $TEST_POSTGRES_CONTAINER_NAME pg_isready -U $TEST_POSTGRES_USER -d $TEST_POSTGRES_DB; do + echo "postgres is not ready yet. Retrying in 3 seconds..." + sleep 3 + done + echo "✅ postgres is ready!" + + echo "${{ secrets.SCHEMA_SQL }}" | docker exec -i $TEST_POSTGRES_CONTAINER_NAME psql -U $TEST_POSTGRES_USER -d $TEST_POSTGRES_DB + echo "${{ secrets.DATA_SQL }}" | docker exec -i $TEST_POSTGRES_CONTAINER_NAME psql -U $TEST_POSTGRES_USER -d $TEST_POSTGRES_DB + + # Gradlew 실행 권한 허용 + - name: Grant Execute Permission for Gradlew + run: chmod +x ./gradlew + + # .env 파일 생성 + - name: Load secrets into .env file + run: | + echo "${{ secrets.ENV }}" >> .env + + # Swagger API 문서화 task 실행 + - name: Apply Swagger API Document Task + run: ./gradlew copyOasToSwagger + + # Rest Docs API 문서화 task 실행 + - name: Apply Rest Docs API Document Task + run: ./gradlew copyDocument + + # Gradle 빌드 + - name: Build with Gradle + id: gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: | + bootJar + --scan + cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} + + # Dockerhub 로그인 + - name: Login to Dockerhub + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} + + # Docker 메타데이터 + - name: Extract Docker metadata + id: metadata + uses: docker/metadata-action@v5.5.0 + env: + DOCKERHUB_IMAGE_FULL_NAME: ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_IMAGE_NAME }} + with: + images: ${{ env.DOCKERHUB_IMAGE_FULL_NAME }} + tags: | + type=sha,prefix= + + # Docker 이미지 빌드, 도커허브 푸시 + - name: Build and Push Docker image + uses: docker/build-push-action@v3 + with: + context: . + push: true + tags: ${{ steps.metadata.outputs.tags }} # 추출된 도커 메타데이터 tags -> "${DOCKERHUB_USERNAME}/${DOCKERHUB_IMAGE_NAME}:{TAG} + + # GCE 배포 + - name: Deploy to GCE Server + uses: appleboy/ssh-action@v1.0.3 + env: + IMAGE_FULL_PATH: ${{ steps.metadata.outputs.tags }} + DOCKERHUB_IMAGE_NAME: ${{ env.DOCKERHUB_IMAGE_NAME }} + with: + host: ${{ secrets.GCE_HOST }} + username: ${{ secrets.GCE_USER }} + key: ${{ secrets.GCE_SSH_PRIVATE_KEY }} + envs: IMAGE_FULL_PATH, DOCKERHUB_IMAGE_NAME # docker-compose.yml 에서 사용할 환경 변수 + debug: true + script: | + echo "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin + docker compose up -d + docker image prune -a -f \ No newline at end of file diff --git a/.github/workflows/develop-build-deploy-gce-manual.yml b/.github/workflows/develop_build_deploy_manual.yml similarity index 97% rename from .github/workflows/develop-build-deploy-gce-manual.yml rename to .github/workflows/develop_build_deploy_manual.yml index 794a272..c7dcfba 100644 --- a/.github/workflows/develop-build-deploy-gce-manual.yml +++ b/.github/workflows/develop_build_deploy_manual.yml @@ -1,10 +1,10 @@ -name: develop push Build and Deploy (GCE) +name: develop push Build and Deploy Manual on: workflow_dispatch: inputs: ref: - description: "배포할 브랜치 (수동 테스트 전용)" + description: "배포할 브랜치 (수동 배포)" required: true default: "chore/#169" diff --git a/build.gradle b/build.gradle index 6174dd5..7a9b78d 100644 --- a/build.gradle +++ b/build.gradle @@ -96,7 +96,8 @@ tasks.named('test') { openapi3 { servers = [ { url = "http://localhost:8080/" }, - { url = "https://dev-api.fittheman.site/" } + { url = "https://dev-api.fittheman.site/" }, + { url = "https://dev-api-gcp.fittheman.site/" } ] title = '핏더맨 API DOCUMENT' description = '핏더맨 api 명세서입니다.' diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 5dff262..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,55 +0,0 @@ -services: - backend: # 서버 컨테이너 - image: ${IMAGE_FULL_PATH} - container_name: ${DOCKERHUB_IMAGE_NAME} - expose: # nginx 에서만 접근 가능하도록 expose 설정 - - "8080" - volumes: - - ./app-logs/errors:/logs # 서버 에러 경로 볼륨 마운트 - restart: always - environment: - - TZ=Asia/Seoul - networks: - - "ftm-network" - env_file: # 배포 환경 .env 파일 참조 - - .env - - redis: # Redis 컨테이너 - image: "redis:alpine" - container_name: redis - ports: - - "6379:6379" - environment: - - TZ=Asia/Seoul - networks: - - "ftm-network" - - nginx: # Nginx 컨테이너 - image: "nginx:alpine" - container_name: nginx - ports: - - "80:80" - - "443:443" - restart: always - volumes: - - ./nginx/default.conf:/etc/nginx/conf.d/default.conf - - ./data/certbot/conf:/etc/letsencrypt - - ./data/certbot/www:/var/www/certbot - environment: - - TZ=Asia/Seoul - networks: - - "ftm-network" - depends_on: - - backend - - certbot - - certbot: # certbot 컨테이너 (ssl/tls 인증서 자동 갱신 및 관리 도구) - image: "certbot/certbot" - volumes: - - ./data/certbot/conf:/etc/letsencrypt - - ./data/certbot/www:/var/www/certbot - entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --quiet && nginx -s reload; sleep 12h & wait $${!}; done;'" - -networks: - ftm-network: - driver: bridge \ No newline at end of file diff --git a/nginx/default.conf b/nginx/default.conf deleted file mode 100644 index 97a45c2..0000000 --- a/nginx/default.conf +++ /dev/null @@ -1,29 +0,0 @@ -server { - listen 80; - server_name dev-api.fittheman.site; - - location /.well-known/acme-challenge/ { - allow all; - root /var/www/certbot; - } - - location / { - return 301 https://$host$request_uri; - } -} - -server { - listen 443 ssl; - server_name dev-api.fittheman.site; - - ssl_certificate /etc/letsencrypt/live/dev-api.fittheman.site/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/dev-api.fittheman.site/privkey.pem; - - location / { - proxy_pass http://fittheman-server:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} \ No newline at end of file diff --git a/src/main/java/com/ftm/server/infrastructure/s3/S3Config.java b/src/main/java/com/ftm/server/infrastructure/s3/S3Config.java index f9f58e1..dab26b7 100644 --- a/src/main/java/com/ftm/server/infrastructure/s3/S3Config.java +++ b/src/main/java/com/ftm/server/infrastructure/s3/S3Config.java @@ -3,9 +3,13 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.sts.StsClient; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; +import software.amazon.awssdk.services.sts.model.AssumeRoleRequest; @Configuration public class S3Config { @@ -13,13 +17,44 @@ public class S3Config { @Value("${aws.s3.region}") private String region; + @Value("${aws.credentials.access-key}") + private String accessKey; + + @Value("${aws.credentials.secret-key}") + private String secretKey; + + @Value("${aws.sts.role-arn}") + private String roleArn; + + @Value("${aws.sts.role-session-name}") + private String roleSessionName; + @Bean public S3Client s3Client() { + StaticCredentialsProvider baseCredentials = + StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey)); + + StsClient sts = + StsClient.builder() + .region(Region.of(region)) + .credentialsProvider(baseCredentials) + .build(); + + StsAssumeRoleCredentialsProvider assumeRoleProvider = + StsAssumeRoleCredentialsProvider.builder() + .stsClient(sts) + .refreshRequest( + AssumeRoleRequest.builder() + .roleArn(roleArn) + .roleSessionName(roleSessionName) + .build()) + .build(); + + assumeRoleProvider.resolveCredentials(); + return S3Client.builder() .region(Region.of(region)) - .credentialsProvider( - DefaultCredentialsProvider - .create()) // EC2에서는 자동 인식 & window&mac 에서는 환경 변수 설정 필요 + .credentialsProvider(assumeRoleProvider) .build(); } } diff --git a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java index 006dc04..3e79a2e 100644 --- a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java +++ b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java @@ -73,7 +73,7 @@ public class SecurityConfig { "/api/posts/products" }; - private static final String[] ANONYMOUS_MATCHERS = {"/docs/**"}; + private static final String[] ANONYMOUS_MATCHERS = {"/docs/**", "/api/internal/s3/**"}; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { diff --git a/src/main/resources/application-storage.yml b/src/main/resources/application-storage.yml index 9a037f6..961fe5b 100644 --- a/src/main/resources/application-storage.yml +++ b/src/main/resources/application-storage.yml @@ -14,4 +14,10 @@ aws: path: user: "users" post: "posts" - product: "products" \ No newline at end of file + product: "products" + sts: + role-arn: ${AWS_ROLE_ARN} + role-session-name: ftm-dev-s3 + credentials: + access-key: ${AWS_ACCESS_KEY_ID} + secret-key: ${AWS_SECRET_ACCESS_KEY} \ No newline at end of file