본문 바로가기
IT이야기/AWS

[GitActions] AWS EC2로 자동 배포하기 (feat Linux) / GITAction + EC2 + CodeDeploy등을 이용한 CD

by JI_NOH 2024. 1. 18.
프로젝트 배포를 위한 EC2+RDS+GIT ACTION 사용기 목차
1. EC2생성 및 연결
2. RDS + EC2설정
3. GITAction + EC2 + CodeDeploy등을 이용한 CD(AWS 자동배포) (현재)

 

 

깃액션으로 메인브랜치에 push되면 AWS로 자동 배포되는 작업을 해보도록 하자.

 

아마 많이 보기는 했을 것이다. "깃액션으로 AWS에 자동 배포 할 수 있대!"

어떤 흐름으로 되느냐 하면

깃허브에서 프로젝트 빌드 -> AWS인증 -> 해당 코드들 AWS S3에 업로드 -> AWS CodeDeploy를 이용하여 S3에서 EC2로 배포 이런 플로우다.

 

설정할 것 짱 많으니까 여유를 많이 두고 트라이하도록 하자!!!!

 

 

목차

     

     

     

    EC2 추가설정


    태그추가

     

    아래쪽에 보이는 태그탭으로 가서 태그 관리를 해도 되고

    오른쪽에 작업 - 인스턴스설정 - 태그관리 로 들어와도 된다.

     

     

     

    IAM역할 추가

     

    식별 가능한 이름으로 입력하고 생성하면 끝

     

    다시 EC2 인스턴스로 돌아가서 IAM역할 수정을 하자

     

     

     

    CodeDeploy 설치 (in EC2 서버)

    참고로 나는 AmazonLinux2023이다. 다른 서버라면 공식문서 참고 요망

    sudo yum update
    sudo yum install ruby
    sudo yum install wget
    
    #홈 디렉토리로 이동. 사실 cd ~ 해도 됨
    cd /home/ec2-user
    
    wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
    
    chmod +x ./install
    sudo ./install auto
    
    #CodeDeploy실행여부
    sudo service codedeploy-agent status
     

    다 하고 status로 확인했는데 뭘까 ???

    마지막 줄을 보면 프로세스가 도는 것 같아 보이는데 중간중간 멘트는 왤케 찝-찝 할까?

    다시 공식 문서를 보니 맞나보다!

     

     

     

     

    S3 생성


    아 여기서 좀 막혔는데 프로젝트에서 S3를 쓰고 있어서 이미 생성을 한 상태다

    그거를 같이 써도 되는지? 안되는지? 가 좀 애-매해서 좀 찾아봤는데

    이거는 배포용 S3로 만들어야 해서 퍼블릭권한이 없으며, 사이트에서 사용하는 이미지 업로드는 퍼블릭으로 만들지 않았나? 결국 따로 해야하는 것으로 결론.

     

    근데 5GB까지만 프리티어다. 많은 용량을 차지하지 않을 것이라면 2개정도 인스턴스를 굴려도 될 것 같다.

    EC2나 RDS는 사용시간(월 750시간)으로 책정하다보니 2개를 돌리면 당연히 오버되는 구조지만 S3는 용량제한이니까

    그러겠지?!!?!?!? 혹시나 과금되면 추가 포스팅을 해놓도록 하겠다..

    기억해두자. GET, PUT많이 하지말자!

     

    -> 최종적으로 많이 사용하지 않으면 과금되지 않았다.

     

    등록하는데 버킷이름에 대문자 들어가면 안된다고 함

    버킷 만들면 끝

     

    추가 정책설정으로

    {
        "Version": "2012-10-17",
        "Id": "본인ID",
        "Statement": [
            {
                "Sid": "Stmt1696948368464",
                "Effect": "Allow",
                "Principal": "*",
                "Action": "s3:*",
                "Resource": "arn:aws:s3:::본인버킷이름/*"
            }
        ]
    }
     해당 S3에 모든 사람들이 접근할 수 있으며 s3에 관한 put, update, delete등도 가능하다. 라는 의미다.
     
     
     
     
     

    CodeDeploy설정


    CodeDeploy IAM설정

    앞서 했던 IAM 역할 설정까지 똑같이 들어가고

    서비스 또는 사용 사례 - CodeDeploy설정

    태그 추가 없이 이름만 넣고 생성

     

     

    CodeDeploy 애플리케이션 생성

     

     

    CodeDeploy 배포 그룹 생성

    배포 그룹 생성을 눌러 생성을 시작해보자.

    화면 캡쳐대로 옵션 선택하면 된다.

    앞서 만들었던 IAM이 여기서 조회가 될 것이다.

    아까 만들었던 태그를 드디어 여기서 쓴다!

    여기까지 설정하고 드디어 생성

     

     

     

     

    GIT ACTION용 IAM 사용자 추가


    이번에는 역할 아니고 사용자다 사용자!!

    예전글을 보니 AccessKey로 발급받아야한다는데

    현재와 와서는 좀 바뀐건지 선택창이 달라서 최신글을 찾아보니 나중에 이 키로 엑세스 키를 발급 받을 수 있다고 한다.

     

    다음 권한 화면에서

    두개를 추가하여 진행해주자.

     

    여기에 암호 잘 기억해두고 AccessKey발급받으러 바로 이동!!

     

    AccessKey발급

    다시 IAM - 사용자로 와서 선택 후 보안 자격 증명 탭으로 이동

    탭 이동 후 아래로 스크롤 내려보면

    잘 메모해두자! 발급이 끝났다.

     

     

    GITHUB Key등록

    배포할 레포지토리 이동 -> Settings -> 왼쪽 아래에 Security의 Actions로 들어가자.

    나는 Organization 으로 프로젝트 진행중이라 이렇게 뜬다. 되나..?

    사실 내 개인 레포가 아니라 키 입력이 우려됐는데 등록 후에 수정하는 걸 해봤더니

    기존 값은 볼 수 없고 새로운 값을 입력하는 것만 가능하다고 뜬다.

     

    하... 얼추 AWS설정들은 끝났다. 그래도 아직 더 설정해야한다.

    때려치고 싶어졌다.

     

     

     

     

    AppSpec.yml작성


    appspec.yml 위치

    배포할 프로젝트의 최상단에 놓으면 된다.

    appspec.yml 구성

    여기서부터 리눅스서버와 우분투 서버는 경로 및 owner등이 조금씩 다르기 때문에 잘 바꿉시다!!

    version: 0.0
    os: linux
    files:
      - source:  /
        destination: /home/ec-user2/app
        overwrite: yes
    
    permissions:
      - object: /
        pattern: "**"
        owner: ec2-user
        group: ec2-user
    hooks:
      AfterInstall:
        - location: scripts/stop.sh
          timeout: 60
      ApplicationStart:
        - location: scripts/start.sh
          timeout: 60
     

    공식문서 예시에는 hooks에 좀 더 많은 것들이 있었는데 다들 두개만 쓰길래 나도 두개만 썼다. 뀨?

     

    그 다음으로 위의 appspec.yml에 선언한 scripts/stop.sh와 scripts/start.sh를 생성해준다.

    마찬가지로 디렉토리와 함께 생성해주면 되겠다.

     

    start.sh

    #!/usr/bin/env bash
    
    PROJECT_ROOT="/home/ec2-user/app"
    JAR_FILE="$PROJECT_ROOT/spring-webapp.jar"
    
    APP_LOG="$PROJECT_ROOT/application.log"
    ERROR_LOG="$PROJECT_ROOT/error.log"
    DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
    
    TIME_NOW=$(date +%c)
    
    # build 파일 복사
    echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG
    cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE
    
    #codedeploy bashrc를 읽어오지 못해 해당 파일 로드하게 작업 - 환경변수 사용 시 추가
    source ~/.bash_profile
    
    # jar 파일 실행
    echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG
    nohup java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG &
    
    CURRENT_PID=$(pgrep -f $JAR_FILE)
    echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG
     

    stop.sh

    #!/usr/bin/env bash
    
    PROJECT_ROOT="/home/ec2-user/app"
    JAR_FILE="$PROJECT_ROOT/spring-webapp.jar"
    
    DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
    
    TIME_NOW=$(date +%c)
    
    # 현재 구동 중인 애플리케이션 pid 확인
    CURRENT_PID=$(pgrep -f $JAR_FILE)
    
    # 프로세스가 켜져 있으면 종료
    if [ -z $CURRENT_PID ]; then
      echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG
    else
      echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG
      kill -15 $CURRENT_PID
    fi

     

     

     

     

    GITHUB Actions WorkFlow


    deploy.yml로 생성

    yml이름은 달라도 되긴하는데 (기본이 main임)

    밑에서 작성할 스크립트 코드에서 조금씩 바꿔야할 것 생각하면 그냥 똑같이 따라하자.

     

    deploy.yml내용

    name: Deploy to Amazon EC2  #이름은 마음대로
    
    on:
      push:
        branches:
          - main
    
    # 리전, 버킷 이름, CodeDeploy 앱 이름, CodeDeploy 배포 그룹 이름
    env:
      AWS_REGION: ap-northeast-2
      S3_BUCKET_NAME: shop-project-cdbucket
      CODE_DEPLOY_APPLICATION_NAME: shop-project-codedeploy
      CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: shop-project-codedeploy-group
    
    permissions:
      contents: read
    
    jobs:
      deploy:
        name: Deploy
        runs-on: ubuntu-latest    #linux서버인데 동일
        environment: production
    
        steps:
        # (1) 기본 체크아웃
        - name: Checkout
          uses: actions/checkout@v3
    
        # (2) JDK 11 세팅 (다른 자바버전이라면 숫자만 바꿔서 진행)
        - name: Set up JDK 11
          uses: actions/setup-java@v3
          with:
            distribution: 'temurin'
            java-version: '11'
    
        # (3) Gradle build (Test 제외)
        - name: Build with Gradle
          uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee
          with:
            arguments: clean build -x test
    
        # (4) AWS 인증 (IAM 사용자 Access Key, Secret Key 활용)
        - name: Configure AWS credentials
          uses: aws-actions/configure-aws-credentials@v1
          with:
            aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
            aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
            aws-region: ${{ env.AWS_REGION }}
    
        # (5) 빌드 결과물을 S3 버킷에 업로드
        - name: Upload to AWS S3
          run: |
            aws deploy push \
              --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
              --ignore-hidden-files \
              --s3-location s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip \
              --source .
        # (6) S3 버킷에 있는 파일을 대상으로 CodeDeploy 실행
        - name: Deploy to AWS EC2 from S3
          run: |
            aws deploy create-deployment \
              --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
              --deployment-config-name CodeDeployDefault.AllAtOnce \
              --deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
              --s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip
     

    참고로 여기서

    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}

    aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}

    이 이름은 우리가 아까 깃 시크릿에다 등록한 이름과 똑같이 해주자.

     

     

     

     

    GIT ACTION 가동 후


    GIT ACTION

     

    S3

     

    CodeDeploy

    대부분 경로 지정 및 이름 문제였다.

    해당 배포들어가서 View events에 들어가면 정말 상세하고 친절하게 왜 틀렸는지 알려줌!

    와!!!! 여러번의 경로 이름 잘못설정한 문제로 실패하고 드디어 성공!!!

    CodeDeploy 배포 화면

    그러고나서 EC2 서버로 가보면 아까 우리가 appspec.yml에 선언해둔 "destination: /home/ec-user2/app" 디렉토리 생성과 함께 빌드된 파일이 잘 들어왔음을 볼 수 있다.

     

     

     

    자동배포 에러


     

    1. /home/ec-user2/app 에 프로젝트 카피가 안 된 경우

    만약에 해당 디렉토리에 파일이 없다면 어디서 부터 빵꾸났는지 순차적으로 확인을 해야한다.

    deploy.yml 내용을 제대로 보면 알겠지만

     

     

    이런식으로 순차 작업을 진행하고 본인이 어디쯤에서 에러가 났는지 Git Actions에 들어가면 친절하게 알려준다.

    결국 프로젝트 빌드까지 문제가 없다면 -> AWS 사용자 만든 것 (AWS_ACCESS_KEY, AWS_SECRET_KEY) 기반으로 자격 증명 -> 배포용 S3에 업로드 -> S3에 업로드 된 내용 CodeDeploy를 통해 EC2로 업로드  ... 와 같은 과정을 거친다.

     

    진행하다보니 나는 CodeDeploy에서 실패가 떴다.

     

    해당 배포 ID를 눌러서 들어가보면 어떤 에러인지 뜨는데 사실 그 에러만 봐서는 무슨 소린지 당최 이해하기가 힘들다.

    EC2 서버에 들어가서 

    cd /var/log/aws/codedeploy-agent

     

     경로에 위치한 로그파일을 확인해보면 보다 세부적인 내용이 뜬다. 해당 내용을 잘 읽어보고 다시 수정 후 재 배포하면 정상적으로 파일이 드랍되고 실행될 수 있을 것이다.

     

    2. /home/ec-user2/app 경로에 프로젝트 드랍은 됐는데 spring-webapp.jar파일이 생성되지 않은 경우

    cd /app/build/libs

     경로에 jar파일이 어떤게 생성되었는지 확인. 

    프로젝트명-0.0.1-SNAPSHOT.jar파일 하나만 생성되어야 하고 -plain이 붙은 것이 있다면 이 파일 때문에 생긴 문제다.

    의존성은 하나도 추가되지 않은 프로젝트 내 자원들로만 build 된 jar파일이기 때문이다.

    이는 build.gradle에 설정을 추가로 해줘야하는데

    jar {
    	// ~~plain.jar파일은 생성되지 않게
    	enabled = false
    }

     

    후 다시 재 배포하거나, 당장 확인을 하고 싶다면 

    rm {~~~-plain.jar}

     

    를 해주면 되겠다.

     

     

     

    최종 배포 확인


    EC2 서버에서 확인하기

    이제 서버도 제대로 돌아가고 있는지 확인하기 위해서

    ps -ef | grep java
     

    명령어를 입력했었을 때 jar파일이 떠있어야 하는데 없으면 실패한 것이다.

    실패

    jar파일 실행 시 실패했는지 궁금하다면

    cat application.log
     

    로 해당 로그파일을 보면 되겠다.

    나는 테스트한다고 아직 git ignore파일 설정에 관해서는 작업하지 않았기때문에 해당 파일의 내용을 못찾는다고 에러가 나는 것임!

     

    성공하면 동일 명령어로 조회했을 때 첫번째 줄에 .jar파일이 뜬다.

     

     

    혹시 환경변수가 제대로 로드가 안되는 것이 있는 것 같다면

    sudo vim ~/.bash_profile
    
    #설정 후 실행
    source ~/.bash_profile
     

    여기에 넣어보자.

    좀 찾아보니 EC2서버에서 직접 명령어를 치면 잘 가지고 오는데

    CodeDeploy가 환경변수를 해당 경로에서 잘 못찾아오는 것일 수도 있다고 함.

    #codedeploy bashrc를 읽어오지 못해 해당 파일 로드하게 작업
    source ~/.bash_profile
     

    그리고 scrpits에 있는 start.sh에서 아마 봤겠지만 해당 명령어도 넣어줘야 한다.

    '나는 환경변수를 bash_profile에서 읽어오겠다.' 라는 뜻이란다.

     

    CodeDeploy를 통한 배포 빌드를 하면 자꾸 이런 문구가 뜬단말이지.. 정상적이라면 "prod"가 들어가야됨 ㅠㅠ

     

    EC2에서는 배포 파일에 있는 실행명령어랑 똑같이 쳤을 때 잘 돌아간단말임!!!

    nohup java -jar /home/ec2-user/app/spring-webapp.jar &
     

    -> 여기서 제일 뒤의 & 는 백그라운드로 돌겠다는 것임

     

     

     

     

     

    ---------- 아래는 빌드 및 배포 성공한 것 같은데 접속이 안되는 경우 확인하는 법 -----------

    자 위에서는 jar파일이 실행이 안돼서 열심히 삽질하다가 해결했고

    본인은 위에서 jar파일 정상적으로 실행이 된 것 같은데 해당 서버 IP로 접속이 안된다???

    거기다 ssh도 미친듯이 느려서 명령어도 잘 안쳐지고?

     

    톰캣 서버가 잘 올라왔는지 확인하는 명령어로 두가지를 실행해봤다.

    #톰캣 서버 상태 확인
    systemctl status tomcat
    
    #오픈된 포트 확인
    netstat -ntlp
     

    이거는 한~~~참 걸리더니 timed out이 떴고

    얼라리오?? 왜 8080포트가 없는 것일까??

     

    프리티어를 사용하면서 램이 1GB밖에 안되니까 메모리오버로 애가 상태가 맛간 것으로 추정

    좀 찾아보니 SWAP설정을 통해 좀 나아진다고 하기에 해당 설정은 따로 해보려고 한다.

     

    SWAP설정 후 다시 서버 올렸더니 8080포트도 정상적으로 리스트에 뜨고 사이트에도 접속이 됐다.

    설정하는 법은 바로 위에 링크 걸어둠!

     

     

    알아두면 좋은 다른 명령어

    현재 백그라운드에서 도는 프로세스 확인 명령어 : jobs

    stopped상태인 것을 running으로 바꾸는 명령어 : bg 작업번호