SRE / DevOps 시작하기 (5) - Docker, 그리고 Sweet Spot
SRE / DevOps 시작하기 (5) - Docker, 그리고 Sweet Spot
SRE / DevOps 시작하기 1-4편을 작성하는 것은 견고한 시스템을 구축하기 위한 순서대로 정리를 하여 작성을 하였지만, 현실의 업무는 이러한 프로세스 순서대로 굴러가지 않는다.
내가 인스턴스를 설정하는 것은 간단한 명령어부터 시작한다.
sudo dnf update -y
sudo dnf install vim firewalld vsftpd httpd -y
이 이상의 명령어는 필요하지 않다.
한 발자국만 더 가보자.
sudo systemctl enable httpd
sudo systemctl start httpd
이제 서버의 공인 IP에 접속하면 화면에 보이는 서버를 볼 수 있을 것이다.
이게 끝이다. 이 두 블록만으로 'Hello World' 가 떠 있는 서버가 완성된다. 그리고 대부분의 스타트업이 PoC 단계에서 실제로 필요한 건 딱 이 정도다.
직전 글인 SRE / DevOps 시작하기 4 에서는 복잡한 nginx의 설정들과, jenkins 에 DoD 소켓을 연결하는 복잡한 과정을 설명했다. 하지만 막상 현실에서 처음 제품을 만드는 스타트업들에게 필요한 것은 복잡하고 고도화 된 CI/CD가 아니다.
대부분의 스타트업의 PoC/MVP는 vercel 배포로 끝난다. 그럼에도 불구하고 내가 instance 를 생성하고, 내 입맛대로 서버를 구축하는 것은 단순히 편하기 때문만은 아니다.
내가 항상 집착하는 개발 철학이 하나 있다.
"직관적인가?" 그리고 "객채지향적인가?"
나는 C/C++를 사랑한다. 추상화의 비용을 숨기지 않으면서도 객체지향이라는 개념을 가장 명시적으로 다룰 수 있는 언어이기 때문이다.
내가 처음 접한 리눅스 운영체제는 Kali Linux였지만 — NMAP, Proxychain, SQL Injection 툴 같은 것으로 처음 운영체제를 배웠기 때문에 — 내가 가장 사랑하는 OS는 RHEL이다. 그럼에도 불구하고 나는 종종 Ubuntu를 강요받는다 — 가령 MariaDB ORM 환경에서 의존성 충돌이 RHEL에서만 깨질 때 같은 상황 말이다.
그런 순간이 내가 가장 거슬려하는 종류의 시스템이다. 누구의 책임인지 모호한 채로, 단지 "특정 환경에서는 된다"는 경험적 사실만 남는 순간. 그리고 그 모호함은 시스템의 직관성과 객체지향성, 두 축 모두에서 실패한 결과다.
단일 조건과 다중 조건
직관적이고, 객체지향적이어야 하며, 추상화가 가능한 아키텍처라는 조건은 각각 하나씩 놓고 보면 만만해 보인다. 그러나 세 조건을 동시에 만족시키려는 순간 갑자기 어려워진다.
여기에 현실의 요구사항이 더 얹힌다. 빠르고, 병렬 지향적이고, 로우레벨 제어가 가능해야 하며, 동시에 운영 비용이 낮아야 하고, 신규 입사자가 일주일 안에 이해할 수 있어야 한다. 이 조건들을 다 붙이다 보면 어느샌가 IEEE 논문이 되어가는 프로젝트를 볼 수 있다.
제품은 논문이 아니다. 잘 굴러가면 장땡이다.
근데 어떻게 잘 굴러가야 할까?
일단 띄워
복잡한 설명, 구성, 아키텍처적 구상 다 필요 없다. 그저, 고객이 당신의 제품을 사용해 볼 수 있는 가장 간단한 형태이기만 하면 된다.
나는 Docker를 사용하는 단계부터 시작하는 것을 선호한다. 나에게 가장 편한 트레이드오프 구간이기 때문이다. 여기서 트레이드오프란 추상화 수준과 통제권 사이의 균형을 말한다. 추상화의 스펙트럼을 한쪽 끝부터 늘어놓으면 이렇게 된다.
한 단계 위인 Kubernetes로 가면 운영 복잡도가 비선형적으로 증가한다. 클러스터 스케일링, 네트워크 정책, RBAC, Helm chart 관리 — 각각이 별도의 학습과 운영 비용을 요구한다. 한 단계 아래인 systemd 직접 관리로 가면 환경 격리와 이식성을 잃는다. 호스트 OS의 패키지 충돌, 의존성 버전 관리, 그리고 앞서 언급한 RHEL/Ubuntu 같은 배포판 이슈가 그대로 노출된다.
사실 이번에 운영환경에서 제대로 K8S를 구축해서 사용해 보려고 UTM 다중인스턴스 위에서 kubectl 을 통해 클러스터를 구축하는 연습을 해 보았는데, 사용해 본 뒤의 나의 결론은 이랬다. Bare Metal Instance 3개 이상의, On-Premise, Cloud Compute를 동시에 사용하는 최소 도합 64 코어 이상의 환경에서만 유의미한, 호박마차.
Docker는 그 사이의 sweet spot이다. 컨테이너라는 객체지향적 추상화를 받되, 그 추상화의 비용을 숨기지 않는다. 각 컨테이너는 자기 환경, 자기 의존성, 자기 프로세스를 격리하면서도, 호스트와 컨테이너 사이의 경계가 명시적으로 노출되어 있다. 무언가 깨지면 그 책임이 어디에 있는지 비교적 명확하게 추적 가능하다.
추상화는 통제권의 양도다. 양도된 통제권만큼 운영 비용은 줄지만, 그만큼 깨졌을 때의 추적 가능성도 줄어든다. 반대로 통제권을 끝까지 쥐고 있으면 그만큼 운영 비용을 직접 떠안는다. 모든 시스템 설계는 이 두 축 사이의 어디에 자기 자리를 잡을지에 대한 의사결정이다.
PYENV, NVM, 직접 Node 설치 — 각자 편한 방식을 고르면 된다. 핵심은 자신이 통제 가능한 범위 안에서 가장 추상화된 레이어를 고르는 것이다. 통제권을 잃은 추상화는 결국 부채가 되고, 추상화 없는 통제권은 결국 운영 비용이 된다.
개인 프로젝트에서 난 NginX도 gcc compile 과정과 modsecurity 연결 과정이 귀찮다고 잘 쓰지 않는다. 그냥 apache, apache-modsecurity 플러그인 딸깍, vim으로 간단한 설정만 20줄 이내로 하는 게 좋다. 내가 통제할 의지가 있는 영역까지만 통제권을 쥐고, 나머지는 양도한다. 그 경계가 어디인지를 아는 것이 결국 SRE의 일이다.
그런데 기획의 의도가 복잡해지면, 이 편안한 과정에서 나와야 한다. 양도해도 되는 통제권의 범위가 달라지기 때문이다. 다음 글에서는 그 경계가 실제로 어떻게 이동하는지를, Keycloak이라는 구체적인 사례로 다뤄보겠다.
Comments ()