DevOps

[Docker]ARM에서 어떻게 AMD 이미지가 돌아가는 거지?(Feat. QEMU 에뮬레이션)

Woonys 2023. 3. 20. 23:36
반응형

Introduction

드디어 도커 스터디가 막을 내렸다. 그간 수업을 진행하면서 정리할 게 산더미인지라 하나씩 블로그에 올려볼 계획이다. 그러고 보니 간만에 올리는 기술 아티클인듯. 그 간만에 올리는 글이 데브옵스 관련이라는 게 아이러니하지만..

 

시작은 Docker Desktop for Mac을 쓰면서였다. 책 <도커, 컨테이너 빌드업!>으로 수업을 진행하던 중, 책의 개발 환경은 리눅스인 것과 달리 우리의 실습 환경은 유닉스(맥북 로컬)이다보니 차이가 여럿 발생했다. 그 중 하나가 바로 플랫폼을 설정하는 것. 맥북 M1은 ARM 기반인지라 도커 데스크탑 환경에서 이미지를 빌드할 때 역시 ARM 기반 이미지를 사용해야 한다. 그런데 실습 과정에 Linux/amd64 기반의 MySQL 5.7 버전 이미지를 빌드해야 하는 실습이 있었다. 문제는 ARM용 MySQL 5.7버전이 없었던 것..(가장 최신 버전인 8버전만 ARM으로 존재했다.)

 

그런데 결과적으로 5.7버전 이미지로 빌드한 컨테이너를 도커 데스크탑에서 띄웠다. 이게 말이 되나? 싶을 수 있는데..어떻게 가능했는지 알아보자. 결론부터 얘기하면 이는 QEMU 에뮬레이션 덕분에 가능했다.

 

QEMU 에뮬레이터?(feat. 핀토스)

내 블로그의 핀토스 파트를 유심히 봤던 사람이라면 어? 하게 될 익숙한 용어인데, 그도 그럴 것이 바로 이미 한참 전에 블로그에서 정리해뒀던 적이 있는 키워드이기 때문이다. 그래서 해당 내용을 처음 찾아볼 때도 대번에 아, 가상화로 지원하는 거구나 하고 대략이나마 빠르게 이해할 수 있었다. 하지만 이렇게 넘어가면 아무 의미 없잖나. 아래는 공식 문서를 참고해 어떻게 가상화하는지 큰 맥락에서 정리한 내용이다.

 

Buildx와 Dockerfile에서 지원하는 세 가지 다른 전략을 사용하여 멀티 플랫폼 이미지를 빌드할 수 있다.

  1. 커널에서 QEMU 에뮬레이션 지원 사용(도커 데스크탑 사용시 자동 지원 -> 이 글에서 다루는 파트에 해당)
  2. 동일한 빌더 인스턴스를 사용하여 여러 네이티브 노드에서 빌드하기
  3. Dockerfile에서 스테이지를 사용하여 다른 아키텍처로 크로스 컴파일하기

이 중 1번을 보자. ARM 기반의 Apple M1 실리콘 맥에서 linux/amd64 플랫폼을 사용하는 컨테이너를 실행할 경우, 원래대로라면 OS 아키텍처가 다르니 돌아가지 않아야 한다. 그런데 Docker 엔진은 에뮬레이션(QEMU), 즉 가상화 기술을 사용하여 x86_64 명령어( linux/amd64 플랫폼에서 사용)을 ARM64 명령어( Apple M1 실리콘 맥에서 사용)로 번역해준다.

 

QEMU 에뮬레이터가 뭐지?

QEMU는 Quick Emulator의 약어로, 컴퓨터 시스템에서 하드웨어 가상화를 지원하는 오픈 소스 가상화 소프트웨어다. QEMU는 여러 아키텍처를 에뮬레이트할 수 있어, 다양한 플랫폼에서 실행되는 소프트웨어 개발 및 테스트에 유용하게 사용된다. 여기서 에뮬레이터란, 컴퓨터에서 하드웨어나 소프트웨어 등을 가상화해 다른 시스템에서 실행되는 프로그램을 원래의 시스템과 같은 환경에서 실행하게 해주는 프로그램을 말한다(보다 정확히 말하면 에뮬레이터는 가상화 기술과 유사하긴 하나, 하드웨어를 직접 가상화하는 것이 아니라 소프트웨어적으로 모방하여 실행한다는 차이점은 있다).

 

이를 Docker에서는 다른 아키텍처에서 빌드된 컨테이너 이미지를 호스트 시스템의 아키텍처와 호환되도록 변환하는데 사용한다. 덕분에 다른 플랫폼에서 빌드된 이미지를 로컬 시스템에서 실행할 수 있게 된다. 이것이 Docker desktop for Mac에서 AMD 기반 이미지가 빌드될 수 있는 이유다. 

그래서 서로 다른 플랫폼의 이미지가 도커 내에서 어떻게 동작한다고?

Apple M1 실리콘 맥에서 실행되는 Docker 버전인 Docker Desktop for Mac 역시 QEMU 에뮬레이터를 레이어 형태로 포함하고 있다. (일종의 가상화해주는 추상 계층이 하나 더 있다고 생각하면 된다.) 이 덕분에 linux/amd64 플랫폼용으로 빌드된 컨테이너를 Apple M1 실리콘 맥에서 실행할 수 있다. 이 에뮬레이션 레이어는 x86_64 명령어를 실시간으로 ARM64 명령어로 번역하는 방식으로 작동한다고 한다.

 

에뮬레이션 레이어를 다는 게 능사냐 하면, 또 그렇지는 않다. 계층이 하나 더 추가되는 만큼 오버헤드가 생겨 네이티브 ARM64 컨테이너를 실행하는 것보다 성능이 느릴 수 있다. 그런데 공식 문서에 의하면 많은 애플리케이션에서 성능 영향이 미미하며, 다른 플랫폼에서 일관된 환경에서 컨테이너를 실행하는 이점이 성능 비용보다 더 크다고 한다.

 

예시를 보자. Apple M1 실리콘 맥에서 linux/amd64 플랫폼을 사용하는 컨테이너를 실행하려면 docker run 명령어에 --platform 플래그를 사용해 플랫폼을 변경할 수 있다. 예를 들어, ARM 환경에서 linux/amd64 플랫폼을 사용하여 hello-world 이미지를 실행하려면 다음과 같이 실행할 수 있다.

 

docker run --platform linux/amd64 hello-world

 

이 명령은 hello-world 이미지를 linux/amd64 플랫폼으로 가져와 QEMU 에뮬레이션을 사용하여 Apple M1 실리콘 맥에서 실행한다.

 

단, 일부 이미지는 특정 플랫폼에서만 사용 가능한 종속성이 있기 때문에, 모든 이미지가 에뮬레이션으로 작동하지 않을 수 있다. 따라서 다른 플랫폼에서 실행하기 전에 이미지의 호환성을 확인하는 것이 좋다.

반응형