Step 8-10. 관찰 가능성 · 헬스체크 · 보안 스캔

인프라 파이프라인 핸즈온의 다섯 번째 파트. 로컬 PC에서 클라우드 비용 없이 직접 구축하는 시리즈.


Step 8. 로그와 모니터링 — 슬롯 ⑩ Observability (관찰 가능성)

슬롯은 학습용 비유 표현이다. 실무에서는 “파이프라인 단계” 또는 **“스테이지(stage)“**라고 부른다.

Observability란?

  • Observability — 영어 뜻: Observe(관찰하다) + ability(능력) = “관찰 가능성”. IT 의미: 시스템 내부에서 무슨 일이 일어나는지 바깥에서 볼 수 있게 하는 것.
  • 비유: 자동차 계기판. 엔진 내부를 직접 열어보지 않아도 속도·연료·온도를 알 수 있다.
  • 실무 용어: “모니터링”, “옵저버빌리티”

왜 필요한가? (WHY)

서비스가 느려졌는데 왜 느린지 모르면 고칠 수 없다. 로그와 메트릭이 있어야 원인을 찾을 수 있다. Observability가 없는 서버는 계기판 없는 자동차와 같다 — 고장이 나야 비로소 알게 된다.

3가지 핵심 요소

요소영어 뜻IT 의미비유
Logs (로그)기록, 일지시스템이 남기는 텍스트 기록항해일지 — 언제 무슨 일이 있었는지 기록
Metrics (메트릭)측정값, 지표숫자로 표현되는 시스템 상태 (CPU, 메모리, 응답 시간)체온계 — 숫자 하나로 상태를 파악
Traces (트레이스)흔적, 자취하나의 요청이 시스템을 통과하는 경로 추적택배 추적 — 내 택배가 지금 어디에 있는지

로깅 미들웨어 추가

app.js에 요청 로그를 남기는 미들웨어를 추가한다.

// ── 로깅 미들웨어: 모든 요청의 시간·메서드·URL·응답시간을 기록한다 ──
app.use((req, res, next) => {
  // 요청이 들어온 시각을 기록한다
  const start = Date.now();

  // 응답이 끝날 때 실행되는 이벤트를 등록한다
  res.on("finish", () => {
    // 응답까지 걸린 시간을 계산한다 (밀리초)
    const duration = Date.now() - start;

    // 타임스탬프, HTTP 메서드, URL, 응답시간을 한 줄로 출력한다
    console.log(
      `[${new Date().toISOString()}] ${req.method} ${req.url} — ${duration}ms`
    );
  });

  // 다음 미들웨어로 넘긴다 (이것이 없으면 요청이 멈춘다)
  next();
});

이 코드는 기존 라우트(/health, /info) 위에 추가해야 한다. 미들웨어는 위에서 아래로 실행되므로, 라우트보다 먼저 선언해야 모든 요청을 잡는다.

Docker 로그 확인

# 컨테이너를 백그라운드로 실행한다
docker compose up -d

# app 서비스의 로그를 실시간으로 따라간다 (-f = follow)
docker compose logs -f app

다른 터미널에서 요청을 보내고 로그를 관찰한다.

# health 엔드포인트를 호출한다
curl http://localhost:3000/health

# info 엔드포인트를 호출한다
curl http://localhost:3000/info

# 존재하지 않는 경로도 호출해본다 (404 로그 확인)
curl http://localhost:3000/nonexistent

로그 출력 예시:

[2026-04-16T09:00:01.123Z] GET /health — 2ms
[2026-04-16T09:00:03.456Z] GET /info — 1ms
[2026-04-16T09:00:05.789Z] GET /nonexistent — 0ms

이것이 Observability의 가장 기본적인 형태다. 누가, 언제, 어떤 요청을 보냈고, 얼마나 걸렸는지 볼 수 있다.


Step 9. Health Check & 자동 복구 — 슬롯 ⑤ Orchestration 심화

슬롯은 학습용 비유 표현이다. 실무에서는 “파이프라인 단계” 또는 **“스테이지(stage)“**라고 부른다.

Health Check란?

  • Health Check — 영어 뜻: Health(건강) + Check(확인) = “건강 검진”. IT 의미: 컨테이너가 정상 동작하는지 주기적으로 확인하는 것.
  • 비유: 병원 정기 검진. 겉으로 멀쩡해 보여도 내부에 문제가 있을 수 있다.
  • 실무 용어: “헬스체크”, Kubernetes에서는 “Liveness Probe”

왜 필요한가? (WHY)

컨테이너가 실행 중이지만 앱이 응답하지 않는 상태(좀비)가 있을 수 있다. docker ps에는 “Up”으로 표시되지만, 실제로 요청을 보내면 먹통이다. Health Check가 이런 상태를 자동으로 감지한다.

Dockerfile에 HEALTHCHECK 추가

# 30초마다 health 엔드포인트를 호출해 정상 여부를 확인한다
# --interval=30s  : 검사 주기 (30초마다)
# --timeout=3s    : 응답을 3초까지 기다린다 (초과하면 실패)
# --start-period=10s : 컨테이너 시작 후 10초는 검사를 건너뛴다 (앱 부팅 시간)
# --retries=3     : 3번 연속 실패해야 "unhealthy"로 판정한다
# curl -f         : HTTP 에러 코드(4xx, 5xx)를 실패로 처리한다 (-f = fail silently)
# || exit 1       : curl이 실패하면 종료 코드 1을 반환한다 (= 비정상)
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:3001/health || exit 1

docker-compose.yml 수정

services:
  app:
    build: .
    ports:
      - "3000:3000"
    # 컨테이너가 종료되면 자동으로 다시 시작한다
    # unless-stopped = 수동으로 stop한 경우를 제외하고 항상 재시작
    restart: unless-stopped

자동 복구 테스트

# 컨테이너를 백그라운드로 실행한다
docker compose up -d

# 컨테이너 상태를 확인한다 — STATUS 열에 "(healthy)" 표시를 찾는다
docker ps
# 출력 예: ... Up 30 seconds (healthy) ...
# 일부러 컨테이너를 강제 종료한다 (kill = 즉시 중단)
docker kill my-infra-lab-app-1

# 몇 초 기다린 후 다시 확인한다
docker ps
# restart: unless-stopped 덕분에 자동으로 다시 살아난다
# 출력 예: ... Up 3 seconds (health: starting) ...

이것이 오케스트레이션의 핵심이다: 죽으면 알아서 다시 살린다. Kubernetes에서는 이것을 더 정교하게 한다 (Liveness Probe + Readiness Probe).


Step 10. 보안 스캔 — 슬롯 ⑫ Security Scanning (취약점 검사)

슬롯은 학습용 비유 표현이다. 실무에서는 “파이프라인 단계” 또는 **“스테이지(stage)“**라고 부른다.

Security Scanning이란?

  • Security Scanning — 영어 뜻: Security(보안) + Scanning(훑어보기) = “보안 검사”. IT 의미: 코드나 이미지에 알려진 취약점이 있는지 자동으로 검사하는 것.
  • Vulnerability — 영어 뜻: Vulnerable(취약한) + ity(상태) = “취약점”. IT 의미: 해커가 이용할 수 있는 보안 구멍.
  • 비유: 건물 안전 점검. 입주 전에 균열이나 누수가 없는지 검사하는 것.
  • 실무 용어: “보안 스캔”, “취약점 스캐닝”, “SCA(Software Composition Analysis)“

왜 필요한가? (WHY)

npm 패키지나 Docker 이미지의 OS 패키지에 이미 알려진 취약점이 있을 수 있다. 내가 짠 코드가 아니라 남이 만든 라이브러리에 구멍이 있는 것이다. 배포 전에 잡아야 한다. 배포 후에 발견하면 이미 해커가 이용했을 수 있다.

GitHub Actions에 Trivy 스캔 추가

.github/workflows/ci.yml의 Docker Build 단계 뒤에 추가한다.

      # ── 보안 스캔: Docker 이미지의 취약점을 검사한다 ──
      - name: Scan image for vulnerabilities
        # Trivy = 오픈소스 취약점 스캐너 (Aqua Security가 만듦)
        uses: aquasecurity/trivy-action@master
        with:
          # 검사할 Docker 이미지 이름
          image-ref: my-infra-lab:latest
          # HIGH와 CRITICAL 등급만 검사한다 (LOW, MEDIUM은 무시)
          severity: "HIGH,CRITICAL"
          # 취약점이 발견되면 CI를 실패시킨다 (exit code 1)
          exit-code: "1"
          # 결과를 테이블 형태로 출력한다
          format: "table"

로컬에서 직접 스캔하기

GitHub Actions 없이도 로컬에서 바로 검사할 수 있다.

# Trivy로 Docker 이미지를 스캔한다
# --rm : 스캔이 끝나면 Trivy 컨테이너를 삭제한다
# -v : Docker 소켓을 공유해서 로컬 이미지에 접근한다
docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image my-infra-lab

결과 읽는 법

┌──────────────┬────────────────┬──────────┬───────────────────┐
│   Library    │ Vulnerability  │ Severity │  Installed Ver    │
├──────────────┼────────────────┼──────────┼───────────────────┤
│ express      │ CVE-2024-XXXX  │ HIGH     │ 4.18.2            │
│ node         │ CVE-2024-YYYY  │ CRITICAL │ 18.17.0           │
└──────────────┴────────────────┴──────────┴───────────────────┘
의미
Library취약점이 발견된 패키지 이름
VulnerabilityCVE 번호 — 전 세계 공통 취약점 식별 번호
Severity심각도 — CRITICAL > HIGH > MEDIUM > LOW
Installed Ver현재 설치된 버전

CVE = Common Vulnerabilities and Exposures. “공통 취약점 목록”이라는 국제 표준 데이터베이스다.


체크리스트

  • app.js에 로깅 미들웨어를 추가했다
  • docker compose logs -f app으로 로그가 출력되는 것을 확인했다
  • Dockerfile에 HEALTHCHECK를 추가했다
  • docker-compose.ymlrestart: unless-stopped를 추가했다
  • docker ps에서 (healthy) 상태를 확인했다
  • 컨테이너를 docker kill로 죽인 후 자동 복구되는 것을 확인했다
  • Trivy 스캔을 CI 또는 로컬에서 실행하고 결과를 읽었다.

다음 단계06-full-pipeline.md

Comments

  • // 댓글을 불러오는 중...
main ⚠ 0 ✕ 0 Ln 1, Col 1 Spaces: 2 UTF-8 LF Markdown