데브시스터즈는 Kubernetes 기반의 게임 개발 및 테스트 인프라를 성공적으로 구축하여 게임 출시에 이르는 모든 과정들을 효율적으로 처리하고 있습니다.
다년간의 성공적인 운영 경험을 바탕으로 Windows Server 기반의 Workload를 지원하기 위한 R&D 작업을 진행하였고, 그 경험담을 공유하는 글을 몇 편에 걸쳐 DevTech 기술 블로그에 연재할 예정입니다.
Windows 컨테이너 시리즈 글 보기
- Windows 컨테이너에 대한 이해
- Windows 컨테이너 개발 환경 구축하고 테스트하기
- NT 서비스를 Windows 컨테이너로 마이그레이션하기
- 베이스 이미지, 격리 방식에 대한 이해
베이스 이미지의 종류
Windows 컨테이너를 사용할 때에는 베이스 이미지로 무엇을 어떻게 사용할 지 결정해야 합니다. 리눅스와 달리 Windows 컨테이너는 기존의 Windows Server용 애플리케이션을 컨테이너에서도 안정적으로 실행할 수 있도록 만드는 것이 목표였습니다. 그렇기 때문에 베이스 이미지를 얼마나 잘 이해하는지가 매우 중요합니다.
모두들 아시는 것처럼 Windows는 리눅스와 달리 Microsoft가 독점하여 개발하고 배포하는 소프트웨어입니다. 그렇기 때문에 Docker에서도 Microsoft가 관리하는 레지스트리에서만 이미지 를 다운로드할 수 있습니다. (물론 기술적으로 예외는 얼마든지 있을 수 있지만 논외로 합니다.)
Windows 컨테이너 베이스 이미지는 여러 가지가 있지만, 대표적으로는 두 가지를 고려합니다. 서버 코어 이미지와 나노 서버 이미지 중 하나를 고르는 것이 일반적입니다.
- 서버 코어 이미지
mcr.microsoft.com/windows/servercore:YYMM
: Windows Server의 코어 버전과 대응되는 컨테이너 이미지로, GUI 애플리케이션을 제외한 거의 모든 부분에서 호환성이 보장됩니다. 대신 이미지 크기가 큰 편으로, 최신 버전의 경우 약 4~5GiB 정도 됩니다. - 나노 서버 이미지
mcr.microsoft.com/windows/nanoserver:YYMM
: 핵심적인 Windows의 기능만을 담고 있는 컨테이너 이미지로, x64 아키텍처로 컴파일된 애플리케이션만 실행할 수 있으며, 핵심적인 Win32 API만 사용하는 애플리케이션 (예: Python, Golang, .NET Core 등)을 실행할 수 있도록 최적화되어있어 크기가 200~500MiB 정도 됩니다. Windows PowerShell과 .NET Framework는 이 이미지에서는 사용할 수 없습니다.
그 외에도 다음의 이미지들을 목적과 상황에 따라 찾습니다.
- Windows 이미지
mcr.microsoft.com/windows:YYMM
: Windows Server에 데스크톱 경험 기능 (DirectX, Internet Explorer 등)까지 얹어 실행할 수 있도록 만든 실제 Windows 운영 체제와 거의 대등한 기능 집합을 포함한 이미지로, 메시지 루프 처리가 필요한 일반적인 Win32 애플리케이션도 화면 출력 수단은 없지만 실행 자체는 지원합니다. 최신 버전의 경우 약 11~12GiB 정도 됩니다. - IoT 이미지
mcr.microsoft.com/windows/iotcore
: 라즈베리 파이 등의 임베디드 컴퓨팅 디바이스 상에서 실행되는 Windows 10 IoT 버전에서도 Docker를 사용할 수 있으며, 여 기에 배포할 애플리케이션은 IoT 베이스 이미지로 빌드해야 합니다. - 머신 러닝 이미지
mcr.microsoft.com/windows/ml/insider
: Windows 10에 내장되어있는 머신 러닝 API인 WinML을 컨테이너에서 사용할 수 있도록 제공하는 것으로 2020년 여름 현재 아직 인사이더 프리뷰 이미지입니다. - 인사이더 프리뷰 채널
mcr.microsoft.com/windows/insider
: 컨테이너 베이스 이미지도 인사이더 프리뷰 프로그램을 운영하며, Windows Server의 인사이더 프리뷰 버전이 SAC 채널 내에서 출시되면 약간의 시차가 존재하지만 대체로 빠른 시일 내에 일치하는 커널 버전을 포함하는 컨테이너 베이스 이미지가 같이 MCR에 게시됩니다. - IIS 이미지
mcr.microsoft.com/windows/servercore/iis
: IIS 서버를 컨테이너 안에서 실행하기 원한다면 이 버전의 이미지를 사용할 수 있습니다. 인사이더 버전의 Windows Server는mcr.microsoft.com/windows/servercore/iis/insider
이미지를 사용할 수 있습니다.
사용할 이미지의 종류를 결정하면, OS 버전을 선택해야 합니다. OS 버전을 선택할 때에는 컨테이너가 실행될 환경을 같이 고려해야 합니다. OS 버전을 적절하게 선택하지 않으면 컨테이너 이미지를 만들어도 실행하지 못할 수 있기 때문입니다.
격리 방식에 대한 개요
이전 글에서도 간단히 언급했는데, Windows 컨테이너는 Windows 운영 체제 제품들 중에서 Windows Server를 위하여 만들어진 기능입니다. 그래서 컨테이너 실행 환경을 격리하는 방법이 처음부터 두 가지로 나뉘어져 있습니다.
프로세스 격리 방식과 Hyper-V 격리 방식으로 나뉘어지는데, 프로세스 격리 방식은 가볍고 효율적인 대신 호스트와의 경계가 엄격하게 구분되어있지 않고, Hyper-V 격리 방식은 완전한 가상 컴퓨터를 만들지는 않는 대신, 커널 수준에서부터 실행 환경을 분리하여 구동하기 때문에 다소 메모리 사용량이 높습니다.
그러나 이 내용 뿐 아니라, 격리 방식을 어디서 어떤 조건 아래에 사용할 수 있는지 정확히 이해하는 것이 좋습니다. 격리 방식의 적용 조건을 잘 이해하지 못하면 Windows 컨테이너 도입을 위한 개발에 아예 진입할 수 없을 수도 있고, 기대했던 성능에 못미치는 결과가 나타날 수도 있기 때문입니다.
격리 방식의 차이점
프로세스 격리 방식은 Windows Server 컨테이너라고도 불리며, 리눅스에서 흔히 사용되는 컨테이너처럼 커널에 네임스페이스 컨셉을 도입하여 보안 계층, 자원을 분리 할당하는 방식으로 실행됩니다. DirectX를 통한 GPU 가속 등 하드웨어 연동에 최적화되어있으며, 실행되는 모양에서 차이가 있을 뿐, 사실상 직접 EXE 파일을 띄워 실행하는 것과 비슷한 성능과 양상을 나타냅니다.
Hyper-V 격리 방식은 Hyper-V 컨테이너라고도 불리며, 가상화 기술을 이용하여 호스트와 분리된 실행 환경을 만들고 여기에 컨테이너 베이스 이미지에 내장된 커널을 불러와 별도의 실행 환경을 만들고 실행합니다. 이 안에서 실행되는 애플리케이션은 호스트 OS의 존재를 네트워크가 아닌 프로세스나 API 레벨에서는 인지할 수 없습니다. 다만 완전한 가상 컴퓨터를 만들어 실행하는 것과 다르 게 특수하게 최적화된 가벼운 가상 계층을 만드는 것이므로 호스트 운영 체제의 커널과 완전히 분리된 것은 아니어서 실행할 수 있는 베이스 이미지의 버전이 호스트 OS보다 높을 수는 없습니다.
알기 쉽게 정리한 격리 방식 적용 조건
어떤 격리 방식을 사용하던 간에 Windows 컨테이너는 기본적으로 다음의 전제를 가지고 사용할 수 있습니다.
컨테이너 베이스 이미지의 OS 버전 <= 호스트 OS 버전
그리고 격리 방식에 따라 세부 조건이 달라집니다. 프로세스 격리 방식의 경우 다음의 조건과 일치해야 합니다.
컨테이너 베이스 이미지의 OS 버전 == 호스트 OS 버전
Hyper-V 컨테이너 격리 방식을 사용하는 경우에는 다음의 조건과 일치해야 합니다.
컨테이너 베이스 이미지의 OS 버전 <= 호스트 OS 버전 &&
호스트는 가상화 또는 중첩 가상화를 지원해야 합니다.
그리고 위의 조건은 Windows Server에서 Docker 컨테이너를 사용할 때만이 아니라 Windows 10에서 컨테이너를 사용할 때에도 동일하게 적용되는 조건들입니다. Windows 10의 경우에도 Windows Server처럼 커널 버전과 일치하는 베이스 이미지를 사용하면 프로세스 격리 방식의 컨테이너를 사용할 수 있기 때문입니다. 관련 Pull Request
실제 사례 살펴보기
위의 조건에 따라 실제 상황을 가정해보면 다음과 같습 니다.
-
게임 A의 게임 서버 Docker 이미지를 Windows Server Core 1809 베이스 이미지를 사용하여 만들었다.
-
해당 이미지를 실행할 서버는 Windows Server 2019이다. Windows Server 2019는 릴리스 버전이 1809이므로 Windows Server 1809이다.
-
프로세스 격리 방식으로 해당 서버에서 게임 서버 Docker 이미지를 레지스트리로부터 Pull하여 사용할 수 있다.
다른 한편으로 아래와 같은 문제를 만날 수도 있습니다.
-
게임 B의 게임 서버 Docker 이미지를 Windows Server Core 1903 베이스 이미지를 사용하여 만들었다.
-
해당 이미지를 실행할 서버는 Windows Server 2004 (2020년 4월 릴리스된 버전)이고, AWS에서 t3.large 인스턴스로 실행하려고 한다.
-
OS 버전과 컨테이너 베이스 이미지 버전이 달라서 Hyper-V 격리 방식을 사용하려고 했지만, AWS EC2는 중첩 가상화가 지원되지 않는다.
이미지의 OS 릴리스 버전을 정확하게 선택하지 않은 상태에서 퍼블릭 클라우드 환경에서 Windows 컨테이너를 실행하려고 하면 이와 같은 상황에 봉착하게 됩니다. 이런 문제를 피하기 위해서는 다음의 사항을 고려해야 합니다.
- 실행하려는 환경이 가상화나 중첩 가상화를 지원하는지 확인합니다. (Hyper-V로 가상 컴퓨터를 실행할 수 있는지의 여부를 확인하면 쉽습니다.)
- 가상화나 중첩 가상화가 지원되지 않는 환경이라면 컨테이너를 실행할 OS의 릴리스 버전 (1809, 1903 등)을 확인하여 일치하는 베이스 이미지로 컨테이너 이미지를 만들어 등록합니다.
OS 릴리스 버전에 대한 이해
Windows 10과 Windows Server 2016부터는 클라이언트 OS 버전과 서버 OS 버전이 동일한 릴리스 버전을 사용합니다. 예를 들어 Windows 10으로 2020년 5월 업데이트가 출시되면, Windows Server의 경우에도 일치하는 커널 버전을 사용하는 2004 (2020년 4월이라는 의미) 버전이 같이 출시됩니다.
다만 Windows 10과 달리 Windows Server의 경우 같이 출시가 되더라도 사용 중인 OS를 그대로 업그레이드 하는 기능은 빠진 상태로 출시됩니다. 이렇게 나오는 버전 사이의 간격이 대략 6개월에 한 번이므로 반기 채널 (SAC, Semi-Annual Channel)이라고 부릅니다.
SAC 버전은 데스크톱 경험이 들어있지 않으며, Server Core 이미지와 컨테이너 이미지만 판올림하여 출시됩니다. 각각의 SAC 버전에는 작던 크던 변화 사항들이 포함되고, 각 버전은 지원 기간이 LTSC 버전에 비해 짧습니다.
반면 장기 지원 채널 (LTSC, Long-Term Servicing Channel) 버전은 이전에 출시된 LTSC 버전의 OS를 그대로 업그레이드할 수 있으며, 데스크톱 경험을 포함하고, SAC 버전보다 훨씬 오랫동안 기술 지원이 되는 버전입니다. Windows 10 출시 이후로 Windows Server는 2016, 2019 두 번에 걸쳐 LTSC 버전이 출시되었습니다. 약 3~4년에 한 번 간격으로 출시됩니다.
AWS, Azure 같은 퍼블릭 클라우드의 경우 복잡한 요구 사항을 충족시키기 위하여, 그리고 Kubernetes와 같은 오픈 소스 기술과의 협업을 위하여 SAC 버전의 Windows Server에 대한 지원을 꾸준히, 자주 업데이트합니다. 반면 GitHub이나 CircleCI 등의 환경에서는 LTSC 버전의 Windows Server만을 지원합니다.
이런 조건 속에서 어떻게 실행 환경을 선택할 것인지에 대한 고민이 따르기 마련입니다.
컨테이너 실행 환경의 선택과 관리
고려해야 할 것이 많은게 Windows 컨테이너 개발과 운영 환경이지만, 아래의 내용에 따라 적절한 선택지를 골라 활용하면 고민을 줄이거나 좀 더 효율적으로 대응할 수 있습니다.
개발 환경과 CI
개발자가 컨테이너 이미지를 만들어야 하는 경우에는 항상 최신 버전의 Windows OS 버전과 Windows 10용 Docker를 설치하는 것이 좋습니다. 항상 최신 버전의 Windows 호스트 OS 버전을 유지하면 Hyper-V 방식을 통해 Windows 10용 Docker에서 하위 컨테이너 이미지 OS 버전을 모두 사용할 수 있기 때문입니다. (단, OS를 Insider Preview 참가 상태, 즉 Dev 채널이나 Beta 채널로 유지할 필요는 없습니다.) Hyper-V 컨테이너로 docker build
명령을 실행하면 빌드 속도가 다소 느리지만, 정식으로 출시되어 MCR 레지스트리에 등재된 모든 Windows 컨테이너 이미지를 버전에 관계없이 완벽하게 다룰 수 있다는 장점이 있습니다.
만약 빌드 자동화를 고려하고 계시다면, 애플리케이션의 유형에 따라 빌드 자동화 방법을 다르게 선택하는 것이 유리합니다. 그리고 컨테이너 베이스 이미지를 SAC 버전으로 정했다면, 시중에서 제공되는 호스팅 방식의 Windows 빌드 노드는 모두 LTSC 버전을 기준으로 하므로 별도로 빌드 노드를 구축하고 운영해야 합니다. 아울러 베어메탈이나 중첩 가상화를 쓸 수 있는 인스턴스 타입이 아닌 경우 빌드 노드의 OS 버전과 컨테이너 이미지의 OS 버전이 일치해야 함을 항상 주의합니다.
컨테이너 오케스트레이션
Kubernetes 환경이 아닌 다른 컨테이너 오케스트레이터 (예: Docker Swarm)를 사용한다면 LTSC 버전을 사용하는 것을 추천합니다. Kubernetes를 제외한 다른 오케스트레이터들은 이미 Windows Server 2016 출시 직후 몇 번의 업데이트로 검증된 성능과 안정성을 충분히 확보한지 오랜 시간이 지났으므로, SAC 버전을 따라서 올라가야 할 필요성이 높지 않습니다.
반면 Kubernetes 환경에서 Windows 컨테이너를 실행하려는 경우 새로운 SAC 버전이 나올 때 마다 업그레이드하여 테스트하시는 것이 유리합니다. Kubernetes에 추가되는 풍부한 기능을 Windows OS에서도 사용할 수 있도록 마이크로소프트에서 Windows OS 자체를 계속 개발하고 있을 뿐만 아니라, 요즈음의 Windows Server 개발 방향 또한 컨테이너와 Kubernetes에 집중하고 있기 때문입니다.
따라서 윈도우 컨테이너를 Kubernetes에서 사용할 때에는 LTSC 버전의 Windows Server 노드와 SAC 버전의 노드를 나누어 관리하고, 적절한 Toleration 및 Node Label 설정을 추가하여 워크로드를 분리하는 것이 좋습니다.
참고 자료
Key Concept for Windows Containers (Unreal Container)
Isolation Modes (Microsoft Docs)
Windows Server Release Cadence by Jeff Wolsey