티스토리 뷰
.travis.yml
Travis CI 의 설정파일로 build.gradle 과 같은 위치에 있어야 한다.
.travis.yml
은 Travis CI가 해야할 일들을 알려준다.
1. language 빌드 환경을 식별하기 위한 설정 JAVA 11을 사용하는 Linux 운영체제에서 실행된다.
language: java
os: linux
jdk:
- openjdk11
2. Travis CI를 master branch에 push 될 때 수행
branches:
only:
- master
3. Travis CI 서버의 Home
gradle을 통해 의존성을 받고 명시한 디렉토리에 보관한 뒤 다음 배포 때 다시 받지 않도록 설정
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
4. 배포 전 실행할 명령어. gradlew에 실행권한 부여
before_install:
- chmod +x ./gradlew
5. master branch에 push시 실행되는 명령어 (빌드)
script: "./gradlew clean build"
6. before-deploy : 배포 전 실행해야하는 명령어
before_deploy:
# zip에 포함시킬 파일들을 담을 디렉토리 생성. before-deploy 폴더 생성.
- mkdir -p before-deploy
# scripts 폴더 밑에있는 스크립트 파일들을 before-deploy 폴더에 복사
- cp scripts/*.sh before-deploy/
# appepc.yml 을 before-deploy에 복사
- cp appspec.yml before-deploy/
# build/libs밑에 있는 jar 파일들을 전부 before-deploy에 복사
- cp build/libs/*.jar before-deploy/
# before-deploy 폴더로 이동 후 before-deploy라는 이름으로 압축
- cd before-deploy && zip -r before-deploy *
# 상위 디렉토리로 이동후 deploy 디렉토리 생성
- cd ../ && mkdir -p deploy
# before-deploy.zip을 deploy 폴더에 portfolio.zip이라는 이름으로 바꿔서 이동
- mv before-deploy/before-deploy.zip deploy/portfolio.zip # deploy로 zip파일 이동
7. 배포 파일을 업로드 또는 codedeploy로 배포 등 외부서비스와 연동될 행위를 선언한다.
deploy:
- provider: s3
# Travis CI에서 빌드한 결과를 s3에 저장해야한다.
# s3에 접근하기 위해서는 access_key와 secret_key가 필요하므로 설정해준다.
access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
bucket: myticket-forbuild # S3 버킷
region: ap-northeast-2
skip_cleanup: true
# zip 파일 접근을 private으로
acl: private
# before_deploy에서 생성한 디렉토리
local_dir: deploy
wait-until-deployed: true
- provider: codedeploy
access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
bucket: myticket-forbuild # Codedeploy가 다운받을 S3 버킷
key: portfolio.zip # 버킷에 있는 파일 이름
bundle_type: zip # 버킷에 저장된 확장자
application: myticket-portfolio-webservice # 웹 콘솔에서 등록한 CodeDeploy 배포 어플리케이션
deployment_group: myticket-portfolio-webservice-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
region: ap-northeast-2
wait-until-deployed: true
8. CI 실행 완료 시 메일로 알람
notifications:
email:
recipients:
- 본인메일@gmail.com
appspec.yml
codedeploy 설정은 appspec.yml
을 이용한다.
1. 프로젝트 버전이 아닌 Codedeploy 버전을 뜻한다.
version: 0.0
os: linux
2. source는 시작 위치 destination은 이동시킬 대상 위치다.
/은 루트 디렉토리로 / 밑에있는 폴더와 파일 전부를 의미하고
/ 하위에 있는 모든 파일을 /home/ubuntu/app/myticket/zip 으로 이동시킨다.
overwrite는 기존에 파일이 있다면 덮어쓰는걸 허용하는 옵션이다.
files:
- source: /
destination: /home/ubuntu/app/myticket/zip/
overwrite: yes
3. codedeploy에서 ec2로 넘겨준 파일 전부 ubuntu 권한을 갖도록 한다.
permissions:
- object: /
pattern: "**"
owner: ubuntu
group: ubuntu
4. 배포 단계에서 실행할 명령어를 지정해준다.
AfterInstall 단계에서는 ubuntu 권한으로 stop 스크립트를 실행해준다. 60초가 넘게되면 실패한다.
ApplicationStart 단계에서는 ubuntu 권한으로 start 스크립트를 실행해준다.
ValidateServicce 단계에서는 ubuntu 권한으로 health 스크립트를 실행하여 배포가
성공적으로 되었는지 확인해준다.
hooks:
AfterInstall:
- location: stop.sh
timeout: 60
runas: ubuntu
ApplicationStart:
- location: start.sh # 엔진엑스와 연결되어 있지 않은 Port로 새 버전의 스프링 부트를 시작합니다.
timeout: 60
runas: ubuntu
ValidateService:
- location: health.sh
timeout: 60
runas: ubuntu
stop.sh
hooks의 생명주기 중 애플리케이션 구성 또는 파일 권한 변경 작업을 진행하는 AfterInstall 단계에서 실행되는 스크립트다.
1. bash 쉘로 스크립트를 실행하겠다고 선언
#!/usr/bin/env bash
2. 현재 실행되고있는 스크립트의 절대경로를 ABSPATH에 저장
스크립트가 저장된 디렉토리의 이름을 ABSDIR에 저장
source는 import문과 비슷하다고 생각하면 된다. profile 스크립트를 import해준다
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
source ${ABSDIR}/profile.sh
3. profile 스크립트의 finde_idle_port 함수를 실행시키면
놀고있는 포트를 리턴받는다.
IDLE_PORT=$(find_idle_port)
4. lsof 명령어를 통해 시스템에서 열려있는 파일의 정보를 확인할 수 있다.
- t 옵션 : 프로세스의 PID 출력 / i 옵션 : 호스트 이름대신 IP 정보로 출력
- TCP:포트 : TCP:포트에서 동작중인 프로세스 출력
echo "> $IDLE_PORT 에서 구동중인 애플리케이션 PID 확인"
IDLE_PID=$(lsof -ti tcp:${IDLE_PORT})
5. -z 는 문자열의 길이가 0인 경우를 의미한다. PID 가 없다면 종료하지 않음.
만약 문자열의 길이가 0이 아니라면 kill 에 15옵션을 줘서 안전하게 프로세스를 종료.
if [ -z ${IDLE_PID}];
then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $IDLE_PID"
kill -15 ${IDLE_PID}
sleep 5
fi
profile.sh
#!/usr/bin/env bash
1. 쉬고있는 profile 찾기
curl : client url 다양한 프로토콜 데이터를 전송해볼 수 있다.
-s : slient 모드. 진행내역이나 메세지를 출력하지 않는다
-o /dev/null : remote에서 받아온 데이터를 /dev/null 파일로 저장한다.
-w httpcode URL : URL 호출한 후 -w을 이용하여 http 응답코드를 반환받는다.
function find_idle_profile()
{
RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/profile)
# 응답 코드가 400 보다 크면 (즉, 40x/50x 에러 모두 포함)
현재 profile 설정을 real2로 한다.
if [ ${RESPONSE_CODE} -ge 400 ]
then
CURRENT_PROFILE=real2
else
# 정상적으로 응답을 받았다면 localhost/profile 요청 후 리턴 값을
현재 profile로 저장한다
CURRENT_PROFILE=$(curl -s http://localhost/profile)
fi
# 현재 profile이 real1이라면 nginx와 연결되지 않은 설정파일은 real2고
if [ ${CURRENT_PROFILE} == real1 ]
then
IDLE_PROFILE=real2
# 현재 profile이 real2라면 nginx와 연결되지 않은 설정파일은 real1이다.
else
IDLE_PROFILE=real1
fi
# 현재 연결되지 않은 설정파일을 리턴하여 무중단 배포 준비를 한다.
echo "${IDLE_PROFILE}"
}
2. 쉬고 있는 profile의 port 찾기
function find_idle_port()
{
IDLE_PROFILE=$(find_idle_profile)
# 연결되지 않은 설정파일이 real1이라면 8082번 포트를 준비시킨다.
if [ ${IDLE_PROFILE} == real1 ]
then
echo "8082"
# 연결되지 않은 설정파일이 real2라면 8083번 포트를 준비시킨다.
else
echo "8083"
fi
}
start.sh
빌드된 jar을 실행시켜주는 스크립트.
#!/usr/bin/env bash
1, 현재 실행되고있는 스크립트의 절대경로를 ABSPATH에 저장
스크립트가 저장된 디렉토리의 이름을 ABSDIR에 저장
source는 import문과 비슷하다고 생각하면 된다. profile 스크립트를 import해준다
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
source ${ABSDIR}/profile.sh
REPOSITORY=/home/ubuntu/app/myticket
PROJECT_NAME=portfolio
2. REPOSITORY/zip 에 있는 jar 파일을 REPOSITORY 하위에 복사
echo "> Build 파일 복사"
echo "> cp $REPOSITORY/zip/*.jar $REPOSITORY/"
cp $REPOSITORY/zip/*.jar $REPOSITORY/
3. REPOSITORY에 있는 jar 형식의 파일목록을 가장 최근수정한 순, 역알파벳 순으로 출력한다.
그리고 마지막 1라인을 출력한다.
echo "> 새 어플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)
echo "> JAR Name: $JAR_NAME"
4. jar파일에 실행권한을 준다
echo "> $JAR_NAME 에 실행권한 추가"
chmod +x $JAR_NAME
echo "> $JAR_NAME 실행"
5. 활성화 상태가 아닌 설정 파일 선택
IDLE_PROFILE=$(find_idle_profile)
6. 선택한 설정파일을 이용하여 웹 애플리케이션 jar을 실행한다.
echo "> $JAR_NAME 를 profile=$IDLE_PROFILE 로 실행합니다."
nohup java -jar \
-Dspring.config.location=classpath:/application.properties,classpath:/application-
$IDLE_PROFILE.properties,/home/ubuntu/app/application-real-db.properties \
-Dspring.profiles.active=$IDLE_PROFILE \
$JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
health.sh
nginx와 연결되지 않은 포트로 스프링 부트가 잘 수행되었는지 체크하는 스크립트.
#!/usr/bin/env bash
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
source ${ABSDIR}/profile.sh
source ${ABSDIR}/switch.sh
IDLE_PORT=$(find_idle_port)
1. NGINX와 연결되지 않은 포트에서 스프링 부트 JAR이 잘 실행중인지 확인
echo "> Health Check Start!"
echo "> IDLE_PORT: $IDLE_PORT"
echo "> curl -s http://localhost:$IDLE_PORT/profile "
sleep 10
2. nginx와 연결되지 않은 포트의 /profile 요청을 한다.
response가 오면 해당 response에 real이 포함되어있고 해당 파일의 라인수 wc -l를
통해 UP_COUNT 변수에 저장한다.
for RETRY_COUNT in {1..10}
do
RESPONSE=$(curl -s http://localhost:${IDLE_PORT}/profile)
UP_COUNT=$(echo ${RESPONSE} | grep 'real' | wc -l)
3. UP_COUNT 가 1 이상이면 health check에 성공하고
포트를 바꿔주기 위해 switch_proxy 함수를 실행시켜준다.
if [ ${UP_COUNT} -ge 1 ]
then # $up_count >= 1 ("real" 문자열이 있는지 검증)
echo "> Health check 성공"
switch_proxy
break
else
echo "> Health check의 응답을 알 수 없거나 혹은 실행 상태가 아닙니다."
echo "> Health check: ${RESPONSE}"
fi
4. 만약 crul 요청을 10번을 했으나 응답이 안온다면
배포를 실패한걸로 간주한다.
if [ ${RETRY_COUNT} -eq 10 ]
then
echo "> Health check 실패. "
echo "> 엔진엑스에 연결하지 않고 배포를 종료합니다."
exit 1
fi
echo "> Health check 연결 실패. 재시도..."
sleep 10
done
switch.sh
nginx 포트를 변경하는 스크립트.
#!/usr/bin/env bash
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
source ${ABSDIR}/profile.sh
1. 포트 전환 함수
function switch_proxy() {
IDLE_PORT=$(find_idle_port)
echo "> 전환할 Port: $IDLE_PORT"
echo "> Port 전환"
# 현재 서비스 중인 url을 변경된 포트로 바꿔준다.
파이프라인 앞에서 넘겨준 문장을 service-url.inc에 덮어쓴다.
echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" | sudo tee /etc/nginx
/conf.d/service-url.inc
echo "> 엔진엑스 Reload"
sudo service nginx reload
}
참고
'TIL' 카테고리의 다른 글
[OS] 프로세스 교환을 유발하는 인터럽트와 트랩 (0) | 2022.02.04 |
---|---|
[OS] 프로세스 상태 (0) | 2022.02.03 |
[OS] 운영체제와 프로세스 (0) | 2022.02.02 |
[CI/CD] Travis CI 에서 S3 그리고 Codedeploy까지의 무중단 배포 동작 과정 (0) | 2021.12.25 |
[CI/CD] 배포 자동화 (0) | 2021.12.23 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 람다식
- ORA-27125
- ddl-auto
- 기술면접
- SpringGraphQL
- CodeDeploy
- graphql
- 트랜잭션
- Til
- db
- nginx
- N+1
- Oracle
- 프로그래머스
- Travis CI
- 네이버클라우드
- 운영체제
- 인덱스
- SpringSecurity
- spring
- TCP
- ci/cd
- 트랜잭션격리성
- EC2
- AWS
- OS
- JPA
- Java
- 파일업로드설정
- level0
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 |
글 보관함