Github Webhook

개요

프로젝트를 진행하면서 빠르게 api 서버를 배포해야 할 일들이 많다. 예를 들자면 해커톤에 참여할 때라던지, 당장 나의 api를 클라이언트 프로그래머가 사용해야 할 경우가 종종 있다. API 서버를 테스트로 배포할 때는 주로 다음의 과정을 거친다.

  1. localhost에서 작업을 하고 Postman를 테스트를 거친다
  2. Github Repository에 Push한다
  3. 현재 돌고있는 코드를 stop 한다.
  4. 서버(AWS, 물리)에 접속하여 Git pull을 받는다.
  5. 다시 코드를 start 한다.

상당히 번거로운 작업이 아닐 수 없다. 그래서 나는 이런 경우 주로 ec2에 git을 설치하고, 서버에 직접 repository를 만든 다음 ec2의 repo push하면 post receive hook을 사용하여 빠른 배포를 진행했다. 다음의 링크와 같은 방법이다. DIY node.js server on Amazon EC2

하지만 이런 방법에는 큰 단점이 있다. 바로 server에 git을 직접 bare로 설치하는 방법이기 때문에 나중에 github으로 프로젝트를 관리하기 어려워진다는 점이다. 최근 AWS직원분들과 이야기를 나눌 기회가 있었는데 Github Webhook에 대해서 말씀해 주셔서 알아보게 되었다.

여담으로, 처음에 “어떻게 빠른 배포를 진행할까?”에 대해서 생각하면서 Jenkins, Travis같은 빌드툴을 이용하고 그 다음 자동 배포 로직을 돌리면 어떨까 생각했는데 생각보다 빌드툴을 셋팅하기도 오래 걸렸기 때문에 간단한 테스트에는 적합하지 않았다. AWS Code Deploy에서 테스트 프로젝트를 돌려보니 Blue Green 배포 방식이고 router를 재연결하는 방식이기 때문에 코드가 빨리 배포되지는 않아서 매우 짜증이 났다. 그래서 결국에 제일 간단한 방법인 Github Webhook을 선택하였다.


# Webhook 이란?
![arrhook](./webhook/arrhook.gif) `Webhook`에 대해 알아보기 전에 우선 `hook`이 무엇인지 알아보자면, `hook`이란 각종 소프트웨어 구성요소 간에 발생하는 함수 호출, 메시지, 이벤트를 중간에서 가로채는 행위를 말한다. `Webhook`은 [Webhooks | GitHub Developer Guide](https://developer.github.com/webhooks/)에 매우 잘 나와 있고, 링크를 요약하자면 git repository에 이벤트가 발생했을 때 이를 미리 설정한 URL에 HTTP POST Method로 보내주는 것을 말한다. 주로 이슈 트래커, CI Build, Update Backup Mirror, deploy에서 사용한다고 한다. 이 글에서는 `deploy`관점에서 `webhook`을 적용한다.

Prerequisite

  1. Apt-get upgrade
  2. Apt-get update
  3. Install Node.js Debian-ubuntu-Node.js | Node.js
  4. Install Pm2 pm2: Production process manager for Node.js
  5. Install Nginx How To Install Nginx on Ubuntu 16.04 | DigitalOcean or Nginx Install

1. Ssh key Setting (On Server)

  1. 서버에서 Generating a new SSH key and adding it to the ssh-agent를 참조하여 ssh 키를 만든다.
  2. 다음링크에서 만들었던 서버의 공개 키를 등록한다. (기본값으로 설정했다면 vi ~/.ssh/id_rsa.pub 에서 볼 수 있다.) https://github.com/settings/ssh
  3. 공개키(idrsa.pub)에 있는 내용을 서버의 ~/.ssh/authorizedkeys 파일의 끝에 copy and paste한다.

2. Github Repository Setting


![Github Setting](./webhook/setting.png) 우선 Github의 Repository에서 Settings 탭을 보면 Add Webhook 버튼이 있다. 친절히 [가이드](https://developer.github.com/webhooks/)도 적혀있으니 시간이 있다면 참조해보자.

3. Secret Setting


Add Webhook을 누르면 다음과 같은 화면을 볼 수 있다. ![SecretSetting](./webhook/webhook.png)
  1. Payload URL에는 원하는 도메인의 Post Request가 날아갈 EndPoint를 적어준다. 내 경우는 추후에 NGINX Proxy를 이용해서 /hook 로 날린 요청을 받기 때문에 저렇게 설정했다.
  2. Content type은 json이나 form을 선택할 수 있다. 필자는 node에서 json으로 처리하는게 편하기 때문에 json을 선택했다.
  3. Secret은 보안상 필요한 부분이다. 생략해도 되지만 설정하기를 권장한다. 자신이 원하는 값을 적으면 된다.

Secret을 생략할 수 있지만 사실 매우 중요한 부분이다. SECRET을 설정하지 않는다면 서버에서는 어떤 Post Request에도 Shell Script나 커맨드 또는 Logic을 무조건 실행하므로 보안상 배우 취약할 수 있다.

4. NGINX Setting

Nginx 설정을 진행한다. $ vi /etc/nginx/sites-available/default

location /hook { // 이 부분이 포인트
                proxy_pass http://127.0.0.1:3100/; //3100 포트로 proxy
        }

설정 파일을 잠깐 살펴보면 /hook 으로 endpoint에 접속하면 3100번 포트로 proxy한다.

5. Hook Script

Github에 Push되었을 때 실행될 스크립트를 작성한다 $ mkdir ~/hook $ vi ~/hook/hook.sh

  1. futsal-node라고 적혀진 부분을 자신의 node server 디렉토리로 바꾼다.
  2. futsal이라고 적혀진 부분을 추후 pm2 에서 실행할 이름으로 미리 정해둔다.

코드를 설명하자면 Github Repository에 코드가 Push되었을때, 서버 디렉토리로 이동 후 Code를 Pull 받은 다음 pm2로 실행중인 기존의 서버를 재시작한다는 내용이다.

그 다음으로 Hook 폴더 내에서 $ npm init을 한 후, start script를 작성한다. 추후에 pm2에 의해 nom start script가 실행된다. $ vi ~/hook/package.json

{
  "name": "hook",
  "version": "1.0.0",
  "description": "",
  "main": "hook.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodejs ./hook.js"
  },
  "author": "",
  "license": "ISC"
}

6. Node.js Server Script

Github에 Push 되었을때 이를 인지할 수 있는 작은 서버스크립트가 필요하다. $ vi ~/hook/hook.js

코드를 설명하자면 Post Request가 왔을 때 클라이언트가 헤더에 x-hub-signature 값을 보내주는데, 이 값을 Server가 가지고 있는 토큰을 기반으로 Body를 암호화한 값과 같은지 보고 맞다면 아까 만들어둔 hook.sh를 실행한다는 내용이다.

여기서 중요한 점은 보안상의 이유로 위에서 진행했던 Secret Setting에 설정했던 키와 똑같은 키를 하드코딩된 변수가 아닌 환경변수(process.env[‘SCRET_TOKEN’]) 에서 불러와서 쓴다는 점이다.

SCRETTOKEN 환경변수는 다음과 같이 설정한다. `$ echo export SECRETTOKEN=아까설정했던Secret >> ~/.bash_profile `

7. Pm2 Setting

$ pm2 start npm --name="hook" -- start 이제 Background에서 Node.js 서버가 돌고있으면서 Post Request가 오기를 기다리고 있다. Github Repo에 Push를 하면 Github이 우리 서버 URL(~~~/hook) 로 Post Request를 날리고, Nginx에서 이것을 3100 port로 proxy한다. 그러면 우리의 hook.js 서버에 도달하고 만약 SECRET이 일치한다면 hook.sh를 실행하고 불일치하면 실행하지 않는다.

  • npm start 커맨드를 pm2에서는 다음과 같이 실행한다.

    • pm2 start npm —name “{appname}” — run {scriptname}
    • (run is optional)

마치면서

빠르게 배포하기 위해서 Github Webhook에 대해서 알아봤다. Bash Script, SSH, PM2, Node.js 에 대해서 미리 어느 정도 알고 있다면 그렇게 어렵지는 않을 것이다. 까먹지 않고 있으면 빠른 배포가 필요한 상황에서 매우 유용할 것 같다.

참고자료

  1. Deploy your site with git · GitHub

Philographer
Written by@Philographer
Fail Fast, Learn Faster

GitHublinkedInFacebook