컨테이너 및 오케스트레이터 활용

이 콘텐츠는 Azure용 클라우드 네이티브 .NET 애플리케이션 설계 eBook 에서 발췌한 것으로, .NET 문서에서 제공되거나 오프라인 상태에서도 읽을 수 있는 PDF(무료 다운로드 가능)로 제공됩니다.

Cloud Native .NET apps for Azure eBook cover thumbnail.

컨테이너 및 오케스트레이터는 모놀리식 배포 방법에 일반적인 문제를 해결하도록 설계되었습니다.

모놀리식 배포의 과제

일반적으로 대부분의 애플리케이션은 단일 단위로 배포되었습니다. 이러한 애플리케이션을 모놀리식이라고 합니다. 애플리케이션이 여러 모듈이나 어셈블리로 구성된 경우에도 단일 단위로 애플리케이션을 배포하는 이러한 일반적인 방법을 그림 3-1과 같이 모놀리식 아키텍처라고 합니다.

Monolithic architecture.

그림 3-1. 모놀리식 아키텍처.

단순성의 이점이 있지만 모놀리식 아키텍처는 다음과 같은 많은 문제에 직면해 있습니다.

배포

또한 애플리케이션을 다시 시작해야 하므로 배포하는 동안 제로 가동 중지 시간 기술이 적용되지 않으면 가용성에 일시적으로 영향을 미칠 수 있습니다.

확장

모놀리식 애플리케이션은 단일 컴퓨터 인스턴스에서 완전히 호스트되며 종종 고성능 하드웨어가 필요합니다. 모놀리식의 일부에 크기 조정이 필요한 경우 전체 애플리케이션의 다른 복사본을 다른 컴퓨터에 배포해야 합니다. 모놀리식을 사용하면 애플리케이션 구성 요소를 개별적으로 크기 조정할 수 없습니다. 모두 조정하거나 아니면 아무 것도 조정하지 않아야 합니다. 크기 조정이 필요하지 않은 구성 요소의 크기를 조정하면 비효율적이고 비용이 많이 드는 리소스 사용량이 발생합니다.

환경

모놀리식 애플리케이션은 일반적으로 운영 체제, 런타임 및 라이브러리 종속성이 미리 설치된 호스팅 환경에 배포됩니다. 이 환경은 애플리케이션을 개발하거나 테스트한 환경과 일치하지 않을 수 있습니다. 애플리케이션 환경 전반의 불일치는 모놀리식 배포의 일반적인 문제 원인입니다.

결합

모놀리식 애플리케이션은 기능 구성 요소 간에 높은 결합을 경험할 수 있습니다. 엄격한 경계가 없으면 시스템 변경으로 인해 의도하지 않은 비용이 많이 드는 부작용이 발생하는 경우가 많습니다. 새로운 기능/픽스는 까다로워지고, 시간이 많이 걸리며, 구현하는 데 비용이 많이 듭니다. 업데이트에는 광범위한 테스트가 필요합니다. 또한 결합을 사용하면 구성 요소를 리팩터링하거나 대체 구현에서 교환하기가 어렵습니다. 엄격하게 문제를 분리하여 구성한 경우에도 모놀리식 코드 기반이 끝없는 "특별한 상황"이 이어져 모놀리식 코드 기반이 악화됨에 따라 아키텍처 침식이 시작됩니다.

플랫폼 잠금

모놀리식 애플리케이션은 단일 기술 스택으로 구성됩니다. 이러한 약정은 균일성을 제공하는 동시에 혁신의 장벽이 될 수 있습니다. 최신 기술이 더 나은 선택이 될 수 있는 경우에도 애플리케이션의 현재 스택을 사용하여 새로운 기능과 구성 요소가 빌드됩니다. 장기적인 위험은 기술 스택이 오래되고 사용되지 않는 것입니다. 전체 애플리케이션을 보다 최신의 새로운 플랫폼으로 재설계하는 것은 비용이 많이 들고 위험합니다.

컨테이너 및 오케스트레이터의 이점은 무엇인가요?

1장에서 컨테이너를 소개했습니다. CNCF(클라우드 네이티브 컴퓨팅 파운데이션)가 클라우드 네이티브 여정을 시작하는 기업에 대한 지침인 클라우드 네이티브 추적 맵의 첫 번째 단계로 컨테이너화의 순위를 지정하는 방법을 강조했습니다. 이 섹션에서는 컨테이너의 이점에 대해 설명합니다.

Docker는 가장 널리 사용되는 컨테이너 관리 플랫폼입니다. Linux 또는 Windows의 컨테이너에서 작동합니다. 컨테이너는 모든 시스템에서 동일한 방식으로 실행되는 개별적이지만 재현 가능한 애플리케이션 환경을 제공합니다. 이러한 측면은 클라우드 네이티브 서비스 개발 및 호스팅에 적합합니다. 컨테이너는 서로 완전히 격리됩니다. 동일한 호스트 하드웨어에 있는 두 개의 컨테이너는 충돌을 일으키지 않고 다른 버전의 소프트웨어를 가질 수 있습니다.

컨테이너는 프로젝트 아티팩트가 되고 소스 제어에 체크인되는 간단한 텍스트 기반 파일로 정의됩니다. 전체 서버 및 가상 머신은 수동으로 업데이트해야 하지만 컨테이너는 쉽게 버전을 관리할 수 있습니다. 컨테이너에서 실행되도록 빌드된 앱은 빌드 파이프라인의 일부로 자동화된 도구를 사용하여 개발, 테스트 및 배포할 수 있습니다.

컨테이너는 변경할 수 없습니다. 컨테이너를 정의한 후에는 정확히 동일한 방식으로 컨테이너를 다시 만들고 실행할 수 있습니다. 이러한 불변성은 구성 요소 기반 디자인에 적합합니다. 애플리케이션의 일부가 다른 부분과 다르게 발전하는 경우 가장 자주 변경되는 부분만 배포할 수 있는데 전체 앱을 다시 배포하는 이유는 무엇인가요? 앱의 다양한 기능과 복합적인 문제는 별도의 단위로 나눌 수 있습니다. 그림 3-2는 모놀리식 앱이 특정 기능을 위임하여 컨테이너와 마이크로 서비스를 활용하는 방법을 보여 줍니다. 앱 자체의 나머지 기능도 컨테이너화되었습니다.

Breaking up a monolithic app to use microservices in the back end.

그림 3-2. 마이크로 서비스를 수용하기 위해 모놀리식 앱을 분해합니다.

각 클라우드 네이티브 서비스는 별도의 컨테이너에 빌드되고 배포됩니다. 각각은 필요에 따라 업데이트할 수 있습니다. 개별 서비스는 각 서비스에 적합한 리소스를 사용하여 노드에서 호스트할 수 있습니다. 각 서비스가 실행되는 환경은 변경할 수 없으며 개발, 테스트 및 프로덕션 환경에서 공유되고 버전 관리가 쉬워집니다. 애플리케이션의 서로 다른 영역 간의 결합은 모놀리식 내의 컴파일 시간 종속성이 아니라 서비스 간의 호출 또는 메시지로 명시적으로 발생합니다. 또한 앱의 나머지 부분을 변경하지 않고도 지정된 기능을 가장 잘 제공하는 기술을 선택할 수 있습니다.

컨테이너화된 서비스에는 자동화된 관리가 필요합니다. 독립적으로 배포된 대규모 컨테이너 집합을 수동으로 관리하는 것은 불가능합니다. 다음 작업을 예로 들 수 있습니다.

  • 컨테이너 인스턴스는 여러 컴퓨터의 클러스터에서 어떻게 프로비저닝되나요?
  • 배포된 컨테이너는 어떻게 서로 검색하고 통신하나요?
  • 컨테이너는 어떻게 주문형으로 규모를 스케일 인 또는 스케일 아웃할 수 있나요?
  • 각 컨테이너의 상태는 어떻게 모니터링하나요?
  • 하드웨어 및 소프트웨어 오류로부터 컨테이너를 어떻게 보호하나요?
  • 가동 중지 시간이 없는 라이브 애플리케이션의 컨테이너는 어떻게 업그레이드하나요?

컨테이너 오케스트레이터는 이러한 문제와 기타 문제를 해결하고 자동화합니다.

클라우드 네이티브 에코시스템에서 Kubernetes는 사실상 컨테이너 오케스트레이터가 되었습니다. CNCF(Cloud Native Computing Foundation)에서 관리하는 오픈 소스 플랫폼입니다. Kubernetes는 컴퓨터 클러스터에서 컨테이너화된 워크로드의 배포, 크기 조정 및 운영 문제를 자동화합니다. 그러나 Kubernetes를 설치하고 관리하는 것은 매우 복잡합니다.

훨씬 더 나은 방법은 Kubernetes를 클라우드 공급업체의 관리되는 서비스로 활용하는 것입니다. Azure 클라우드에는 AKS(Azure Kubernetes Service)라는 완전 관리형 Kubernetes 플랫폼이 있습니다. AKS는 Kubernetes 관리의 복잡성과 운영 오버헤드를 추상화합니다. Kubernetes를 클라우드 서비스로 사용합니다. Microsoft에서 관리 및 지원을 담당합니다. 또한 AKS는 다른 Azure 서비스 및 개발 도구와 긴밀하게 통합됩니다.

AKS는 클러스터 기반 기술입니다. 페더레이션된 가상 머신 또는 노드의 풀이 Azure 클라우드에 배포됩니다. 이들은 함께 고가용성 환경 또는 클러스터를 형성합니다. 클러스터는 클라우드 네이티브 애플리케이션에 원활한 단일 엔터티로 표시됩니다. 내부적으로 AKS는 부하를 균등하게 분산하는 미리 정의된 전략에 따라 이러한 노드에 컨테이너화된 서비스를 배포합니다.

크기 조정의 이점은 무엇인가요?

컨테이너를 기반으로 하는 서비스는 Kubernetes와 같은 오케스트레이션 도구에서 제공하는 크기 조정 이점을 활용할 수 있습니다. 설계상 컨테이너는 자신에 대해서만 알고 있습니다. 함께 작업해야 하는 컨테이너가 여러 대 있으면 더 높은 수준에서 구성해야 합니다. 많은 수의 컨테이너와 네트워크 구성과 같은 공유 종속성을 구성하는 경우 오케스트레이션 도구를 통해 시간을 절감할 수 있습니다. Kubernetes는 컨테이너 그룹에 대한 추상화 계층을 만들고 Pod로 구성합니다. Pod는 노드라고 하는 작업자 컴퓨터에서 실행됩니다. 이 구조를 클러스터라고 합니다. 그림 3-3은 Kubernetes 클러스터의 다양한 구성 요소를 보여 줍니다.

Kubernetes cluster components.그림 3-3. Kubernetes 클러스터 구성 요소.

컨테이너화된 워크로드 크기 조정은 컨테이너 오케스트레이터의 핵심 기능입니다. AKS는 컨테이너 인스턴스와 컴퓨팅 노드라는 두 가지 차원에서 자동 크기 조정을 지원합니다. 이를 통해 AKS는 급증하는 수요에 빠르고 효율적으로 대응하고 추가 리소스를 추가할 수 있습니다. 이 장의 뒷부분에서 AKS의 크기 조정에 대해 설명합니다.

선언적 vs 명령적

Kubernetes는 선언적 구성과 명령적 구성을 모두 지원합니다. 명령적 접근 방식에는 Kubernetes에 각 단계를 수행할 작업을 알려주는 다양한 명령을 실행하는 작업이 포함됩니다. 이 이미지를 실행합니다. 이 Pod를 삭제합니다. 이 포트를 노출합니다. 선언적 접근 방식을 사용하면 매니페스트라는 구성 파일을 만들어 수행할 작업 대신 원하는 작업을 설명합니다. Kubernetes는 매니페스트를 읽고 원하는 종료 상태를 실제 종료 상태로 변환합니다.

명령적 명령은 학습 및 대화형 실험에 적합합니다. 그러나 신뢰할 수 있고 반복 가능한 배포를 제공하는 코드 접근 방식으로 인프라를 수용하기 위해 Kubernetes 매니페스트 파일을 선언적으로 만들려고 합니다. 매니페스트 파일은 프로젝트 아티팩트가 되며 Kubernetes 배포를 자동화하기 위해 CI/CD 파이프라인에서 사용됩니다.

명령적 명령을 사용하여 클러스터를 이미 구성한 경우 kubectl get svc SERVICENAME -o yaml > service.yaml을 사용하여 선언적 매니페스트를 내보낼 수 있습니다. 이 명령은 아래와 유사한 매니페스트를 생성합니다.

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2019-09-13T13:58:47Z"
  labels:
    component: apiserver
    provider: kubernetes
  name: kubernetes
  namespace: default
  resourceVersion: "153"
  selfLink: /api/v1/namespaces/default/services/kubernetes
  uid: 9b1fac62-d62e-11e9-8968-00155d38010d
spec:
  clusterIP: 10.96.0.1
  ports:
  - name: https
    port: 443
    protocol: TCP
    targetPort: 6443
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

선언적 구성을 사용하는 경우 구성 파일이 있는 폴더에 대해 kubectl diff -f FOLDERNAME을 사용하여 커밋하기 전에 수행할 변경 내용을 미리 볼 수 있습니다. 변경 내용을 적용하려면 kubectl apply -f FOLDERNAME을 실행합니다. 폴더 계층 구조를 재귀적으로 처리하려면 -R을 추가합니다.

다른 Kubernetes 기능과 함께 선언적 구성을 사용할 수도 있습니다. 그중 하나는 배포입니다. 선언적 배포는 릴리스, 업데이트 및 크기 조정을 관리하는 데 도움이 됩니다. 새로운 변경 내용을 배포하거나, 부하를 스케일 아웃하거나, 이전 수정 버전으로 롤백하는 방법에 대해 Kubernetes 배포 컨트롤러에 지시합니다. 클러스터가 불안정한 경우 선언적 배포는 자동으로 클러스터를 원하는 상태로 되돌립니다. 예를 들어, 노드가 충돌해야 하는 경우 배포 메커니즘은 원하는 상태를 달성하기 위해 대체를 재배포합니다.

선언적 구성을 사용하면 인프라를 애플리케이션 코드와 함께 체크인 및 버전 관리할 수 있는 코드로 나타낼 수 있습니다. 빌드 및 배포 파이프라인을 사용하여 지속적인 배포를 위한 개선된 변경 제어 및 더 나은 지원을 제공합니다.

컨테이너 및 오케스트레이터에 적합한 시나리오는 무엇인가요?

다음 시나리오는 컨테이너 및 오케스트레이터를 사용하는 데 적합합니다.

높은 가동 시간 및 확장성이 필요한 애플리케이션

가동 시간 및 확장성 요구 사항이 높은 개별 애플리케이션은 마이크로 서비스, 컨테이너 및 오케스트레이터를 사용하는 클라우드 네이티브 아키텍처에 적합한 후보입니다. 컨테이너에서 개발하고 버전이 지정된 환경에서 테스트하고 가동 중지 시간 없이 프로덕션에 배포할 수 있습니다. Kubernetes 클러스터를 사용하면 이러한 앱이 필요에 따라 크기 조정되고 노드 오류로부터 자동으로 복구할 수도 있습니다.

많은 수의 애플리케이션

많은 수의 애플리케이션을 배포하고 유지 관리하는 조직은 컨테이너와 오케스트레이터의 이점을 누릴 수 있습니다. 컨테이너화된 환경과 Kubernetes 클러스터를 설정하기 위한 선행 활동은 기본적으로 고정 비용입니다. 개별 애플리케이션을 배포, 유지 관리 및 업데이트하는 데 드는 비용은 애플리케이션 수에 따라 다릅니다. 몇 가지 애플리케이션 외에도 사용자 지정 애플리케이션을 수동으로 유지 관리하는 복잡성은 컨테이너 및 오케스트레이터를 사용하여 솔루션을 구현하는 비용을 초과합니다.

컨테이너 및 오케스트레이터를 사용하지 않아야 하는 경우는 언제인가요?

12단계 앱 원칙에 따라 애플리케이션을 빌드할 수 없는 경우 컨테이너 및 오케스트레이터를 사용하지 않는 것이 좋습니다. 이러한 경우 VM 기반 호스팅 플랫폼 또는 일부 하이브리드 시스템을 고려합니다. 이를 통해 항상 특정 기능을 별도의 컨테이너 또는 서버리스 기능으로 분리할 수 있습니다.

개발 리소스

이 섹션에서는 다음 애플리케이션에 컨테이너 및 오케스트레이터 사용을 시작하는 데 도움이 될 수 있는 개발 리소스의 짧은 목록을 보여 줍니다. 클라우드 네이티브 마이크로 서비스 아키텍처 앱을 설계하는 방법에 대한 지침을 찾고 있는 경우 이 책의 부록인 .NET 마이크로 서비스: 컨테이너화된 .NET 애플리케이션을 위한 아키텍처를 참조하세요.

로컬 Kubernetes 개발

Kubernetes 배포는 프로덕션 환경에서 큰 가치를 제공하지만 개발 머신에서 로컬로 실행할 수도 있습니다. 개별 마이크로 서비스에서 독립적으로 작업할 수 있지만 프로덕션에 배포할 때 실행되는 것처럼 전체 시스템을 로컬에서 실행해야 하는 경우가 있습니다. Minikube 및 Docker Desktop과 같은 여러 가지 도구가 도움이 될 수 있습니다. Visual Studio도 Docker 개발을 위한 도구를 제공합니다.

Minikube

Minikube란? Minikube 프로젝트는 "Minikube는 macOS, Linux 및 Windows에서 로컬 Kubernetes 클러스터를 구현합니다."라고 말합니다. 주요 목표는 "로컬 Kubernetes 애플리케이션 개발을 위한 최고의 도구와 그에 맞는 모든 Kubernetes 기능을 지원하는 것"입니다. Minikube 설치는 Docker와 별개이지만 Minikube는 Docker Desktop이 지원하는 것과 다른 하이퍼바이저를 지원합니다. 현재 Minikube에서 지원하는 Kubernetes 기능은 다음과 같습니다.

  • DNS
  • NodePorts
  • ConfigMaps 및 비밀
  • 대시보드
  • 컨테이너 런타임: Docker, rkt, CRI-O 및 컨테이너화됨
  • CNI(컨테이너 네트워크 인터페이스) 사용
  • 수신

Minikube를 설치한 후 이미지를 다운로드하고 로컬 Kubernetes 클러스터를 시작하는 minikube start 명령을 실행하여 Minikube를 빠르게 사용할 수 있습니다. 클러스터가 시작되면 표준 Kubernetes kubectl 명령을 사용하여 클러스터와 상호 작용합니다.

Docker Desktop

Windows의 Docker Desktop에서 직접 Kubernetes 작업을 할 수도 있습니다. Windows 컨테이너를 사용하는 경우 유일한 옵션이며 비 Windows 컨테이너에도 적합합니다. 그림 3-4는 Docker Desktop을 실행할 때 로컬 Kubernetes 지원을 사용하도록 설정하는 방법을 보여 줍니다.

Configuring Kubernetes in Docker Desktop

그림 3-4. Docker Desktop에서 Kubernetes 구성

Docker Desktop은 컨테이너화된 앱을 로컬로 구성하고 실행하는 데 가장 널리 사용되는 도구입니다. Docker Desktop을 사용하는 경우 프로덕션에 배포할 동일한 Docker 컨테이너 이미지 집합에 대해 로컬로 개발할 수 있습니다. Docker Desktop은 컨테이너화된 앱을 로컬로 "빌드, 테스트 및 배송"하도록 설계되었습니다. Linux 및 Windows 컨테이너를 모두 지원합니다. Azure Container Registry 또는 Docker Hub와 같은 이미지 레지스트리에 이미지를 푸시하면 AKS에서 해당 이미지를 가져와 프로덕션에 배포할 수 있습니다.

Visual Studio Docker 도구

Visual Studio는 웹 기반 애플리케이션을 위한 Docker 개발을 지원합니다. 새 ASP.NET Core 애플리케이션을 만들 때 그림 3-5와 같이 Docker 지원으로 구성할 수 있는 옵션이 있습니다.

Visual Studio Enable Docker Support

그림 3-5. Visual Studio Docker 지원 사용

이 옵션을 선택하면 루트에 Dockerfile이 있는 프로젝트가 만들어집니다. 이 파일을 사용하여 Docker 컨테이너에서 앱을 빌드하고 호스트할 수 있습니다. Dockerfile의 예는 그림 3-6에 나와 있습니다.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["eShopWeb/eShopWeb.csproj", "eShopWeb/"]
RUN dotnet restore "eShopWeb/eShopWeb.csproj"
COPY . .
WORKDIR "/src/eShopWeb"
RUN dotnet build "eShopWeb.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "eShopWeb.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "eShopWeb.dll"]

그림 3-6. Visual Studio에서 생성된 Dockerfile

지원이 추가되면 Visual Studio의 Docker 컨테이너에서 애플리케이션을 실행할 수 있습니다. 그림 3-7은 Docker 지원이 추가되어 만들어진 새 ASP.NET Core 프로젝트에서 사용할 수 있는 다양한 실행 옵션을 보여 줍니다.

Visual Studio Docker Run Options

그림 3-7. Visual Studio Docker 실행 옵션

또한 언제든지 기존 ASP.NET Core 애플리케이션에 Docker 지원을 추가할 수 있습니다. 그림 3-8과 같이 Visual Studio 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가>Docker 지원을 선택합니다.

Visual Studio Add Docker Support

그림 3-8. Visual Studio에 Docker 지원 추가

Visual Studio Code Docker 도구

Docker 개발을 지원하는 Visual Studio Code에 사용할 수 있는 확장이 많이 있습니다.

Microsoft는 Visual Studio Code 확장용 Docker를 제공합니다. 이 확장은 애플리케이션에 컨테이너 지원을 추가하는 프로세스를 간소화합니다. 필요한 파일을 스캐폴드하고 Docker 이미지를 빌드하며 컨테이너 내에서 앱을 디버그할 수 있습니다. 확장에는 시작, 중지, 검사, 제거 등과 같은 컨테이너 및 이미지에 대한 작업을 쉽게 수행할 수 있는 시각적 탐색기가 있습니다. 또한 확장은 실행 중인 여러 컨테이너를 단일 단위로 관리할 수 있도록 Docker Compose를 지원합니다.