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: truebefore_deployzip -r freelec-springboot2-webserivcemkdir -p deploymv freelec-springboot2-webservice.zip deploy/freelec-springboot2-webservice.zipdeploylocal_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-2download: s3://aws-codedeploy-ap..(중략).. ./installchmod +x ./installsudo ./install autosudo service codedeploy-agent statusThe AWS CodeDeploy agent is running as PID xxxsudo yum install rubyIAM - 역할 - 역할 만들기 - AWS 서비스 -> CodeDeploy를 차례로 선택Name / CodeDeploy-roleCodeDeploy를 위한 역할 이름과 선택 항목들을 확인한 뒤 생성 완료를 한다.
codedeploy-roleCode 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/zipappspec.yml로 진행한다. (.travis.yml과 같은 계층(위치)에서 생성하면 된다)appspec.yml 파일
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app/step2/zip/
overwrite: yesversion: 0.0sourcedestinationoverwritedeploy:
...
- 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: truelanguage: 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/zipll그러면 프로젝트 파일들이 잘 도착한 것을 확인할 수 있다
#!/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-deploycp 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-userpermissionshooks전체 코드는 다음과 같다.
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로 혼자 구현하는 웹 서비스 - 이동욱