diff --git a/.github/workflows/backend.yml b/.github/workflows/ci_cd_pipeline.yml similarity index 60% rename from .github/workflows/backend.yml rename to .github/workflows/ci_cd_pipeline.yml index d9d8efe..27a0d33 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/ci_cd_pipeline.yml @@ -1,23 +1,21 @@ -name: Deploy Backend to EC2 +name: CI/CD Pipeline on: push: branches: - main - develop + - feature/** + workflow_dispatch: jobs: - deploy: + frontend: + name: Build & Push Frontend runs-on: ubuntu-latest - steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Get GitHub Actions Public IP - id: ip - uses: haythem/public-ip@v1.3 - - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: @@ -25,14 +23,29 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - - name: Add GitHub Actions IP to EC2 security group + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build Docker image and push to AWS ECR run: | - echo "Authorizing IP ${{ steps.ip.outputs.ipv4 }}" - aws ec2 authorize-security-group-ingress \ - --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} \ - --protocol tcp \ - --port 22 \ - --cidr ${{ steps.ip.outputs.ipv4 }}/32 + cd frontend + docker build -t frontend-service . + docker tag frontend-service:latest ${{ secrets.AWS_ECR_FRONTEND_REPOSITORY }}:latest + docker push ${{ secrets.AWS_ECR_FRONTEND_REPOSITORY }}:latest + + backend: + name: Build & Push Backend + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 - name: Login to AWS ECR run: | @@ -47,69 +60,75 @@ jobs: docker tag backend-service:latest ${{ secrets.AWS_ECR_REPOSITORY }}:latest docker push ${{ secrets.AWS_ECR_REPOSITORY }}:latest - - name: Logout from AWS ECR - run: | - docker logout ${{ secrets.AWS_ECR_REPOSITORY }} - echo "Logged out from AWS ECR." + deploy: + name: Deploy to EC2 + runs-on: ubuntu-latest + needs: [frontend, backend] + steps: + - uses: actions/checkout@v3 - - name: Copy backend directory to EC2 - uses: appleboy/ssh-action@master + - name: Get GitHub Actions Public IP + id: ip + uses: haythem/public-ip@v1.3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 with: - host: ${{ secrets.EC2_HOST }} - username: ${{ secrets.EC2_USERNAME }} - key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} - port: 22 - source: "backend/*,docker-compose.yml" - target: "/home/${{ secrets.EC2_USERNAME }}/backend" + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 - - name: Deploy to EC2 via SSH - uses: appleboy/ssh-action@master + - name: Authorize GitHub IP + run: | + aws ec2 authorize-security-group-ingress \ + --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} \ + --protocol tcp \ + --port 22 \ + --cidr ${{ steps.ip.outputs.ipv4 }}/32 + + - name: Deploy to EC2 with docker-compose + uses: appleboy/ssh-action@v0.1.10 with: host: ${{ secrets.EC2_HOST }} username: ${{ secrets.EC2_USERNAME }} key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} - port: ${{ secrets.PORT }} script: | - cd /home/${{ secrets.EC2_USERNAME }}/backend + cd /home/${{ secrets.EC2_USERNAME }}/postdm + echo "Logging in to ECR..." aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${{ secrets.AWS_ECR_REPOSITORY }} + aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${{ secrets.AWS_ECR_FRONTEND_REPOSITORY }} - echo "PULLING LATEST IMAGE..." - docker pull ${{ secrets.AWS_ECR_REPOSITORY }}:latest - - echo "STOPPING AND REMOVING EXISTING CONTAINERS..." - sudo docker compose down --remove-orphans || true - - echo "REMOVING UNUSED DOCKER IMAGES..." - docker image rm ${{ secrets.AWS_ECR_REPOSITORY }}:latest || true - - echo "UPDATING .env FILE..." + echo "Updating .env file" cat < .env MYSQL_URL=${{ secrets.MYSQL_URL }} MYSQL_USERNAME=${{ secrets.MYSQL_USERNAME }} MYSQL_PASSWORD=${{ secrets.MYSQL_PASSWORD }} MYSQL_ROOT_PASSWORD=${{ secrets.MYSQL_ROOT_PASSWORD }} MYSQL_DATABASE=${{ secrets.MYSQL_DATABASE }} + SPRING_PROFILES_ACTIVE=${{ secrets.SPRING_PROFILES_ACTIVE }} JWT_SECRET=${{ secrets.JWT_SECRET }} JWT_EXPIRATION=${{ secrets.JWT_EXPIRATION }} JWT_EXPIREDMS=${{ secrets.JWT_EXPIREDMS }} JWT_REFRESHEDMS=${{ secrets.JWT_REFRESHEDMS }} + SPRING_MAIL_HOST=${{ secrets.SPRING_MAIL_HOST }} SPRING_MAIL_PORT=${{ secrets.SPRING_MAIL_PORT }} SPRING_MAIL_USERNAME=${{ secrets.SPRING_MAIL_USERNAME }} SPRING_MAIL_PASSWORD=${{ secrets.SPRING_MAIL_PASSWORD }} + EOF - echo "STARTING NEW CONTAINER..." - sudo docker compose up -d --force-recreate + echo "Pulling latest images..." + docker compose pull - echo "✅ DEPLOYMENT COMPLETE!" + echo "Starting containers..." + docker compose up -d --force-recreate - - name: Remove GitHub Actions IP from EC2 security group + - name: Revoke GitHub IP if: always() run: | - echo "Revoking IP ${{ steps.ip.outputs.ipv4 }}" aws ec2 revoke-security-group-ingress \ --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} \ --protocol tcp \ diff --git a/docker-compose.yml b/docker-compose.yml index 548186f..713ae8f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,29 +1,6 @@ version: '3.8' services: - backend: - container_name: backend - image: backend-service - build: - context: backend - dockerfile: Dockerfile - ports: - - "8080:8080" - environment: - SPRING_DATASOURCE_URL: ${MYSQL_URL} - SPRING_DATASOURCE_USERNAME: ${MYSQL_USERNAME} - SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD} - env_file: - - .env - healthcheck: - test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ] - interval: 10s - retries: 5 - start_period: 20s - timeout: 5s - volumes: - - ./config/application-prod.yml:/app/config/application-prod.yml - db: container_name: mysql image: mysql:8.0 @@ -37,6 +14,48 @@ services: - "3306:3306" volumes: - mysql_data:/var/lib/mysql # 데이터를 Docker Volume에 저장 + healthcheck: + test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + env_file: + - .env + + frontend: + container_name: frontend + image: 536697226608.dkr.ecr.ap-northeast-2.amazonaws.com/postdm/frontend-app + ports: + - "3000:3000" + restart: always + + backend: + container_name: backend + image: 536697226608.dkr.ecr.ap-northeast-2.amazonaws.com/postdm/backend-app + ports: + - "8080:8080" + environment: + SPRING_DATASOURCE_URL: ${MYSQL_URL} + SPRING_DATASOURCE_USERNAME: ${MYSQL_USERNAME} + SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD} + env_file: + - .env + + nginx: + image: nginx:1.25-alpine + container_name: nginx + depends_on: + - frontend + - backend + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - /etc/letsencrypt:/etc/letsencrypt:ro + - /var/www/html:/var/www/html + restart: always volumes: mysql_data: # 데이터 영구 저장 Docker Volume \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..36c6c6c --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,11 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY . . + +RUN npm install +RUN npm run build + +EXPOSE 3000 +CMD ["npm", "start"] \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..c247a79 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,30 @@ +events {} + +http { + server { + listen 80; + server_name postdmex.com www.postdmex.com; + + location ~ /.well-known/acme-challenge/ { + root /var/www/html; + } + + location / { + proxy_pass http://frontend:3000; + proxy_http_version 1.1; + 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; + } + + location /api/ { + proxy_pass http://backend:8080; + proxy_http_version 1.1; + 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