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 | 취약점이 발견된 패키지 이름 |
| Vulnerability | CVE 번호 — 전 세계 공통 취약점 식별 번호 |
| Severity | 심각도 — CRITICAL > HIGH > MEDIUM > LOW |
| Installed Ver | 현재 설치된 버전 |
CVE = Common Vulnerabilities and Exposures. “공통 취약점 목록”이라는 국제 표준 데이터베이스다.
체크리스트
-
app.js에 로깅 미들웨어를 추가했다 -
docker compose logs -f app으로 로그가 출력되는 것을 확인했다 - Dockerfile에
HEALTHCHECK를 추가했다 -
docker-compose.yml에restart: unless-stopped를 추가했다 -
docker ps에서(healthy)상태를 확인했다 - 컨테이너를
docker kill로 죽인 후 자동 복구되는 것을 확인했다 - Trivy 스캔을 CI 또는 로컬에서 실행하고 결과를 읽었다.
다음 단계 → 06-full-pipeline.md
Comments
// admin login