September 19, 2020
CI(Continuous Integration - 지속적 통합)
은 코드 버전 관리를 하는 VCS 시스템(Git, SVN 등)에 PUSH가 되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일을 만드는 과정CD(Continuous Deployment - 지속적인 배포)
은 위의 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행하는 과정CI와 CD의 배경
자동화
. 이 덕에 개발에만 집중 가능주의할 점
은 단순히 CI 도구를 도입했다고 해서 CI를 하고 있는 것이 아니다라는 것. CI에 대해 4가지 규칙이 중요테스팅을 자동화
해서 단일 명령어로 언제든지 시스템에 대한 건전한 테스트 수트를 실행할 수 있게 한 것테스팅 자동화
가 가장 주요. 지속통합을 위해서는 이 프로젝트가 완전한 상태임을 보장하기 위해 테스트 코드가 구현되어 있어야만 하기 때문이다.Travis CI 웹 서비스 설정
계정명
-Settings
클릭프로젝트 설정
.travis.yml
파일로 할 수 있다. cf) YAML이란?.travis.yml
생성 language: java
jdk:
- openjdk8
branches:
only:
- master
# Travis CI 서버의 Home
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
before_install:
"chmod +x gradlew"
script:
"./gradlew clean build"
# CI 실행 완료 시 메일로 알람
notifications:
email:
recipients:
- (메일)
branchs
cache
script
notifications
.travis.yml
에 등록한 이메일 확인
S3(Simple Storage Service)란?
파일 서버
이미지 파일을 비롯한 정적 파일들을 관리하거나 지금 진행하는 것처럼 배포 파일들을 관리하는 등의 기능 지원. 보통 이미지 업로드를 구현한다면 이 S3를 이용하여 구현하는 경우가 많다. ex) 게시글에 첨부파일 등록 구현할 때 많이 사용AWS CodeDeploy
라는 서비스 이용. 하지만 S3 연동이 먼저 필요한 이유는 Jar 파일을 전달하기 위해서다. CodeDeploy는 저장 기능이 없다. 그래서 Travis CI가 빌드한 결과물을 받아서 CodeDeploy가 가져갈 수 있도록 보관할 수 있는 공간
이 필요하다. 이럴 때 AWS S3를 이용한다. cf) CodeDeploy가 빌드도 하고 배포도 할 수 있는데 왜 굳이 빌드와 배포를 분리하나접근 가능한 권한 가진 Key
를 생성해서 사용해야 한다. AWS에서는 이러한 인증과 관련된 기능을 제공하는 서비스로 IAM(Identity and Access Management)
가 있다.IAM
검색하여 이동. IAM 왼쪽 사이드바에서 사용자
-> 사용자 추가
버튼을 차례로 클릭생성할 사용자의 이름과 엑세스 유형 선택
freelec-travis-deploy
프로그래밍 방식 엑세스
권한 설정 방식
기존 정책 직접 연결
선택s3full
로 검색하여 체크하고 다음 권한으로 CodeDeployFull
을 검색하여 체크액세스 키
와 비밀 엑세스 키
가 생성된다. 이 두 값이 Travis CI에서 사용될 키.settings
설정 화면 내리면 Environment Variables
항목이 있다.
Add
.travis.yml
에서 $AWS_ACCESS_KEY
, AWS_SECRET_KEY
란 이름으로 사용할 수 있다S3
를 검색 후 이동. 버킷 만들기
클릭버킷명
생성. 이 버킷에 배포할 Zip파일이 모여있는 장소임을 의미하도록 짓는 것 추천. 여기서는 freelec-springboot-build-june
버전관리
는 그냥 넘어감보안과 권한 설정
. 퍼블릭 엑세스를 여는 경우가 있는데 모든 차단(모든 퍼블릭 엑세스 차단
해야 한다. 현재 프로젝트는 이미 깃허브에 오픈소스로 풀렸으니 문제없지만, 실제 서비스에서 할 때는 Jar파일이 퍼블릭이면 누구나 내려받을 수 있어 코드나 설정값, 주요키 값들이 다 탈취될 수 있다.퍼블릭이 아니더라도
우리는 IAM 사용자로 발급받은 키를 사용하니 접근 가능.Travis CI에서 빌드하여 만든 Jar 파일을 S3에 올릴 수 있도록 .travis.yml 에 다음 코드 추가
(...생략...)
before_deploy:
- zip -r freelec-springboot2-webservice *
- mkdir -p deploy
- mv freelec-springboot2-webservice.zip deploy/freelec-springboot2-webservice.zip
deploy:
- provider: s3
access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
access_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
bucket: freelec-springboot-build-june # S3 버킷
region: ap-northeast-2
skip_cleanup: true
acl: private # zip 파일 접근을 private으로
local_dir: deploy # before_deploy에서 생성한 디렉토리
wait-until-deployed: true
before_deploy
zip -r freelec-springboot2-webserivce
mkdir -p deploy
mv freelec-springboot2-webservice.zip deploy/freelec-springboot2-webservice.zip
deploy
local_dir: deploy
설정이 끝났다면 깃허브로 푸시. Travis CI에서 자동으로 빌드가 진행되는 것 확인하고, 모든 빌드가 성공하는지 확인. 다음과 같은 로그가 나온다면 Travis CI의 빌드가 성공한 것 (current
탭에서 마지막 부분에서 확인 가능)
Installing deploy dependenceis
Logging in with Access Key: ****************88
Beginning ...
...(중략)...
Done. Your build exited with 0.
역할
탭을 클릭해서 이동한다. 역할
-> 역할 만들기
버튼을 차례로 클릭참고로 이전의 단계에서 만들어떤 IAM의 사용자
와 여기서 만드려고 하는 역할
은 어떤 차이가 있을까?
AWS 서비스에만
할당할 수 있는 권한AWS 서비스 외에
사용할 수 있는 권한역할
로 처리.AWS 서비스
-> EC2
차례로 선택 -> 다음
EC2RoleForA
를 검색하여 AmazonEC2RoleforAWS-CodeDeploy
를 선택태그는 본인이 원하는 이름으로 짓는다
역할의 이름을 등록하고 나머지 등록 정보를 최종적으로 확인
인스턴스 설정
-> IAM 역할 연결/바꾸기
를 차례로 선택ec2-codedpeloy-role
재부팅
. 재부팅을 해야만 역할이 정상적으로 적용되니 꼭 한 번은 재부팅해야 한다.aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2
download: s3://aws-codedeploy-ap..(중략).. ./install
chmod +x ./install
sudo ./install auto
sudo service codedeploy-agent status
The AWS CodeDeploy agent is running as PID xxx
sudo yum install ruby
IAM
- 역할
- 역할 만들기
- AWS 서비스
-> CodeDeploy
를 차례로 선택Name
/ CodeDeploy-role
CodeDeploy를 위한 역할 이름과 선택 항목들을 확인한 뒤 생성 완료를 한다.
codedeploy-role
Code commit
Code Build
CodeDeploy
깃허브
가, Code Build의 역할은 Travis CI
가 하고 있다. 그래서 추가로 사용할 서비스는 CodeDeploy
이다.애플리케이션 생성
버튼 클릭.freelec-springboot-webserivce
과 컴퓨팅 플랫폼을 선택. 컴퓨팅 플랫폼에선 EC2/온프레미스
를 선택.배포 그룹 생성
버튼 클릭현재 위치
를 선택. 만약 본인이 배포할 서비스가 2대 이상이라면 블루/그린을 선택하면 된다. 여기선 1대의 EC2에만 배포하므로 선택하지 않는다.Amazon EC2 인스턴스
에 체크. 키/값은 각각 Name
, freelec-springboot2-webservice
.마지막으로 배포 구성을 선택 CodeDeployDefaultAllAtOnce
하고 로드 밸런스 체크 해제
CodeDeployDefaultAllAtOnce
는 한번에 다 배포하는 것을 의미한다. 배포 구성이란 한 번 배포할 때 몇 대의 서버에 배포할지를 결정. 2대 이상이라면 1대씩 배포할지, 30% 혹은 50%로 나눠서 배포할지 등등을 선택하는 것. 여기서는 1대 서버다 보니 전체 배포하는 옵션으로 체크mkdir ~/app/step2 && mkdir ~/app/step2/zip
appspec.yml
로 진행한다. (.travis.yml과 같은 계층(위치)에서 생성하면 된다)appspec.yml
파일
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app/step2/zip/
overwrite: yes
version: 0.0
source
destination
overwrite
deploy:
...
- provider: codedeploy
access_key_id: #AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: #AWS_ACCESS_KEY # Travis repo settings에 설정된 값
bucket: freelec-springboot-build-june # S3 버킷
key: freelec-springboot2-webservice.zip # 빌드 파일을 압축해서 전달
bundle_type: zip #압축 확장자
application: freelec-springboot2-webservice # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
deployment_group: freelec-springboot2-webservice-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
region: ap-northeast-2
wait-until-deployed: true
language: java
jdk:
- openjdk8
branches:
only:
- master
# Travis CI 서버의 Home
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
before_install:
"chmod +x gradlew"
script:
"./gradlew clean build"
before_deploy:
- zip -r freelec-springboot2-webservice *
- mkdir -p deploy
- mv freelec-springboot2-webservice.zip deploy/freelec-springboot2-webservice.zip
deploy:
- provider: s3
access_key_id: #AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: #AWS_ACCESS_KEY # Travis repo settings에 설정된 값
bucket: freelec-springboot-build-june # S3 버킷
region: ap-northeast-2
skip_cleanup: true
acl: private # zip 파일 접근을 private으로
local_dir: deploy # before_deploy에서 생성한 디렉토리
wait-until-deployed: true
- provider: codedeploy
access_key_id: #AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: #AWS_ACCESS_KEY # Travis repo settings에 설정된 값
bucket: freelec-springboot-build-june # S3 버킷
key: freelec-springboot2-webservice.zip # 빌드 파일을 압축해서 전달
bundle_type: zip #압축 확장자
application: freelec-springboot2-webservice # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
deployment_group: freelec-springboot2-webservice-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
region: ap-northeast-2
wait-until-deployed: true
# CI 실행 완료 시 메일로 알람
notifications:
email:
recipients:
- (메일주소)
커밋하고 푸시
한다. 깃허브로 푸시가 되면 Travis CI가 자동으로 시작된다. (travis-ci.org에서 확인가능). Travis CI가 끝나면 CodeDeploy 화면 아래에서 배포가 수행되는 것을 확인할 수 있다. (로그로 성공 확인하기)cd /home/ec2-user/app/step2/zip
ll
그러면 프로젝트 파일들이 잘 도착한 것을 확인할 수 있다
#!/bin/bash
REPOSITORY=/home/ec2-user/app/step2
PROJECT_NAME=freelec-springboot2-webservice
echo "> Build 파일 복사"
cp $REPOSITORY/zip/*.jar $REPOSITORY/
echo "> 현재 구동중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -fl freelec-springboot2-webservice | grep jar | 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.properties,classpath:/application-real.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties \
-Dspring.profiles.active=real \
$JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
CURRENT_PID
chomod +x $JAR_NAME
$JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
현재는 프로젝트의 모든 파일을 zip파일로 만드는데, 실제로 필요한 파일들은 Jar, appspec.yml, 배포를 위한 스크립트들이다. 이 외 나머지는 배포에 필요하지 않으니 포함하지 않겠다. 그래서 .travis.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/freelec-springboot2-webservice.zip # deploy로 zip파일 이동
mkdir -p before-deploy
cp scripts/*.sh before-deploy/
cd before-deploy && zip -r before-deploy *
appspec.yml 파일에 다음 코드 추가한다. location, timeout, runas의 들여쓰기를 주의해야 한다. 들여쓰기가 잘못될 경우 배포가 실패한다.
permissions:
- object: /
pattern: "*"
owner: ec2-user
group: ec2-user
hooks:
ApplicationStart:
- location: deploy.sh
timeout: 60
runas: ec-user
permissions
hooks
전체 코드는 다음과 같다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app/step3/zip/
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
ApplicationStart:
- location: start.sh # 엔진엑스와 연결되어 있지 않은 Port로 새 버전의 스프링 부트를 시작.
timeout: 60
runas: ec2-user
모든 설정이 완료되었으니 깃허브로 커밋과 푸시. Travis CI에서 다음과 같이 성공 메시지 확인하고 CodeDeploy에서도 배포가 성공한 것 확인
Preparing deploy
Deploying application
$ mkdir -p before-deploy
$ cp scripts/*.sh before-deploy/
...
(중략)
...
Deploying ...
Done. Your build exited with 0.
http://ec2-3-35-70-236.ap-northeast-2.compute.amazonaws.com:8080/
build.gradle에서 프로젝트 버전 변경
version '1.0-SNAPSHOT'
VERSION '1.0.1-SNAPSHOT'
간단하게나마 변경된 내용을 알 수 있게 src/main/resources/templates/index.mustache 내용에 다음과 같이 Ver.2 텍스트 추가
(중략)
<h1>스프링 부트로 시작하는 웹 서비스 Ver.2</h1>
(중략)
그리고 깃허브로 커밋과 푸시. 그럼 당므과 같이 변경된 코드(Ver.2
)가 배포된 것을 확인할 수 있다.
http://ec2-3-35-70-236.ap-northeast-2.compute.amazonaws.com:8080/
에서 확인CodeDeploy에 관한 대부분 내용은 /opt/codedeploy-agent/deployment-root
에 있다. 해당 디렉토리로 이동(cd /opt/codedeploy-agent/deployment-root)한 뒤 목록을 확인해보면(ll
) 다음과 같은 내용을 확인할 수 있다.
(권한, 날짜 생략) (숫자+영문자)-(숫자-영문자)
(권한, 날짜 생략) deployment-instructions
(권한, 날짜 생략) deployment-logs
(권한, 날짜 생략) ongoing-deployment
(숫자+영문자)-(숫자-영문자)
/opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
서비스 중단 없는 배포
방법을 소개. 무중단 배포
. 다음 장에서!YMAM(야믈)
이란 쉽게 말해 JSON
에서 괄호를 제거한 것이다. YAML 이념이 기계에서 파싱하기 쉽게, 사람이 다루기 쉽게
이다보니 익숙치 않은 독자라도 읽고 쓰기가 쉽다.항상 빌드를 하게 되니(안해도 되는 경우에도)
, 확장성이 많이 떨어진다. 그래서 웬만하면 빌드와 배포는 분리하는 것을 추천참고 : 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 이동욱