늘어나는 개발 프로젝트, 빌드환경을 어떻게 모니터링 할까?

MUSINSA tech
Jun 10 · 16 min read

Prometheus와 Grafana를 활용한 Jenkins 모니터링 및 Alarm 구현

안녕하세요. 무신사 인프라보안팀 SRE(Site Reliability Engineering) 파트에서 AWS 기반 인프라 운영 및 인프라 플랫폼 개발을 하는 심호진입니다.

무신사는 초기 모놀리스(Monolith) 환경에서 MSA(MicroService Architecture) 환경으로 진화하고 있습니다. 이를 위해 다수의 개발팀이 도메인 별로 나뉘어진 어플리케이션을 개발 중이고, 이를 통해 무신사라는 하나의 서비스를 만들어가고 있습니다.

업무 도메인에 따라 한 팀에서 개발/운영하는 어플리케이션이 계속 증가하고, 이 어플리케이션을 구성하는 프로젝트가 많아질수록 CI(Continuous Integration) 도구에 등록된 빌드 설정도 많아지고, 관리의 복잡도가 높아집니다.

무신사의 경우 VCS(Version Control System)를 Atlassian 제품군 중 Bitbucket을 사용하고 있기 때문에, CI 도구의 선택 옵션이 그리 많지 않았습니다. 현재 Jenkins를 활용하여 모든 프로젝트를 빌드하고 있고, 그에 따라 Jenkins에 등록되어있는 프로젝트의 수가 상당히 많아졌습니다.

단일 Jenkins를 다수의 개발팀이 사용하는 데는 플러그인 의존성 충돌, 버전 문제, 장애의 영향도 등 여러가지 문제가 있습니다. 이를 해소하기 위해 여러 대의 Jenkins로 분리하는 작업을 진행했고, 이 과정에서 겪은 모니터링의 어려움을 Prometheus와 Grafana 기반으로 어떻게 해결했는지 공유하고자 합니다.

Jenkins 개선 배경

Prometheus와 Grafana를 활용해 Jenkins를 모니터링하게 된 계기를 설명하기에 앞서, Jenkins 서버가 많아지게 된 개선 과정을 간략히 설명드리겠습니다.

초기의 Jenkins 사용 모습

초기 프로젝트가 단일 어플리케이션(Monolithic Application) 몇 개로 구성된 시점에는 Jenkins 1대로 CI 과정을 수행하는 것이 전혀 문제 되지 않았습니다. 모든 개발팀에서 같은 프로젝트를 빌드하고, CI 도구의 요구사항이 적기 때문입니다.

하지만 MSA 구조로 분리되기 시작하고, 개발팀이 늘어날수록 Jenkins 운영의 복잡도가 높아지기 시작합니다.

하나의 Jenkins를 모든 팀이 같이 사용할 때 문제점

SRE 파트에서 Jenkins 서버 유지보수를 위해 협조를 요청하는 과정

Jenkins Main Server를 유지보수를 하다보면 경우에 따라 Jenkins 재시동이 필수인 경우가 있습니다.

  • 플러그인 설치 시 Jenkins Main Server 재시작을 요구하는 경우
  • Jenkins Main Server 버전 업그레이드 시
  • AWS EC2의 유지보수 알람이 오는 경우 등

위와 같은 상황으로 Jenkins Main Server를 재시동해야 하는 경우 모든 개발팀에 공지를 하고 배포 중단에 대한 양해를 구해야 합니다. 아주 드물지만 재시동 후 부득이하게 Jenkins가 바로 복구되지 않는다면, 원인을 파악하고 해결하는 동안 배포가 중단됩니다.

이런 문제를 최대한 방지하기 위해 개발팀이 근무하지 않은 야간/새벽 시간에 작업을 하게 되는데, 이런 작업이 많을수록 SRE파트의 피로도가 높아지게 됩니다.

Jenkins의 큰 장점 중 하나는 플러그인 시스템입니다. 누구나 Jenkins 플러그인을 개발할 수 있기 때문에 Jenkins에는 정말 많은 플러그인이 있고, 어떤 플러그인 들은 생산성에 큰 도움을 주기도 합니다. 하지만 각 개발팀에서 사용하려는 플러그인이 늘어나면서, Jenkins를 유연하게 만들어주는 이 플러그인 시스템이 오히려 독이 되기 시작했습니다.

플러그인은 각 플러그인이 의존성을 갖는 라이브러리와 함께 설치되어야 하는 경우가 있습니다. 서로 다른 플러그인이 동일한 라이브러리에 의존할 경우, 라이브러리 호환성 이슈가 발생하는 경우도 있습니다. 게다가 특정 플러그인은 Jenkins Main Server 버전에 의존적인 경우도 있습니다.

한 대의 Jenkins Main Server에 수많은 플러그인 설치 과정에 문제가 생기고, 플러그인 사용 주체를 파악하기 힘들어지면서 운영의 복잡도가 높아집니다.

한대로 운영되는 Jenkins Main Server에 문제가 발생하는 경우, 모든 프로젝트의 배포가 중단된다는 점은 누구나 쉽게 예상할 수 있습니다. 어플리케이션이 Monolithic에서 MSA로 전환하듯, Jenkins도 단일 서버에서 다수의 Jenkins 서버로 분리가 필요해져 다음과 같이 개선 작업에 들어갔습니다.

개선 1단계 — Jenkins를 팀 / 프로젝트 단위로 분리하자

Jenkins를 개발팀 혹은 비즈니스 도메인 단위의 프로젝트 분리를 시작하면서 Jenkins Main Server가 여러 대가 운영되기 시작 했습니다.

비용 효율적인 아키텍처를 위해 각 Jenkins Main Server 들은 Kubernetes Pod (StatefulSets) 으로 운영하고, 각 Jenkins Main Server에 붙어있는 Worker를 EC2 하나 혹은 이상을 사용하도록 하였습니다.

* Kubernetes: 컨테이너 기반의 어플리케이션을 배포, 스케일링, 운영 등 기능을 포함하는 오픈소스 시스템 (https://kubernetes.io)

각 개발팀 혹은 프로젝트 단위로 Jenkins 를 따로 쓰기 시작함

Jenkins Main Server를 분리하면서 얻은 주요 장점은 아래와 같습니다.

  • Jenkins 플러그인 간 의존성 낮아짐
  • Jenkins 장애의 영향도 낮아짐
  • 최소 AWS IAM 권한 할당 — 각 Jenkins Main Server 필요한 만큼 권한 할당
여러대의 Jenkins 에 서로 다른 문제가 발생 및 분산된 Jenkins 를 모니터링 해야 하는 문제

하지만 Jenkins Main Server / Worker가 많아지다 보니 여러 대의 Jenkins Main Server의 상태를 한눈에 볼 수 없다는 새로운 문제점이 발생합니다.

개선 2단계 — Jenkins Prometheus 플러그인을 활용해 Metric 얻기

Jenkins의 각종 메트릭(metric)을 얻는 방법은 다양합니다. Jenkins는 모든 페이지의 기능을 API로 제공하니 직접 조회가 가능하며, 플러그인을 설치하여 모니터링 기능을 활용 할 수도 있습니다.

무신사의 인프라는 EC2 및 Amazon EKS(Elastic Kubernetes Service) 환경에서 서비스를 운영하고 있으며, Kubernetes 환경의 모니터링을 위해 Prometheus를 사용 중이며, Node 및 Pod 등의 정보를 수집하고 있습니다.

Jenkins에는 모니터링을 쉽게 도와주는 다양한 플러그인이 있는데, 그중 Prometheus metric 플러그인은 저희 환경에서 바로 쓸 수 있는 좋은 선택지 중 하나였습니다. Jenkins prometheus metric을 설치하면 Prometheus Server가 바로 수집 가능한 데이터를 /prometheus path로 제공해줍니다.

플러그인 설정 화면에서 Prometheus 라는 이름으로 검색하면 Prometheus metrics 플러그인을 설치할 수 있습니다.

  • 메뉴: Jenkins > Manage Jenkins > Manage Plugin > Available 탭 > “Prometheus metrics” 검색
Jenkins 플러그인 설치 화면

* Jenkins Main Server의 버전이 낮은 경우 해당 플러그인이 호환되지 않습니다.이 경우 Jenkins Main Server를 업그레이드해야 합니다.

Prometheus 플러그인 설치 후, 아래와 같이 curl로 /prometheus 호출 시 Jenkins에 로그인을 하거나 토큰을 사용하여 인증하지 않더라도, Prometheus metrics 플러그인에서 제공하는 데이터는 바로 볼 수 있습니다.

# Prometheus 플러그인 이 제공하는 정보는 Jenkins의 인증을 요구하지 않는다. 
$ curl https://teamA.jenkins.musinsa.com/prometheus/
...
... (중간 생략)
...
# HELP jenkins_job_count_history Generated from Dropwizard metric import (metric=jenkins.job.count.history, type=jenkins.metrics.util.AutoSamplingHistogram)
# TYPE jenkins_job_count_history summary jenkins_job_count_history{quantile="0.5",} 44.0 jenkins_job_count_history{quantile="0.75",} 44.0 jenkins_job_count_history{quantile="0.95",} 44.0 jenkins_job_count_history{quantile="0.98",} 44.0 jenkins_job_count_history{quantile="0.99",} 44.0 jenkins_job_count_history{quantile="0.999",} 44.0 jenkins_job_count_history_count 29774.0
# HELP jenkins_queue_blocked_value Generated from Dropwizard metric import (metric=jenkins.queue.blocked.value, type=jenkins.metrics.impl.JenkinsMetricProviderImpl$5)
# TYPE jenkins_queue_blocked_value gauge
jenkins_queue_blocked_value 0.0
# HELP jenkins_runs_not_built_total Generated from Dropwizard metric import (metric=jenkins.runs.not_built, type=com.codahale.metrics.Meter)
# TYPE jenkins_runs_not_built_total counter jenkins_runs_not_built_total 0.0
...
... (이하 생략)
...

Prometheus의 scrape_configs에 Jenkins Main Server를 target으로 등록합니다.

serverFiles:# ... ( 중간 생략 ) ...prometheus.yml:# ... ( 중간 생략 ) ...scrape_configs:
- job_name: jenkins
metrics_path: /prometheus
# Jenkins Main Server 가 https 방식으로 되는 경우,
# 아래와 같이 scheme 을 https 로 지정합니다.
# 명시하지 않을 경우 scheme 의 기본값은 http 입니다.
scheme: https
static_configs:
- targets:
- teamA.jenkins.musinsa.com:443
- teamB.jenkins.musinsa.com:443
- teamC.jenkins.musinsa.com:443
- ProjectD.jenkins.musinsa.com:443
- ProjectE.jenkins.musinsa.com:443
# ... ( 이하 생략 ) ...

아래 예시는 Prometheus를 helm을 이용해 설치하고 운영하는 상황에 해당합니다. 다른 방식으로 Prometheus를 설치하더라도 prometheus.yml 파일의 설정 방식은 크게 달라지진 않습니다.

$ helm upgrade -f values.yaml prometheus -n prometheus

위와 같이 Prometheus 설정을 마치게 된다면, 아래 와 같이 Prometheus가 주기적으로 Jenkins Metric을 수집하게 됩니다.

Prometheus 를 활용해 여러대의 Jenkins Main Server 정보 수집

개선 3 단계 — Grafana에 Jenkins 대시보드 구성 및 알람 설정

Prometheus의 정보를 시각화하기 위해 가장 많이 사용되는 오픈소스 기반 도구는 Grafana가 대표적입니다. 저희 역시 Grafana를 통해 대시보드를 구성하고, 메트릭 값 기반으로 알람을 구현하였습니다.

특정 Jenkins Main Server 한 대의 Dashboard

* Sample 대시보드 구성 다운로드하기 : https://grafana.com/grafana/dashboards/14550

대시보드에서 주로 모니터링하는 데이터는 다음과 같습니다.

  • 해당 Jenkins Main Server에 등록된 총 Job의 수
  • 빌드 단계 대기 중인 Queue에 누적된 Job의 수
  • 최근 성공 혹은 실패 Job의 수
  • Jenkins Worker의 상태
  • Jenkins Main Server의 CPU / Memory 등 리소스 사용량

SRE 파트에서 Jenkins 상태를 빠르게 파악하고 싶은 메트릭은 아래와 같습니다.

  • Jenkins Worker가 Offline(연결실패)인 경우 알람 발생
  • Queue에 대기 중인 Job이 계속 많은 경우 알람 발생

Prometheus를 사용하는 경우 AlertManager를 활용해 알람 구현도 가능하지만, 이번 예시에서는 Grafana Dashboard에서 직접 알람을 구현하였습니다. 각 개발팀에서는 비교적 Grafana의 사용이 친숙하며, Grafana Dashboard의 UI 기반 알람 구현은 누구나 쉽게 따라 할 수 있습니다.

Alert Rule 추가 및 Notification Channel 설정

Graph view의 Alert Tab으로 이동하면, Alert Rule 설정이 가능합니다.

Grafana의 Alert은 Graph view일 경우에만 설정이 가능합니다.

위 예시는 Jenkins Node(Worker)가 Jenkins Main Server와 연결이 유실되는 경우에 알람을 보내도록 구현하였습니다. Grafana에서 1분마다 측정하여 2분 이상 Worker가 1대 이상이라도 Offline(연결실패)이 될 경우 Slack에 알람을 보내도록 합니다.

Slack 알람 수신

마치며

이렇게 한 대의 Jenkins를 모든 팀이 같이 사용하는 방식에서 각 팀 / 프로젝트 단위로 Jenkins로 분리 사용하는 방식으로 변경하고, Prometheus를 활용한 Jenkins 모니터링 및 알람을 구현한 과정까지 소개해 드렸습니다. 본 작업으로 개선된 점은 다음과 같습니다.

  • Jenkins Main Server 분리를 통해 장애의 영향도 낮춤
  • Prometheus 기반의 중앙 집중식 모니터링
  • 개발팀에서도 쉽게 Jenkins Server 모니터링 및 알람 추가 가능
  • Jenkins 사용량 통계 (누적 총 빌드 및 실패 횟수, 평균 빌드 시간, Job의 총 개수 등)

근래에는 Travis CI, Bamboo, Gitlab CI, Circle CI, Github Action 등 유료 혹은 Open Source 기반의 좋은 CI 도구가 많이 출시되고 있습니다. 하지만 여전히 Jenkins는 기능의 자유도가 높고, 플러그인 시스템을 잘 갖추었으며, 많은 기업에서 채택되고 있는 범용 CI 도구 중 하나입니다.

Jenkins를 분리하는 과정은 큰 노력이 동반되지만 분리된 이후의 Jenkins 모니터링을 구축하는 과정은 생각보다 매우 쉽습니다. 여러분도 저희와 비슷한 경험을 하시게 될 때 이 글이 도움이 되시기를 바랍니다.

마지막으로, 무신사 인프라보안팀 SRE 파트는 무신사 개발자들이 더 편하게 개발할 수 있는 환경을 만들기 위해 노력하고 있습니다. 이번 Jenkins 모니터링 및 Alarm 구현 작업도 그 일환이었는데요. 향후에도 효율적인 인프라 운영을 위해 자동화·모니터링 업무를 꾸준히 개선해 나갈 계획이니 많은 관심 부탁드립니다.

감사합니다.

MUSINSA tech

무신사 기술 블로그

MUSINSA tech

무신사 기술 블로그

MUSINSA tech

Written by

무신사 기술 블로그

MUSINSA tech

무신사 기술 블로그