ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 나날이 백엔드 CI-CD 구축의 과정
    프로젝트/나날이 2023. 11. 20. 22:17

    이전 2번의 프로젝트 Trend_Pick, 피료해에서는 수동으로 배포를 했다. ec2 서버에 연결한 다음에 로컬에서 JAR 파일을 보낸 뒤 직접 자바 파일을 실행시켰다.

     

    배포를 한 후에도 계속해서 변경사항이 생겼고, 생길 때마다 ec2 서버에 JAR 파일을 보내고 기존의 실행되는 애플리케이션을 kill 한 다음에 다시 자바를 실행시키는 일이 매우 번거로웠다.

     

    계속 CI-CD를 구축해봐야지라고 생각만 하고 다른거 하기 바빠서 안했었는데 이번에는 시간을 들여서 제대로 해봤다.

    (물론 계속 안돼서 10시간은 넘게 썼다. 구글링해도 다 달라서 잘 안되더라......)

     

     

     

    scripts/deploy.sh

    #!/usr/bin/env bash
    
    REPOSITORY=/home/ubuntu/nanali
    cd $REPOSITORY
    
    APP_NAME=Nanali
    JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep 'SNAPSHOT.jar' | tail -n 1)
    JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME
    
    CURRENT_PID=$(pgrep -f $APP_NAME)
    
    if [ -z $CURRENT_PID ]
    then
      echo "> 종료할것 없음."
    else
      echo "> kill -9 $CURRENT_PID"
      kill -15 $CURRENT_PID
      sleep 5
    fi
    
    echo "> $JAR_PATH 배포"
    nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null &

     

    여기서는 ec2에 올라간 자바 파일을 자동으로 재시작해주는 역할을 한다. 여기서 중요한 점은 경로를 제대로 입력 해야한다. 경로를 제대로 입력 안하고 /home/ec2-user 경로로 해가지고 한참 시간을 날린 기억이 있다. 꼭 제대로 된 경로를 입력해서 애플리케이션이 잘 실행되도록 설정하자!

     

     

     

    appspec.yml

    version: 0.0
    os: linux
    
    files:
      - source: /
        destination: /home/ubuntu/nanali
    
    permissions:
      - object: /home/ubuntu/
        owner: ubuntu
        group: ubuntu
    
    hooks:
      AfterInstall:
        - location: scripts/deploy.sh
          timeout: 60
          runas: ubuntu

    여기서도 파일이 어디로 이동하는지 설정하는건데, 역시 경로도 잘 설정해야하고.. 꼼꼼히 해야겠다는 생각이 많이 들었다.

     

     

     

    이제 깃허브 workflows에서 설정을 해야한다.

     

    deploy.yml

    name: Build and Deploy Spring Boot to AWS EC2
    
    on:
      push:
        branches: [main]
    
    env:
      PROJECT_NAME: Nanali
      BUCKET_NAME: nanali
      CODE_DEPLOY_APP_NAME: nanali
      DEPLOYMENT_GROUP_NAME: nanali_deploy
    
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout
            uses: actions/checkout@v2
    
          - name: Setup Java JDK 17
            uses: actions/setup-java@v1
            with:
              java-version: 17
    
          - uses: actions/checkout@v2
          - run: touch ./src/main/resources/application.properties
          - run: echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.properties
    
          - name: Grant execute permission for gradlew
            run: chmod +x ./gradlew
            shell: bash
    
          - name: Build with Gradle
            run: ./gradlew build
            shell: bash
    
          - name: Modify application.properties
            run: |
               sed -i 's~spring.datasource.url =.*~spring.datasource.url = '"${{ secrets.DB_URL }}"'~' ./src/main/resources/application.properties
               sed -i 's~spring.datasource.username =.*~spring.datasource.username = '"${{ secrets.DB_username }}"'~' ./src/main/resources/application.properties
               sed -i 's~spring.datasource.password =.*~spring.datasource.password = '"${{ secrets.DB_password }}"'~' ./src/main/resources/application.properties
               sed -i 's~cloud.aws.credentials.access-key =.*~cloud.aws.credentials.access-key = '"${{ secrets.AWS_ACCESS_KEY_ID }}"'~' ./src/main/resources/application.properties
               sed -i 's~cloud.aws.credentials.secret-key =.*~cloud.aws.credentials.secret-key = '"${{ secrets.AWS_SECRET_ACCESS_KEY }}"'~' ./src/main/resources/application.properties
         
          - name: Make Zip File
            run: zip -qq -r ./$GITHUB_SHA.zip .
            shell: bash
    
          - name: Configure AWS credentials
            uses: aws-actions/configure-aws-credentials@v1
            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: Upload to S3
            run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip
    
          - name: Code Deploy
            run: aws deploy create-deployment --application-name $CODE_DEPLOY_APP_NAME --deployment-config-name CodeDeployDefault.OneAtATime --deployment-group-name $DEPLOYMENT_GROUP_NAME --s3-location bucket=$BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip

    이 포스팅의 하이라이트이다. 가장 많은 시간을 들인 것이 '환경변수 설정' 이다.

     

     

    application.properties

    server.port=8080
    
    spring.datasource.url = ${DB_URL}
    spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
    spring.datasource.username = ${DB_username}
    spring.datasource.password = ${DB_password}
    
    spring.jpa.show-sql = true
    spring.jpa.properties.hibernate.format_sql = true
    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
    spring.jpa.hibernate.ddl-auto = update
    
    cloud.aws.credentials.access-key = ${AWS_ACCESS_KEY_ID}
    cloud.aws.credentials.secret-key = ${AWS_SECRET_ACCESS_KEY}
    
    cloud.aws.s3.bucket = nanali
    cloud.aws.region.static = ap-northeast-2
    cloud.aws.stack.auto = false

     

    github에 올릴 때 중요한 값들은 그냥 올리면 안된다.

    (예전에 aws key를 그냥 올렸다가 aws에서 경고 메일 날라오고 기능이 잠긴 기억이 있다.)

     

    그래서 환경 변수로 설정해야하는데, 배포 환경에서 환경 변수를 어떻게 설정해야 하는지에 대한 의문이 계속 들었다. 구글을 찾아봐도 사람마다 다르고 마음대로 가져다 썼다가 빌드 에러가 더 심해질까봐 건드리지도 못했다. 계속 찾아가면서 겨우 환경 변수를 설정할 수 있었다.

     

     

    github의 settings에 key를 저장하는 곳이 있다.

    여기에다가 key를 저장한 다음에 배포를 할 때 환경변수로 넘겨주는 것이다. 보안 관련해서 매우 중요한 일이기 때문에 할 때마다 조심스럽게 해보고 오랜 시간을 들여서 겨우 할 수 있었다.

     

    ec2의 application.properties를 덮어쓰고 s3의 JAR 파일에도 환경변수가 들어간 application.properties를 올려준다.

     

     

     

    이 외에도 AWS에서 설정한 것들이 매우 많지만 내가 헤맸던 부분을 되짚어보고 실수하지 않도록 포스팅을 하게 되었다.

     

    인프라 구축은 개발만큼 매우 중요한 것이므로 이후에도 CI-CD를 활용해서 배포 자동화를 잘 하고싶다. 그리고 방학이 되면 도커와 쿠버네티스를 공부해서 클라우드 쪽도 제대로 알고싶다.

Designed by Tistory.