ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 9장) 코드가 푸시되면 자동으로 배포해 보자 - Travis CI 배포 자동화
    Java & Spring/스프링 부트와 AWS로 혼자 구현하는 웹 서비스 2020. 7. 15. 21:31
    반응형

    CI & CD 소개

    • CI => Continuous Integration - 지속적 통합
      • VCS (코드 버전 관리 시스템)에 PUSH가 되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일을 만드는 과정
    • CD => Continuous Deployment - 지속적 배포
      • 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행되는 과정
    • CI 4 규칙
      • 모든 소스 코드가 살아있고(현재 실행되고) 누구든 현재의 소스에 접근할 수 있는 단일 지점을 유지할 것
      • 빌드 프로세스를 자동화해서 누구든 소스로부터 시스템을 빌드하는 단일 명령어를 사용할 수 있게 할 것
      • 테스팅을 자동화해서 단일 명령어로 언제든지 시스템에 대한 건전한 테스트 수트를 실행할 수 있게 할 것
      • 누구나 현재 실행 파일을 얻으면 지금까지 가장 완전한 실행 파일을 얻었다는 확신을 하게 할 것

    Travis CI 연동하기

    • 깃허브에서 제공하는 무료 CI 서비스

      • 젠킨스 같은 CI 도구도 있지만, 설치형이기 때문에 이를 위한 EC2 인스턴스가 하나 더 필요
    • Travics CI 웹 서비스 설정

    • 프로젝트 설정

      • .travis.yml 파일을 build.gradle과 같은 위치에 만들기

        • yml => YAML(야믈) ~> JSON에서 괄호를 제거한 것이라고 생각하면 됨
          language: java
          jdk:

          • openjdk8

          branches:
          only:

          - master

          before_install:

          • chmod +x gradlew

          Travis CI 서버의 Home

          cache:
          directories:

          - '$HOME/.m2/repository'
          - '$HOME/.gradle'

          script: "./gradlew clean build"

      # CI 실행 완료시 메일로 알람
      notifications:
        email:
          recipients:
            - dimple0416@naver.com
    • branches
      • Travis CI를 어느 브랜치가 푸시될 때 수행할지 지정
    • cache
      • 그레이들을 통해 의존성을 받게 되면 이를 해당 디렉토리에 캐시하여, 같은 의존서ㄴㅇ은 다음 배포 때부터 다시 받지 않도록 설정
    • script
      • master 브랜치에 푸시되었을 때 수행하는 명령어
      • 여기서는 프로젝트 내부에 둔 gradlew을 통해 clean & build를 수행
    • notifications
      • Travis CI 실행 완료 시 자동으로 알람이 가도록 설정
    • 위 과정에서 에러가 한 번 났었는데, gradlew에 실행 권한이 없어서였다.
      before_install:
        - chmod +x gradlew
      이 코드를 통해, 실행 전에 권한을 부여해줘서 해결했다.

    Travis CI와 AWS S3 연동하기

    • AWS S3

      • 일종의 파일 서버
      • 이미지 파일을 비롯한 정적 파일들을 관리하거나 배포 파일들을 관리하는 등 기능 지원
      • 보통 이미지 업로드를 구현할 때 S3를 많이 쓴다.
    • Travis CI와 AWS S3 연동하기

      • 실제 배포는 AWS CodeDeploy를 통해 하지만, CodeDeploy에는 저장 기능이 없다. 그래서, Travis CI가 빌드한 결과물을 S3에 보관해서 CodeDeploy에서 이를 이용해서 배포하도록 설정하기 위해 AWS S3 사용.
    • AWS Key 발급

      • AWS 서비스에 외부 서비스가 접근할 수 없다. 그래서 접근 권한을 가진 Key를 생성해서 이용해야 한다.
      • IAM 서비스 이용하기
        • AWS에서 제공하는 서비스의 접근 방식과 권한을 관리하는 서비스
      • IAM 검색 후, 사용자 추가 ~> 사용자 이름 기입, 액세스 유형 ~> 프로그래밍 방식 엑세스
        사용자 이름 : zin0study-travis-deploy 이런 느낌으로 관리하는 듯 하다
      • 권한 설정 ~> 기본 정책 직접 연결
      • 정책 검색 ~> AmazonS3FullAccess, AWSCodeDeployFullAccess 추가
      • 태그 등록 ~> 키 = Name, 값 = zin0study-travis-deploy
    • Travis CI에 키 등록

      • Settings ~> Environment Variables에, 위에서 발급받았던 Key ID와 Secret을 value에 넣기
        Name => AWS_ACCESS_KEY , Value => 키 ID
        Name => SECRET_KEY , Value => 키 Secret
        이런 방식으로 입력
        • 여기에 등록된 값들은 .travis.yml 에서 $AWS_ACCESS_KEY와 같이 변수로 사용 가능
    • S3 버킷 생성

      • Travis CI에서 생성된 Build 파일을 저장하도록 구성
      • AWS S3 검색 ~> 버킷 만들기
        • 버킷 이름 짓기 => 배포할 Zip 파일이 모여있는 장소임을 의미하도록 짓는 것을 추천
        • 버전관리 설정 Skip
        • 퍼블릭 엑세스 차단(버킷 설정) => 모든 퍼블릭 엑세스 차단
          • 왜냐하면, IAM 사용자로 발급받은 키를 사용
          • 오픈할 경우, 중요한 정보(코드, 설정값, 주요 키 값 등)가 다 탈취당한다.
    • .travis.yml 추가

      ...
      
      before_deploy:
        - zip -r study-springboot2-webservice *
        - mkdir -p deploy 
        - mv study-springboot2-webservice.zip deploy/study-springboot2-webservice.zip # deploy로 zip파일 이동
      
      deploy:
        - provider: s3
          access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
          secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
          bucket: zin0study-springboot-build # S3 버킷
          region: ap-northeast-2
          skip_cleanup: true
          acl: private # zip 파일 접근을 private으로
          local_dir: deploy # before_deploy에서 생성한 디렉토리
          wait-until-deployed: true
      
      ...
      • before_deploy
        • deploy 명령어가 실행되기 전에 수행된다.
        • CodeDeploy는 Jar 파일은 인식하지 못하므로 Jar + 기타 설정 파일들을 모아 압축
      • zip -r study-springboot2-webservice *
        • 현재 위치의 모든 파일을 study-springboot2-webservice 이름으로 압축
        • 명령어의 마지막 위치는 본인의 프로젝트 이름이어야 한다.
      • mkdir -p deploy
        • deploy라는 디렉토리를 Travis CI가 실행 중인 위치에 생성
      • deploy
        • S3로 파일 업로드 or CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언
      • local_dir: deploy
        • 앞에서 생성한 deploy 디렉토리를 지정
        • 해당 위치의 파일들만 S3로 전송
      • 여기서 에러 잡는데 조금 고생했다.
        • 첫째, 공백에 유의할 것
        • 둘째, 오타 확인할것...
        • 그리고 셋째는 이어질 CodeDeploy에서 다루겠다. 왜냐하면, S3까지는 문제가 되지 않기 때문.

    Travis CI와 AWS S3, CodeDeploy 연동하기

    • EC2가 CodeDeploy를 연동 받을 수 있게 IAM 역할을 생성

    • EC2에 IAM 역할 추가하기

      • IAM 검색 ~> 역할 ~> 역할 만들기
        • 역할 = AWS 서비스에만 할당할 수 있는 권한 (ex : EC2, CodeDeploy)
        • 사용자 = AWS 서비스 외에 사용할 수 있는 권한(ex : Local PC, IDC 서버 등)
      • AWS 서비스 , EC2 차례대로 선택
      • 정책 ~> EC2RoleForA를 검색해서 AmazonEC2RoleforAWS-CodeDeploy를 선택
      • 태그 등록
        • 키, VALUE에 원하는 이름 짓기
        • 키 => NAME, VALUE => ec2-codedeploy-role 이런 방식으로 짓는 듯 함
      • 역할의 이름 등록하기
        • 역할 이름 => ec2-codedeploy-role 이런 방식
      • EC2 서비스로 이동해서, 프로젝트 인스턴스에 오른쪽 클릭 ~> 인스턴스 설정 ~> IAM 역할 연결/바꾸기 ~> 방금 생성한 역할 선택 ~> 재부팅
    • CodeDeploy 에이전트 설치

      • 책에서 나온 코드로는 설치가 되지 않아서, 검색을 통해 찾았다.
        aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install .--region ap-northeast-2
        wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
        위에가 책에서 나온 명령어, 아래가 찾은 명령어 => EC에 접속해서 명령어 입력
      • install 파일에 실행 권한 추가 => chmod +x ./install
      • intall 파일로 설치 진행 => sudo ./install auto
      • Agent 실행 확인하기 => sudo service codedeploy-agent status
        => 결과 : The AWS CodeDeploy agent is running as PID XXX 면 성공
    • CodeDeploy를 위한 권한 생성

      • IAM 역할 생성하기 ~> AWS 서비스, CodeDeploy를 차례대로 선택
      • 정책 필터 ~> AWSCodeDeployRole
      • 태그 작성
        • 키 => NAME, VALUE => codedeploy-role
      • 역할 이름 작성
        • 역할 이름 => codedeploy-role
    • CodeDeploy 생성

      • AWS 배포 3형제
        • Code Commit
          • Github과 같은 코드 저장소, 하지만, Github를 가장 많이 이용
        • Code Build
          • Travis CI와 같은 빌드용 서비스
          • 멀티 모듈을 배포하는 경우 사용해 볼만하지만, 규모 있는 서비스는 젠킨스/팀시티 등을 이용
        • CodeDeploy
          • 배포 서비스, 대체재가 없다. ~> 이용
      • CodeDeploy 검색 ~> 애플리케이션 생성
      • 애플리케이션 이름 작성 => zin0Study-springboot2-webservice
      • 컴퓨팅 플랫폼 => EC2/온프레미스
      • 배포그룹 생성
        • 배포그룹 이름과 서비스 역할 입력
          배포그룹 이름 => zin0Study-springboot2-webservice-group
          서비스 역할 => codedeploy-role
        • 배포 유형 => 현재 위치
          • 배포할 서비스가 2대 이상이면 블루/그린 선택
      • 환경 구성
        • Amazon EC2 인스턴스 선택
        • 키, 값은 위에서 저장한 값 선택
      • 배포 설정
        • 배포 구성 => CodeDeployDefault.AllAtOnce 선택
          • 한번 배포할 때 몇 대의 서버에 배포할지 결정하는 것
        • 로드 밸런싱 활성화 해제
    • Travis CI, S3, DodeDeploy 연동

      • EC2에 S3에서 넘겨줄 zip 파일을 저장할 디렉토리 생성하기

        • mkdir ~/app/step2 && mkdir ~/app/step2/zip
      • Local에 Build.gradle과 같은 위치에 appspec.yml 생성하기

        version: 0.0
        os: linux
        files: # Travis 구동을 위해 주석 수정
          - source : /
            destination: /home/ec2-user/app/step2/zip/
            overwrite: yes
        • 띄어쓰기 유의할 것
        • version: 0.0
          • CodeDeploy 버전
          • 프로젝트 버전이 아니므로 0.0 외에 다른 버전을 사용하면 오류 발생
        • source
          • CodeDeploy에서 전달해 준 파일 중 destination으로 이동시킬 대상 지정
          • 루트 경로(/)를 지정하면 전체 파일
        • destination
          • source에서 지정된 파일을 받을 위치
      • .tarvis.yml에 CodeDeploy 내용 추가

        deploy:
          ...
        
          - provider: codedeploy
            access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
            secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
            bucket: zin0study-springboot-build # S3 버킷
            key: study-springboot2-webservice.zip # 빌드 파일을 압축해서 전달
            bundle_type: zip # 압축 확장자
            application: zin0Study-springboot2-webservice # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
            deployment_group: zin0Study-springboot2-webservice-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
            region: ap-northeast-2
            wait-until-deployed: true
        • 여기서 에러가 많이 떠서, 시간을 많이 보냈다...
        • 그 이유는 오타나 이런 경우도 있었지만, 가장 중요한 점은 CodeDeploy로 값을 전달할 때, 값이 모두 소문자로 변환돼서 이동한다.
        • 처음에는 알집 생성을 zin0Study-springboot2-webservice.zip 으로 했었는데, key 오류가 계속 떠서 로그를 따라가다 보니까, CodeDeploy에 zin0study-springboot2-webservice.zip을 요청하고 있었다. ~> zip 이름을 소문자로 바꿔주고 해결
      • 배포가 끝났다면, 파일이 잘 도착했는지 EC2에서 확인

        • cd /home/ec2-user/app/step2/zip 으로 이동 ~> ll ~> build랑 gradle 등 도착 확인

    배포 자동화 구성

    • Local 최상단에 scripts 디렉토리 생성 ~> deploy.sh파일 추가

      #!/bin/bash
      
      REPOSITORY=/home/ec2-user/app/step2
      PROJECT_NAME=springboot-webservice
      
      echo "> Build 파일 복사"
      
      cp $REPOSITORY/zip/*.jar $REPOSITORY/
      
      echo "> 현재 구동중인 애플리케이션 pid 확인"
      
      CURRENT_PID=$(pgrep -fl springboot-webservice | grep jar | awk '{print $1}') # 에러가 뜬다면 pgrep으로 잡아올 jar 이름부터 확인
      
      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를 탐색
        • 실행 중이면 종료하기 위해서
        • 스프링 부트 애플리케이션 이름으로 된 다른 프로그램들이 있을 수 있어서 jar 프로세스를 찾은 뒤 ID를 찾는다.
      • chmod +x $JAR_NAME
        • nohup으로 실행하도록 Jar파일에 실행 권한 부여
      • $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
        • nohup 실행 시, CodeDeploy는 무한 대기한다.
        • 이 이슈를 해결하기 위해 nohup.out 파일을 표준 입출력용으로 별도로 사용
        • 이렇게 하지 않으면 nohup.out 파일이 생기지 않고, CodeDeploy 로그에 표준 입출력이 출력된다.
        • nohup이 끝나기 전까지 CodeDeploy도 끝나지 않으니 꼭 이렇게 해야함
    • .travis.yml 수정

      • 위에까지 진행했으면, 프로젝트의 모든 파일을 zip 파일로 만드는데, 실제로 필요한 파일들은 Jar, appspec,yml, 배포를 위한 스크립트들이다. 이 외에는 필요하지 않으니 포함 X

        ...
        
        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/study-springboot2-webservice.zip # deploy로 zip파일 이동
        
        deploy:
          ...
        • mkdir -p before-deploy
          • Travis CI는 S3로 특정 파일만 업로드가 안된다.
          • 디렉토리 단위만 업로드할 수 있기 때문에, deploy 디렉토리는 항상 생성
        • cp scripts/*.sh before-deploy/
          • before-deploy에는 zip 파일에 포함시킬 파일들을 저장
        • cd before-deploy && zip -r before-deploy
          • zip -r 명령어를 통해 before-deploy 디렉토리 전체 파일을 압축
    • appspec.yml 수정

      ...
      
      permissions:
        - object: /
          pattern: "**"
          owner: ec2-user
          group: ec2-user
      
      hooks:
        ApplicationStart:
          - location: deploy.sh
            timeout: 60
            runas: ec2-user
      • permissions
        • CodeDeploy에서 EC2 서버로 넘겨준 파일들을 모두 ec2-user 권한을 갖도록 함.
      • hooks
        • CodeDeploy 배포 단계에서 실행할 명령어를 지정
        • ApplicationStart라는 단계에서 deploy.sh를 ec2-user 권한으로 실행
        • timeout: 60으로 스크립트 실행 60초 이상 수행되면 실패가 된다. (시간제한을 두지 않을 경우 무한정 기다릴 수 있음)
    • 실제 배포 과정 체험

      • build.gradle에서 프로젝트 버전 변경
        • version '1.0.1-SNAPSHOT'
      • index.mustache 수정
        • 스프링 부트로 시작하는 웹 서비스 Ver.2

      • 커밋 - 푸시 하면 반영이 잘 되는 것을 확인

    CodeDeploy 로그 확인

    • AWS가 지원하는 서비스에서는 오류가 발생할 때 로그 찾는 법을 모르면 오류 해결하기가 어려움, 그래서 배포가 실패하면 어떤 로그를 봐야할 지 정리
    • CodeDeploy에 관한 대부분 내용은 /opt/codedeploy-agent/deployment-root에 있다.
      • cd/opt/codedeploy-agent/deployment-root로 이동 ~> ll
        • 여러 목록이 존재
        • 최상단의 영문과 대시(-)가 있는 디렉토리명은 CodeDeploy ID
          • 해당 디렉토리에 들어가 보면 배포한 단위별로 배포 파일들 존재
          • 배포 파일이 정상적으로 왔는지 확인할 수 있다.
        • /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
          • CodeDeploy 로그 파일
          • CodeDeploy로 이루어지는 배포 내용 중 표준 입/출력 내용이 담겨있다.
          • 작성한 echo 내용도 모두 표기됨
    반응형

    댓글

Designed by Tistory.