주니어 DevOps 엔지니어가 바라 본 CPU 아키텍처 (docker pull이 안 된다!)
목차
오늘은 주니어 DevOps 엔지니어가 바라본 CPU 아키텍처라는 제목으로 항상 헷갈리는 개념일 수도 있는 arm, amd, x86 등 아키텍처 이야기와 이와 응용되는 이야기를 해보도록 하겠습니다. (사실 아직 주니어도 아닙니다. 신입도 아니고, 예비?)
docker pull 이 내 컴퓨터에서 안먹어!!
혹시 여러분들은 다음과 같은 오류를 마주하신 적이 있으신가요? 보통은 구글링으로 에러를 붙여놓고 그냥 그렇구나~ 하고 지나갈 수 있지만 이번엔 왜 오류가 났는지, 해결방안은 없는지 살펴보도록 하겠습니다.
먼저 대표적인 CPU 제조사 3곳을 살펴봐야 합니다.
Intel (인텔)
컴알못도 CPU! 하면 인텔이 바로 나오는 그 유명한 CPU 도 만들고 여러 가지 만드는 종합 반도체 회사입니다. i3, i5 등의 CPU 칩으로 유명합니다.
대표적으로 x86 아키텍처를 만들었는데, 아키텍처 이야기는 밑에서 하도록 하겠습니다.
AMD (에이엠디, 또는 암드)
암드입니다. 리사 수로 더 친숙한 회사일 수도 있는 반도체 제작 회사입니다. 라이젠 시리즈가 유명합니다.
AMD64 아키텍처를 만들었습니다. 역시 아키텍처 이야기는 밑에서 자세히 나옵니다.
ARM
Apple의 arm 아키텍처 프로세서 칩 M1이라는 이야기를 많이 들어보셨을 텐데, 이 때문인지 arm이 회사가 아니라, 아키텍처의 한 이름으로 생각하는 분이 많습니다. (제 얘기입니다..)
ARM은 영국의 반도체 기업입니다. 소프트뱅크가 인수하고 있죠. 그래서 뉴스를 간간히 보시는 분들은 이재용 삼성 회장 부회장과 손정의 소뱅 회장이 ARM 인수 관련 논의를 한다는 소식을 들으셨을 텐데요. 소프트뱅크가 ARM의 소유권을 가지고 있고, 삼성이 이를 인수를 원한다는 그런 이야기입니다. (실제로는 NVIDIA의 인수 실패와 같이 난항일 것이라는 추측이 있습니다.)
intel의 x86, AMD의 AMD64 같이 ARM은 ARM 아키텍처를 만들었습니다. ARM 아키텍처를 하나로 요약하자면, 전력 소모는 최소화하면서 성능은 최대로. 그러기에 소형 장비에서 주로 쓰입니다. (스마트폰, 심지어 공유기까지도)
ARM 아키텍처를 사용한 대표적인 칩셋이 삼성의 엑시노스, 퀄컴의 스냅드래곤 등이 있습니다. 대략 3년 전쯤까지는 데스크톱, 노트북은 AMD64 아키텍처를 사용. 스마트폰은 ARM 아키텍처를 사용하는 것이 국룰로 정해지는 분위기였습니다.... 만
ARM 아키텍처를 채용한 Apple silicon M1이 나오면서 시장의 흐름이 바뀌고 있습니다. Apple이 역대급 성능의 칩셋을 발표하고, 시장의 보급을 위해 엄청난 가성비의 M1 Mac을 판매하면서 많은 개발자 분들이 Apple slicon Mac으로 이주하고 계신데, 이 나비효과가 초반에 나온 docker pull 오류까지 가져오게 됩니다. 계속 가봅시다.
이제 각 아키텍처의 특징을 살펴보겠습니다.
x86
Intel이 1978년 개발한 32bit 아키텍처입니다.
범용성이 가장 큰 장점인데, 후술 할 x86의 확장 64bit 아키텍처인 AMD64가 호환해주기 때문입니다. (IPv4의 확장인 IPv6가 IPv4도 호환성을 지니는 것과 같은 맥락입니다.)
AMD64 (x86-64 , x64)
AMD가 1999년에 발표한 x86의 64bit 확장 아키텍처입니다.
앞서 말했던 것처럼 x86과 호환됩니다.
그럼 여기서 의문이 들 수 있습니다. AMD가 만든 AMD64니까, 인텔은 이 아키텍처를 쓰지 않는가? 그건 또 아닙니다. 인텔과 AMD의 크로스 라이선스로 인해 두 회사 모두 아키텍처를 사용할 수 있고 (그러니 AMD가 x86의 64bit 확장 아키텍처를 설계할 수 있었겠죠?) 인텔 CPU를 사용하더라도 Windows를 포함한 64비트 기반 프로그램에서는 인텔의 x64 기반 프로세서 아키텍처 식별자를 AMD64로 인식합니다.
ARM
ARM의 32bit CPU 아키텍처입니다
x86과 완전히 다른 아키텍처라 호환이 불가능합니다.
하지만 x86과 같이 후술 할 ARM의 확장 64bit 아키텍처인 ARM64가 호환해줍니다.
ARM64 (arm64/v8)
ARM 기반의 64bit CPU입니다.
앞서 말했던 것처럼 ARM과 호환됩니다.
대표적으로 Apple의 Apple silicon, 퀄컴의 스냅드래곤, AWS의 Gravition이 이에 해당됩니다.
아키텍처의 예시를 좀 더 알아보기 위해 AWS EC2 인스턴스 콘솔 화면을 잠깐 확인해볼까요?
AMI 선택 후 아키텍처를 선택하게 되어 있습니다. 이제 64비트(x86)와 64비트(Arm)를 비교할 수 있겠죠?
또, AWS의 자체 설계 제작 칩인 graviton을 사용하는 인스턴스 유형인 A 시리즈를 선택할 때도 있습니다.
x86_64 아키텍처 선택 후 a1 유형의 인스턴스를 선택하려 할 때, 왜 지원하지 않는지 알 수 있습니다. graviton은 ARM64 아키텍처를 기반으로 설계되었기 때문에 호환되지 않음을 알 수 있죠.
또한 A 시리즈는 평균적으로 약 20% 가격이 저렴하고, Docker build 같은 여러 성능 테스트에도 퍼포먼스가 더 좋게 나왔다고 하니, ARM 아키텍처를 사용하는 조직이라면 긍정적으로 검토할만하겠습니다.
다시 Apple Silicon 이야기를 해볼까요?
앞서 말했던 것처럼, 기존 시장에서는 데스크톱과 노트북은 AMD64, 스마트폰은 ARM 아키텍처를 쓰는 모양새였습니다. 그런데 M1이 발표되고 놀라운 성능을 보여주고 시장 보급을 위해 엄청난 가성비로 맥북을 풀어버리면서 (x86 프로그램을 M1에서도 동작할 수 있도록 도와주는 에뮬레이터인 로젠타도 많은 영향을 끼쳤음도 분명합니다.) 많은 개발자들이 ARM 칩 환경으로 개발환경이 전환됩니다. 그러나 개발자 중 Apple silicon을 사용하지 않는 분이나 많은 서버 컴퓨터에서는 여전히 AMD를 사용하게 됩니다.
이는 단순한 32bit -> 64bit 급 상황이 아님을 알 수 있는데요, AMD와 ARM 아키텍처는 전혀 다른 설계 방식이기 때문이죠. 이에 인프라 개발에서는 서버 인스턴스를 ARM을 써? AMD를 써? 하는 고민이나 서두에 나온 docker pull 오류 같은 상황을 맞게 됩니다.
다시 오류 상황으로 돌아가 봅시다. 현재 작성자의 환경은 Apple M1 pro, 즉 ARM64입니다. 그러니 에러 로그를 분석하면 해당 도커 이미지가 준비한 실행환경 리스트에는 우리의 로컬 환경과 맞는 환경이 없음을 알 수 있습니다. 더 정확히 알아볼까요?
mquery 도구를 사용하면 모든 공개 리포지토리의 공개 이미지들의 실행환경을 쿼리 할 수 있습니다. (Docker Hub에서도 확인할 수 있지만 mquery를 사용해 CLI 환경에서 더 편리하게 확인이 가능합니다.) 에러가 발생한 이미지를 쿼리 하니 위와 같은 결과를 볼 수 있는데, 해당 이미지가 지원하는 플랫폼은 오직 AMD64 임을 알 수 있죠. 현재 환경은 ARM이니 맞지 않아 오류가 났음을 파악할 수 있습니다.
예시 이미지
docker build시 다른 옵션을 주지 않는다면 빌드한 환경의 아키텍처만 지원이 됩니다. 그러기에 mysql:8.0.23 이미지는 AMD64 아키텍처의 환경에서 빌드되었으므로 해당 환경만 지원이 되었다고 추측할 수 있겠죠.
다른 이미지를 한번 확인해볼까요? Docker Hub 공식 이미지 중 하나인 nginx를 쿼리 해보니, 정말 다양한 환경을 지원함을 알 수 있습니다. 이렇듯 Docker Hub의 공식 이미지는 다양한 플랫폼을 제공합니다. 이렇게 AMD, ARM 두 아키텍처 환경을 모두 지원하는 이미지를 만들려면 어떻게 해야 할까요?
이를 위해 docker에서는 buildx 명령어를 지원합니다. buildx는 다중 플랫폼으로 빌드하고 싶거나, 실행 환경과 다른 플랫폼에서 빌드되도록 설계되어, buildx를 통해 실행된 모든 빌드는 Moby BuildKit 빌더 엔진을 통해 실행됩니다. --platform 옵션을 통해 실행 환경을 지정해주기만 하면 됩니다.
이제 Mac M1용 Docker Desktop을 사용하여 Apple M1 Mac에서 ARM 또는 AMD Docker 컨테이너를 실행할 수도 있습니다. 물론 기본값은 ARM 버전을 실행하는 것이지만 --platform linux/amd64 매개변수를 사용하면 Docker가 AMD 버전을 실행하게 됩니다.
여기서 끝내기엔 조금 아쉬우니 조금 더 알아보고 가겠습니다. 앞의 과정을 보신 분들이라면 다음과 같은 의문을 가질 수도 있습니다.
docker는 빌드한 이미지를 pull만 하면 어떤 환경에서든 사용할 수 있는 무적 아니었어? 왜 실행 환경마다 오류가 나는 거지?
그 이유는 Windows와 MacOS에서 Docker 환경을 위해 사용되는 Docker Desktop의 아키텍처를 보면 알 수 있습니다.
x86 아키텍처는 CISC , ARM 아키텍처는 RISC라는 설계 방식을 사용하는데, 너무 깊게 들어가지는 않고 이해하자면 각 아키텍처마다 컴퓨터 명령어의 집합이 다릅니다. 즉, x86 CPU용으로 컴파일된 소프트웨어와 ARMCPU 에서 컴파일된 소프트웨어는 호환될 수가 없습니다. 이는 docker도 예외는 아니죠. 그러기에 docker 또한 이미지를 빌드하는 환경의 영향을 받아 그에 맞는 이미지가 만들어집니다.
그래서 이를 해결하기 위해 QEMU와 Docker를 사용하여 에뮬레이트된 환경을 설정할 수 있습니다. 오픈 소스 머신 에뮬레이터 및 가상화 프로그램인 QEMU를 통해 사용자는 크로스 컴파일러 없이도 x86 시스템에서 ARM 바이너리를 빌드할 수 있게 됩니다.
앞서 봤던 다중 아키텍처 이미지 빌드를 지원하는 buildx 명령어의 옵션 중 하나가 바로 이 QEMU입니다. buildx가 다중 아키텍처 이미지를 빌드하기 위해 세 가지 옵션을 제공하는데, 자세한 내용은 해당 포스팅을 참고해주시길 바랍니다.
지금까지 docker pull 오류로부터 시작해 정말 많은 내용을 알아봤습니다. 헷갈리던 AMD, ARM부터 docker가 빌드하는 과정까지 알아봤는데요,
에러를 방지하고 협업을 위해서라도 멀티 아치로 빌드하는 것이 좋아 보입니다. 더불어 AWS EC2 인스턴스를 구성할 때도 어떤 CPU 아키텍처를 사용할지, 비용이나 성능을 계산하고 모니터링하며 개선하는 것이 필요할 것 같습니다.