누구나 ‘방 안의 코끼리*’의 존재를 느껴본 적이 있을 것입니다. 위협적인 전조, 먹먹한 소음, 그대로 두면 분명 큰일이 날 것만 같은 불안감이 엄습합니다. 그런데도 우리는 거대한 무언가가 방 안을 배회하도록 방치하곤 합니다. 필자는 이 현상을 생생히 목격한 증인이며, 방관자였습니다. 심지어 나 자신이 코끼리라고 불리는 독특한 영광도 누려봤죠.
방 안의 코끼리(elephant in the room): 누구나 알고 있지만 그 누구도 먼저 이야기를 꺼내지 못하는 크고 무거운 문제를 비유하는 표현 (출처: 시사상식사전, pmg 지식엔진연구소)
컨테이너화의 맥락에는 두 마리의 코끼리가 있습니다. 이들을 방에서 몰아낼 방안을 논의하기 위해 두 마리를 각각 질문의 형태로 바꾸어 보겠습니다. ‘컨테이너란 무엇인가?’ 그리고 ‘왜 컨테이너를 사용하는가?’. 둘 다 매우 간단한 질문이지만 이토록 기본적인 사고의 출발점을 간과해 버리는 경우가 많습니다. 이유가 무엇일까요?
어쩌면 현재의 기술적인 흐름에 뒤처지면 안 된다는 무언의 압박 때문일지 모릅니다. 최근 마이크로서비스 분야는 그 어느 때보다 컨테이너 배포에 대한 논의가 활발히 이루어지고 있기 때문입니다. 컨테이너 구현 자체가 기본 사양으로 취급되기 시작해서일 수도 있습니다. 쿠버네티스Kubernetes가 엄청난 인기를 구가함에 따라 ‘K8s 클러스터’ 같은 용어를 모르면 대화가 되지 않을 정도입니다. 그도 아니면 순전히 개발자 본연의 두려움 때문일지도 모릅니다.
데브옵스 생태계는 최첨단 기술과 신무기로 융단폭격을 맞고 있습니다. 새로운 기술에 대한 질문을 멈추는 순간 개발자는 퇴행의 공포를 느낄 수밖에 없죠. 자바 개발자도 예외는 아닙니다. 이유야 어쨌든, 근본적인 문제를 해결하지 않으면 상세한 주제로 넘어갈 수 없습니다. 컨테이너를 구축하고 사용하려면 앞서 제기한 ‘무엇’과 ‘왜’라는 질문에 답할 수 있어야 합니다.
어째서 컨테이너가 개발자의 관심사여야 하는가?
물론 이 컨테이너는 아닙니다....
컨테이너를 사용하면 어떠한 이점이 있을까요? 프로젝트에 변화를 가져오고 새로운 기술 스택을 도입하려면 신중하게 고려한 비용편익 분석을 바탕으로 의사를 표명해야 합니다. 단지 대세를 따른다는 취지만으로는 충분한 근거가 성립되지 않습니다.
가장 먼저 제기될 만한 질문은, ‘어째서 컨테이너가 개발자의 관심사여야 하는가?’입니다. 제법 그럴듯한 문제 제기죠. 컨테이너가 단순히 배포 방식에 관한 기술이라면 운영 부서의 조타실에서 관장하는 것이 일견 타당해 보입니다. 데브옵스적 사고 방식에 의하면 이 지점이 바로 개발과 운영 사이의 모호한 경계에 해당합니다.
애플리케이션을 컨테이너에 패키징하려면 최초 개발자의 관점에서 가늠했던 것보다 더 많은 고려와 예측이 필요합니다. 컨테이너 기술의 모범 사례들을 배우고 타인의 시행착오를 간접적으로 경험한 이후에는 애플리케이션을 개발하며 동시에 패키징을 고려하게 될 것입니다. 배포 프로세스는 특성상 무수한 선택의 기로를 낳습니다. 애플리케이션 및 서비스의 메모리 사용 방식, 파일 시스템 사용 방식, 관찰 가능성 훅observability hook을 거는 방식, 다중 설정을 허용하는 방식, 데이터베이스 같은 타 서비스와 소통하는 방식 등, 이 정도는 일부에 불과하죠. 팀 구성 방식에 따라 다르겠지만 적어도 데브옵스 팀의 개발자라면 컨테이너 이미지를 구축하고 관리하는 능력을 갖추고 컨테이너 환경의 가치를 이해해야 합니다.
컨테이너 분야의 많은 기술은 이제 막 초기 단계에 진입했습니다. 반면 시장은 이미 완제품에 대한 기대로 무르익은 상태이죠. 클라우드는 소프트웨어와 서비스에 완전한 확장성, 가용성, 탄력성을 부여하고 이를 패키지화, 단순화시킨 상품을 제안할 수 있어야 합니다. 그러나 아직 우리는 이러한 제품을 구성하는 개별적인 부품 조각을 설계하는 수준에 머물러 있습니다.
컨테이너는 이러한 방향성에 부합하는 크나큰 진전입니다. 애플리케이션 패키징과 배포 인프라 사이에서 컨테이너가 유의미한 수준의 추상화를 제공할 수 있기 때문입니다. 개발자가 더 이상 컨테이너 수준까지 세세하게 관여하지 않아도 되는 때가 언젠가는 올 것이라 예상합니다. 그러나 아직은 아니죠. 적어도 배포와 관련된 현안을 모두 해소하고 다음 단계로 나아가기 전까지는 자리를 지켜야만 합니다. 그런 의미에서, 또한 컨테이너라는 주제를 다루는 이유에 대해 아직도 남아 있는 궁금증을 해결하는 차원에서, 논의를 좀 더 이어보겠습니다.
자바 애플리케이션을 패키징, 배포, 실행하는 전체 과정을 머릿속에 그려봅시다. 개발을 시작할 때는 먼저 특정 JDKJava Development Kit 버전을 개발 환경에 설치합니다. 다음으로 아파치 메이븐Maven이나 그레이들Gradle 같은 의존성 관리자를 설치하죠. 이들은 애플리케이션이 의존하는 서드파티 라이브러리를 설정하고 구비하는 툴입니다. 또한 애플리케이션을 WAR이나 JAR로 패키징하는 역할도 합니다. 이쯤 되면 개발 환경에 맞춰 배포 서버 환경도 준비가 완료되어야 합니다. 하지만 과연 그러한가요?
문제는 여기서 시작됩니다. 프로덕션 서버에 무엇이 설치되어 있는가? 자바 런타임 버전은 몇 인가? 애플리케이션 서버(JBoss, 아파치 톰캣, WildFly 등)는 무엇인가? 애플리케이션의 성능에 영향을 미칠 다른 프로세스가 실행되고 있는가? 애플리케이션이 혹시 루트 권한을 필요로 하는가? 그렇다면 애플리케이션 실행 계정에 적절한 권한이 설정되어 있는가? 애플리케이션이 활성 또는 부하 상태를 체크하는 외부 API나 데이터베이스에 접근할 수 있어야 하는가? 이 모든 질문에 답하기에 앞서 일단 프로덕션 전용 서버에 접근할 수 있는가? 아니면 애플리케이션 프로비저닝 서버를 먼저 요청해야 하는가? 애플리케이션에 과부하가 걸리면 어떻게 되는가? 신속하게 자동 스케일링이 적용되는가? 혹은 프로비저닝 프로세스를 처음부터 다시 시작해야 하는가?
이렇듯 수많은 질문 공세에 시달리다 보면 자연히 가상 머신(VM)과 가상화 기술에 시선이 끌리기 마련입니다. VM은 더 유연한 조건으로 애플리케이션 프로세스를 격리할 수 있으며 VM 스냅샷은 배포의 일관성을 보장하는 좋은 수단입니다. 그러나 VM 이미지는 OS 전체를 담고 있어 덩치가 크고 이동시키기 쉽지 않죠. 전체 저장 공간에서 차지하는 비중도 큽니다.
동료 개발자에게 컨테이너를 처음 소개하면 ‘아, 컨테이너란 VM 같은 건가요?’라는 반응을 보이는 경우가 제법 있습니다. 컨테이너가 유사 VM이라고 말하긴 어렵습니다. 둘 사이의 차이점 때문이죠.
VM과 컨테이너
먼저 VM(VMware vSphere, 마이크로소프트 하이퍼 VHyper-V 등)은 하드웨어를 추상화시켜 서버 전체를 에뮬레이트합니다. 달리 표현하면, 운영체제가 온전히 VM에 포함됩니다. VM은 하이퍼바이저hypervisor라는 소프트웨어 계층이 관리하며 필요에 따라 호스트의 리소스를 나누어 VM에 할당합니다.
반면 컨테이너는 기성 VM만큼 무겁지 않습니다. 예를 들어 리눅스 컨테이너는 전체 OS 대신 호스트 운영체제만 공유하는 별도의 리눅스 배포판이라 할 수 있습니다. 위의 그림에 보이듯 VM과 컨테이너는 추상화 수준이 다르지만 자바 가상 머신 Java Virtual Machine (JVM)의 위치는 그대로입니다. 그렇다면 JVM은 어느 쪽에 어울리는 존재일까요? 가상 머신이라는 용어를 여기저기에서 쓰다 보면 헷갈리기 시작합니다. JVM의 추상화는 다른 것들과 완전히 다른 프로세스 가상 머신을 구현합니다. 시스템 가상 머신의 대척점에 있다고 봐도 좋습니다. JVM의 주된 관심사는 자바 애플리케이션을 실행할 자바 런타임 환경Java Runtime Environment (JRE, JVM 구현체 등)을 제공하는 것입니다. JVM은 자바 바이트코드를 실행하기 위해 호스트의 프로세서를 가상화하죠.
컨테이너는 애플리케이션 일관성consistency, 프로세스 격리, OS 수준 의존성에 관련된 대부분의 문제를 해결하도록 고안된 경량 솔루션입니다. 서비스나 애플리케이션을 컨테이너로 패키징하면 캐싱 메커니즘을 통해 배포 및 구동 시간을 극적으로 단축시킬 수 있습니다. 직접 설정한 프로비저닝이 완료될 때까지 기다릴 필요 없이 이미 존재하는 인프라에 컨테이너를 바로 배포할 수 있기 때문입니다. 전용 서버, 기존 VM, 온프레미스on-premise 데이터 센터, 클라우드 환경 등 인프라 종류에 구애받지 않습니다.
심지어 프로덕션 환경이 아니더라도 개발이나 테스트 환경에 컨테이너를 활용하는 다양한 사례들이 있죠.
신입 개발자가 들어와 적응하는 동안 가장 많은 시간이 소요되는 단계는 로컬 개발 환경 설정입니다. 처음으로 버그를 수정하거나 개선 사항을 반영하기까지 시간이 다소 걸리더라도 이러한 적응 기간을 감안해 어느 정도 눈감아 주는 관행이 있습니다. 심지어 개발 툴을 지정해 주는 회사도 있습니다. 일관성을 지켜야 적응을 돕기 쉽고 효율도 높아진다는 믿음에서 비롯된 행태입니다. 그러나 요즘 개발자는 그 어느 때보다 넓은 선택의 자유를 누릴 수 있습니다. 필자는 이미 자신의 방식에 익숙해진 개발자에게 특정 개발 툴을 강요하면 역효과가 난다고 생각합니다. 사실은, 그냥 그럴 필요가 없는 경우가 많죠. 특히 컨테이너를 활용할 수 있게 된 뒤로는 더욱 그렇습니다.
컨테이너는 런타임 환경을 일관적으로 유지하며 설정에 따라 개발, 테스트, 프로덕션 모드를 손쉽게 선택하고 실행할 수 있습니다. 환경 자체를 애플리케이션과 함께 컨테이너 이미지에 탑재하기 때문에 의존성 누락 등 환경적 요인으로 서비스나 애플리케이션이 다르게 작동할 위험을 크게 줄일 수 있습니다.
이렇듯 컨테이너는 이식성portability이 높아 개발자가 로컬 환경에서 온전성sanity 테스트를 수행하기 쉽습니다. 버그를 발견했을 때 프로덕션과 동일한 버전의 코드를 간단히 배포할 수 있어 문제를 재현하기도 쉽죠. 또한 통합 테스트에 컨테이너를 활용하면 프로덕션과 최대한 비슷하게 환경을 재현할 수 있다는 장점이 있습니다.
예를 들어 통합 테스트에서 프로덕션과 달리 인메모리 데이터베이스를 쓰고 있다고 가정해 봅시다. 이럴 때 프로덕션과 동일한 데이터베이스 소프트웨어가 담긴 컨테이너를 구동하면 테스트 환경을 일치시킬 수 있습니다. 데이터베이스 버전이 다를 때 TestContainers 같은 프로젝트를 부수적으로 활용하면 좋습니다. SQL 문법 등의 미묘한 차이로 발생하는 불규칙성이 해소됩니다. 컨테이너를 적절히 응용하면 로컬 시스템에 신규 소프트웨어 버전을 매번 설치하거나 동일 버전을 중복으로 설치하지 않아도 됩니다. 복잡성은 감소하고 효율성은 향상됩니다.
지금까지 배운 내용이 여러분에게 쓸모가 없다고 해도, 컨테이너는 어떠한 형태로든 살아남아 지속될 가능성이 큽니다. 지난 몇 년간 컨테이너 사용량은 기하급수적으로 증가했습니다. 관련 툴 또한 컨테이너 생태계를 중심으로 지속해 개발 및 개선되고 있죠. 컨테이너는 개발과 운영 프로세스 모두의 지지를 받는 견고한 발판 위에 서 있습니다. 이미 50년 이상의 역사를 지닌 기술임을 감안한다면 아직 알려지지 않은 완전히 다른 방향으로 진화가 일어나고 있을 가능성은 있습니다. 그러나 지금 당장은 컨테이너 생태계를 공부하고 그 안의 기술을 최대한 내 것으로 만들어 두는 편이 좋을 것입니다.
이 글은 도서 『자바 개발자를 위한 데브옵스 툴』 내용 중 일부를 편집하여 작성되었습니다. 데브옵스가 아직 낯선 노련한 경력자, 컨테이너와 클라우드 지식을 바탕으로 데브옵스에 뛰어들려는 초심자 모두를 데브옵스의 올바른 길로 이끌어 줄 『자바 개발자를 위한 데브옵스 툴』에서 더 많은 내용을 확인할 수 있습니다.
최신 콘텐츠