본문 바로가기

AWS

CodePipeline과 CodeDeploy만으로 배포 자동화하기 (CodeBuild 없이)

시작하며

인터넷상의 CodePipeline 사용법에 대한 정보를 찾아보면 죄다 CodePipelineCodeBuildCodeDeploy를 묶어서 이용하는 방법을 알려준다. 하지만, 이제 막 배포 자동화 접하는 사람의 경우 사실 Build 과정은 필요가 없는 경우가 많을 것 같다. 예를 들어 test case가 완전하게 작성되지 않은 토이프로젝트라거나, 매번 test 할 필요는 없는. 즉 build 보다는 잦은 deploy에 초점을 맞추어 빠르게 deploy 하고싶은 경우들말이다.

CodePipeline을 처음 이용해보면 은근 CodeBuild의 build 과정에서 많은 시간을 잡아먹는다. 이 중 대부분의 시간이 CI 서버를 Provisioning 하는 동안 소모된다. 즉 굳이 CI가 필요없는 단계이거나, CI 서버에서 테스트 할 필요 없이 로컬에서 테스트한 뒤 repository에 push 하는 프로젝트라면 쓸 데 없이 많은 시간을 CodeBuild에서 잡아먹힌다는 것이다.

그래서 이번엔 간단하게 CodeBuild 없이 CodePipeline과 CodeDeploy만으로 배포 자동화를 시켜보는 핸즈온을 적어본다.

작업 과정 요약

세세한 내용들은 이미 앞선 CodePipelineCodeDeploy에 대한 글들에서 작성하였기에 어느정도 생략한다.

CodeDeploy가 배포하는 환경은 EC2/On-premise이다.

Express 앱을 배포할 것이다. - 아주 간단하게 Express 앱의 페이지 내용이 변경되는 Deploy

프로세스를 kill 하기 귀찮으니까 그냥 Docker을 이용하겠습니다.

( 의도치않게 글의 취지와 맞지않을 수 있을만한 진입장벽으로 느껴지실지도 모르겠군요...)

  • github repository를 만든다.
  • github repository를 clone따고 express-generator을 이용해 express application 생성 후 서버를 돌리고, 작동하는지 확인한다.
  • appspec.yml을 작성한다. Dockerfile, .gitignore을 작성
  • EC2 Instance에 codedeploy-agentdocker을 설치한다. Security group에서 Inbound를 설정한다.
  • CodeDeploy에서 Application, 배포그룹을 생성한다.
  • CodePipeline에서 방금 생성한 CodeDeploy Application을 설정한다.
  • github repository에 push 한다.

세부 작업과정

Github Repository 생성

배포에 사용할 빈 github repository를 생성한다.

express-generator을 이용해 Express Application 생성

git clone {REPOSIOTRY_URL}
npm install -g express-generator
express tutorial-codepipeline
cd tutorial-codepipeline
npm install
npm start

express-generator을 설치하면 express 라는 명령어로 express application을 만들 수 있다.

이때 application 이름을 자신의 github repository 디렉토리랑 똑같이 하는 것이 편하다. 그래야 .git 디렉토리가 express application 디렉토리에 함께 위치할 수 있기 때문이다.

(Directory가 이미 존재하고 empty가 아니라는 문구가 떠도 괜찮다고 y를 눌러주자.)

npm start 를 했으면 [http://localhost:3000으로](http://localhost:3000으로) 접속해서 express app이 잘 돌아가고 있는 지 확인한다.

appspec.yml 작성

맨날 CodeDeploy만 달고사는 사람이 아니라면 appspec.yml을 모두 암기할 순 없다.... Reference를 보고 찾아 쓰는 능력을 기르자.

How to write appspec.yml ⇒ AWS 의 레퍼런스 => EC2 파트

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ubuntu/tutorial-codepipeline

hooks:
  BeforeInstall:
    # 프로젝트 디렉토리 기준으로의 경로를 적어줍니다.
    # local이나 server 인스턴스의 기준이 아닙니다!
    - location: /scripts/initialize.sh
  AfterInstall:
    - location: /scripts/buildImage.sh
      runas: root
  ApplicationStart:
    - location: /scripts/start.sh

#   ValidateService:
#     - location: Scripts/MonitorService.sh
#       timeout: 3600
#       runas: codedeployuser

initialize.sh 로 실행중이던 원래 컨테이너를 종료시키고 삭제시킬 것이고

rm -rf /home/ubuntu/tutorial-codepipeline
docker stop rabbitmq || true && docker rm rabbitmq || true

buildImage.sh 로 변경사항을 적용한 container를 volume을 이용해 실행시킬 것이고

docker build -f /home/ubuntu/tutorial-codepipeline/Dockerfile . -t tutorial:latest

start.sh 로 docker run 시킬 것이다.

docker run -d --rm --name tutorial -p 3000:3000 -v /home/ubuntu/tutorial-codepipeline:/app tutorial:latest

EC2 Instance 생성 및 IAM Role 생성

EC2 에 CodeDeploy와 Docker를 설치하는 과정은 몇 번을 이 블로그 포스트에 작성하였으므로 필요하신 분은 참고하시길 바랍니다.

간단하게만 정리합니다.

  • express 앱은 0.0.0.0:3000 에 서버를 띄운다. 따라서 외부에서 이 포트에 접속이 가능하도록 EC2 Instance 의 Security group에서 Inbound로 3000 번 포트를 허용해준다.
  • codedeploy-agent 설치, docker 설치 (node는 docker hub의 이미지를 이용하므로 필요가 없다~!)
  • EC2 Instance에서 codedeploy 작업을 수행할 IAM Role 생성
  • CodeDeploy에서 작업을 수행할 IAM Role 생성

CodeDeploy 애플리케이션과 그것의 배포 그룹 생성

CodeDeploy 애플리케이션(express 애플리케이션과 혼동할 것 없다. 쉽게 말해 그냥 CodeDeploy 설정 하 나 하나를 의미)을 생성하기에 앞서 CodeDeploy에서 이용할 IAM Role을 생성해줘야한다.

즉 CodeDeploy가 어떤 작업을 수행할 수 있는지 정의해주는 것이다.

CodeDeploy-CodeDeploy를 선택해준다.

CodeDeploy 애플리케이션을 만들고 (실수로 tutorial-codepipeline이 아니라 tutorial-codedeploy로 만들었는데, 이따가 CodeDeploy 애플리케이션 만들 때에만 제대로 선택하면 된다.)

배포그룹까지 만들어준다. 방금 만든 IAM Role 을 부여해주자.

경험상 굳이 CodePipeline의 Role을 직접 만들 필요는 없었다. 웬만하면 새 서비스 역할을 설정하는 게 골치아플 일 없었고, 어떤 경우에 따라 필요한 IAM Role 을 다르게 설정하는 식의 커스터마이징이 필요없었기 때문이다.

하부의 고급설정은 안 건드려도 된다. S3 에 빌드 후 빌드된 아티팩트를 업로드 한다는 것인데, 이번 글에선 CodeBuild를 안 쓰고 간단하게 배포하는 게 목표이기 때문에 어차피 S3 에 업로드 되지 않는다. (아마도)

소스 공급자로 아까 생성한 나의 github repository를 설정해준다.

여기가 중요. 빌드 스테이지를 건너 뛰는 게 우리의 이번 글의 목표!

 

아까 설정한 CodeDeploy의 배포그룹까지 설정해준다.

이후 CodePipeline 생성을 완료하면 가장 최근 커밋을 기준으로 배포가 진행된다.~!

 

배포가 성공적으로 진행된 모습.

배포가 잘 이루어지나 다시 확인

배포가 잘 되나 다시 한 번 확인하기 위해 NEW VERSION이라는 문구를 추가해본다.

배포가 잘된 모습. 아주 편리하다. 흐뭇.

How to fix errors

한 번에 배포가 성공될 리는 없다... 수 많은 착오가 기다리는 법.. 너무 낙담하기 말고 아래의 꿀팁들을 명심하자.

내 블로그의 방향은 단순히 단계 설명만 하기 보단 최대한 실제로 발생할 수 있는 에러들을 잡아나가는 방법을 적는 것이다!

계속되는 "보류 중"

Deploy 과정은 그리 빠르진 않지만 또 그리 오래걸리지 않는다. 1분이상 시작조차 안 되고 있다면 다음의 몇 가지 사항들을 의심해보자.

  1. EC2 Instance가 꺼져있거나 CodeDeploy 배포그룹에서 EC2 Instance를 잘 못 골랐다.

  2. EC2 Instance의 codedeploy-agent가 제대로 작동하지 않는다.

    sudo service codedeploy-agent status 로 확인 가능

  3. EC2 의 IAM Role이 제대로 설정되어있지 않다

    ⇒ EC2의 CodeDeploy Role을 추가한 뒤 EC2 에 붙여준다.

Script failed 에러

스크립트가 stderr, 즉 에러 스트림을 이용해 출력할 경우 deploy가 중단된다.

redirect stderr to stdout 이라는 키워드를 통해 구글링해보자. 참고

0은 stdin

1은 stdout

2는 stderr

이므로 docker stop tutorial 2>&1 이런 식으로 stderr을 stdout으로 redirect 시킬 수 있다.

에러가 나든 안 나든 한 번 수행하고 싶은 작업을 수행할 때 유용하다.

환경 변수 에러 혹은 그로 인한 no such file or directory

CodeDeploy의 hooks의 commands 를 수행할 때에는 환경변수를 최대한 사용하지 않는 것이 정신건강에 이롭다. /etc/environment 파일에 환경변수를 작성해야지 잘 알아듣고 .profile, .bash_profile 등의 환경변수는 인식하지 않는 듯 하다.

따라서 $HOME/tutorial-codepipeline 이런 식의 경로를 이용하는 script가 있을 경우 제대로 작동하지 않을 수 있고, 에러가 날 수 있다. 따라서 script 상에서의 환경변수의 사용은 자제해야한다.(.env 등을 통한 프로젝트의 환경변수는 문제 없음!)

마치며

CodePipeline을 이용해 AWS 에서 가장 간단한 방법으로 Deploy하는 방법을 알아보았다. 그 동안은 CodeBuild가 꼭 필요한 줄 알고 사실상 빌드 과정이 없는 프로젝트에서도 CodeBuild를 이용했는데, 그럴 필요가 없어졌고, 작업이 훨씬 빨라져서 좋았다.