깃랩(Gitlab)은 온프레미스 설치형 깃 호스팅으로 유명하고, 깃헙처럼 웹서비스 형태로도 사용이 가능합니다. 다만 깃헙이 선점 효과를 톡톡히 누리고 있는지라 만년 2인자 자리를 지키고 있죠. 깃랩의 장점이라면 CI/CD가 잘되어 있습니다. 심지어 설치형이 아니라도 public 레포인 경우 무료, private 레포인 경우 그룹 당 월 2000분(약 33.3시간)까지 무료입니다. 유료 플랜까지 가게되면 깃랩 안에서 모든걸 다 처리하라고 왠만한 솔루션 뺨치는 쿠버네티스 관리 보드까지 달아줍니다.

최근 깃헙도 이에 질세라 깃헙 액션을 베타로 공개했지만, 아직까지는 Circle CI, Travis CI 같은 외부 서비스를 사용하는게 일반적이죠

큰 그림을 보자면 깃랩의 CI/CD는 도커에서 돌아갑니다. 그리고 연결된 쿠버네티스에 배포됩니다. 프로젝트 루트에 .gitlab-ci.yml이 있는 상태로 새로운 푸시가 들어오면 일련의 파이프라인(Job 묶음)이 실행됩니다. 각 Job은 개발자의 선택에 달렸지만 보통은 아래와 같습니다.

  • 테스트: 유닛 테스트, 통합 테스트, 테스트 커버리지 측정
  • 린트: 코드 퀄리티 측정, 코드 컨벤션 점검
  • 빌드: 빌드, 번들링, Dockerfile 빌드 및 컨테이너 레지스트리 푸시
  • 배포: Helm Chart, KNative Functions, KNative App(컨테이너) 배포

사전 지식

  • git
  • node (javascript)
  • docker
  • kubernetes

준비물

  • 개인 도메인 (istio-ingress 혹은 nginx-ingress 를 사용하기 위한)
  • 깃랩 온프레미스 혹은 gitlab.com 계정
  • 온프레미스일 경우 Gitlab Container Registry 활성화
  • 온프레미스일 경우 Shared Runner 활성화 (Kubernetes를 통해 Specific Runner를 활성화 할수 있기 때문에 필수는 아님)
  • 온프레미스 Kubernetes 혹은 GCP 결제가 활성화된 구글 계정
    • 만약 Kubernetes를 직접 리눅스 서버에 설치하실 경우 k3s를 사용하시면 편리합니다. 다만 istio가 포함되어 있으므로 깃랩의 Ingress 혹은 KNative(istio 포함) 연동을 할때 포트가 충돌하니 istio 관련을 삭제해주어야 합니다.

사전 준비

저는 이 튜토리얼에서 온프레미스가 아닌 gitlab.com 을 사용합니다. 프로젝트 이름은 CI-CD-Tutorial로 지어줬습니다. 그리고 Create project를 눌러줍니다.

image.png

그 다음 Operations > Kubernetes > Add Kubernetes cluster 에 들어가 GKE(Google Kubernetes Engine)를 연동합니다. GKE 연동을 하려면 구글 클라우드 결제(비용이 발생)가 활성화 되어 있어야하고, Kubernetes Engine 및 Compute Engine API를 활성화 시켜야합니다.

  • GKE 연동시 가장 가까운 리전은 asia-northeast1이고, 권장 사양은 n1-standard-2 x 3대입니다. 약 월 20만원이지만 상단의 Apply for credit을 요청하면 2~300$ 크레딧을 제공합니다

비용이 걱정된다면 남는 리눅스 서버에 Kubernetes 를 설치한 뒤 Add existing cluster 에서 연동할 수도 있습니다. Using a k3s Kubernetes Cluster for Your GitLab Project 포스팅을 참조하여 연동하시면 됩니다. 다만 istio 관련은 Ingress, KNative 연동시에 충돌이 나게되니 삭제합니다

image.png

연동 후 잠시 기다리면 아래와 같은 화면이 나옵니다. Base domain 에는 배포에 사용할 도메인(저는 제 도메인을 적었습니다)을 적고 Save changes를 눌러줍니다. 그 다음 Helm Tiller Install 을 눌러 설치합니다. Helm은 쿠버네티스 어플리케이션 메니저로써 클라이언트인 Helm, 서버인 Tiller(쿠버네티스에 설치되는) 로 구성되며, 구성을 정의한 Chart를 통해 쿠버네티스에 복잡한 구성의 서비스를 배포할 수 있습니다.

image.png

그 다음 필요에 따라 Prometheus, Cert-Manager 를 설치합니다. 만약 온프레미스 환경에서 Runner를 추가하지 않았다면, GitLab Runner 도 설치합니다,

그 다음이 중요한 부분인데, 2가지 선택지가 있습니다.

  • Auto DevOps:
    • .gitlab-ci.yaml을 작성하지 않더라도 임의의 파이프 라인을 구성하여 배포
    • 빌드, 코드 퀄리티, 테스트, 배포시 롤아웃, 퍼포먼스 테스트까지 구성
    • Helm Chart를 수정하여 배포되는데 기본으로 PostgresQL이 딸려옴
    • 커스터마이즈의 어려움

image.png

  • KNative:
    • 직접 .gitlab-ci.yaml을 작성함
    • Pivotal에서 개발한 kubernetes + istio 기반 서버리스 배포 환경
    • 스테이징 환경 (실제 배포가 아닌 내부 배포) 구성에 용이함
    • KNative는 tm을 통해 배포되며, 현재 깃랩 버전에서는 subdomain을 지정할 수 없음
      • ci.ci-13952126-development.codesanctum.net 이런식
    • 커스터마이즈가 쉬움

image.png

만약 Auto DevOps를 사용하시려면 Ingress를 설치하시고, KNative를 사용하시려면 KNative를 설치합니다. 왜냐하면 KNative가 istio를 사용하는데, Ingress는 nginx를 사용하기 때문에 포트가 쫑이납니다. istio-ingress-controller가 깔리느냐 nginx-ingress-controller가 깔리느냐 차이입니다.

어찌됐건 복잡한 배포를 하려면 Helm Chart 같은걸 직접 작성해야하니, 뭘 선택하던 위에 두 방법은 못쓰므로 부담 없이 깔아줍시다. 저는 스테이징 환경 구성에 용이한 KNative를 선택하였습니다. 설치 후 잠시 기다리면 아래와 같이 보여지는데, Knative Endpoint를 와일드 카드 도메인의 A 레코드에 연결하고 Save changes 누르시면 됩니다.

image.png

다음은 배포시 사용할 Gitlab Container Registry를 위해 Settings > Repository > Deploy Tokens 에 가서 아래와 같이 입력하고 Create deploy token을 눌러줍니다. 그리고 생성된 토큰과 패스워드는 따로 적어둡니다.

image.png

생성된 토큰과 패스워드는 Settings > CI/CD > VariablesCI_DEPLOY_USER, CI_DEPLOY_PASSWORD 라는 키값으로 입력후 저장해주시면 됩니다.

image.png

이제 준비는 끝났습니다.

프로젝트 작성

저는 express node 서버를 작성하여, .gitlab-ci.yaml를 통해 CI/CD를 수행하여 Dockerfile을 통해 이미지를 빌드한 뒤 KNative serverless app(컨테이너)을 통해 배포할겁니다. 작성할 파일은 .gitignore, .gitlab-ci.yml, Dockerfile, index.js, package.json 이렇게 5개 입니다. 적당히 프로젝트를 clone 하여 아래와 같이 작성후 push 합니다.

KNative는 서비스 노출에 8080 포트(Auto DevOps 의 경우 5000)를 사용합니다.

.gitignore

.DS_Store
node_modules

.gitlab-ci.yml

image: node:latest

stages:
  - test
  - build
  - deploy

cache:
  paths:
    - node_modules/

include:
  template: Serverless.gitlab-ci.yml

test:
  script:
    - npm install
    - npm test

build:
  extends: .serverless:build:image  

deploy_staging:
  stage: deploy
  extends: .serverless:deploy:image

deploy_prod:
  stage: deploy
  extends: .serverless:deploy:image
  environment:
    name: production
  when: manual
  only:
  - master

Dockerfile

# This file is a template, and might need editing before it works on your project.
FROM node

WORKDIR /usr/src/app

ARG NODE_ENV
ENV NODE_ENV $NODE_ENV

COPY package.json /usr/src/app/
RUN npm install

COPY . /usr/src/app

# replace this with your application's default port
EXPOSE 8080
CMD [ "npm", "start" ]

index.js

const express = require('express')
const app = express()

app.get('/', function(req, res) {
    res.send('Hello Gitlab!')
})

app.listen(8080, function() {
    console.log('Example app listening on port 8080!')
})

package.json

{
  "name": "CI-CD-Tutorial",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

image.png

푸시를 한 후 CI/CD > Pipelines에 가면 해당 커밋에 대한 파이프라인이 도는 것을 보실 수 있습니다. 일부러 수동 배포도 넣어봤는데 궁금하시면 deploy_prod 옆에 화살표를 눌러보시면 배포가 되는 것을 보실 수 있습니다.

image.png

여기서 배포된 express 앱은 Operations > Serverless 에 들어가면 보여집니다.

image.png

접속해보니 잘 나오는군요.

image.png

마치며

보통 CI 까지는 오픈소스나 개발 환경이 성숙한 회사에서 많이 접할 수 있지만, 사내에 숙련된 DevOps 기술자가 있지 않는 이상 CD까지 하기는 쉽지 않습니다. 하지만 Gitlab 처럼 CI/CD 를 한군데 잘 녹여낸 서비스라면 많은 개발자들이 쉽게 사용할 수 있을 것 같네요. 왜 MS가 Github을 인수했는지 알 것 같은 느낌이 듭니다. 해보시고 안되는 부분은 댓글로 남겨주세요 :)

지금까지 작성한 코드는 gitlab.com/wickedev/ci-cd-tutorial 에 공개되어 있습니다.