EC2에 자바 설치/폴더 생성

# 릴리스 보관 폴더(버전별 JAR 저장)
sudo mkdir -p /opt/plus/releases
sudo chown -R ubuntu:ubuntu /opt/plus

# (선택) 그레이스풀 셧다운 여유
echo -e "[Service]\\nTimeoutStopSec=30" | sudo tee /etc/systemd/system/todo-management.service.d/timeout.conf >/dev/null
sudo systemctl daemon-reload

GitHub Secrets 등록

EC2_HOST : EC2 퍼블릭 IP/도메인
EC2_SSH_KEY : ubuntu 계정의 SSH 개인키(-----BEGIN ~ END----- 포함)
EC2_USER : ubuntu

깃허브 워크플로 추가 (.github/workflows/deploy.yml)

name: CI-CD to EC2 (systemd)

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Build jar
        run: ./gradlew clean bootJar -x test

      - name: Rename jar with commit sha + date
        run: |
          FILE=$(ls build/libs/*.jar | head -n1)
          SHA=${GITHUB_SHA::8}
          TS=$(date -u +'%Y%m%d-%H%M%S')
          NEW="app-${TS}-${SHA}.jar"
          mv "$FILE" "build/libs/$NEW"
          echo "ARTIFACT=$NEW" >> $GITHUB_ENV

      - name: Detect runner public IP
        id: ip
        run: echo "MY_IP=$(curl -s <https://checkip.amazonaws.com>)/32" >> $GITHUB_OUTPUT

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Open SSH for this runner
        run: |
          aws ec2 authorize-security-group-ingress \\
            --group-id sg-0893adff359fd7994 \\
            --ip-permissions "IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp='${{ steps.ip.outputs.MY_IP }}',Description='gha-temporary'}]"

      - name: Upload jar to EC2
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          source: "build/libs/${{ env.ARTIFACT }}"
          target: "/opt/plus/releases/"
          strip_components: 2

      - name: Switch symlink & restart service
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            set -e
            JAR="/opt/plus/releases/${{ env.ARTIFACT }}"
            echo "Switch to $JAR"
            ln -sfn "$JAR" /opt/plus/app.jar
            sudo systemctl restart todo-management.service
            sudo systemctl status todo-management.service --no-pager --full || true

      - name: Close SSH rule
        if: always()
        run: |
          aws ec2 revoke-security-group-ingress \\
            --group-id sg-0893adff359fd7994 \\
            --ip-permissions "IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp='${{ steps.ip.outputs.MY_IP }}'}]"

롤백

ls -lt /opt/plus/releases/*.jar

# 원하는 버전으로 링크 교체 후 재시작
ln -sfn /opt/plus/releases/your-prev.jar /opt/plus/app.jar
sudo systemctl restart todo-management.service

트러블 슈팅