멋진 신세계, 컨테이너: 프로덕션에 Docker사용하기

Premist (Minku Lee)
Making Shakr
Published in
16 min readNov 20, 2017

--

마이크로소프트웨어 390호, <오픈의 꿈>에 투고한 글을 옮겼습니다.

최근에는 인터넷에 게시되어 있는 기술 예제가 많아져서, 어떤 기술을 어떻게 사용하는지 궁금하다면 몇 번의 검색으로 기술을 쉽게 접하는 것이 가능해졌다. 하지만 튜토리얼을 따라 하다 보면 로컬 환경에서 간단한 애플리케이션을 구동할 때는 큰 문제가 없지만, 프로젝트에 참여하는 팀원이 늘어나고 애플리케이션의 규모가 커지면서 튜토리얼에서 다루지 않는 새로운 구조를 이용하여 시스템을 설계해야 할 순간이 찾아온다. 이전 글에서 설명한 예제에서는 아주 간단한 웹 애플리케이션을 Docker와 Google App Engine을 사용하여 애플리케이션을 배포하는 예제를 살펴보았는데, 지금부터는 조금 더 나아가서 보다 지속 가능한 개발을 위해 빌드 파이프라인을 구축하고, 여러 서비스로 구성된 애플리케이션을 배포할 수 있는 몇 가지 솔루션을 소개하려고 한다.

컨테이너 이미지 호스팅

공개를 하기 원하는 컨테이너 이미지를 제작한 경우에는 Docker HubQuay와 같은 레지스트리를 사용하여 무료로 공개 이미지를 저장할 수 있지만, 상용 애플리케이션의 경우에는 이미지를 공개할 수 없는 경우가 대부분이기에 어느 레지스트리를 사용할 것인지 고민해 보아야 한다.

  • Docker Hub (Docker Inc.): Docker의 기본 레지스트리로 지정이 되어 있기 때문에 URL 지정 없이도 바로 사용이 가능하며(docker pull hub.docker.com/shakr/helloworld 대신 간단하게 docker pull shakr/helloworld 를 입력해서 사용 가능하다) 추가적인 인증 설정 없이 docker login 만으로 사용 가능하다는 장점이 있다. 내부적으로는 AWS에 파일을 호스팅하는 것으로 알려져 있어, AWS 사용자에게는 좋은 선택지가 될 수 있다.
  • Quay (CoreOS Inc.): Container Linux를 개발하는 CoreOS, Inc. 에서 운영 중이다. 자체적으로 개발한 오픈 소스 취약점 분석 도구 Clair를 이용하여, 레지스트리에 업로드한 이미지의 취약점을 자동으로 분석하고 발견된 보안 결점의 심각도와 관련된 CVE 정보를 보여주는 기능이 강점이다.
  • AWS EC2 Container Registry (Amazon Web Services): AWS 인프라스트럭쳐 내부에 호스팅 되어 AWS 내 같은 리전에서 사용할 때에는 별도의 트래픽 요금이 발생하지 않고, AWS IAM을 이용해 권한을 관리할 수 있다. US East (N. Virgina)를 포함하여 11개의 AWS 리전을 지원하고, 최근 서울 리전 지원이 추가되었다.
  • Google Container Registry (Google Cloud): GCP 내부에 호스팅 되어 GCP 내 같은 리전에서 사용할 때는 별도의 트래픽 요금이 발생하지 않고, GCP의 Andromeda 네트워크 스택을 통해 VM에서 이미지를 효율적으로 받아올 수 있다. Google Compute Engine에서 구동되는 VM에서는 생성시 Container Registry의 권한을 허용하면 VM 내부에서는 별도의 인증 절차 없이 사용할 수 있고, GCP 인프라 외에서는 Service Account를 통해 레지스트리에 접근할 수 있다. 미국, 유럽, 아시아 세 곳에 이미지를 호스팅 하는 것을 지원한다.
  • 직접 호스팅: 호스팅 서비스를 사용하길 원하지 않는 사용자는 Docker Registry 이미지를 사용하여 직접 레지스트리 서버를 구축할 수 있다. 설정을 통해 이미지를 파일시스템에 저장할 지, S3나 Google Cloud Storage, OpenStack Swift와 같은 원격 객체 저장소에 저장할 지 설정할 수 있다.

지속적 통합(CI; Continuous Integration)과 지속적 배포(CD; Continuous Delivery)

현대적인 애플리케이션 개발 프로세스에 지속 통합과 지속 배포는 더이상 선택이 아닌 필수이다. 기능 업데이트에 대해 빌드와 유닛 테스트를 자동으로 실행하고, 이러한 결과를 간편하게 받아볼 수 있으므로써 개발 과정의 생산성이 크게 올라가고 여러 개발자 사이에 보다 효율적인 협업을 할 수 있다. 이러한 지속 통합 이후 빠르게 고객에게 새로운 변경점을 선보이기 위해 배포 과정을 자동화 하는 것도 일반적인 애플리케이션에서 갖춰야 할 과정이 되었다.

Docker를 이미 사용하고 있는 애플리케이션의 경우에는 이미 어느 환경에서나 재현 가능한 상태일 확률이 높은데, Dockerfile 명세에 애플리케이션 단위의 의존성 뿐만이 아니라 서버에서의 라이브러리 의존성 등이 모두 Dockerfile에 포함되어 있기 때문이다. 운영체제 버전이나 사용하고 있는 의존성 라이브러리의 버전만 고정하면, 지속 통합을 하기 위한 준비를 쉽게 끝마칠 수 있다.

Travis CI + App Engine

앞서 살펴본 예제의 Google App Engine을 이용한 웹 애플리케이션의 경우, 개발자의 로컬 환경에서 gcloud app deploy를 사용하여 수동으로 배포를 하는 방식이었는데, 이러한 애플리케이션에 지속적 배포를 적용하려면 어떤 과정을 거쳐야 할까? 소스 코드 저장소에서 협업할 때 GitHub Flow와 같은 풀 리퀘스트 기반 협업 방식을 사용하고 있다면, master 브랜치에 풀 리퀘스트가 머지되었을 때 Travis CI와 같은 지속 통합 서비스에서 유닛 테스트를 실행하고, 테스트를 모두 통과하면 Google App Engine으로 자동으로 배포하도록 설정할 수 있다. Google Cloud Platform에서 이와 같은 시나리오를 위한 예제 튜토리얼(영문)를 제공하고 있다.

클라우드 플랫폼별 지속 통합 및 배포 서비스

대부분의 기업에서는 이미 AWS나 Google Cloud 등 하나의 클라우드 서비스를 주력으로 사용하고 있는 경우가 많을텐데, 각 클라우드별로 대표적인 지속 통합 및 지속 배포 솔루션을 지원하고 있어 이를 사용하면 다른 별도의 서비스에 가입할 필요가 없고, 기존에 사용하는 클라우드 서비스와 연동이 용이하며, 네트워크 트래픽 비용을 절감할 수 있다.

  • AWS CodePipeline: Amazon Web Services의 CodePipeline를 사용하면 애플리케이션 빌드부터 유닛 테스트, 그리고 배포에 이르는 과정을 파이프라인으로 정의할 수 있다. GitHub이나 AWS CodeCommit 저장소에 새로운 변경점이 추가되면 이를 받아온 후, AWS CodeBuild나 Jenkins를 통해 빌드 아티팩트를 생성하고 테스트를 실행한다. 유닛 테스트를 통과하고 정상적으로 빌드에 성공했다면, 애플리케이션을 AWS CodeDeploy나 AWS CloudFormation 등을 사용하여 배포할 수 있다. 각 단계를 파이프라인으로 관리하여 현재 지속 통합 과정이 어떻게 이루어지고 있는지를 쉽게 인지할 수 있다는 것이 큰 장점이며, AWS Lambda와 연동하여 파이프라인의 각 단계에 대한 알림을 받아 사용자가 원하는 코드를 실행할 수 있다는 점도 파이프라인을 보다 유연하게 운영할 수 있게 해 준다.
  • Google Cloud Container Builder: Google Cloud Platform의 Container Builder를 사용하면 AWS CodePipeline과 마찬가지로 애플리케이션 빌드부터 유닛 테스트, 그리고 배포에 이르는 과정을 파이프라인으로 정의할 수 있다. Google Cloud Source Repository와 GitHub, Bitbucket에서 코드를 자동으로 받아오는 것을 지원하며, YAML 형태로 된 파이프라인 설정 파일에 빌드와 유닛 테스트, 그리고 배포에 필요한 단계를 정의할 수 있다. 사용자가 지정한 Docker 이미지 내에서 커스텀 스크립트을 실행할 수 있기에, 쉘 스크립트로 자동화가 가능한 모든 작업을 수행할 수 있다. Google Cloud Pub/Sub 으로 빌드의 진행 상황을 이벤트로 받을 수 있으며, Google Cloud KMS를 통해 외부로 유출되면 곤란한 민감한 설정 파일을 저장해 두었다가 배포 과정에서 사용할 수 있다.

컨테이너 애플리케이션 호스팅

이전 글에서 살펴본 애플리케이션 개발 및 배포 예제에서는 로드 밸런싱이나 로그 저장과 같은 작업을 모두 매니지드 서비스 형태로 제공해주는 App Engine을 사용하였는데, 모든 비즈니스에서 App Engine을 사용하기에는 한계가 있다. 마이크로서비스 형태의 아키텍처로 애플리케이션을 설계한 경우에는 서비스 디스커버리와 같은 기능이 필요할 것이고, GPU나 높은 IO 성능을 가진 스토리지 등 호스트 머신에 특정한 요구 사항이 있는 경우에도 App Engine을 주력으로 사용하기에는 어려움이 있을 것이다. 이럴 때 사용할 수 있는 애플리케이션 호스팅 소프트웨어 중 대표주자인 Kubernetes에 대해서 알아보자.

Kubernetes

Kubernetes는 컨테이너 기반 애플리케이션을 배포하고, 확장하고, 관리하는 작업을 자동화 해 주는 소프트웨어이다. Kubernetes는 오토 스케일링 그룹이나 로드 밸런서, 디스크 볼륨과 같은 애플리케이션의 각 구성 요소를 이해하기 쉽도록 추상화 해 주고, 사용자가 설정한 구성 요소를 관리해준다. Kubernetes의 초기 개발은 구글이 주도했는데, 구글이 리눅스의 cgroups의 모태가 된 Process Containers, 그리고 내부적으로 컨테이너를 운영할 때 사용한 Borg와 Omega를 개발하고 사용해 본 경험이 녹아들어가 높은 완성도의 소프트웨어가 되었다. 여기서 그치지 않고 Google이 Kubernetes를 Linux Foundation과 함께 Cloud Native Computing Foundation의 첫 번째 프로젝트로 이관하면서, 특정 기업에 종속되지 않고 여러 관련 기업이 함께 만들어나가는 오픈 소스 프로젝트가 되었다.

현재 Shakr에서는 Kubernetes를 이용하여 컨테이너 애플리케이션을 운영하고 있는데, Kubernetes를 선택한 이유는 다음과 같다.

  • Google Cloud Platform에서 쉽게 사용 가능: 오퍼레이션 팀이 본격적으로 갖춰져 있지 않은 스타트업의 입장에서는 서비스 형태로 인프라를 제공하는 곳이 있는지를 찾아보는 것이 가장 중요한 우선 순위였다. GCP에서 아주 적은 비용(5개의 노드까지는 관리 비용 무과금, us-central-1 리전 기준 한 달에 109.50 미국 달러) 으로 매니지드 형태로 사용이 가능하다는 것은 큰 이점이다.
  • 유연한 자원 활용: Shakr에는 앱 스토어나 구글 플레이처럼 디자이너가 비디오 디자인을 입점할 수 있는데, 앱 스토어에 앱 패키지를 업로드하듯이 디자이너도 Shakr에 디자인 패키지를 업로드한다. 이 때 Shakr 시스템에서는 이를 사용자에게 제공해 줄 수 있도록 패키지의 각 구성 요소를 모든 환경에서 사용 가능하게 변환하는데, 이 작업은 컴퓨팅 집약적이지만 일반 사용자의 트래픽처럼 빈번하게 일어나지는 않는다. 이전에는 이러한 작업을 위해 비교적 CPU와 메모리를 많이 할당한 가상 머신을 프로비저닝 해 두었는데, 대부분의 경우 리소스 사용량이 그리 많지 않아 효율적으로 자원을 활용하지 않는 상태였다. Kubernetes의 경우 단일 호스트에도 많은 애플리케이션을 프로비저닝 하면서도 유연하게 리소스 관리를 할 수 있어서, 유휴 자원을 최소화 할 수 있게 되었다.
  • Batteries Included: 애플리케이션의 동작 상태를 확인할 수 있는 웹 대시보드를 쉽게 설치하여 사용할 수 있고(kubernetes-ui), 패턴 매칭이 가능한 로드 밸런싱 기능을 제공하고(Ingress/Service), 구동 중인 컨테이너의 상태를 모니터링할 수 있는 방법(cAdvisor, Heapster)을 제공하는 등 인프라 운영에 필요한 여러 구성 요소를 쉽게 설정할 수 있다.
  • 적은 벤더 종속성: 현재는 GCP 위에서 Google Container Engine을 통해 Kubernetes를 사용하고 있지만, Kuberentes 프로젝트는 오픈 소스(Apache License 2.0)로 공개가 되어 있고, Google 직원뿐만 아닌 다양한 기업의 엔지니어가 활발하게 참여하여 공동 개발을 하고 있다. 또한 Google Cloud Platform에서만 사용할 수 있는 것이 아니라 CoreOS, Inc. 와 같은 기업에서 다른 클라우드 서비스나 물리적인 서버 인프라에 Kubernetes를 설치할 수 있는 솔루션을 제공해 주기 때문에 GCP에 종속되지 않고 추후 필요시 다른 벤더로 서비스를 이전하기도 용이하다.
Shakr에서는 영상 및 사진 등 미디어 처리 파이프라인과 분산 서비스 운영에 Kubernetes를 활용하고 있다
Datadog과 같은 모니터링 솔루션을 사용하면 Kubernetes 클러스터의 상황을 한 눈에 볼 수 있다

Kuberentes의 주요 리소스 소개

  • Pod: Pod은 가장 작은 컴퓨팅 유닛이다. 컨테이너 하나 혹은 여러개로 구성되어 있으며, 일반적인 클라우드 인프라스트럭쳐로 비교하면 VM과 같은 위치에 있는 리소스이다. Pod은 일반적으로 휘발성이기 때문에 Pod이 소멸될 때 내부의 데이터도 같이 삭제되지만, PersistentVolume과 같은 리소스를 사용하여 이를 외부 저장소에 보존할 수 있다.
  • Deployment: Deployment는 여러 개의 Pod을 관리하는 리소스이다. 클라우드 인프라스트럭쳐에서는 오토 스케일링이 켜져 있는 인스턴스 그룹과 비교해 볼 수 있는데, 정해진 replica 갯수에 따라 Pod을 자동으로 생성하고 삭제한다. Horizontal Pod Autoscaling을 이용하여 CPU나 다른 지표에 따라 Pod 갯수를 동적으로 조정하는 것 또한 가능하다.
  • Service: Service를 사용하여 같은 역할을 하는 Pod의 그룹을 어떻게 접근할 지 정의할 수 있다. 가령 마이크로서비스 아키텍쳐에서 서비스 A가 서비스 B로 접근하고자 할때 Deployment에 의해 생성된 Pod의 IP를 사용한다면, 해당 Pod이 어떠한 이유로 소멸되었을 때 같은 Deployment 내 다른 Pod은 정상적으로 구동되고 있음에도 불구하고 서비스 B에 접속을 하지 못하게 된다. Service를 이용하면 조건문으로 어떤 Pod을 선택할지 정의하고, 이 Pod의 어떤 포트가 접근 가능해야 하는지를 설정해 줄 수 있다. Kubernetes 내의 서비스 디스커버리(Service Discovery) 기능과 밀접하게 연결되어, 같은 네임스페이스의 다른 Pod에서 서비스 내 Pod의 IP를 알지 않고도 서비스 이름으로 접속이 가능하다. LoadBalancer 타입으로 Service를 생성하면 외부 트래픽을 수용할 수 있는 간단한 로드 밸런서로도 사용할 수 있다.
  • Ingress: 일반적으로 Service와 Pod은 클러스터 내부에서만 접근 가능한데, Ingress는 외부 트래픽을 이러한 Service로 전달해 주는 역할을 한다. HTTP 요청의 경로에 따라 다른 서비스가 요청을 처리하도록 설정하는 것도 가능한데, 예를 들면 /api/ 경로로 오는 요청은 백엔드 서비스가 처리하도록 설정하고 나머지는 프론트엔드 서비스가 처리하도록 설정할 수 있다. Kubernetes가 설치된 환경에 따라 다르지만, Google Container Engine에서 Ingress를 사용하면 Google Cloud HTTP(S) 로드 밸런서를 자동으로 생성한다.
  • Secret/ConfigMap: 애플리케이션 구동에 필요한 설정 파일이나 민감한 환경 변수를 저장할 때 사용할 수 있다. Pod을 설정할 때 환경 변수를 직접 추가하는 대신 Secret이나 ConfigMap에서 값을 참조하도록 설정할 수 있는데, 이렇게 설정할 경우 여러 서비스에서 공통으로 사용되는 환경 변수를 별도로 저장하지 않아도 되기 때문에 편리하다.
2017년 초 공개하여 현재까지 서비스 중인 StoriesAds.com의 시스템 다이어그램. 개발부터 프로덕션 공개까지 Kubernetes를 이용하여 배포를 진행하였다

Kubernetes 외 컨테이너 호스팅 솔루션

AWS ECS

  • Amazon Web Services에서 대표적으로 컨테이너를 관리할 수 있는 서비스이다. 자체적인 Task Definition 파일이나 Docker Compose 파일 형식으로 컨테이너 워크로드를 정의할 수 있고, Application Load Balancer나 CloudTrail과 같은 다른 AWS 서비스와 연동이 가능하다. ECS는 서비스 형태로 제공되기 때문에 소스가 공개되어 있지 않지만, ECS에서 사용할 수 있는 스케쥴러인 Blox(https://blox.github.io/)를 최근에 오픈소스로 공개하였다.

Mesosphere Marathon

  • Mesosphere, Inc. 가 제작한 Apache Mesos 위에서 컨테이너 기반 애플리케이션을 구동할 수 있는 오픈 소스 작업 스케쥴러이다. Mesosphere, Inc. (https://mesosphere.com) 에서 제작한 운영체제인 DC/OS (https://dcos.io)와 DC/OS가 기반을 두고 있는 Apache Mesos에서도 사용 가능하다. Mesos는 트위터나 애플에서도 사용하고 있는만큼 아느 정도의 안정성이 증명된 클러스터 관리 시스템인데, 이 위에서 컨테이너 기반 애플리케이션을 구동할 수 있다.
  • 라이센스: Apache License 2.0

HashiCorp Nomad

  • Terraform, Vagrant, Consul 등의 DevOps 소프트웨어로 유명한 HashiCorp이 만든 작업 스케쥴러이다. Docker를 사용하는 작업 뿐만 아니라 rkt, LXC와 같은 다른 컨테이너 런타임, 그리고 Qemu를 이용한 가상 머신이나 컨테이너 없이 프로세스를 실행하는 것과 같은 다양한 작업 형태를 지원한다. 서비스 디스커버리와 같은 핵심 기능에 Consul을 사용하며, 민감한 정보 저장에 Vault를 사용할 수 있다.
  • 라이센스: Mozilla Public License 2.0

마치며

몇 년 전 까지만 해도 Docker를 도입하는 것의 위험 부담이 어느 정도 남아 있었지만, 많은 클라우드 서비스에서 Docker 컨테이너 배포를 지원하기 시작하고 Kubernetes와 같이 활발하게 개발되는 오픈 소스 프로젝트가 생기면서 스타트업뿐만 아니라 대기업도 컨테이너를 높은 안정성을 요구하는 워크로드에도 도입하고 있다. Shakr에서 인력 대비로 운영하는 내부 컴포넌트가 많다 보니 겪었던 여러 가지 문제들도 Kubernetes로 대부분의 컴퓨팅 워크로드를 옮긴 이후로 대부분 해결할 수 있었다. 아무런 연구 없이 새로운 기술을 도입하는 것은 바람직하지 않지만, Docker와 같은 컨테이너 기반 인프라 구축에 익숙해진다면 인프라 운영에서 누구나 가지고 있는 큰 짐을 덜 수 있을 것이다.

--

--