Github Webhook과 jenkins로 배포 자동화하기

LJH·2021년 6월 13일
4

DevOps 강의 (feat. Foo)

목록 보기
10/16

해당 내용은 Class101의 현직 대기업 개발자 푸와 함께하는 진짜 백엔드 시스템 실무! 강의를 기반으로 작성했습니다.


😀 목표

  • 배포를 자동화 하는 과정 이해하기
  • Github Webhook과 jenkins로 배포 자동화 세팅하기

참고로 이번에 자동화 하는 과정에서 Docker는 사용하지 않는다.


1. 전체적인 과정 이해하기

  • 먼저 Github Webhook 이란?

    • 특정 이벤트가 발생 시 다른 URL로 API 호출을 할 수 있게 해주는 역할
  • 자동화 적용 후 예상 과정

    1. CPU 바운드 애플리케이션을 로컬에서 수정한 후 Github에 push한다.
    2. Github Webhook이 동작해 jenkins에게 API 요청을 날린다.
    3. jenkins는 Github로부터 온 API 요청을 받아서 저장소의 소스코드를 다운받고 스프링 부트
      프로젝트를 빌드하여 JAR파일로 만든다.
    4. jenkins가 JAR파일을 CPU 워커 인스턴스들에 배포하고 실행시킨다.
  • 자동화 적용을 위한 작업 과정

    1. Github 에서 jenkins로 API요청이 오면 해당 리포지토리로 접근해 소스를 다운받고 JAR파일로 만들고 배포하도록 설정한다.
    2. Github Webhook 기능을 활성화 시킨다.
    3. 애플리케이션의 포트를 8080포트로 변경시킨다.
    4. 테스트해보면서 삽질한다.
    5. 자동화 성공 이후 무중단 배포를 위해 추가 설정을 한다.

2. jenkins에 Github Webhook 빌드 유발 설정

  • 빌드에서 해야하는 일은 소스코드를 저장소에서 pull 받고 해당 소스코드의 의존성을 다운로드 받은 후, 애플리케이션으로 바로 실행할 수 있도록 jar파일로 묶는 행위가 포함된다.

  • 위 과정을 빌드에서 수행해주면 배포될 인스턴스가 여러개 있어도 소스코드와 의존성을 다운로드 받는 과정은 오직 한 번만 이루어 지면 된다.(각 인스턴스마다 소스코드, 의존성을 다운로드 받게되면 배포시간이 늘어날 것 이다.)

2-1. 리포지토리 fork

  • 해당 링크의 리포지토리를 fork한다. 내 리포지토리에 추가된다.

2-2. 리포지토리 등록

  • 젠킨스가 리포지토리에 접근해서 소스를 다운받기위해 등록하는 과정이다.
  • 지난번에 생성해둔 deploy → 구성 으로 들어간다.
  • 소스코드 관리에서 git으로 선택하고 url에 fork한 리포지토리의 주소를 넣어주면된다.
    • 이 때 그대로 리포지토리의 url이 아닌, code 버튼을 클릭해서 나오는 url을 넣어야 한다.

2-3. 빌드 유발 체크

2-4. 빌드 설정

  • 젠킨스가 리포지토리에서 소스코드를 받아온 후 실행할 명령어를 입력하는 과정이다.(jar파일을 만드는 과정)
  • Execute shell을 클릭하면 위와 같이 명령어를 입력할 수 있는 공간이 생긴다.
  • 해당 빌드는 Github에서 받아온 소스코드를 Maven으로 jar파일을 생성해 Deploy(배포)할 준비를 해야한다.
  • 그 전에 어떤 명령어를 입력해야 jar파일이 생성되는지 알아보자.

2-5. jar파일 생성 과정 살펴보기

  • 빌드할 때 jar파일이 어떻게 생성되는지 살펴보기 위한 과정이다. 넘어가도 좋다.
과정 살펴보기
  • 이전에 만든 CPU 바운드 애플리케이션으로 확인해보자
  • 먼저 배포 시 빌드를 하기 위해 꼭 해야하는 작업은 clean과 package 작업이다.
  • clean 시 target폴더 부분이 어떻게 변하는지 확인해보자.

  • target 폴더가 사라진 걸 확인할 수 있다.

  • package를 하니 다시 target 폴더가 생겼고 안에 jar파일도 존재한다.

그냥 간단히 생각하면
원래 있던 jar파일은 수정된 소스코드가 반영되기 전 jar파일이므로 지워주고
새로 패키징하여 jar파일을 만드는 과정이다.

이 과정을 Execute shell에다 실행해주면 되는데 mvnw라는 실행파일을 이용하면 된다.

2-6. 빌드 명령어 입력 후, 빌드

  • 간단하다 위에서 확인한 것 처럼 클린과 패키징 해주는 명령어만 입력하면 된다.
  • 저장을 누르고 빌드를 해보면 에러가 나온다. 콘솔 로그를 확인해보자.

  • mvnw 명령어를 사용할 때 Permission denied가 뜬걸 볼 수 있다.
  • 이유는 우리가 만든 CPU 바운드 애플리케이션은 윈도우에서 생성되었고
    윈도우에서는 mvnw에 실행권한을 주지 못하게 때문에 애플리케이션이 리눅스 환경에 옮겨왔을 때는 실행 권한이 없다는 에러가 나온다.

2-7. 권한 추가후 다시 빌드

  • 권한이 없다면 권한을 추가해주자. ./mvnw 명령어에 544로 실행권한을 준다.

  • 강의는 정상적으로 빌드가 됬는데 나는 빌드가 되지 않는다.
  • 심지어 빌드 취소버튼을 눌러도 동작하지않는다. 서버가 죽어버렸다.. ssh로 접속도 안된다.
  • 인스턴스를 몇번 재실행하면서 해보니 그냥 maven repository에서 의존성을 다운받느라 시간이 조금 걸리는거였다... 빨간색이 뜬다고 겁먹지말고 기다리자..

2-8. jar파일로 배포

  • 처음에 말했듯이 docker를 사용하지않고 인스턴스에 jar파일을 실행시켜서 애플리케이션을 띄울거다.
  • 근데 현재는 docker run 명령어를 사용해서 배포중이다. 이걸 수정해주자.

  • target~~~.jar 부분 즉 jar파일의 경로를 복사한다.

  • 다시 구성으로 가서 Source files를 추가하고 prefix를 제거해준다.
  • 실행 명령어는 jar파일을 실행하도록 입력해준다.
  • 1,2,3 인스턴스 모두 똑같이 설정해준 후 빌드해보자.

2-9. 삽질과정

  • java 명령어를 실행할 수 없다는 로그가 찍혔다.
    즉 워커 인스턴스에 java가 설치가 안 되어 있다. 각 워커 인스턴스에 자바를 설치해주자.

    sudo yum install -y java
  • 다시 빌드해보면 지난번과 같이 빌드가 완료되지 않는다.
    각 워커 인스턴스 Exec command를 아래와 같이 수정한다.

    sudo java -jar cpu-0.0.1-SNAPSHOT.jar > nohup.out 2>&1 &
  • 이유는 이전 글 에서 확인하자.

  • 이후 빌드해보면 정상적으로 배포가 되었다.

3. Github Webhook 설정

  • 이제 우리가 코드를 수정 후 push하면 github가 젠킨스에게 API 요청을 날려야한다.
    github에게 젠킨스의 ip를 알려주자.

  • 깃허브 리포지토리로 돌아와서 Settings → webhooks로 들어간다.

  • Payload URL에 젠킨스의 ip와 /github-webhook/ 을 입력해준다.

  • Content type을 json으로 지정하고 Just the push event를 클릭해주면
    psuh 이벤트가 발생했을 때만 웹훅이 동작한다.


4. 포트 변경

  • 이전에는 도커 컨테이너를 띄우면서 8080 포트로 띄웠는데, 지금은 docker를 이용하지않고
    jar파일을 실행해서 애플리케이션을 실행시키기 때문에 http 기본 포트인 80포트로 연결이 된다.

  • 그런데 Nginx를 8080포트로 연결했기 때문에 애플리케이션의 포트를 8080으로 변경하고
    다시 배포해야 한다.

  • 변경 전에 먼저 각 인스턴스에 애플리케이션들을 죽여주자.

  • 실행중인 프로세스들을 확인하고, 각 PID를 입력해 죽이면 된다

ps -aux | grep java // 프로세스 정보 확인
sudo kill -9 [PID] // 프로세스 kill

  • webhook 이벤트 설정 후 해당 리포지토리를 sourcetree를 이용해 clone 받아오자

  • 클론 받은 프로젝트를 열어주자.

  • port를 8080으로 변경해준다.

5. 자동화 결과 확인

  • 이제 github에 push하면 젠킨스가 자동으로 빌드 후 배포해줄 것이다.

  • 커밋 후 푸시해준다.

  • 젠킨스로 돌아와서 확인해보면 빌드를 누르지 않았는데 새로운 빌드가 생긴걸 볼 수 있다.

  • 워커 인스턴스에 요청을 보내면 정상적으로 응답이 오는 걸 볼 수 있다.
  • 그런데 이거는 이전에 실행된 애플리케이션 아닐까? 라는 생각이 들 수 있다.
  • 테스트해보자.

6. 한번 더 자동화 확인

  • 현재는 위처럼 /hello url로 요청하면 hello 메시지가 응답이 온다. 이걸 변경해보자.

  • hello → hello deply로 변경 후 push해보자.

6-1. lsof 설치 후 실행 명령어 추가

  • push 하기 전 실행중인 애플리케이션을 죽여줘야한다. 8080포트를 이미 사용중이기 때문에
    새로 띄우는 애플리케이션은 띄어지지 않는다.

  • 그래서 8080포트를 사용중인 프로세스를 죽이고 배포해야한다. 아까는 수동으로 해줬지만 매번 수동으로 해줄 순 없다. 그럴려면 lsof 라는 명령어가 필요하므로 설치해준다.

  • 젠킨스 설정에 와서 8080포트를 사용중인 프로세스를 죽이는 명령어를 추가해 준다.

6-2. push

  • 다시 push를 해야 하기 때문에 응답 문자열을 수정해주고 다시 push 하자

  • push 후 젠킨스 빌드 콘솔 로그를 확인해보면 성공적으로 빌드가 되었는데 url입력해보니
    응답이 오지않는다.

  • 워커 인스턴스에서 확인해보니 애플리케이션이 실행되지 않았다.
  • 그래서 젠킨스에서 따로 빌드해봤는데 되지 않았고 결국 젠킨스 서버가 또 죽어버렸다.
  • 젠킨스 인스턴스를 중지 후 실행시켰고 빌드해보니 다시 정상적으로 실행됬다.
  • 이젠 애플리케이션에서 코드 수정 후 push 했더니 젠킨스에서 빌드를 하지 않는다.
  • 확인해보니 젠킨스를 껐다가 키는 과정에서 젠킨스의 ip가 변경되어 깃허브 webhook 이벤트 설정이 동작하지 않았다. 다시 웹훅 설정에서 Payload URL을 변경해주자.

6-3. 다시 push

  • push후 젠킨스에서 자동으로 빌드가 되긴 했는데 또 애플리케이션에서 응답이없다.
  • 로그를 확인해보니 nohup, java 등등 프로세스를 찾을 수 없단다.
  • 프로세스 죽이는 명령어랑 jar파일 실행시키는 명령어는 별도인데 한줄로 처리되는것 같다.

  • 그렇다 명령어 실행부분에 한줄로 입력해놨다.
    프로세스 죽이는 명령이 이후에 엔터키를 누르자..
  • 이후 push하면 젠킨스에서 자동으로 빌드 후 배포한다.
    애플리케이션에서 성공적으로 응답이 온다.

문제가 생긴 이유는 명령어를 한줄로 입력해서 애플리케이션이 실행되지 않았고,
그 과정에서 젠킨스 서버가 죽어버려서 재실행 하는 바람에 젠킨스 인스턴스의
ip가 변경되었고 깃헙이 젠킨스에게 API 요청을 날리지 못했다..


7. 무중단 배포 테스트

  • 현재는 3개의 인스턴스가 한번에 배포되고 있어서 무중단 배포가 되지 않는다.
  • 실제로 airtillery로 스크립트를 실행시키고 push해서 배포하면 502에러가 나는 요청이 존재한다.
  • 여러 방법이 있지만 간단하게 배포되는동안 인스턴스에 딜레이를 주면 된다.
  • 예로 sleep 5 명령어를 주게되면 5 초이후에 쉘이 명령을 입력받는다.

  • 인스턴스 2,3번 실행 명령어에 sleep 30 을 추가해주자.
  • 이후 테스트해보면 에러나는 요청없이 모두 처리된다.

해당 과정은 생략했다..


8. 만약 도커로 배포한다면

  • 기존

  • docker 추가

  • 깃허브와 젠킨스사이에 도커허브가 끼면 된다.

  • 그리고 이미지에 대한 빌드를 도커허브에서 automated build 기능을 이용해서 진행한다.

  • 이전에는 젠킨스가 빌드했지만 도커허브가 빌드를 한다.

  • 그리고 인스턴스에서는 java -jar 명령어가 아닌 docker run 명령어로 실행하도록 변경한다.


9. 마치며

  • 거의 반나절이 걸렸다.. 강의를 구매하면서 가장 궁금했던 과정 중 하나였고 내 프로젝트에 적용시켜보고자 하는 부분이였다.

  • 그런데 나는 docker를 이용해서 애플리케이션을 띄었기 떄문에 이거보다 더 복잡하고 엄청난 삽질이 예상된다. 사실 할 수 없을지도 모른다..

  • 그래서 일단은 docker를 이용하지않고 위 과정과 똑같이 먼저 내 프로젝트에 적용해보고
    성공하면 이후에 docker를 이용해서 자동화 해볼 생각이다.

0개의 댓글