ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CodeDepoly로 EC2에 배포하기
    인프라/aws 2024. 3. 18. 13:52

     

    프로젝트를 진행하면서 게시글 서비스를 github action과 docker hub를 사용해서 ci/cd를 구축해서 사용하고 있었습니다.

    이번에 유저 서비스를 새로 만들면서 인프라 툴을 여러 곳에 분산해서 사용하는 것보다 한 곳으로 모으는 것에 이점이 있을 것 같아서 

    이미지 관리와 CI/CD 방법을 변경하면서 작성하는 글입니다.

     

    여러 차례 막히는 구간이 생겨서 하루 종일 고생하다가 정리해두는 것이 좋을 것 같아 오랜만에 글로 남깁니다.

     

    [관련 포스팅]

    1. Elastic Container Registry (ECR) 로 도커 이미지 관리하기

    2. CodeBuild로 ECR 갱신하기

    3. CodeDeploy로 EC2에 배포하기
    4. CodePipeline으로 CI/CD 구축하기

     

     


     

    글은 크게 5가지 항목으로 진행됩니다.

     

    1. 내가 이해한 Code Deploy

    2. EC2 세팅하기

    3. appspec.yml 작성하기

    4. CodeDeploy의 Application 생성하기

    5. 정상 작동 확인하기

     

     

     

    1.  내가 이해한 Code Deploy

    Code Deploy는 라이프 사이클(특정 시점)에 맞추어 미리 정의해둔 script를 실행시켜주는 서비스라고 보면 될 것 같습니다.

    Code Deploy는 보통 Code pipeline의 한 stage로 사용이 됩니다.  소스 코드의 root 경로에 appspec.yml을 정의해주어야합니다.

     

    2. EC2 세팅하기

    기본적으로 서버를 배포할 EC2는 준비가 된 상태에서 시작합니다. Code Deploy 설정을 하기전에 필요한 EC2 설정을 진행합니다.

    EC2가 ECR에 있는 이미지를 사용하기 때문에 적절한 권한 추가와 Code Deploy가 EC2를 사용하기 위한 agent 설치가 필요합니다.

     

    2-1. EC2를 위한 IAM 권한 설정하기

    aws 서비스 사용은 대부분 기본적인 네트워크 지식과 권한에 대한 이해에 있는 것 같습니다.

    ECR에 있는 이미지를 사용해서 배포를 진행할 것이기 때문에 Code Deploy를 허용하는 권한과 ECR에 접근할 수 있는 권한을 부여해주어야 합니다.

     

    Action > Security > Modify IAM role 순으로 이동합니다.

    Create new IAM role로 이동해서 적절한 role을 생성해주겠습니다.

     

    EC2에 적용할 권한이기 때문에 EC2를 골라줍니다.

     

     

    ECR에 있는 이미지를 가져와야하기 때문에 ECR에 접근할 수 있는 권한을 부여합니다.

     

    다시 EC2 대시보드로 돌아와서 방금 만들어준 role을 적용해줍니다.

     

     

     

    2-2. CodeDeploy Agent 설치하기

    Code Deploy가 EC2를 제어하기 위한 agent를 설치해줍니다.

    자주 사용될 것으로 보이니 인스턴스 초기화를 위한 스크립트에 추가해서 관리하면 좋을 것 같습니다.

    $ sudo yum update
    $ sudo yum install -y ruby
    $ sudo yum install -y wget
    $ 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

     

    잘 설치 되었는지 확인해봅니다.

    $ sudo service codedeploy-agent status   // The AWS CodeDeploy agent is running as PID 26895

     

     

    2-3. Tag 설정하기

    Code Deploy가 제어해야하는 EC2를 지정해주기 위한 태그가 필요합니다.

    EC2 Instance 설정에서 Tag를 추가해주세요.

     

     

    2-4. 환경변수 설정하기

    Code Deploy는 적절한 시점에 개발자가 작성한 script를 실행시키는 형태로 작동합니다. 작성한 내용을 git에 올려야하기 때문에 보안에 신경써야하는 데이터는 환경변수 처리가 필요합니다.

     

    #!/bin/bash
    echo "export USER_ID=your_aws_id" >> ~/.bash_profile
    echo "export REGION=your_aws_region" >> ~/.bash_profile
    echo "export REPO=your_ecr_repo_name" >> ~/.bash_profile
    echo "export IMAGE_TAG=your_docker_image_tag" >> ~/.bash_profile
    echo "export CONTAINER_NAME=your_docker_container_name" >> ~/.bash_profile
    echo "export PORT=your_server_port" >> ~/.bash_profile
    source ~/.bash_profile

     

     

     

    3. appspec.yml 작성하기

    처음에 이야기한 것처럼 Code Deploy가 제공하는 특정 시점들에 실행될 script를 정의하는 appspec.yml을 작성해주어야합니다.

    작동하는 시점을 잘 이해하고 사용해야합니다. ( 여기서 하루를 통째로 허비했습니다...)

     

    스크립트를 실행 시킬 수 있는 라이프 사이클에 대한 간단한 설명입니다.

    개별 스크립트를 작성해서 EC2에서 잘 작동하는지 먼저 테스트 해보고 Code Deploy에 연결해서 쓰는 것도 하나의 방법이 될 수 있을 것 같습니다. 

     

     

     

    저는 소스 코드에 cleanup_script를 포함시켜서, 기존에 작동하던 도커 설정들을 정리해주고 로그를 남기려고 합니다.

    #!/bin/bash
    
    # sudo 미적용으로 사용자의 .bash_profile에서 환경변수 로드 가능
    echo "@@@@@ Clean up started @@@@@" >> /home/ec2-user/deploy.log
    
    source ~/.bash_profile
    
    # 컨테이너 정지 및 삭제
    if docker ps -a | grep -q "$CONTAINER_NAME"; then
         "Stopping and removing container: $CONTAINER_NAME" >> /home/ec2-user/deploy.log
         docker stop "$CONTAINER_NAME"
         docker rm "$CONTAINER_NAME"
    else
        echo "Container $CONTAINER_NAME does not exist." >> /home/ec2-user/deploy.log
    fi
    
    # Docker 이미지 삭제
    IMAGE="$USER_ID.dkr.ecr.$REGION.amazonaws.com/$REPO"
    if docker images | grep -q "$REPO"; then
        echo "Removing Docker image: $IMAGE" >> /home/ec2-user/deploy.log
        docker rmi "$IMAGE"
    else
        echo "Image $IMAGE does not exist." >> /home/ec2-user/deploy.log
    fi

     

     

    작성한 스크립트를 소스폴더의 root 경로에 있는 appspec.yml 에 연결해줍니다.

    저는 소스코드가 모두 다운된 시점인 AfterInstall 시점을 선택하였습니다.

    version: 0.0
    os: linux
    files:
      - source: /
        destination: /home/ec2-user/user_service
    hooks:
      AfterInstall:
        - location: deploy_scripts/cleanup_script.sh
          timeout: 300
          runas: ec2-user

     

     

    이제 작성한 appspec.yml과 scripts들을 커밋해서 깃헙에 push를 진행합니다.

    배포를 원하는 커밋의 commit Id를 이후에 Code Deploy에서 사용할 예정입니다.

     

    4.  Code Deploy의 Application 생성하기

     

    4-1. CodeDeploy를 위한 IAM 권한 생성하기

    CodeDeploy가 EC2를 사용해서 배포를 진행해야하기 때문에 CodeDeploy에게 적절한 권한을 부여해주어야합니다.

    IAM에 가서 권한을 부여합니다. ( 이후에 Deployment group 생성에 사용됩니다. )

     

     

    4-2. Application 생성하기 & Deployment Group 생성하기

    codedeploy의 getting started에 가보면 처음에 create application을 하도록 안내하고 있습니다.

    EC2에 배포를 진행할 것이기 때문에 EC2를 선택하고 생성해줍니다.

     

    Application 생성을 완료하면 이어서 deployment group을 생성해주어야합니다.

     

    생성에 필요한 요소들을 잘 채워주면 됩니다.

    위에서 만들었던 권한을 선택해줍니다. 또 EC2에서 생성한 태그 역시 골라주면 연결이 됩니다.

     

    deployment group 까지 잘 생성되었습니다.

     

     

    4-3. Create Deployment

    S3를 사용하는 방식과 깃헙을 사용하는 방식이 있습니다. 저는 github의 커밋을 사용하는 방법을 선택하였습니다.

    Github token name에 원하는 이름을 적고 Connect to github 버튼을 눌러줍니다.

    내가 사용하는 repository와 배포를 진행할 commit hash를 복사해서 넣어주면 됩니다.

    (위에서 push한 commit id를 복사해서 붙여넣으면 됩니다.)

     

     

     

    5. 정상작동 확인

    Code Deploy가 정상적으로 작동하면 만날 수 있는 화면입니다.

     

     

    부적절한 라이프 사이클에 스크립트가 연결되어있으면 위 화면이 나오더라도 실행되지 않았을 수 있습니다. ( 여기서 하루를 날렸습니다.. )

    EC2에 접근하여 원하는 스크립트가 잘 실행되었는지 확인해줍니다.

    스크립트에 설정해둔 로그가 잘 찍힌 것을 확인할 수 있습니다.

     

     

     

    최종적으로 사용된 스크립트는 2개입니다. deploy_scripts 디렉토리에 cleanup_script.sh 와 start_script.sh를 작성하였고

    각각 appspec.yml의 AfterInstall과 ApplicationStart 시점에 작동하도록 설정하였습니다.

     

    // cleanup_script.sh
    
    
    #!/bin/bash
    
    # sudo 미적용으로 사용자의 .bash_profile에서 환경변수 로드 가능
    echo "@@@@@ Clean up started @@@@@" >> /home/ec2-user/deploy.log
    
    source ~/.bash_profile
    
    # 컨테이너 정지 및 삭제
    if docker ps -a | grep -q "$CONTAINER_NAME"; then
         "Stopping and removing container: $CONTAINER_NAME" >> /home/ec2-user/deploy.log
         docker stop "$CONTAINER_NAME"
         docker rm "$CONTAINER_NAME"
    else
        echo "Container $CONTAINER_NAME does not exist." >> /home/ec2-user/deploy.log
    fi
    
    # Docker 이미지 삭제
    IMAGE="$USER_ID.dkr.ecr.$REGION.amazonaws.com/$REPO"
    if docker images | grep -q "$REPO"; then
        echo "Removing Docker image: $IMAGE" >> /home/ec2-user/deploy.log
        docker rmi "$IMAGE"
    else
        echo "Image $IMAGE does not exist." >> /home/ec2-user/deploy.log
    fi
    
    echo "@@@@@ Clean up end @@@@@" >> /home/ec2-user/deploy.log

     

    // start_script.sh
    
    
    #!/bin/bash
    
    # sudo 미적용으로 사용자의 .bash_profile에서 환경변수 로드 가능
    echo "@@@@@ Start up started @@@@@" >> /home/ec2-user/deploy.log
    
    source ~/.bash_profile
    
    echo "ECR Login try" >> /home/ec2-user/deploy.log
    aws ecr get-login-password --region "$REGION" | docker login --username AWS --password-stdin "$USER_ID.dkr.ecr."$REGION".amazonaws.com"
    echo "ECR Login success" >> /home/ec2-user/deploy.log
    
    
    IMAGE="$USER_ID.dkr.ecr.$REGION.amazonaws.com/$REPO"
    docker pull "$IMAGE"
    docker run -d -p 80:"$PORT" --name "$REPO" "$IMAGE"
    
    echo "@@@@@ Start up finished @@@@@" >> /home/ec2-user/deploy.log

     

     

    // appspec.yml
    
    
    version: 0.0
    os: linux
    files:
      - source: /
        destination: /home/ec2-user/user_service
    hooks:
      AfterInstall:
        - location: deploy_scripts/cleanup_script.sh
          timeout: 300
          runas: ec2-user
      ApplicationStart:
        - location: deploy_scripts/start_script.sh
          timeout: 300
          runas: ec2-user

     

     

    감사합니다.

Designed by Tistory.