시작

1) 개발동기

제가 다니고 있는 학교에는 전철역과 학교를 연결해주는 셔틀버스가 있습니다. 학교 정문에서 역까지 걸어서 30분정도 걸리는 만만치 않은 등교길을 구원해주는 고마운 버스이지요. 전철역 뿐만 아니라 터미널로 가는 노선도있어서 시외로 나가고자 하는 학생들이나 중심가로 가고자 하는 분들이 주로 이용합니다.

보통 10분에 한대 꼴로 온다고 알려져 있지만 실상은 그렇지 않은 경우가 꽤 많습니다. 시간대별로 배차간격도 다르고, 노선에 따른 종류도 다양하여 원하는 목적지로 가기 위해서는 시간표가 필수가 되었죠.

기존에는 이러한 정보를 알려주는 “셔틀콕” 이라는 앱이 있었습니다. 각 정류장별 버스출발 잔여시각을 메인화면에 띄워서 ‘방에서 언제 나오면 버스를 타고 역까지 갈 수 있겠구나’ 를 가늠할 수 있게 도와주었습니다.

해당 앱은 학교에서 제공하는것이 아니라 저희 컴퓨터공학과 선배님께서 만드셨습니다. 하지만 졸업을 하시고 유지보수가 어려워져 얼마 전 서비스 종료를 선언하셨습니다.

때마침 학교측에서도 버스운영업체가 바뀌어 새로운 버스회사와 계약하며 실시간으로 위치를 나타내 주는 서비스를 도입했습니다.

셔틀나우 라는 서비스 였는데, 이전 앱과는 달리 버스의 현재 위치에 초점을 맞추어 실시간 위치정보를 구글맵에 표시해주는것이 주 기능이었습니다.

불편한 앱을 쓰는 것 보다는 이걸 개선해서 새로운 앱을 만들어 보자 라는데 의견이 모아져 뜻이 맞는 사람들을 모아 새로운 서비스를 런칭하기로 했습니다.

2) API 개발

서비스는 크게 API 단과 웹 단으로 나누기로 했습니다.

API는 JSON 화 된 시간표 데이터를 요청에 맞게 전달합니다.
웹은 사용자에게 실제로 보여지는 부분으로 vue 를 통해 PWA 형태로 개발하고자 합니다.

현재 팀원들 모두가 vue는 처음 접한터라 학기 중반까지는 러닝커브로 잡고 각자 vue에 대한 기본 실습을 진행하기로 했으며, API 는 Nodejs 개발경험이 있는 제가 기본 뼈대를 만들고 팀원들이 같이 살을 붙여나가는 방향으로 개발하였습니다.

Ver1 : 뼈대코드 작성

URL 쿼리에 따라 라우팅을 처리하는 Express 기반의 Nodejs 앱을 만들어서 Docker 컨테이너에 올렸습니다.

Ver2 : https 지원

사실 https 지원이 꼭 필요한 부분은 아니었습니다.
오히려 https 연결을 성립시키는 과정에서 전송지연이 발생하는 부작용도 있었습니다. 하지만 처음으로 Cloudflare 도메인을 만들고, priv key를 삽입하여 Express 에서 https 연결을 성립시키는 과정을 직접 개발하면서 새로운 내용을 많이 배울 수 있었습니다.
우리의 앱에서 단순히 요청을 할 경우에는 일반 http 프로토콜로도 충분하지만, 사용자가 우리의 API 를 이용해 다른 앱을 개발하고자 할 때 조금라도 안전하게 하고자 Cloudflare DNS 를 통해 Proxy 를 설정함으로서 Cloudflare 의 인증서가 우리의 도메인에 적용되게 할 수 있었습니다.
image

Ver3 : 도커 컨테이너 배포 자동화

도커를 공부하다 우연히 Jenkins가 도커를 통해서도 배포되고 있는것을 보고 우리 프로젝트에 적용해보고자 했습니다.
지금까지는

1) 새로운 소스코드 수정사항이 Git에 푸시되면,

2) 서버 Host OS에서 git pull 을 하고,

3) docker build -t 명령어를 이용해 새로운 도커 이미지를 빌드한 뒤,

4) docker run -d ~~ 옵션을 통해 새로운 컨테이너를 띄웠습니다.

이 과정을 Jenkins와 Docker 를 이용해 자동화하여 CI/CD를 조금이나마 맛볼 수 있었습니다.

Jenkins with docker 설치

Jenkins 는 Dood(Dood)를 위하여 Jenkins LTS 이미지를 받아와 그 위에 도커 바이너리를 설치하여 이미지로 빌드하는 형식으로 구축하였습니다.
사용된 Dockerfile은 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM jenkins/jenkins:lts
USER root

# Install the latest Docker CE binaries
RUN apt-get update && \
apt-get -y install apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(lsb_release -cs) \
stable" && \
apt-get update && \
apt-get -y install docker-ce

빌드된 이미지를 이용해 컨테이너를 생성해 줍니다.
이때 DooD 를 구현하기 위하여 Host OS 의 docker.sock 파일을 젠킨스 컨테이너에 마운트 해 줍니다.

1
2
3
4
5
docker run \
-p 8080:8080, \
-v /var/run/docker.sock:/var/run/docker.sock \
--name jenkins \
cxz7720/이미지명

docker.sock 파일을 마운트해 줌으로써 젠킨스 컨테이너에 설치된 도커 명령어를 통해 호스트의 도커 컨테이너를 관리할 수 있게 됩니다.

완료 후 설정한 주소로 브라우저를 이용해 접속하면 초기 설정 화면이 나타납니다.
기본적인 플러그인이 설치 되고, 관리자 계정까지 생성하고 나면 젠킨스 구축이 완료됩니다.
login

친절한 웨이터가 맞이해주는 젠킨스 첫 로그인 화면

저는 추가적으로 Slack Notifier 및 Blue Ocean 플러그인을 설치해 슬랙 알림 연동과 좀 더 깔끔한 UI를 구축하였습니다.

Jenkins 파이프라인 작성

pipeline

젠킨스 파이프라인은 크게 7단계로 구성하였습니다.

1. Host OS에 따로 저장해 둔 SSL 인증서를 Jenkins 빌드 프로젝트 폴더로 복사.

Jenkins 컨테이너를 실행시킬 때 -v 옵션으로 미리 마운트 해 두었습니다.

2. docker build 명령어를 통한 이미지 빌드

이미지에 태그를 부여하는 과정에서 $BUILD_NUMBER 변수를 사용하여 Jenkins의 프로젝트 빌드넘버와 결과 이미지의 빌드넘버가 일치하도록 하였습니다.

3. 기존에 가동중이던 컨테이너 정지

4. 정지된 컨테이너를 삭제

5. 새로운 이미지를 이용하여 컨테이너 생성

6. 이전에 사용하던 이미지를 삭제

$BUILD_NUMBER 로 이미지 태그를 관리하였기 때문에 현재 이미지 번호 - 1 을 이용하여 이전버전 이미지를 자동으로 삭제하도록 하였습니다.

7. 빌드 결과를 슬랙 채널에 공지

슬랙 워크스페이스에 연동된 Jenkins 봇을 통해 빌드 결과를 자동으로 알려주도록 설정하였습니다.
jenkins빌드결과 대령이오~

빌드 결과 확인

Portainer 를 통해 빌드되어 가동중인 컨테이너의 상태를 확인해봅니다.
portainer

마무리 : Readme 작성

리드미에는 기본적으로 API를 사용하는 방법에 대하여 서술해 두었습니다.
시간표 종류별, 날짜 종류, 정류소 종류 별로 URL 파라미터를 구분해 두었으며 리턴값의 구조에 대하여 기술하였습니다.

자세한 내용은 Github 레포를 참조하세요.

마지막으로 리드미의 꽃이라고 할 수 있는 뱃지를 달아주었습니다.

현재 이 프로젝트 레포에 달려있는 뱃지는 총 3개입니다.

1. Jenkins Build Status 뱃지
젠킨스 플러그인 중 하나인 ‘Embeddable Build Status Icon’ 을 이용해 최종 빌드 결과를 실시간으로 표현할 수 있도록 하였습니다.
Build Status
또한, 뱃지를 클릭할 경우 자동으로 Jenkins 로 연결되도록 하여 빌드의 경과를 확인할 수 있또록 하였습니다. 물론 Anonymous 사용자는 View 권한만 부여되어 있습니다.

2. API Status 뱃지
API가 정상적으로 동작하는지 여부를 쉽게 판단하기 위하여 shield.io 에서 제공하는 HTTP Ping 체크 기능이 있는 뱃지를 삽입하였습니다. 루트 디렉토리 외에도 하위 폴더에 접근이 가능한 점을 이용하여 semester/week/giksa.json이 정상적으로 로드되는지를 판별하도록 하였습니다.

모종의 이유로 연결이 불가능하면 API Offline 상태로 변합니다.

API Status

3. License 뱃지
마지막으로 소스코드의 라이센스를 나타내는 뱃지를 달았습니다.
팀원들 간 회의를 통해 GPL3.0 라이센스로 배포하기로 하였고, shield.io 의 라이센스 뱃지를 이용하였습니다.
license



개발과 구축에 대한 내용은 여기까지 입니다.

이제 남은건 실제 서비스 시 API 가 얼마나 안정적일지를 확인하는 단계입니다.
관련해서 각종 네트워크 문제 등을 해결한 내용이 이어집니다.

한 포스팅에 모두 이어서 적기에는 내용이 길어져 다음 글로 나누어 적겠습니다.
이어지는 두번째 이야기는 Nginx로 방화벽을 우회는 내용을 다룹니다.