Web/CI, CD
Travis CI 배포 자동화
태윤2
2021. 8. 23. 03:54
CI/CD 란?
- 코드 버전 관리를 하는 VCS 시스템(Git, SVN 등)에 PUSH가 되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일은 만드는 과정을 CI(Continuous Integration - 지속적 통합)한다
- 이 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행되는 과정을 CD(Continuous Deployment - 지속적 배포)라고 한다
마틴 파울러의 CI에 대한 4가지 규칙
- 모든 소스 코드가 살아 있고(현재 실행되고) 누구든 현재의 소스에 접근할 수 있는 단일 지점을 유지할 것
- 빌드 프로세스를 자동화해서 누구든 소스로부터 시스템을 빌드하는 단일 명령어를 사용할 수 있게 할 것
- 테스팅을 자동화해서 단일 명령어로 언제든지 시스템에 대한 건전한 테스트 수트를 실행할 수 있게 할 것
- 누구나 현재 실행 파일을 얻으면 지금까지 가장 완전한 실행 파일을 얻었다는 확신을 하게 할 것
지속적으로 통합하기 위해서는 프로젝트가 완전한 상태임을 보장하기 위한 테스팅자동화가 중요하다
- TDD 강의
Travis CI 연동하기
- Travis CI는 깃허브에서 제공하는 무료 CI 서비스
- 젠킨스는 설치형이기 때문에 EC2 인스턴스 하나가 더 필요
- 로그인한 뒤 [계정명 -> Settings] 클릭
- 깃허브 저장소 확인
프로젝트 설정(.travis.yml)
- build.gradle과 같은 위치에서 .travis.yml 파일 생성
language: java
jdk:
- openjdk11
branches:
only:
- master
# Permission Denied
before_install:
- chmod +x gradlew
# Travis CI 서버의 Home
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
script: "./gradlew clean build"
# CI 실행 완료 시 메일로 알람
notifications:
email:
recipients:
- 본인 메일 주소
- branches
- Travis CI를 어느 브랜치가 푸시될 때 수행할지 지정
- 현재 옵션은 오직 master 브랜치에 push될 때만 수행
- cache
- 그레이들을 통해 의존성을 받게 되면 이를 해당 디렉토리에 캐시하여, 같은 의존성은 다음 배포 때 부터 다시 받지 않도록 설정
- script
- master 브랜치에 푸시되었을 때 수행하는 명령어
- 프로젝트 내부에 둔 gradlew을 통해 clean & build를 수행
- notifications
- Travis CI 실행 완료 시 자동으로 알람이 가도록 설정
Travis CI와 AWS S3 연동하기
- S3 란 AWS에서 제공하는 일종의 파일 서버
- AWS key 발급
- Travis CI에 키 등록
- AWS_ACCESS_KEY: 엑세스 키 ID
- AWS_SECRET_KEY: 비밀엑세스 키
S3 버킷 생성
- S3(Simple Storage Service)에 관해 설정을 진행
- AWS의 S3서비스는 일종의 파일 서버
- AWS에서 S3를 검색하여 이동하고 버킷을 생성
- 엑세스 모두 차단할것
.travis.yml에 코드 추가
before_deploy:
- zip -r yoon-springboot2-webservice *
- mkdir -p deploy
- mv yoon-springboot2-webservice.zip deploy/yoon-springboot2-webservice.zip
deploy:
- provider: s3
access_key_id: $AWS_ACCESS_KEY # Travis repo settings 에 설정된 값
secret_access_key: $AWS_SECRET_KEY # Travis repo settings 에 설정된 값
bucket: yoon-springboot-build # S3 버킷
region: ap-northeast-2
skip_cleanup: true
acl: private # zip 파일 접근을 private
local_dir: deploy # before_deploy 에서 생성한 디렉토리
wait-until-deployed: true
- before_deploy
- deploy 명령어가 실행되기 전에 수행
- CodeDeploy는 Jar 파일은 인식하지 못하므로 Jar+기타 설정 파일들을 모아 압축(zip)함
- zip -r yoon-springboot2-webservice
- 현재 위치의 모든 파일을 yoon-springboot2-webservice 이름으로 압축(zip)함
- 명령어의 마지막 위치는 본인의 프로젝트 이름이어야 함
- mkdir -p deploy
- deploy라는 디렉토리를 Travis CI가 실행 중인 위치에서 생성
- mv yoon-springboot2-webservice.zip deploy/yoon-springboot2-webservice.zip
- mv yoon-springboot2-webservice.zip 파일을 deploy/yoon-springboot2-webservice.zip으로 이동
- deploy
- S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언
- local_dir: deploy
- 앞에서 생성한 deploy 디렉토리를 지정
- 해당 위치의 파일들만 S3로 전송
- 깃허브로 푸시!
Travis CI와 AWS S3, CodeDeploy 연동하기
- EC2에 IAM 역할 추가하기
- 역할
- AWS 서비스에만 할당할 수 있는 권한
- EC2, CodeDeploy, SQS 등
- 사용자
- AWS 서비스 외에 사용할 수 있는 권한
- 로컬 PC,IDC 서버 등
- 재부팅 완료 후 CodeDeploy의 요청을 받을 수 있게 에이전트 설치
CodeDeploy 에이전트 설치
- EC2에 접속
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
- install 파일에 실행 권한 추가
chmod +x ./install
- ruby: No such file or directory가 뜨면
sudo yum install ruby
- install 파일로 설치
sudo ./install auto
- Agent가 정상 실행 되는지 검사
sudo service codedeploy-agent status
- The AWS CodeDeploy agent is running as PID XXXX 뜨면 정상
CodeDeploy를 위한 권한 생성
- CodeDeploy에서 EC2에 접근하려면 권한이 필요
- IAM 역할을 생성 서비스는 [AWS 서비스 -> CodeDeploy]를 선택
CodeDeploy 생성
CodeDeploy는 AWS의 배포 삼형제다
- Code Commit
- 깃허브와 같은 코드 저장소의 역할
- 프라이빗 기능을 지원한다는게 강점이지만, 현재 깃허브에서 무료로 프라이빗 지원을 하고 있어서 거의 사용 X
- Code Build
- Travis Ci와 마찬가지로 빌드용 서비스
- 멀티 모듈을 배포해야 하는 경우 사용해 볼만, 규모가 있는 서비스에는 대부분 젠킨스/팀시티 등을 이용
- CodeDeploy
- AWS의 배포 서비스
- 앞의 다른 서비스들은 대체재가 있지만 CodeDeploy는 대체재가 없다
- 오토 스케일링 그룹 배포, 블루 그린 배포, 롤링 배포, EC2 단독 배포 등 많은 기능을 지원
CodeDeploy 설정이 끝났다면 TravisCi와 CodeDeploy를 연동 하자
Travis CI, S3, CodeDeploy 연동
- EC2 서버에서 디렉토리 생성
mkdir ~/app/step2 && mkdir ~/app/step2/zip
- AWS CodeDeploy 설정은 appspec.yml로 진행
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app/step2/zip/
overwrite: yes
- version: 0.0
- CodeDeploy 버전을 이야기
- 프로젝트 버전이 아니므로 0.0 외에 다른 버전을 사용하면 오류가 발생
- source
- CodeDeploy에서 전달해 준 파일 중 detination으로 이동시킬 대상을 지정
- 루트 경로(/)를지정하면 전체 파일을 이야기
- destination
- source에서 지정된 파일을 받을 위치
- 이후 Jar를 실행하는 등은 destination에서 옮긴 파일들로 진행
- overwrite
- 기존에 파일들이 있으면 덮어쓸지를 결정
- 현재 yes라고 했으니 파일을 덮어씀
- .travis.yml에도 CodeDeploy 내용을 추가
배포 자동화 구성
- step2 환경에서 실행될 deploy.sh를 생성
#!/bin/bash
REPOSITORY=/home/ec2-user/app/step2
PROJECT_NAME=yoon-springboot2-webservice
echo "> Build 파일 복사"
cp $REPOSITORY/zip/*.jar $REPOSITORY/
echo "> 현재 구동중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -fl $PROJECT_NAME | grep java | awk '{print $1}')
echo "현재 구동중인 어플리케이션 pid: $CURRENT_PID"
if [ -z "$CURRENT_PID" ]; then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 새 어플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)
echo "> JAR Name: $JAR_NAME"
echo "> $JAR_NAME 에 실행권한 추가"
chmod +x $JAR_NAME
echo "> $JAR_NAME 실행"
nohup java -jar \
-Dspring.config.location=classpath:/application.yml,classpath:/application-real.yml,/home/ec2-user/app/application-oauth.yml,/home/ec2-user/app/application-real-db.yml \
-Dspring.profiles.active=real \
$JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
- CURRENT_PID
- 현재 수행 중인 스프링 부트 애플리케이션의 프로세스 ID를 찾음
- 실행 중이면 종료하기 위해서
- chmod +x $JAR_NAME
- Jar 파일은 실행 권한이 없는 상태
- nohup으로 실행할 수 있게 실행 권한을 부여
- $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
- nohup 실행 시 CodeDeploy는 무한 대기함
- 이 이슈를 해결하기 위해 nohup.out 파일을 표준 입출력용으로 별도로 사용
- 이렇게 하지 않으면 nohup.ou 파일이 생기지 않고, CodeDeploy 로그 표준 입출력이 출력
- nohup이 끝나기 전까지 CodeDeploy도 끝나지 않으니 꼭 이렇게 해야함
.travis.yml 파일 수정
- zip 파일로 만드는데 실제로 필요한 파일은 Jar, appspec.yml, 배포를 위한 스크립드 들이다. 이외에는 필요하지 않으니 before_deploy를 수정하자
before_deploy:
- mkdir -p before-deploy # zip 에 포함시킬 파일들을 담을 디렉토리 생성
- cp scripts/*.sh before-deploy
- cp appspec.yml before-deploy
- cp build/libs/*.jar before-deploy
- cd before-deploy && zip -r before-deploy * # before-deploy 로 이동 후 전체 압축
- cd ../ && mkdir -p deploy # 상위 디렉토리로 이동 후 deploy 디렉토리 생성
- mv before-deploy/before-deploy.zip deploy/yoon-springboot2-webservice.zip # deploy 로 zip 파일 이동
- Travis CI는 S3로 특정 파일만 업로드가 안됨
- 디렉토리 단위로만 업로드 할 수 있기 때문에 before-deploy 디렉토리는 항상 생성
- before-deploy에는 zip파일에 포함시킬 파일들을 저장
- zip -r 명령어를 통해 before-deploy 디렉토리 전체 파일을 압축
appsepc.yml 파일 수정
permission:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
ApplicationStart:
- location: deploy.sh
timeou: 60
runas: ec2-user
- permissions
- CodeDeploy에서 EC2 서버로 넘겨준 파일들을 모두 ec2-user 권한을 갖도록 함
- hooks
- CodeDeploy 배포 단계에서 실행할 명령어를 지정
- ApplicationStart라는 단계에서 deploy.sh를 ec2-user 권한으로 실행
- timeout: 60으로 스크립트 실행 60초 이상 수행되면 실패가 됨(무한정 기다릴 수 없으니 시간 제한을 둬야만 함)
Github로 푸시하자
- codedeploy 로그는 /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log 에서 확인가능!
- reference
스프링부트와 AWS로 혼자 구현하는 웹 서비스