SRE / DevOps 시작하기 (2) — 운영 환경 세팅

SRE / DevOps 시작하기 (2) — 운영 환경 세팅
Photo by Sam Roy / Unsplash

SRE / DevOps 시작하기 (2) — 운영 환경 세팅

Container Runtime

Kubernetes Docs에 따르면 "Dockershim은 쿠버네티스 릴리스 1.24부터 쿠버네티스 프로젝트에서 제거되었다." K8S 클러스터를 커스텀 구축하려면 이제 containerd와 같은 표준 컨테이너 런타임에 대한 이해가 선행되어야 한다.

Dockershim은 K8S가 Docker를 지원하기 위해 만든 임시 어댑터였다. Docker가 CRI 표준을 직접 구현하지 않으면서 K8S가 중간 변환 레이어를 유지해야 했고, containerd 같은 CRI 표준 런타임이 성숙해지면서 자연스럽게 제거됐다.

container와 vm에 대한 차이 또한 상당히 재미있는 부분이지만, 이런 내용은 다른 주제의 글에서 다루어 보도록 하겠다.

K8S, 지금은 아니다

UTM으로 가상화한 환경 위에 K8S를 구성하는 연습을 해보았다. 운영 환경에 K8S 클러스터를 커스텀 구축하려다가, 구축 일보직전에서 잠시 멈추고 프로젝트 빌딩을 이어가기로 했다.

K8S는 실제 대규모 운영 환경을 가정하고 만들어진 세팅이라, 서비스가 검증되지 않은 지금 단계에서 잉여 컨테이너를 생성하여 클러스터를 구축하는 것은 오버엔지니어링에 가깝다고 판단했다. 클러스터 관리 오버헤드까지 감수하는 것은 득보다 실이 크다.

K8S는 지금 단계에서 오버엔지니어링이지만, 그렇다고 기존 방식으로 돌아갈 수도 없었다. pm2 + FTP 또는 GitHub Actions 기반의 딸깍 배포는 이 프로젝트의 요구 조건에서 치명적인 서비스 중단으로 이어질 수 있다. Next.js, NestJS처럼 배포와 운영이 편한 스택을 선호하지만, 안정성 요구 수준이 다르다.

대신 Dockerfile 컨테이너를 유지하면서, 나중에 service-pod.yml만 추가하면 바로 K8S로 전환 가능한 환경을 만드는 것을 현실적인 목표로 잡았다. 컨테이너 기반 배포 구조를 유지하는 한, K8S 전환은 인프라 전체를 갈아엎지 않아도 된다.

운영 환경 세팅

개발 이전에 잡고 들어간 소프트웨어 스택은 다음과 같다.

  • Nginx + mod_security — 리버스 프록시 및 WAF
  • Jenkins — CI/CD 오케스트레이션
  • Vault — 시크릿 관리
  • OCIR — 멀티클라우드 Container Registry
  • Prometheus / Grafana / Loki — Observability 스택

Jenkinsfile과 Vault 시크릿 주입으로 CI/CD를 재현 가능하게 자동화했다. 어느 환경에서도 동일한 파이프라인이 재현되는 구조다.

물리적인 인프라 구성은 아래와 같다.

  • OCI ARM Instance × 2 — 저비용 ARM 기반 워커노드
  • AWS Lightsail Instance × 1 — x86/64 기반 메인 서버
  • OCI ADB (Oracle DB 19c) × 1 — 플랫폼 메인 서비스 DB
  • NEON DB (PostgreSQL 17) × 1 — Keycloak 전용 DB
  • Cloudflare R2 — 스토리지
  • AWS SES — 이메일 인증 및 알림

AWS SES는 Keycloak SSO 인증 플로우에서 이메일 인증이 필요한 구조라 초기부터 포함했다. OCI ADB와 NEON DB를 분리한 것은 장애 격리를 위해서다. 인증 시스템과 서비스 데이터를 물리적으로 분리함으로써, Keycloak 장애가 서비스 DB에 영향을 주지 않는 구조를 만들었다.

왜 이렇게 구성했나

각 선택에는 이유가 있다.

비용

OCI ARM Instance는 동급 AWS 대비 압도적으로 저렴하고, Always Free 티어도 넉넉하다. AWS Lightsail은 EC2 On-demand 대비 고정 요금제로 예측 가능한 비용 구조를 가진다. Cloudflare R2는 egress 비용이 없다. S3는 데이터를 꺼낼 때마다 과금되는데, 콘텐츠 플랫폼에서 트래픽이 늘어나면 egress 비용만으로 서비스가 죽을 수 있다. NEON DB는 PostgreSQL Serverless라 사용한 만큼만 과금된다. Keycloak DB는 평소 트래픽이 크지 않으니 Serverless가 맞는 선택이었다.

Vendor Lock-in 제거

AWS 단일 구성이었으면 AWS가 가격을 올리거나 정책을 바꿀 때 꼼짝 못 한다. OCI + AWS + Cloudflare로 분산하면 어느 한 벤더가 정책을 바꿔도 이탈 가능한 구조가 된다. 이것이 1편에서 말한 "어떤 벤더가 정책을 바꾸더라도, 인프라가 흔들리지 않아야 한다"는 원칙의 실제 구현이다.

역할 분리와 장애 격리

인증 시스템(Keycloak + NEON DB)과 서비스 데이터(서비스 + OracleDB)를 물리적으로 분리한 것은 장애 격리를 위해서다. Keycloak이 죽어도 이미 발급된 JWT로 서비스는 계속 돌아갈 수 있고, OracleDB 장애가 인증에 영향을 주지 않는다. 인증과 서비스를 같은 DB에 묶는 순간, 둘 중 하나의 장애가 전체 서비스 중단으로 이어진다.

확장 경로

지금은 Docker Compose 기반이지만 service-pod.yml 추가만으로 K8S 전환이 가능한 구조다. 서비스가 검증되면 인프라 전체를 갈아엎지 않고 클러스터로 올라갈 수 있다. 처음부터 컨테이너 기반으로 잡아둔 이유가 여기 있다.

소프트웨어 스택 선정 이유

Nginx + mod_security

원래 Apache2를 선호한다. 설정이 직관적이고 mod_security 연동도 자연스럽다. 문제는 C10K, 즉 동시 1만 연결 처리다. Apache는 연결마다 프로세스 또는 스레드를 생성하는 구조라 트래픽이 몰리면 메모리가 폭발한다. 대규모 서비스를 목표로 하는 프로젝트인 만큼 안정성을 우선했고, 이벤트 기반 비동기 구조인 Nginx에 mod_security를 커스텀으로 올렸다.

Prometheus / Grafana / Loki

ELK 스택은 너무 크고 무겁고 비싸다. Elasticsearch 하나만 올려도 메모리를 상당히 잡아먹는다. 필요한 건 메트릭 수집, 시각화, 로그 집계인데, Prometheus + Grafana + Loki 조합으로 같은 기능을 훨씬 가볍고 저렴하게 구성할 수 있다. 전부 오픈소스 기반이라 비용도 없다.

Vault

AWS KMS는 비용, 성능, 효율 모두 마음에 들지 않는다. 시크릿 하나 조회할 때마다 과금되는 구조는 운영 비용이 예측 불가능하게 늘어난다. Vault를 직접 운영하면 무료로 동일한 기능을 커스텀하게 쓸 수 있다. Vendor Lock-in 없이 시크릿을 관리하는 가장 현실적인 선택이다.

OCIR

Docker Hub는 무료 플랜에 제약이 많고, AWS ECR은 유료다. Harbor로 자체 구축하면 관리 오버헤드가 너무 크다. OCIR은 OCI 생태계 안에서 무료로 쓸 수 있는 Container Registry다. 별도 구축 없이 딸깍 배포가 가능하면서 비용도 없다.

Jenkins

Vault 연동, 멀티 에이전트 구성, 커스텀 파이프라인 자유도를 동시에 만족하는 무료 오픈소스 옵션으로는 Jenkins가 가장 현실적인 선택이었다. GitHub Actions는 유연성이 떨어지고, GitLab CI/CD는 GitLab 자체를 운영해야 하는 오버 스택이 된다. Jenkins는 무료에 커스텀 자유도가 높고, Vault 연동과 멀티 에이전트 구성이 그대로 가능하다.

다음 글에서는 이 스택들을 실제로 연결하고 운영하면서 맞닥뜨린 문제들을 다뤄볼 예정이다.

Subscribe to Minseok Doo

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe