본문 바로가기
Web/SpringBoot

9. Travis CI 배포 자동화

by SeleniumBindingProtein 2022. 3. 14.
728x90
반응형

24시간 무중단 배포 

 

  1. CI & CD
    1. CI(Continuous Integration) : 코드 버전 관리하는 VCS 시스템(Git, SVN 등)에 PUSH가 되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일을 만드는 과정
    2. CD(Continuous Deployment) : 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행되는 과정
    3. CI 규칙
      1. 모든 소스 코드가 살아있고, 누구든 현재의 소스에 접근할 수 있는 단일 지점을 유지할 것
      2. 빌드 프로세스를 자동화해서 누구든 소스로부터 시스템을 빌드하는 단일 명령어를 사용할 수 있게 할 것
      3. 테스팅을 자동화해서 단일 명령어로 언제든지 시스템에 대한 건전한 테스트 수트를 실행할 수 있게 할 것
      4. 누구나 현재 실행 파일을 얻으면 지금까지 가장 완전한 실행 파일을 얻었다는 확신을 하게 할 것
  2. Travis CI 연동
    1. Travis CI : 깃허브에서 제공하는 무료 CI 서비스
    2. Travis CI 웹 서비스 설정
      1. https://travis-ci.org/에서 깃허브 계정으로 로그인한 뒤, 계정명 클릭 후에 Setting 클릭
      2. 설정 페이지에 깃허브 저장소 검색창에 이름을 입력해서 찾은 다음, 활성화 시킴
      3. 활성화한 저장소를 클릭 후, 저장소 빌드 히스토리 페이지로 이동
    3. 프로젝트 설정
      1. Travis CI의 상세한 설정은 프로젝트에 존재하는 .travis.yml 파일로 할 수 있으며, yml 파일 확장자를 YAML(야믈)이라 함
      2. YAML은 JSON에서 괄호를 제거한 것
      3. Travis CI 설정
        1. 프로젝트의 build.gradle과 같은 위치에서 .travis.yml 생성 후 코드 추가
          1. branches : Travis CI를 어느 브랜치가 푸시될 때, 수행할 지 지정함. 현재 옵션은 오직 master 브랜치에 push될 때만 수행함
          2. cache : 그레이들을 통해 의존성을 받게 되면, 이를 해당 디렉토리에 캐시하여, 같은 의존성은 다음 배포 때부터 다시 받지 않도록 설정함
          3. script : master 브랜치에 푸시되었을 때, 수행하는 명령어이며, 프로젝트 내부에 둔 gradlew을 통해 clean & build를 수행함
          4. notifications : Travis CI 실행 완료 시 자동으로 알람이 가도록 설정함
        2. 빌드가 성공한 것이 확인되면, .travis.yml에 등록한 이메일을 확인하면 됨
language: java
jdk : 
	- openjdk8
    
branches :
	only :
    	- master
        
# Travis CI 서버의 Home
cache :
	directories : 
    	- '$HOME/.m2/repository'
        - '$HOME/.gradle'
script : "./gradlew clean build"

# CI 실행 완료 시 메일로 알람
notifications :
	email :
    	recipients :
        	- 본인 메일 주소

 

3. Travis CI와 AWS S3 연동

  • S3 : AWS에서 제공하는 일종의 파일 서버
  • 이미지 파일을 비롯한 정적 파일들을 관리하거나 지금 진행하는 것처럼 배포 파일들을 관리하는 등의 기능 지원
  • S3를 비롯한 AWS 서비스와 Travis CI를 연동하게 되면 전체 구조

  • 첫 번째 단계, Travis CI와 S3 연동
    • 실제 배포는 AWS CodeDeploy라는 서비스 이용하지만, S3 연동이 먼저 필요한 이유는 Jar 파일을 전달하기 위해서임
    • CodeDeloy는 저장 기능이 없고, Travis CI가 빌드한 결과물을 받아서 CodeDeploy가 가져갈 수 있도록 보관할 수 있는 공간이 필요하며, 보통 이럴 때는 AWS S3를 이용함
    • Travis CI와 AWS S3 연동
  1. AWS key 발급
    1. 일반적으로 AWS 서비스에 외부 서비스가 접근할 수 없으므로, 접근 가능한 권한을 가진 Key를 생성해서 사용해야 함
    2. AWS에서는 이러한 인증과 관련된 기능을 제공하는 서비스로 IAM(Identity and Access Management) 있음
    3. IAM은 AWS에서 제공하는 서비스의 접근 방식과 권한을 관리함
      1. IAM을 통해 Travis CI가 AWS의 S3와 CodeDeploy에 접근
      2. AWS 웹 콘솔에서 IAM을 검색하여 이동함
      3. IAM 페이지 사이드 바에서 사용자 추가버튼을 클릭
      4. 생성할 사용자의 이름과 엑세스 유형 선택하고, 엑세스 유형은 프로그래밍 방식 엑세스임
      5. 권한 설정 방식은 3개 중 기존 정책 직접 연결 선택
      6. 화면 아래 정책 검색 화면에서 s3full로 검색하여 체크하고, 다음 권한으로 CodeDeployFull을 검색하여 체크함
      7. 실제 서비스 회사에서는 권한도 S3와 CodeDeploy를 분리해서 관리하기도 하며, 간단하게 둘을 합쳐서 관리함. 2개의 권한이 설정되었으면 다음으로 넘어감
      8. 태그 Name 값을 지정하는데, 본인이 인지 가능한 정도의 이름으로 만들고, 본인이 생성한 권한 설정 항목을 확인함
      9. 최종 생성 완료되면, 엑세스 키와 비밀 엑세스 키가 생성되고, 이 두 값이 Travis CI에서 사용될 키이고, 이 키를 Travis CI에 등록함
  2. Travis CI에 키 등록
    1. 먼저 TravisCI의 설정화면으로 이동함
    2. 설정화면 아래로 내려보면, Environment Variables 항목이 있음
    3. AWS_ACCESS_KEY, AWS_SECRET_KEY를 변수로 해서 IAM 사용자에서 발급받은 키 값들을 등록함
      1. AWS_ACCESS_KEY : 엑세스 키 ID
      2. AWS_SECRET_KEY : 비밀 엑세스 키
    4. 등록된 값들을 .travis.yml에서 $AWS_ACCESS_KEY, $AWS_SECRET_KEY란 이름으로 사용할 수 있고, 키를 사용해서 Jar를 관리할 S3버킷을 생성함
  3. S3 버킷 생성
    1. S3에 관해 설정하고, AWS의 S3 서비스는 일종의 파일 서버임(순수하게 파일들을 저장하고 접근 권한을 관리, 검색 등을 지원하는 파일 서버의 역할을 함)
    2. S3 : 보통 게시글을 쓸 때, 나오는 첨부파일 등록을 구현할 때 파일 서버의 역할 때문에 많이 이용하며, Travis CI에서 생성된 Build 파일을 저장
    3. S3에 저장된 Build 파일은 이후 AWS의 CodeDeploy에서 배ㅗ할 파일로 가져가도록 구성할 예정이고, AWS 서비스에서 S3를 검색하여 이동하고 버킷을 생성함
    4. 원하는 버킷명을 작성하고, 이 버킷에 배포할 Zip 파일이 모여있는 장소임을 의미하도록 짓는 것을 추천
    5. 다음으로, 버젼관리를 설정하고, 별다른 설정 없이 바로 넘어감
    6. 버킷의 보안과 권한 설정 부분이고, 퍼블릭 액세스를 열어두지 말고, 모든 차단을 해야 함(실제 서비스에서 할 때는 Jar 파일이 퍼블릭일 경우 누구나 내려받을 수 있어 코드나 설정값, 주요 키값들이 다 탈취될 위험이 있음)
    7. 버킷이 생성되면 다음과 같이 버킷 목록에서 확인할 수 있음
    8. S3가 생성되었으면, 이제 S3로 배포 파일을 전달
  4. .travis.yml 추가
    • 이전에 만든 .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 setting에 설정된 값
      secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
      bucket: freelec-springboot-build # S3 버킷
      region: ap-northeast-2
      skip_cleanup: true
      acl: private # zip 파일 접근을 private으로 
      local dir: deploy # before_deploy에서 생성한 디렉토리
      wait-until-deployed: true
  • Travis CI Settings 항목에서 등록한 $AWS_ACCESS_KEY와 $AWS_SECRET_KEY가 변수로 사용됨
    • before_deploy: deploy 명령어가 실행되기 전에 수행되며, CodeDeploy는 Jar 파일은 인식하지 못하므로 Jar+기타 설정 파일들을 모아 압축함
    • zip -r freelec-springboot2-webservice : 현재 위치의 모든 파일을 freelec-springboot2-webservice 이름으로 압축하고, 명령어의 마지막 위치는 본인의 프로젝트 이름이어야 함
    • mkdir -p deploy : deploy라는 디렉토리를 Travis CI가 실행 중인 위치에서 생성함
    • mv freelec-springboot2-webservice.zip deploy/freelec-springboot2-webservice.zip : deploy로 이동시킴
    • deploy : S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언함
    • local_dir: deploy : 앞에서 생성한 deploy 디렉토리를 지정하고, 해당 위치의 파일들만 S3로 전송함
  • 설정이 다 되면 깃허브로 푸시하고, Travis CI에서 자동으로 빌드가 진행되는 것을 확인하고, 모든 빌드가 성공하는 지 확인함
  • 로그가 나오면 Travis CI의 빌드가 성공한 것이며, S3 버킷을 가보면 업로드가 성공한 것을 확인할 수 있음

4. Travis CI와 AWS S3, CodeDeploy 연동

    1. AWS 배포 시스템인 CodeDeploy를 이용하기 전에 배포 대상인 EC2가 CodeDeploy를 연동 받을 수 있게 IAM 역할 하나 생성
    2. EC2에 IAM 역할 추가
      1. S3와 마찬가지로 IAM을 검색하고, 이번에는 역할 탭을 클릭해서 이동하여 역할 만들기 버튼을 차례로 클릭함
      2. IAM 사용자와 역할 차이
        1. 역할 : AWS 서비스에만 할당할 수 있는 권한. EC2, CodeDeploy, SQS 등
        2. 사용자 : AWS 서비스 외에 사용할 수 있는 권한. 로컬 PC, IDC 서버 등
      3. 지금 만들 권한은 EC2에서 사용할 것이기 때문에 사용자가 아닌 역할로 처리하고, 서비스 선택에서는 AWS서비스 클릭 후 EC2 클릭
      4. 정책에선 EC2RoleForA를 검색하여 AmazonEC2RoleforAWSCodeDeploy 선택
      5. 태그는 본인이 원하는 이름으로 작명
      6. 마지막으로 역할의 이름을 등록하고, 나머지 등록 정보를 최종적으로 확인함
      7. 이렇게 만든 역할을 EC2 서비스에 등록하고, EC2 인스턴스 목록으로 이동한 뒤, 본인의 인스턴스를 마우스 오른쪽 버튼으로 눌러 인스턴스 설정 -> IAM 역할 연결/바꾸기 를 차례로 선택
      8. 방금 생성한 역할을 선택하고, 완료되면 해당 EC2 인스턴스를 재부팅함(재부팅해야만 역할이 정상적으로 적용됨)
      9. 재부팅이 완료되었으면 CodeDeploy의 요청을 받을 수 있게 에이전트를 하나 설치함
    3. CodeDeploy 에이전트 설치
      1. EC2에 접속해서 다음 명령어 입력
        1. aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install .--region ap-northeast-2
      2. 내려 받기가 성공했다면, 메세지 콘솔 출력됨
        1. downlaod: s3://aws-codedeploy-ap-northeast-2/latest/install to ./install
      3. intall 파일에 실행 권한이 없어서 실행 권한을 추가함
        1. chmod +x ./install
      4. install 파일로 설치를 진행함
        1. sudo ./install auto
      5. 설치가 끝났으면 Agent가 정상적으로 실행되고 있는지 상태 검사
        1. sudo service codedeploy-agent status
        2. runnig 메시지 출력 : The AWS CoedDeploy agent is running as PID xxx
    4. CodeDeploy를 위한 권한 생성
      1. CodeDeploy에서 EC2에 접근하려면 마찬가지로 권한이 필요하며, AWS의 서비스이니 IAM 역할 생성하고, 서비스는 AWS 서비스 -> CodeDeploy를 차례로 선택함
      2. CodeDeploy는 권한이 하나뿐이라서 선택 없이 바로 다음으로 넘아가면 됨
      3. 태그 역시 본인이 원하는 이름으로 작명
      4. CodeDeploy를 위한 역할 이름과 선택 항목들을 확인한 뒤, 생성완료함
    5. CodeDeploy 생성
      1. CodeDeploy는 AWS의 배포 
        1. Code Commit
          1. 깃허브와 같은 코드 저장소의 역할을 함
          2. 프라이빗 기능을 지원한다는 강점이 있지만, 현재 깃허브에서 무료로 프라이빗 지원을 하고 있어서 거의 사용되지 않음
        2. Code Build
          1. Travis CI와 마찬가지로 빌드용 서비스
          2. 멀티 모듈을 배포해야 하는 경우, 사용해볼만하지만 규모가 있는 서비스에서는 대부분 젠킨스/팀시티 등을 이용하니 이것 역시 사용할 일이 없음
        3. CodeDeploy
          1. AWS의 배포 서비스
          2. 다른 서비스들은 대체제가 있고, 딱히 대체제보다 나은 점이 없지만, CodeDeploy는 대체제가 없음
          3. 오토 스케일링 그룹 배포, 블루 그린 배포, 롤링 배포, EC2 단독 배포 등 많은 기능을 지원함
        4. 현재 프로젝트는 Code Commit의 역할을 깃허브가, Code Build의 역할을 Travis CI가 하고 있기 때문에 추가로 사용할 서비스는 CodeDeploy임
      2. CodeDeploy 서비스로 이동해서 화면 중앙에 있는 애플리케이션 생성 버튼을 클릭함
      3. 생성할 CodeDeploy의 이름과 컴퓨팅 플랫폼을 선택하고, 컴퓨터 플랫폼에선 EC2/온프레미스를 선택
      4. 생성이 완료되면 배포 그룹을 생성하라는 메시지를 볼 수 있으며, 화면 중앙의 배포 그룹 생성 버튼을 클릭함
      5. 배포 그룹 이름과 서비스 역할을 등록하고, 서비스 역할은 좀 전에 생성한 CodeDeploy용 IAM 역할을 선택함
      6. 배포 유형에서는 현재 위치를 선택(만약 본인이 배포할 서비스가 2대 이상이라면 블루/그린을 선택하면 됨)
      7. 환경 구성에서는 Amazon EC2 인스턴스에 체크함 
      8. 마지막으로 배포 구성을 선택하고 로드밸런싱은 체크 해제함
        1. 배포 구성이란 한번 배포할 때, 몇 대의 서버에 배포할지를 결정함
    6. Travis CI, S3, CodeDeploy 연동
      • S3에서 넘겨줄 zip 파일을 저장할 디렉토리를 하나 생성하고, EC2 서버에 접속해서 다음과 같이 디렉토리 생성함
        1. mkdir ~/app/step2 && mkdir ~/app/step2/zip
      • Travis CI의 Build가 끝나면 S3에 zip 파일이 전송되고, zip 파일은 /home/ec2-user/app/step2/zip로 복사되어 압축을 풀 예정이고, Travis CI의 설정은 .travis.yml로 진행하고, AWS CodeDeploy의 설정은 appspec.yml로 진행함
        • version: 0.0 
          • CodeDeploy 버젼을 말하며, 프로젝트 버전이 아니므로 0.0 외에 다른 버전을 사용하면 오류가 발생함
        • source 
          • CodeDeploy에서 전달해준 파일 중 destination으로 이동시킬 대상을 지정함
          • 루트 경로(/)를 지정하면 전체 파일을 말함
        • destination
          • source에서 지정된 파일을 받을 위치이며, 이후 Jar를 실행하는 등은 destination에서 옮긴 파일들로 진행됨
        • overwrite
          • 기존에 파일들이 있으면 덮어쓸지를 결정하고, 현재 yes라고 했으니 파일들을 덮어쓰게 됨 
            version: 0.0
            os: linux
            files: 
            	- source: /
                  destination: /home/ec2-user/app/step2/zip/
                  overwrite: yes
  • .travis.yml에도 CodeDeploy 내용 추가하고, deploy 항목에 다음 코드 추가
deploy:
	...
    
   - provider: codedeploy
     access_key_id: $AWS_ACCESS_KEY # Travis repo setting에 설정된 값
     secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
     bucket: freelec-springboot-build # 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
  • 모든 내용을 작성했다면, 프로젝트를 커밋하고 푸시하고, 깃허브로 푸시가 되면 Travis CI가 자동으로 시작됨
  • Travis CI가 끝나면 CodeDeploy 화면 아래에서 배포가 수행되는 것을 확인할 수 있음
  • 배포가 끝났다면, 다음 명령어로 파일들이 잘 도착했는지 확인할 수 있음
    • cd /home/ec2-user/app/step2/zip
    • ll

5. 배포 자동화 구성

  • Travis CI, S3, CodeDeploy 연동까지 구현된 것을 기반으로 실제로 Jar를 배포하여 실행
  • deploy.sh 파일 추가
    • step2 환경에서 실행될 deploy.sh를 생성하고, scripts 디렉토리를 생성해서 여기에 스크립트를 생성함 
    • 우선 git pull을 통해 직접 빌드했던 부분을 제거하고 Jar를 실행하는 단계에서 몇가지 코드가 추가됨

 

 

#!/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 : 현재 수행 중인 스프링부트 애플리케이션의 프로세스 ID를 찾고, 실행 중이면 종료하기 위해서임. 스프링부트 애플리케이션 이름으로 된 다른 프로그램이 있을 수 있어 freelec-springboot2-webservice로 된 jar 프로세스를 찾은 뒤 ID를 찾음
    • chmod +x $JAR_NAME : Jar 파일은 실행 권한이 없는 상태이고, nohup으로 실행할 수 있게 실행 권한을 부여함
    • $JAR_NAME > $REPOSITORY/nohup.out 2>&1 & : nohup 실행 시 CodeDeploy는 무한대기하며, 이 이슈를 해결하기 위해서는 nohup.out 파일을 표준 입출력용으로 별도 사용함. 이렇게 하지 않으면, nohup.out 파일이 생기지 않고, CodeDeploy 로그에 표준 입출력이 출력됨. nohup이 끝나기 전까지 CodeDeploy도 끝나지 않으니 꼭 이렇게 해야 함
  • .travis.yml 파일 수정
before_deploy:
  - mkdir -p before-deploy # zip에 포함시킬 파일들을 담을 디렉토리 생성
  - cp out/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파일 이동
  • appspec.yml 파일 수정
    • permissions : CodeDeploy에서 EC2 서버로 넘겨준 파일들을 모두 ec2-user 권한을 갖도록 함
    • hooks : CodeDeploy 배포 단계에서 실행할 명령어를 지정하고, ApplicationStart라는 단계에서 deploy.sh를 ec2-user 권한으로 실행하게 하고, timeout:60으로 스크립트 실행 60초 이상 수행되면 실패가 됨
version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ec2-user/app/step2/zip/
    overwrite: yes
permissions:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

hooks:
  ApplicationStart:
    - location: deploy.sh # 엔진엑스와 연결되어 있지 않은 Port로 새 버전의 스프링 부트를 시작합니다.
      timeout: 60
      runas: ec2-user
  • 모든 설정이 완료되었으니 깃허브로 커밋과 푸시를 하고, Travis CI에서 성공 메시지 확인 및 CodeDeploy에서 배포 성공을 확인함
728x90
반응형

댓글