본문 바로가기

Infra/AWS

[AWS/Spring boot] AWS CodeDeploy로 웹 애플리케이션 배포하기

AWS CodeDeploy란?

AWS에서는 Developer Tools이라는 이름으로 누구나 쉽게(는 아닌 것 같지만 ,,?) 소스코드를 관리하고 빌드 및 배포할 수 있는 다양한 서비스를 제공한다.

 

요즘 CI/CD 라는 소스코드 빌드 및 배포 자동화 솔루션이 인기인데, CodeDeploy는 그 중 CD 서비스를 제공한다.

특징에는 Appspec이라는 배포 방식을 정의하는 문서를 기준으로 EC2, ECS, 온프레미스 서버로 배포할 수 있도록 돕는다는 점이 있다.

 

 

CodeDeploy를 통해 배포하면 롤백, 컨테이너배포 등 다양한 이점이 있는데, 아래 링크에서 자세한 내용을 파악할 수 있다.

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/welcome.html

 

CodeDeploy란 무엇입니까? - AWS CodeDeploy

CodeDeploy란 무엇입니까? CodeDeploy는 Amazon EC2 인스턴스, 온프레미스 인스턴스, 서버리스 Lambda 함수 또는 Amazon ECS 서비스로 애플리케이션 배포를 자동화하는 배포 서비스입니다. 다음을 포함하여 다양한 애플리케이션 콘텐츠를 거의 무제한으로 배포할 수 있습니다. 코드 서버리스 AWS Lambda 함수 웹 및 구성 파일 실행 파일 패키지 스크립트 멀티미디어 파일 CodeDeploy는 서버에서 실행되고 Amazon S3

docs.aws.amazon.com

 

CodeDeploy는 S3 또는 Git hub에 정의된 파일들을 기준으로 배포를 수행한다.

배포할 애플리케이션 파일, 배포 전 후 실행할 shell script, 그리고 가장 중요한 appspec 을 포함한 zip, tar, tar.gz 폴더를 S3에 업로드하거나 git hub에 업로드 한 뒤 해당 배포를 생성하면 사전에 정의한 인스턴스에서 배포를 수행한다.

 

 

appspec은 yaml 형식을 갖추어야 하며 다음 링크에서 템플릿을 확인할 수 있다.

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/application-revisions-appspec-file.html

 

CodeDeploy EC2 배포(S3, Docker 배포)

이 포스팅에서는 EC2 에서 Docker를 통해 컨테이너 배포를 수행한다.

위에서 언급했다시피 CodeDeploy는 CD만 수행하기 때문에 빌드 및 ECR push는 로컬 터미널에서 진행한다.

과정은 다음과 같다.

Spring boot Application Docker build

ECR push(AWS CLI)

CodeDeploy EC2 Deploy(AWS Console, AWS CLI)

Step1. Spring boot Application 생성

먼저 배포할 샘플 애플리케이션을 생성할 것이다.

 

Spring boot 프로젝트 생성(IntelliJ)

 

HomeController.java

package com.aws.codedeploy.web.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

    @GetMapping
    public ResponseEntity<String> helloCodeDeploy(){
        return new ResponseEntity<>("Hello CodeDeploy!", HttpStatus.OK);
    }
}

 

 

Step 2. Application Docker Build 후 ECR Push

위에서 생성한 애플리케이션을 빌드 한 후 jar 실행파일 위치를 확인하여 다음 Dockerfile ADD 다음에 입력한다.

도커를 자세히 다루진 않지만 게시글 최하단에 각각의 내용이 무엇을 의미하는지 설명해두었다.

 

Dockerfile

FROM openjdk:8-jdk-alpine
ADD ./build/libs/codedeploy-0.0.1-SNAPSHOT.jar hello-codedeploy.jar
ENTRYPOINT ["java", "-jar", "/hello-codedeploy.jar"]

 

위의 Dockerfile을 프로젝트 루트디렉터리에 추가한 후 프로젝트 경로에서 docker build를 수행한다.

Dockerfile 이라는 파일을 기준으로 현 위치의 jar파일을 hello-codedeploy 라는 이름의 이미지로 빌드한다는 뜻이다.

docker build -f Dockerfile hello-codedeploy .

위와 같은 Successfully 메시지가 뜨면 빌드가 정상적으로 된 것이다.

제대로 빌드되었는지 로컬에서 확인하기 위해 docker run을 수행하여 컨테이너를 띄운다.

 

로컬 8888포트로 접속하면 컨테이너 8888포트로 포트포워딩 한다는 뜻이다.

내 애플리케이션의 포트설정이(server.port) 8888이기 때문에 아래와 같이 한 것이고, 자신의 설정대로 수정하면 된다.

docker run -p 8888:8888 hellocodedeploy

정상적으로 애플리케이션이 실행되는 것을 확인하였으니 이제 이 도커이미지를 ECR로 푸쉬한다.

 

 

 

AWS ECR 연결 및 로그인

자신의 AWS 계정의 Access Key, Secret Access Key, region을 다음 명령어를 실행한 뒤 입력한다.

aws configure

 

 

그 후, 아래 명령을 통해 configure에서 설정한 정보로 ECR에 로그인을 진행한다.

리전 정보는 자신의 리전으로 변경한다.(ap-northeast-2: Asia Seoul)

eval $(aws ecr get-login --region apnortheast-2 --no-include-email)

 

자신의 ECR 리포지토리를 들어가면 상단에 "푸시명령보기" 버튼을 볼 수 있다.

해당 버튼을 누르면 푸시를 위한 명령어를 쭉 보여주니 그대로 복붙하도록 한다.

정상적으로 푸쉬되었다.

 

 

 

 

일반적은 웹애플리케이션 배포는 다음과 같은 로직으로 수행된다.

jar 실행파일 빌드 -> 인스턴스에 파일 전송 -> 배포

 

CodeDeploy도 마찬가지인데, 지금같은 경우는 ECR을 사용하기 때문에 실행 파일을 전송할 필요가 없다.

따라서 가장 마지막 단계인 "배포"만 수행한다.

 

EC2 에서 방금 푸쉬한 이미지를 가져오고 실행하는 Shell script를 작성한다.

 

hello-codedeploy.sh

$(aws ecr get-login --region ap-northeast-2 --no-include-email)
sudo docker rm -f codedeploy
sudo docker rmi 이미지이름
sudo docker pull 이미지이름:latest<tag>
sudo docker run -d --name codedeploy -p 80:8888 이미지이름

물론, 인스턴스 내에서도 "aws configure"를 통해 설정을 완료해야한다.

 

순서대로 다음과 같은 과정을 수행한다.

ECR 로그인 > 기존에 실행 중인 애플리케이션 컨테이너 종료 및 삭제 > 이전에 Pull 했던 이미지 삭제 > 방금 Push 한 이미지 pull > docker run

 

배포가 처음이라 삭제과정이 필요 없어도 상관없다. 없으면 없는대로 다음 명령어 실행할테니까!

 

CodeDeploy의 동작을 제어하는 파일인 appspec.yml 을 작성한다.

 

위에서 언급했다시피 docker build 및 push 까지는 Jenkins 또는 Terminal 작업 후 진행하기 때문에 BeforeInstall Option만 사용한다.

 

appspec.yml

version: 0.0
os: linux
hooks:
  BeforeInstall:
    - location: /scripts/hello-codedeploy.sh

version 정보는 건드리면 안된다.

os 는 인스턴스 OS 를 따라 적으면 되고, hooks 이 전후 작업 등을 작성하는 내용인데 Install 이전에 hello-codedeploy.sh 를 실행하라는 의미이다.

Install이 바로 애플리케이션을 옮겨와서 실행하는 그 과정을 말하는데 현재 케이스에는 필요없어서 제외했다.

 

일반적인 appspec.yml 구조는 다음과 같으니 참고만하자.

version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/html/WordPress
hooks:
  BeforeInstall:
    - location: scripts/install_dependencies.sh
      timeout: 300
      runas: root
  AfterInstall:
    - location: scripts/change_permissions.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: scripts/start_server.sh
    - location: scripts/create_test_db.sh
      timeout: 300
      runas: root
  ApplicationStop:
    - location: scripts/stop_server.sh
      timeout: 300
      runas: root

appspec.yml이 있는 디렉터리의 루트부터 있는 모든 파일을 현 EC2 Instance의 /var/www/html/WordPress 아래에 모두 설치하라는 뜻이다. 아래 hooks 섹션을 통해 전후 작업을 처리함을 의미한다.

 

 

위에서 언급했다싷피 CodeDeploy의 개정파일은 github이나 S3에 업로드하여 사용할 수 있다.

S3의 경우 zip/tar/tar.gz 형식만 지원하기 때문에 압축해야함을 유의하자! 

 

S3 버킷 생성

 

반드시 Application을 배포할 EC2 인스턴스와 같은 리전에 버킷을 생성해야한다.

 

해당 버킷에 appspec.yml 과 scripts 폴더가 포함된 zip파일을 업로드한다.

 

 

이제 EC2 인스턴스에 접속해 CodeDeploy를 감지하고 appspec 대로 수행할 수 있도록 도와주는 CodeDeploy Agent를 설치해야 한다.

그 전에 EC2에 S3 access Role을 부여해야 정상적으로 수행된다! 

 

다음 정책을 가지는 롤을 생성한 뒤 EC2 에 할당하도록 한다.

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": [
          ... Some actions may already be listed here ...
          "s3:Get*",
          "s3:List*"
          ... Some more actions may already be listed here ...
        ],
        ...
      }
    ]
  }

 

Docker 설치

sudo yum install -y docker
sudo systemctl start docker

 

CodeAgent 설치

sudo yum update
sudo yum install ruby
sudo yum install wget
cd /home/ec2-user
wget https://aws-codedeploy-리전id.s3.리전id.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto

 

설치가 제대로 되어있는지 확인하려면 다음 명령어를 입력한다.

sudo service codedeploy-agent status

 

Step 4. CodeDeploy 실행

 

애플리케이션 생성

 

 

배포그룹 생성

해당 작업이 처음이라면 서비스 역할을 생성해주어야한다.

위의 롤을 생성한 뒤 적용시켜주자.

 

EC2는 현재의 인스턴스에 배포하는 유형 또는 블루그린 배포를 지원한다.

본 포스팅은 단일 인스턴스를 가정하기 때문에 "현재 위치"를 선택한다. 

 

EC2 인스턴스 선택은 "태그"를 기준으로한다.

인스턴스에 Name을 설정해준 뒤 적용한다.

정상적을 검색된다면 하단에 *개의 인스턴스 대상 이라는 문구가 뜰 것이다. 

 

 

EC2 배포의 경우 AllAtOnce가 디폴트이기 때문에 건드리지 않는다.

역시나 단일 인스턴스이기에 로드밸런서도 체크해제해준다.

 

배포그룹 생성 완료 후 배포생성을 진행한다.

 

배포생성

 

개정 위치를 아까 zip파일을 저장한 위치로 입력한 뒤 배포생성을 하면 성공이 떠야하는데 ,,

 

다음과 같은 에러를 마주쳤다 ㅠㅠ 

 

 

처음엔 성공했었는데 sh을 살짝 수정하고나니 계속 안되었다 계속~ 

웃긴건 같은 파일을 CLI 로 처리하였을 때는 또 정상적으로 배포되었다. 

예측하건데, 개정파일을 zip으로 번들링하는 과정에서 문제가 있는 것 같았다.

 

결국, CLI 로 진행,,, 

 

애플리케이션 생성, 배포그룹 생성까지는 콘솔로 작업하고!

 

개정을 S3로 푸쉬하는 과정과 배포생성을 CLI로 진행하였다.

 

 

 

S3에 업로드 할 개정 디렉터리에서 다음 명령어 입력한다.(압축되어있는 디렉터리면 안된다! CLI를 통해 압축까지 하여 push 한다)

aws deploy push \
  --application-name HELLO_CODEDEPLOY \
  --description "This is Hello CodeDeploy Revision file" \
  --ignore-hidden-files \
  --s3-location s3://버킷경로/hello_codedeploy.zip \
  --source .

 

각자의 터미널에 뜨는 S3 푸쉬 성공 후 아래 뜨는 To deploy with this revision, run: 이후의 명령어를 참고하여 배포생성을 수행한다.

 

aws deploy create-deployment --application-name HELLO_CODEDEPLOY --s3-location bucket=seojaeyeon-deploy,key=hello_codedeploy.zip,bundleType=zip,eTag=812f9a05573bf4bc1ad57657d5b1a3aa --deployment-group-name DGP_HELLOCODEDEPLOY --description "THIS IS CODEDEPLOY"

 

 

 

 

배포 성공이 뜨면 인스턴스에 접속해서 제대로 컨테이너가 띄워졌는지 확인해본다.

sudo docker ps

 

 

80 -> 8888로 포트포워딩 했으니 EC2의 80포트를 열어주고, 

 

쟌 성공~! 

 

 

 

 

— Dockerfile 정리—

 

FROM

FROM <image>[:<tag>] [AS <name>]

Docker는 기존 이미지를 기반으로 빌드를 수행하기 때문에 Dockerfile의 시작은 항상 FROM이어야 함. Java Application build이기 때문에 jdk 이미지를 기반으로 수행하도록 설정. tag를 생략하면 자동적으로 latest 적용됨

ADD

ADD [--chown=<user>:<group>] <src>... <dest>

src에 해당하는 파일 또는 디렉터리를 image filesystem으로 복사하는 명령어.

ENTRYPOINT

ENTRYPOINT ["executable", "param1", "param2"]

컨테이너 생성 시 발생하는 명령 지정.

위의 경우 컨테이너 생성 시 java application을 실행하는 것을 의미함.