MSA와 컨테이너 스터디
모노리딕에서 MSA로 넘어갈 때 여러 서버에 반복 작업하는 위험을 줄이려 팀에서 쿠버네티스와 컨테이너 기술 스터디를 시작했다. 그런데 대부분의 예제가 멀티노드 클러스터를 전제로 해 미니큐브로는 한계가 있었고, 상용 서비스는 과금 부담이 커서 학습용으로 쓰기 어려웠다.
돈을 안 쓰고 클러스터를 만들려고 집에서 블로그 서버로 쓰던 싱글보드 컴퓨터를 활용하기로 했다. 제대로 된 클러스터를 만들자는 욕심에 보드를 추가로 사서 예쁘게 조립하고 프로젝트 이름을 '스카이넷'이라 지었다. 도커를 설치해 hello-world 이미지를 실행하니 잘 동작했다.
PC에서 만든 파이썬 Flask 웹 애플리케이션 이미지를 보드에서 실행하자 '실행 포맷이 잘못됐다'는 에러가 났다. 구글링해 보니 PC에서 빌드한 이미지는 ARM 아키텍처에서 실행할 수 없다는 것이었다. 그런데 hello-world는 세 환경 모두 되고, 보드에서 빌드한 이미지는 PC에서 되는 이상한 현상이 남아 원인 조사를 시작한다.
도커 빌드 결과물은 보통 AMD64·리눅스 의존적 이미지라 타겟 환경도 같아야 실행된다. PC의 AMD64는 CISC, 보드의 ARM은 RISC 기반으로 인스트럭션 셋이 달라 실행이 불가능했다. hello-world가 되는 이유는 레지스트리에 아키텍처별로 다른 태그의 이미지가 올라가 있고, 풀 요청 시 환경 정보가 실려 맞는 이미지를 받아오기 때문이다.
ARM 보드에서 빌드한 이미지가 PC에서 실행된 건 리눅스 커널의 binfmt_misc가 바이너리의 아키텍처를 감지해 맞는 인터프리터를 제공하고, QEMU가 사용자 공간을 에뮬레이션해 주기 때문이다. 맥·윈도우의 도커 데스크탑도 하이퍼바이저 위에 리눅스를 올리고 QEMU를 함께 설치한다. 다만 에뮬레이션은 네이티브보다 성능이 현저히 떨어진다.
멀티 아키텍처 이미지를 만드는 방법으로 세 가지를 비교한다. 여러 디바이스에 소스를 분배해 각자 빌드하는 무식하지만 확실한 방법, 임베디드에서 여전히 쓰이는 크로스 컴파일 방식(빌드 스크립트 작성 부담), 그리고 앞서 설명한 QEMU로 시스템 전체 또는 사용자 공간만 에뮬레이션해 빌드하는 방식이다.
도커는 사용자 공간만 에뮬레이션하는 방식을 buildx 기능으로 통합했다. 한 번의 명령으로 멀티 아키텍처 빌드·푸시·레지스트리 아키텍처별 등록까지 처리하고 기존 빌드 명령어와도 비슷해 쉽다. 사용하려면 커맨드라인에서 실험적 기능을 활성화·재시작하고 빌더 인스턴스를 생성한 뒤 타겟 플랫폼을 설정해 빌드하면 된다.
buildx는 도커 19.03 이하에선 플러그인 설치, 리눅스 커널 4.8 이하에선 binfmt_misc 부재, QEMU 미설치 시 추가 설치가 필요하다는 제약이 있다. 쿠버네티스 클러스터 구축 중 에러 한 줄에 막혔던 이야기지만, AWS가 일부 인스턴스를 저전력 ARM으로 교체하는 흐름에서 애플리케이션 호환성은 개발자의 숙제가 될 수 있다고 마무리한다.