코딩하는 문과생

[Spring boot] CI/CD - CI(지속적통합)(step2) 본문

웹 프로그래밍/Spring Boot

[Spring boot] CI/CD - CI(지속적통합)(step2)

코딩하는 문과생 2020. 12. 12. 00:41

※ 해당 글은 튜토리얼이 아닌 헷갈리거나 중요한 개념 위주로 정리한 글입니다. 

 

[CI - 지속적 통합]

  • CI: 지속적 통합, 개발한 코드가 버전관리 시스템(SVN, GIT)에 push가 되면 자동으로 테스트, 빌드가 이루어져 안정적인 배포파일을 만드는 과정
  • CD: 지속적 배포, 빌드파일을 자동으로 운영 서버에 무중단 배포까지 진행하는 과정

 

 

[CI의 4대규칙]

  • 누구든 현재 소스에 접근할 수 있는 단일지점을 유지할 것
  • 누구든 시스템을 빌드하는 단일 명령어 사용할 수 있게 할 것
  • 단일 명령어로 건전한 테스트 실행할 수 있게 할 것
  • 누구나 현재 실행파일이 완전한 실행파일이게 확신하게 할 것

 

[Travis CI]

: CI툴, 깃허브와 연동되어 사용된다.

- 프로젝트 전체 흐름

* 빌드(2, 3, 4)는 Travis CI가, 배포(5)는 AWS CodeDeploy가 한다.

 

 

- 흐름

1. 프로젝트 설정

:Travis CI웹사이트 설정 + .travis.yml 파일(json에서 괄호를 제거한 것) 작성

language: java
jdk:
  - openjdk8

branches:
  only:
    - master

# Travis CI 서버의 홈
cache: # 같은 의존성은 다음 배포 때부터 안 받는다.
  directories:
    - '$HOME/.m2/repository'
    - '$HOME/.gradle'

script: "./gradlew clean build" # 브랜치 푸시시 수행하는 명령어

# CI 실행 완료 시 메일로 알림
notifications:
  email:
    recipients:
      - sijune0525@gmail.com

* git push후 travis에 찍히는 빌드성공 로그를 확인한다.

 

 

2. Travis CI와 AWS S3연동

: AWS에 외부 서비스(Travis CI)가 접근하기 위해서는 AWS IAM이 필요

* IAM(Identity and Access Management): AWS 서비스의 접근 방식과 권한을 관리

* S3역할: aws의 파일 서버, Jar파일(빌드파일) 저장

 

  1. S3와 CodeDeploy에 접근할 수 있는 IAM를 생성한다.
  2. 발급받은 액세스키와 비밀키를 travis CI에 등록한다.(AWS_ACCESS_KEY, AWS_SECRET_KEY)
  3. 저장된 변수를 .travis.yml에서 사용이 가능하다.
  4. S3버킷 생성(모든 퍼블릭 액세스 차단, 외부에서 IAM을 사용해 접근 가능)
  5. .travis.yml 수정(S3에 빌드파일 올리는 코드 추가)
  6. git commit, push를 진행하면 빌드파일이 자동으로 생성되어 S3에 저장된다.
language: java
jdk:
  - openjdk8

branches:
  only:
    - master

# Travis CI 서버의 홈
cache: # 같은 의존성은 다음 배포 때부터 안 받는다.
  directories:
    - '$HOME/.m2/repository'
    - '$HOME/.gradle'

script: "./gradlew clean build" # 브랜치 푸시시 수행하는 명령어

before_deploy:
  - zip -r blogProject * # 현재 경로 모든 파일 압축
  - mkdir -p deploy # 현재 travis CI가 실행중인 위치에서 생성
  - mv blogProject.zip deploy/blogProject.zip

deploy:
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: blogproject-build
    region: ap-northeast-2
    skip_cleanup: true
    acl: private # zip파일 접근을 private하게
    local_dir: deploy # 해당위치만 S3로 전달
    wait-until-deployed: true

# CI 실행 완료 시 메일로 알림
notifications:
  email:
    recipients:
      - sijune0525@gmail.com

* travis-ci.org 는 2020년을 마지막으로 서비스를 중단하고, travis-ci.com 에서 똑같이 CI서비스 이용이 가능하다

* git push후 s3버킷 확인 완료

 

 

3. Travis와 aws s3, aws codedeploy 연동

  1. EC2가 codedeploy와 연동될 수 있게 EC2를 위한 IAM를 하나 더 생성한다.(AmazonEC2RoleforAWSCodeDeploy)
  2. 등록된 역할을 EC2에 등록한 후 재부팅을 한다.
  3. EC2내 Codedeploy Agent 설치 및 실행확인
  4. codedeploy에 사용할 IAM를 생성한다.(AWSCodeDeployRole)
  5. codedeploy생성, 이 때, 생성된 IAM를 부여하고, 배포그룹(EC2)을 연결시킨다.
  6. codedeploy에서 넘겨받은 파일을 저장할 폴더 생성(app/step2/zip)
  7. CodeDeploy의 설정 진행(appspec.yml): 저장할 폴더(app/step2/zip)를 지정
  8. .travis.yml에 codedeploy에 대한 설정 추가
  9. git push후 app/step2/zip폴더 내 파일 확인

* IAM: 외부서비스의 접근을 위한 "사용자"와 AWS 내부서비스 접근을 위한 "역할"로 분리된다.

* travis의 접근은 IAM사용자로 생성, codedeploy와 ec2간의 접근은 IAM역할로 생성한다.

 

-codedeploy 클라이언트 설치

$ aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2
$ chmod +x ./install
$ sudo ./install auto
$ sudo service codedeploy-agent statussudo service codedeploy-agent status
//서비스가 잘돌아가는 지 체크

- appspec.yml

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/app/step2/zip/
    overwrite: yes

* source: codedeploy에서 전달해준 파일 중 destination으로 전달할 대상 지정, destination: 압축을 풀 위치

 

-.travis.yml

language: java
jdk:
  - openjdk8

branches:
  only:
    - master

# Travis CI 서버의 홈
cache: # 같은 의존성은 다음 배포 때부터 안 받는다.
  directories:
    - '$HOME/.m2/repository'
    - '$HOME/.gradle'

script: "./gradlew clean build" # 브랜치 푸시시 수행하는 명령어

before_deploy:
  - zip -r blogProject * # 현재 경로 모든 파일 압축
  - mkdir -p deploy # 현재 travis CI가 실행중인 위치에서 생성
  - mv blogProject.zip deploy/blogProject.zip

deploy:
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: blogproject-build
    region: ap-northeast-2
    skip_cleanup: true
    acl: private
    local_dir: deploy
    wait-until-deployed: true

  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY

    bucket: blogproject-build
    key: blogProject.zip

    bundle_type: zip
    application: blogProject # codedeploy에 등록한 애플리케이션

    deployment_group: blogProject-group # codedeploy에 등록한 배포그룹

    region: ap-northeast-2
    wati-until-deployed: true

# CI 실행 완료 시 메일로 알림
notifications:
  email:
    recipients:
      - sijune0525@gmail.com

 

 

4. 배포자동화

: git push 시 자동으로 배포까지 진행

  1. step2 환경에서 실행될 deploy.sh 생성(프로젝트 내 scripts/deploy.sh), 그리고 git push
  2. .travis.yml 수정: jar파일, scripts파일만 압축되도록 수정
  3. appspec.yml 추가: 배포 주체 및 배포파일(ec2-user, deploy.sh) 추가

-deploy.sh

#!/bin/bash

REPOSITORY=/home/ec2-user/app/step2
PROJECT_NAME=blogProject

echo "> 빌드 파일 복사"

cp $REPOSITORY/zip/*.jar $REPOSITORY

echo "> 현재 구동중인 애플리케이션 pid확인"

CURRENT_PID=$(pgrep -fl blogProject | 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 &

-.travis.yml

language: java
jdk:
  - openjdk8

branches:
  only:
    - master

# Travis CI 서버의 홈
cache: # 같은 의존성은 다음 배포 때부터 안 받는다.
  directories:
    - '$HOME/.m2/repository'
    - '$HOME/.gradle'

script: "./gradlew clean build" # 브랜치 푸시시 수행하는 명령어

before_deploy:
#  - zip -r blogProject * # 현재 경로 모든 파일 압축
#  - mkdir -p deploy # 현재 travis CI가 실행중인 위치에서 생성
#  - mv blogProject.zip deploy/blogProject.zip
  - mkdir -p before-deploy
  - cp scripts/*.sh before-deploy/
  - cp appspec.yml before-deploy/
  - cp build/libs/*.jar before-deploy/
  - cd before-deploy && zip -r before-deploy *
  - cd ../ && mkdir -p deploy
  - mv before-deploy/before-deploy.zip deploy/blogProject.zip

deploy:
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: blogproject-build
    region: ap-northeast-2
    skip_cleanup: true
    acl: private
    local_dir: deploy
    wait-until-deployed: true

  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY

    bucket: blogproject-build
    key: blogProject.zip

    bundle_type: zip
    application: blogProject

    deployment_group: blogProject-group

    region: ap-northeast-2
    wati-until-deployed: true

# CI 실행 완료 시 메일로 알림
notifications:
  email:
    recipients:
      - sijune0525@gmail.com

-appspec.yml

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/app/step2/zip/
    overwrite: yes

permissions: # codedeploy에서 ec2로 넘어온 모든파일에 권한 부여
  - object:
      pattern: "**"
      owner: ec2-user
      group: ec2-user

hooks:
  ApplicationStart: # ec2-user권한으로 deploy.sh실행
    - location: deploy.sh
      timeout: 60
      runas: ec2-user

 

그러나, 지속적통합 후 배포시 서비스가 중단되는 상황 발생 -> 무중단배포 필요

 

 

-codedeploy 참고사항

americanopeople.tistory.com/283

 

AWS CodeDeploy와 AWS CodeDeploy Agent (1) - 1. CodeDeploy

요즘 AWS CodeDeploy Agent 오픈소스 커미터되기 스터디를 하고 있다. AWS CodeDeploy Agent 코드를 분석하면서 알게 된 내용들을 틈틈이 정리해보고자 한다  👩‍🎓 1. CodeDeploy CodeDeploy의 기본적인 구..

americanopeople.tistory.com

※ 해당 글은 "스프링 부트와 AWS로 혼자 구현하는 웹 서비스(이동욱 저)"를 참고해 작성하였습니다.