죽지 않는 컴퓨터는 없다

요즘 제가 맡은 회사일의 핵심은 인프라 스트럭처 입니다. 회사에서 만든 제품을 안정적으로 구동할 수 있는 환경을 만드는 일이죠. 혼자 만들고 사용하는 환경과는 다르게 확장성과 안정성이 필요합니다.

기존의 환경은 Docker Compose를 이용한 배포였습니다. AWS EC2 Instance를 필요한 만큼 만들고 각 인스턴스들에서 docker compose를 실행하여 여러 서비스들을 동작시킵니다. 만약 소프트웨어 오류로 컨테이너가 정지한다면 재시작 해줄 수 있고, 가상화된 네트워크와 볼륨을 이용하여 서로 다른 서비스끼리 연결하거나 같은 볼륨을 공유할 수 있습니다. Docker Compose에 내장된 로드 밸런싱 기능이 있을지 모르겠지만 간단하게는 Nginx 등 다른 소프트웨어를 통해 로드밸런싱을 수동으로 설정할 수도 있을겁니다. 하지만 인스턴스 자체의 오류로부터는 무방비 상태입니다.

AWS 같은 클라우드 환경을 사용하더라도 종종 인스턴스 장애에 대한 메일 안내를 받습니다. 인스턴스의 하드웨어 결함을 발견했으니 교체 예정이다, 이에 대비하여 필요한 작업을 수행하라 이런 내용입니다. 저희가 집 혹은 회사에서 사용하는 단일 데스크탑 혹은 랩탑의 경우 이 장치의 수명이 다할 때까지 하드웨어 결함을 만나기 어려울 수 있습니다. 하지만 데이터 센터와 같은 규모에선 상황이 다릅니다. 수천 수만개의 스토리지를 사용하므로 매일 어떤 스토리지는 고장나게 마련입니다. 글의 제목처럼 “죽지 않는 컴퓨터”는 없습니다.

저는 일하면서 Kubernetes를 익힐 기회가 있었는데 유용하게 활용하고 있습니다. 앞서 말씀드렸던 안정성과 확장성을 고려하여 컨테이너 관리를 할 수 있고 이들 작업은 코드의 형태로 관리할 수 있습니다. 지난 창업의 경험에선 혼자 제품을 위한 인프라를 구축하고 관리해야 했습니다. 이때 Kubernetes의 사용이 빛을 발했습니다. 중간에 클라우드 서비스 제공자를 바꿔야하는 경우도 있었는데 아주 적은 수정만 가해 쉽게 옮길 수 있었습니다.

회사 제품의 인프라를 생각하면서 제품을 구성하는 기능들은 최소 2개 이상의 복제본을 갖도록 했습니다. 치밀하게 생각한다면 이들의 복제본은 서로 다른 인스턴스(작업 노드)로 스케줄 되어야 하며, 더 치밀하게 생각한다면 서로 다른 가용 영역 Availability Zone 을 사용할 수 있고 (한국 내 Region에서도 다수의 데이터센터에서 인스턴스를 운영하며 이들이 서로 다른 가용 영역을 이룹니다. 곧 서울에 있는 데이터센터가 망가지더라도 판교에 있는 데이터센터는 살아있을 수 있으니 서로 다른 가용 영역을 이용하여 다중화를 시켜두면 더 안정적인 인프라를 갖출 수 있습니다) 나아가 Multi Region, 더 나아가 Multi Cloud Service Provider 전략을 취할 수도 있습니다.

복제본을 만들기 위해선 가급적 서비스가 Stateless 성격을 가져야 합니다. 그래야 쉽게 복제본을 추가하거나 제거할 수 있기 때문입니다. 다행히 저희 제품의 웹서버들은 이런 성격을 갖추고 있었으며 Job Queue-Worker 구조로 동작하는 나머지 부분 역시 쉽게 Worker(복제본)를 추가하거나 제거할 수 있었습니다. Job Queue를 사용하는 경우엔 신경써야하는 설정 부분이 있습니다. Job Queue에서 작업을 제거하는 시점이 단순히 Worker가 작업을 가져가는 순간이라면 문제가 발생합니다. 작업을 가져간 후 Worker에서 오류가 발생해도 해당 작업을 다시 시도할 수 없기 때문이죠. 따라서 이런게 실패하는 작업들을 모아서 다시 처리하기 위한 Dead Letter Queue를 관리하거나, Job Queue에서 작업을 제거하는 시점을 Worker의 작업 처리 성공 시점으로 설정해야 합니다.

인프라 스트럭처는 단순히 구축에서 끝나는 작업이 아닙니다. 실제 제품의 기반을 Docker Compose에서 Kubernetes로 바꾼 후 예기치 못한 장애들을 자주 만났습니다. 가능한 한 제품의 가시성을 높이고 장애가 발생할 수 있는 지점을 파악할 수 있게 노출시켜야 합니다. 저는 Kubernetes에서 널리 사용하는 Prometheus/Grafana 도구를 이용하여 대시보드를 만들어 사용하고 있습니다. 제품이 제대로 동작하는지 확인하기 위해 1시간에 한번씩 Kubernetes CronJob으로 간단한Integration Test를 합니다. 또한 추가로 내부 서비스들 간의 연결 확인을 위해 1분에 한번씩 CronJob을 수행합니다. Grafana 대시보드에선 마지막으로 성공한 CronJob 시각과 현재 시각의 차이를 구해 임계치를 넘어가는 경우 알람을 주도록 설정했습니다.

이번 작업을 위해 새롭게 많은 것들을 익혀야 했습니다. Terraform을 이용한 AWS 인프라 자동 구축, Kapenter를 이용한 Node Autoscaling, Skupper를 이용한 Kubernetes 클러스터 간 연결 등. 인프라 작업이 어려운 이유는 문제가 발생할 구석이 많고 모든 부분을 명료하게 파악하기엔 범위가 크다는 점입니다. 소스코드에서 발생한 오류의 경우, 범위를 한정하고 나면 그 안의 논리를 확인하는 것으로 문제를 해결할 수 있습니다. 인프라 구축에서 발생하는 문제는 우선 다양한 컴포넌트들의 상호작용을 파악하고 문제가 발생할 만한 가능성이 높은 부분을 추려내는 것에서 시작합니다. 하지만 때론 이런 분석이 무색하게 전혀 엉뚱한 이유로 문제가 발생하기도 합니다. 오류 메시지 혹은 상황이 때론 작업자를 기만하기도 합니다. 하지만 이렇게 신경 쓸 것이 많고 트러블슈팅도 까다로운 인프라 작업이지만 그것이 또 묘미 아니겠습니까. 소프트웨어 엔지니어의 근성을 기르기 위한 훈련에도 이만큼 적당한 작업도 없을 겁니다.