[Project] On-Prem Multi-Node Observability System & Analyzer AI Agent - 2️⃣ Observability 구축 과정

developowl·2026년 7월 10일
post-thumbnail

지난번 포스트에 이어서 이번에는 Observability 구축 과정을 정리해보겠습니다.
(언제 다 정리하지,,)

구축 과정

0. 초기 상태

📌 주요 포인트

옵저버빌리티 시스템을 구축하기 전, 초기 상태입니다.

  • 관측 대상: OpenStack 클러스터를 구성하는 노드들
  • 고려사항 1: On-Prem Node를 원격 관측
  • 고려사항 2: 옵저버빌리티 인프라 구축 상태 'Zero'

위와 같은 고려사항들이 있었습니다.

1. Provisioning - IaC (Ansible)

📌 주요 포인트

  • IaC (Infrastructure as Code)
    • 코드 기반 인프라 구축
    • Ansible 사용
  • SSOT (Single Source Of Truth)
    • GitHub 소스 코드를 단일 진실 공급원으로
  • 구독 대상
    • On-Prem Node & Public Cloud Instance

인프라 구축이 필요했고, IaC + GitOps 방식으로 인프라 구축을 하고자 했습니다.
제가 관리를 해야하는 인스턴스는 [수집기 / 메트릭 서버 / 로그 서버 / 대시보드 서버] 등 서로 다른 역할을 하는 다수의 서버였습니다.

해당 인스턴스들의 초기 구축 / 원격 구독 / 공통 작업 등 반복되는 작업들이 예상되었기에 Ansible을 사용하여 프로비저닝 자동화를 하고자 했습니다.

Ansible 이란?

수십, 수백 대의 서버를 명령어 몇 줄로 자동 제어할 수 있게 도와주는 오픈소스 인프라 자동화 툴
(우리 프로젝트에서는 10대 미만의 서버를 다루지만, 그래도 반복되는 작업은 번거로우니까 IaC를 도입해봅시다..!)

Ansible은 저도 이번 프로젝트에서 처음 사용해보게 되었는데, 주요 특징으로는

  1. Agentless
    • 다른 도구들은 제어 대상 서버마다 에이전트 프로그램을 깔아야 하고, 이들이 자원을 소모하고, 관리가 번거롭다
    • 앤서블은 오직 SSH 통신만 사용함.
  2. Idempotence (멱등성)
    • 연산을 여러 번 적용하더라도 결과가 달라지지 않음
    • 똑같은 앤서블 코드를 여러 번 돌려도 서버는 항상 내가 선언한 정상 상태를 유지함
  3. YAML 문법

이러한 것들이 있습니다.

Ansible 운영의 핵심 요소로는

  1. Control Node & Target Node
    • Control: 앤서블 프로그램이 깔려 있고, 명령을 내리는 주체
    • Target: 명령을 받아서 수행하는 대상 서버
  2. Inventory
    • 앤서블이 제어할 서버들의 주소록
  3. Playbook
    • 앤서블이 서버에서 실행해야 할 작업 명세서

위와 같은 것들이 있습니다.

작업 흐름

  1. Control Node 에서 Playbook과 Inventory 를 준비
  2. Ansible 동작 명령 실행 (예시: ansible-playbook -i inventory.ini setup.yml)
  3. Ansible이 Inventory에 적힌 구독 서버들로 SSH 접속을 시도
  4. 접속이 성공하며 Playbook에 적힌 명령들을 파이프라인처럼 순서대로 실행하여 서버 세팅

기본적인 내용은 이정도이고, 파악했으니 만들어봅시다!

1. Control Node 등록

프로젝트 규모나 전체 맥락을 보았을 때, 컨트롤 노드를 또 준비하는 것보다 현재 개발 중인 로컬을 컨트롤 노드로 등록하는 것이 관리하기 편하다고 생각을 했습니다.
-> 그래서 제 맥북을 컨트롤 노드로 두고 작업을 이어나갔습니다.

2. Inventory 생성하기

앞서 설명했듯이 Ansible Inventory는 "앤서블이 제어할 서버들의 주소록" 입니다.
사전에 생성만 하고 VPN으로 묶어두었던 여러 인스턴스들을 등록해주었습니다.

# inventory.ini

[argocd_server]
argocd ansible_host=100.91.4.60

[monitoring_servers]
metric ansible_host=100.87.44.83
log    ansible_host=100.68.2.91
dashboard   ansible_host=100.73.160.40

[onprem_pcs]
onprem-pc1   ansible_host=100.65.153.106 ansible_user=highcloud
onprem-pc2   ansible_host=100.123.230.70 ansible_user=highcloud

[all:vars]
ansible_user=ubuntu

ansible_ssh_private_key_file=~/.ssh/observability.pem

(⚠️ 프로젝트가 종료되었고, 모든 인스턴스가 내려갔기 때문에 굳이 IP를 가리지 않았습니다. 실제 운영 환경 및 GitHub에는 절대로 IP를 그대로 올리지 마시길...)

3. Playbook 작성 - (k3s 일괄 설치 명세)

Ansible Playbook은 "앤서블이 서버에서 실행해야 할 작업 명세서" 라고 했습니다. GitOps 방식을 채택했고, (추후) ArgoCD를 도입할 것이기에 ArgoCD의 명령을 받으면서 서버 리소스는 너무 잡아 먹지 않는 경량 쿠버네티스를 설치하는 작업이 필요했습니다.

# setup-k3s.yaml

---
- name: Bootstrap
  hosts: all 
  become: true
  gather_facts: yes

  tasks:
    # -----------------------------------------------------------------
    # Task 1: k3s 바이너리 설치
    # -----------------------------------------------------------------
    - name: 1. k3s 초경량 핵심 엔진 설치 (불필요한 컴포넌트 비활성화 및 TLS 설정)
      shell: |
        curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik --disable metrics-server --disable local-storage --tls-san {{ ansible_host }} --node-name {{ inventory_hostname }}" sh -
      args:
        creates: /usr/local/bin/k3s

    # -----------------------------------------------------------------
    # Task 2: 일반 유저 환경 세팅
    # -----------------------------------------------------------------
    - name: 2. 현재 접속 유저의 홈 디렉토리에 .kube 폴더 생성
      file:
        path: "/home/{{ ansible_user }}/.kube"
        state: directory
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0755'

    - name: 3. 쿠버네티스 인증 설정 파일(k3s.yaml)을 유저 폴더로 복사
      copy:
        src: /etc/rancher/k3s/k3s.yaml
        dest: "/home/{{ ansible_user }}/.kube/config"
        remote_src: true
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0600'
    # -----------------------------------------------------------------
    # ★ 추가된 구간: 외부 원격 접속 및 타임아웃 방지를 위한 주소 자동 치환(05.19 18:38 추가)
    # -----------------------------------------------------------------
    - name: "3-1. [Fix] 복사된 config 파일 내의 접속 주소를 각 서버의 실제 IP로 자동 치환"
      replace:
        path: "/home/{{ ansible_user }}/.kube/config"
        regexp: 'https://127.0.0.1:6443'
        replace: "https://{{ ansible_host }}:6443"
    # -----------------------------------------------------------------
    # Task 3: k3s 서비스 정상 기동 확인
    # -----------------------------------------------------------------
    - name: 4. k3s 서비스가 정상적으로 활성화 및 동작 중인지 상태 검증
      systemd:
        name: k3s
        state: started
        enabled: true

Ansible은 구독한 서버에 SSH 접속이 되면, 위 Playbook에 작성한 명세를 순차적으로 실행합니다.

4. group_vars 설정

Ansible이 원격 구독 서버에 SSH 접속을 하기 위해서는 당연히 SSH 비밀번호 혹은 키가 필요합니다. 이에 대한 등록/설정이 group_vars 입니다.

# infrastructure/all.yaml

---
# .pem 키 사용 시
ansible_ssh_private_key_file: "/Users/shinji/.ssh/observability.pem"

---
# 비밀번호 사용 시
ansible_ssh_private_key_file: ""
ansible_ssh_pass: "1025"

5. Ansible 명령 실행하기

앞선 작업들로 Control Node 설정 -> inventory 생성 -> playbook 작성 -> group_vars 설정 을 완료하였습니다. 이제 Control Node 에서 실행 명령을 입력하면 됩니다.

ansible-playbook -i inventory.ini setup-k3s.yaml

-> k3s 설치를 위한 Playbook이 모든 구독 서버에 대해 동작을 완료했습니다.


2. GitOps - 중앙 관리형 ArgoCD

📌 주요 포인트

  • IaC Pipeline 활용
    • k3s 공통 설치
    • 각 서버의 역할에 맞추어 구축
  • ArgoCD 도입
    • 개별 ArgoCD 구축 -> 중앙 관리형 ArgoCD 구축
    • App of Apps Pattern

앞서 구축한 IaC 파이프라인을 활용하여 각 서버의 역할에 맞게

  • (공통) k3s
  • OpenTelemetry Collector
  • VictoriaMetrics
  • Loki
  • Grafana
  • ArgoCD

를 설치 및 구축했습니다.

초기 구축 시 ArgoCD를 각각의 인스턴스에 구축했는데, 관리포인트가 많다는 문제점을 느껴 싹 다 밀고 중앙 관리형 ArgoCD 서버 한 대만 두기로 하였습니다.
실제로 이러한 상황에서 중앙 관리형 ArgoCD 도입이 적절한지 조사를 해보았는데, Best Practice 중 "App of Apps Pattern" 이 있더군요. 말 그대로 Application들의 Application을 두는 패턴입니다.

(중앙 관리형 ArgoCD가 관리하고 있는 Application들 입니다.)


3. Basic Flow

앞선 과정들을 통해 기본적인 옵저버빌리티 흐름을 만들었습니다.

  • 온프레미스 노드에 DaemonSet 형식으로 배포한 OTel CollectorMetric/Log 시그널을 수집하여 적절한 서버에 보냄. (Metric Server / Log Server)
  • 이를 전달 받은 Metric/Log Server는 각각의 보관 정책에 알맞게 Signal을 저장 및 관리
    • EBS(Elastic Block Storage): Metric(hot) 저장
    • S3(Simple Storage Service): Metric(cold), Log 저장
  • Grafana 는 주기적으로 두 서버에 query를 보내어 대시보드 형성
  • 일정 시간 이상 '이상 신호' 감지 시 Alertmanager를 통해 관리자용 Slack으로 Alert 발송

"수집 -> 저장 및 쿼리 -> 관측 -> 알람" 까지 동작하는 기본적이면서도 필수적인 흐름입니다.

📌 Signal Store

Signal 저장 부분을 확대해서 가져왔습니다.

🤔 왜 EBS/S3 와 같은 외장 스토리지에 저장을 했나요?

우선 각 인스턴스의 로컬 디스크에 시그널을 저장하지 않고, 외장 스토리지를 도입한 이유는

  • EC2 종료/장애 시 즉시 소실
  • 인스턴스 디스크 용량 한계
  • 인스턴스 비용 추가 청구
  • 백업/복구 직접 구현 필요
    등의 이유 때문입니다.

외장 스토리지를 연결하면 위 문제점들에 대해

  • 인스턴스와 별개로 시그널 보존
  • 대용량 확장 가능
  • 저장 비용 저렴 (EBS, S3 >> EC2)
  • Snapshot 기반 백업, AZ/region 단위 내구성, 라이프사이클 자동화
    등의 장점을 확보할 수 있었습니다.

🤔 그렇다면 왜 EBS, S3 두 개를 썼나요?

위 사진에서 볼 수 있듯이 외장 스토리지로 사용하는 스토리지는 EBS와 S3 두 개 입니다.

이러한 구조는 초기 설계 단계에서 구상하진 못했습니다. VictoriaMetrics 에 대해 공부해보니, VictoriaMetrics는 로컬 블록 스토리지를 기본 스토리지 드라이버로 사용한다고 합니다. 추가적으로 VictoriaMetrics는 S3 Native 모드 자체가 없습니다..!
그렇기에 운영을 위한 스토리지로 Metric은 EBS에 저장하는 방식을 택했고(택할 수 밖에 없었고) 장기 보관은 보관 비용 절약 및 보관 용량을 고려하여 S3에 저장하는 방식을 택했습니다.

hot - 운영
cold - 장기 보관

(보관 정책은 별다른 기준이 없었기에 적절히 지정했습니다.)

Metric(V.M)Log(Loki)
1차 저장EBSS3
쿼리 가능 기간15d7d
백업/안전망 위치S3(별도 버킷)S3(같은 버킷, lifecycle)
백업/안전망 활성화매일 자동compactor 실패 한정
장기 보관 - ArchiveGlacier IR(90~365d)Glacier Flexible(14~365d)

4. Alert

Alert?

임계치를 넘는 이상 신호 발견 시 관리자용 Slack으로 알람을 보내는 기능입니다.

동작 방식

각각의 Metric / Log Server에는 임계치를 설정하고 감지하는 ruler 라는 컴포넌트가 있습니다. VictoriaMetrics의 경우 vmalert이고, Loki의 경우 그냥 Ruler 입니다..ㅋㅋ

각각의 ruler는 이상 신호의 기준이 되는 임계치 정보를 갖고 있습니다. (<- 임계치는 관리자가 커스텀 가능합니다.)
ruler는 Metric/Log Server에 일정 간격으로 query를 보내고, 일정 시간 이상 임계치가 넘는 이상 신호를 감지하면 Alertmanager에게 Firing Signal을 보내게 됩니다.
최종적으로 Alertmanager는 설정해둔 관리자용 Slack 채널로 알람을 보내게 됩니다.

(vmalert, Ruler에 설정한 임계치 정보입니다.)

실제로 관측한 이상 신호가 있었나요?

-> 넵!
가장 크리티컬(?) 했던 알람으로는 NodeDown과 HighFileSystem이 있었습니다.


1️⃣ NodeDown

  • 발생 일시: 06월 06일
  • 증상: PC VPN 접속 불가
  • 원인: 부트캠프 입주 건물 시설 점검으로 인한 정전...
  • 해결: 출근 후 PC 재시작 및 VPN 정상화

2️⃣ HighFileSystem

  • 발생 일시: 06월 18일
  • 분석: /var/lib/docker(24 GiB), /var/log/journal(4.1GiB)
  • 해결: Docker 미사용 리소스 정리, Docker 로그 드라이버 제한

(이왜진ㅋㅋㅋㅋㅋㅋㅋ 옵저버빌리티 일 잘하네ㅋㅋㅋㅋㅋㅋㅋ)


정리

짜잔

여러 과정을 거쳐 Observability 구축이 완료되었습니다. 이전 백엔드 프로젝트 때는 Prometheus + Grafana 구축만 해두고 "옵저버 간단하네" 라고 했던 것 같은데 (늘 겸손하기..),,,
제대로 구축해보려고 하니 복잡한 내용도 많았고 재밌는 내용이 정말 많았던 것 같습니다.

여기까지 구축하니 "관측도 잘 되고, 알람도 잘 가고 좋네" 라는 생각과 함께 "장애 원인 분석과 해결을 해주는 녀석이 있으면 좋겠는데?" 라는 생각이 들었습니다. (트러블슈팅은 항상 어렵기 때문에,,)

다음은 이러한 생각에서 시작된 "분석용 AI Agent 구축 과정"을 정리해보겠습니다.
감사합니다.

profile
Don’t get mad at the computer.

0개의 댓글