도커 생태계: 핵심 원리부터 현대 소프트웨어 배포까지 1부: 컨테이너화의 기원과 핵심 원리 1.1 컨테이너 이전 시대의 딜레마: 환경 불일치의 세계 현대 소프트웨어 개발 및 배포 환경의 중심에 있는 도커(Docker)를 깊이 이해하기 위해서는, 도커가 해결하고자 했던 근본적인 문제들을 먼저 살펴봐야 합니다. 도커의 등장은 갑작스러운 혁명이 아니라, 오랜 기간에 걸쳐 누적된 운영의 비효율성과 개발 환경의 불일치 문제를 해결하기 위한 진화의 결과물입니다. 수동 문서화의 시대 초창기 서버 관리는 주로 사람이 직접 작성한 문서에 의존했습니다. 개발팀이나 운영팀은 워드 프로세서 파일이나 위키에 “어떤 운영체제(OS)를 설치하고, 어떤 버전의 라이브러리를 설치하며, 특정 설정을 어떻게 변경해야 한다”는 식의 절차를 기록했습니다. 이 방식은 몇 가지 치명적인 약점을 내포하고 있었습니다. 첫째, 사람의 개입이 필수적이므로 실수가 발생하기 쉬웠습니다. 명령어 하나를 잘못 입력하거나 순서를 바꾸는 것만으로도 전체 시스템이 오작동할 수 있었습니다. 둘째, 문서는 시간이 지남에 따라 실제 서버 환경과 달라지기 쉬웠습니다. 업데이트 사항이 문서에 제때 반영되지 않으면, 문서는 더 이상 신뢰할 수 없는 정보가 되었습니다. 이러한 문제들은 결국 개발자의 로컬 환경에서는 잘 작동하던 애플리케이션이 테스트나 프로덕션 서버에서는 실패하는, 이른바 “내 컴퓨터에서는 되는데(It works on my machine)”라는 고질적인 문제를 낳았습니다. 구성 관리 도구의 부상 이러한 수동 관리의 한계를 극복하기 위해 등장한 것이 바로 코드로서의 인프라(Infrastructure as Code, IaC) 개념과 이를 구현하는 구성 관리 도구(Configuration Management Tools)였습니다. Puppet, Chef, Ansible과 같은 도구들은 서버의 구성 상태를 코드로 정의하고 관리할 수 있게 해주었습니다. 이를 통해 반복적인 작업을 자동화하고 사람의 실수를 줄일 수 있었으며, 서버 환경의 일관성을 어느 정도 확보할 수 있었습니다. 하지만 이들 도구 역시 만능은 아니었습니다. 배우고 사용하는 데 상당한 시간이 필요했고(높은 학습 곡선), 코드 자체가 복잡해질 수 있었습니다. 더 근본적인 문제는, 이들 도구가 하나의 OS 내에서 발생하는 의존성 충돌 문제를 완벽하게 해결하지 못했다는 점입니다. 예를 들어, 한 서버에서 애플리케이션 A는 Python 2.7 버전을, 애플리케이션 B는 Python 3.6 버전을 필요로 할 경우, 두 애플리케이션을 동시에 안정적으로 운영하기가 매우 까다로웠습니다. 가상 머신(VM) 솔루션과 그 한계 이러한 의존성 문제를 해결하기 위한 다음 단계는 가상 머신(Virtual Machine, VM)이었습니다. 하이퍼바이저(Hypervisor)라는 소프트웨어를 통해 물리적 하드웨어를 가상화하고, 그 위에 독립적인 게스트(Guest) OS를 설치하는 방식은 완벽한 격리 환경을 제공했습니다. 이제 하나의 물리 서버 위에서 서로 다른 OS나 다른 버전의 라이브러리를 사용하는 여러 애플리케이션을 충돌 없이 실행할 수 있게 되었습니다. 그러나 이 완벽한 격리는 상당한 대가를 요구했습니다. 각 VM은 자체적인 OS 커널을 포함한 완전한 OS 복사본을 실행해야 했기 때문에 극심한 자원 비효율성을 초래했습니다. VM 이미지는 수 기가바이트(GB)에 달하는 용량을 차지했고, 부팅하는 데 수 분이 소요되었습니다. 이는 서버 한 대에 올릴 수 있는 VM의 수를 제한(낮은 집적도)했으며, 빠른 개발과 배포를 추구하는 애자일(Agile) 환경에는 치명적인 단점이었습니다. 이처럼 소프트웨어 배포 기술의 역사는 하나의 문제를 해결하면 새로운 문제가 나타나는 순환의 연속이었습니다. 수동 문서의 불안정성은 IaC를 낳았고, IaC의 불완전한 격리는 VM을 필요로 했습니다. 그러나 VM의 무거움과 느림은 다시 ‘속도’와 ‘효율성’이라는 새로운 과제를 남겼습니다. 바로 이 지점에서 업계는 VM의 격리 수준을 유지하면서도 훨씬 가볍고 빠른 대안을 절실히 필요로 하게 되었고, 컨테이너 기술과 도커가 그 해답으로 부상하게 된 것입니다. 1.2 도커의 출현: 애플리케이션 이식성의 패러다임 전환 도커는 앞서 언급된 문제들, 특히 개발 환경과 운영 환경 간의 불일치 문제를 해결하기 위해 등장한 오픈소스 플랫폼입니다. 도커의 핵심은 애플리케이션을 실행에 필요한 모든 구성 요소—코드, 런타임, 시스템 도구, 라이브러리 등—와 함께 ‘컨테이너(Container)’라는 표준화된 단위로 패키징하는 것입니다. 이것은 소프트웨어 배포 방식에 있어 근본적인 패러다임 전환을 의미했습니다. 과거에는 애플리케이션을 배포하기 위해 서버 환경을 애플리케이션에 맞게 구성해야 했습니다. 하지만 도커는 애플리케이션 자체를 필요한 환경과 함께 하나의 ‘상자’에 담아버립니다. 이 컨테이너는 물류 산업의 선적 컨테이너와 비견될 수 있습니다. 내용물이 무엇이든 표준 규격의 컨테이너에 담기만 하면 전 세계 어느 항구에서나 동일한 장비로 하역하고 운송할 수 있는 것처럼, 도커 컨테이너 역시 일단 만들어지면 도커가 설치된 어떤 환경에서든—개발자의 노트북, 사내 테스트 서버, AWS나 Google Cloud 같은 클라우드 환경—동일하게 실행되는 것을 보장합니다. 이로써 “내 컴퓨터에서는 되는데”라는 오랜 난제는 마침내 해결의 실마리를 찾게 되었습니다. 도커의 진정한 혁신은 컨테이너 기술 자체를 발명한 것이 아니라, 기존에 존재하던 복잡한 기술을 개발자들이 쉽게 사용하고 공유할 수 있는 생태계로 구축했다는 점에 있습니다. 컨테이너의 기반이 되는 프로세스 격리 기술은 이미 리눅스 커널에 LXC(Linux Containers) 형태로 존재했습니다. 하지만 LXC는 시스템 관리자 수준의 깊은 지식이 필요한 저수준(low-level) 도구였으며, 애플리케이션을 표준화된 방식으로 패키징하고 배포하는 간편한 방법을 제공하지 못했습니다. 도커는 바로 이 지점에서 빛을 발했습니다. Dockerfile이라는 간단한 텍스트 파일로 이미지 생성 과정을 코드로 정의하고(docker build), 이렇게 만들어진 이식성 높은 도커 이미지를 도커 허브(Docker Hub)라는 중앙 저장소(Registry)를 통해 쉽게 공유(docker push, docker pull)할 수 있는 일련의 워크플로우를 제공했습니다. 즉, 도커는 복잡한 리눅스 커널 기술을 추상화하고 뛰어난 개발자 경험(Developer Experience, DX)을 제공함으로써, 컨테이너 기술을 소수의 전문가를 위한 도구에서 모든 개발자를 위한 현대 소프트웨어 개발의 핵심 요소로 탈바꿈시켰습니다. 1.3 기술적 기반: 리눅스 컨테이너 기술 심층 분석 도커의 마법과 같은 기능 뒤에는 리눅스 커널이 오랫동안 제공해 온 강력한 기술들이 자리 잡고 있습니다. 도커는 이러한 커널 기능들을 조합하고 추상화하여 사용자 친화적인 인터페이스를 제공하는 것입니다. 도커의 작동 원리를 전문가 수준에서 이해하기 위해서는 이 기반 기술들에 대한 탐구가 필수적입니다. 컨테이너화의 두 기둥: 네임스페이스와 Cgroups 도커가 컨테이너에 격리된 환경을 제공하는 핵심 기술은 크게 두 가지입니다. 네임스페이스 (Namespaces): 격리의 기반 네임스페이스는 하나의 시스템에서 특정 프로세스의 관점을 제한하여, 마치 자신만의 독립된 시스템에서 실행되는 것처럼 보이게 만드는 커널 기능입니다. 도커는 여러 종류의 네임스페이스를 활용하여 컨테이너의 각 요소를 격리합니다. PID 네임스페이스 (Process ID): 프로세스 트리를 격리합니다. 컨테이너 내부의 애플리케이션은 자신이 시스템의 첫 번째 프로세스(PID 1)인 것처럼 인식하게 됩니다. 호스트 시스템의 다른 프로세스들은 컨테이너 내부에서 보이지 않습니다. NET 네임스페이스 (Network): 독립적인 네트워크 스택을 제공합니다. 각 컨테이너는 자신만의 가상 네트워크 인터페이스, IP 주소, 라우팅 테이블, 포트 공간을 가집니다. 이 덕분에 여러 컨테이너가 동일한 포트(예: 80번 포트)를 충돌 없이 사용할 수 있습니다. MNT 네임스페이스 (Mount): 파일시스템 마운트 포인트를 격리합니다. 컨테이너는 호스트의 파일시스템 구조와는 다른, 자신만의 독립적인 루트 파일시스템을 가질 수 있습니다. 기타 네임스페이스: 이 외에도 호스트 이름을 격리하는 UTS, 프로세스 간 통신(IPC)을 격리하는 IPC, 사용자 및 그룹 ID를 격리하는 User 네임스페이스 등이 사용됩니다. 컨트롤 그룹 (Control Groups, cgroups): 자원 제어 Cgroups는 프로세스 그룹이 사용할 수 있는 시스템 자원(CPU, 메모리, 디스크 I/O 등)의 양을 제한하고 측정하며 격리하는 커널 기능입니다. 네임스페이스가 ‘무엇을 볼 수 있는가’를 격리한다면, Cgroups는 ‘얼마나 사용할 수 있는가’를 제어합니다. 이를 통해 특정 컨테이너가 호스트의 모든 CPU나 메모리를 독점하여 다른 컨테이너나 호스트 시스템 자체를 마비시키는 상황을 방지할 수 있습니다. 커널 공유 패러다임: 효율성과 보안의 양날의 검 컨테이너와 가상 머신(VM)의 가장 근본적인 아키텍처 차이는 바로 ‘커널 공유’에 있습니다. 이 차이점은 컨테이너의 가장 큰 장점이자 동시에 잠재적인 약점이 됩니다. VM은 하이퍼바이저 위에서 하드웨어를 가상화하고, 각 VM은 독립적인 게스트 OS와 커널을 가집니다. 이 구조는 매우 강력한 격리를 제공하지만, 각 OS를 구동하기 위한 오버헤드가 막대합니다. 반면, 컨테이너는 하드웨어를 가상화하지 않습니다. 대신 호스트 OS의 커널을 모든 컨테이너가 공유하며, 네임스페이스와 Cgroups를 통해 프로세스 수준에서 격리를 구현합니다. 게스트 OS가 없기 때문에 컨테이너는 극도로 가볍고(이미지 크기가 수십 메가바이트(MB) 수준), 시작 속도가 수 초에 불과하며, 단일 호스트에 훨씬 더 많은 수의 컨테이너를 실행할 수 있습니다(높은 집적도). 이것이 컨테이너가 VM에 비해 압도적인 효율성을 갖는 이유입니다. 하지만 이 커널 공유 모델은 보안적 측면에서 트레이드오프를 가집니다. 모든 컨테이너가 동일한 커널을 공유하므로, 만약 호스트 커널 자체에 보안 취약점이 존재한다면, 악의적인 컨테이너가 이 취약점을 공격하여 격리 경계를 넘어 호스트 시스템이나 다른 컨테이너에 영향을 미칠 가능성이 이론적으로 존재합니다. 이는 각자 독립된 커널을 가진 VM에서는 발생하기 어려운 유형의 보안 위협입니다. 따라서 컨테이너 환경을 운영할 때는 루트(root) 권한으로 컨테이너를 실행하는 것을 피하고, AppArmor나 SELinux와 같은 추가적인 보안 프로파일을 적용하며, 신뢰할 수 있는 이미지만을 사용하는 등의 보안 강화 조치가 중요합니다. 이처럼 커널 공유 패러다임을 이해하는 것은 컨테이너 기술의 본질을 파악하고, 특정 시나리오에 VM과 컨테이너 중 무엇이 더 적합한지 정보에 입각한 결정을 내리는 데 있어 매우 중요합니다. 2부: 아키텍처 심층 분석: 도커 구성 요소 및 비교 2.1 도커화된 애플리케이션의 해부학 도커 플랫폼은 여러 핵심 구성 요소들의 유기적인 상호작용으로 이루어집니다. 이들 각 요소의 개념과 관계를 이해하는 것은 도커를 효과적으로 활용하기 위한 필수 과정입니다. 도커 이미지: 불변의 설계도 도커 이미지는 컨테이너를 생성하기 위한 모든 정보를 담고 있는 읽기 전용(read-only) 템플릿입니다. 이는 단순히 애플리케이션 코드만 포함하는 것이 아니라, 해당 코드를 실행하는 데 필요한 런타임, 라이브러리, 환경 변수, 설정 파일 등 모든 종속성을 하나의 패키지로 묶은 것입니다. 이미지는 한번 생성되면 그 내용이 변하지 않는 ‘불변성(Immutability)’을 특징으로 합니다. 이미지의 가장 중요한 기술적 특징은 계층화된 파일 시스템(Layered Filesystem)입니다. 이미지는 여러 개의 얇은 계층(Layer)들이 겹쳐진 구조로 이루어져 있습니다. Dockerfile의 각 명령어(instruction)는 새로운 계층을 생성합니다. 예를 들어, 우분투(Ubuntu) 이미지를 기반으로(FROM ubuntu), 웹 서버(Nginx)를 설치하고(RUN apt-get install nginx), 애플리케이션 코드를 복사하면(COPY. /app), 각 단계가 별도의 계층으로 저장됩니다. 도커는 이러한 계층들을 재사용하여 이미지 빌드와 배포를 매우 효율적으로 만듭니다. 만약 로컬 시스템에 이미 우분투 계층과 Nginx 계층이 존재한다면, 새로운 이미지를 빌드할 때 도커는 변경된 애플리케이션 코드 계층만 새로 생성합니다. 마찬가지로, 이미지를 다른 서버로 전송할 때도 해당 서버에 이미 존재하는 계층은 다시 다운로드하지 않고, 없는 계층만 전송합니다. 이 ‘Copy-on-Write’ 전략 덕분에 이미지의 저장 및 전송에 필요한 시간과 용량을 획기적으로 줄일 수 있습니다. 도커 컨테이너: 실행 가능한 살아있는 인스턴스 컨테이너는 이미지의 실행 가능한 인스턴스입니다. 이미지가 ‘설계도’라면, 컨테이너는 그 설계도로 지은 ‘집’에 비유할 수 있습니다. 이미지를 기반으로 컨테이너를 시작하면, 도커는 이미지의 읽기 전용 계층들 위에 얇은 쓰기 가능한 컨테이너 계층(Writable Container Layer)을 추가합니다. 컨테이너가 실행되는 동안 발생하는 모든 변경 사항—새로운 파일 생성, 기존 파일 수정, 로그 기록 등—은 이 쓰기 가능한 계층에 저장됩니다. 이때 원본 이미지 계층들은 전혀 변경되지 않습니다. 이 구조 덕분에 하나의 이미지로 수많은 컨테이너를 동시에 생성하고 실행할 수 있으며, 각 컨테이너는 서로에게 영향을 주지 않는 독립적인 상태를 유지합니다. 컨테이너는 본질적으로 ‘일시적(ephemeral)’입니다. 컨테이너를 삭제하면 이 쓰기 가능한 계층도 함께 사라지므로, 컨테이너 내부에서 작업한 내용은 모두 소실됩니다. 따라서 데이터베이스 파일이나 사용자가 업로드한 파일처럼 영구적으로 보존해야 하는 데이터는 컨테이너 외부에 저장해야 합니다. 이를 위해 도커는 ‘볼륨(Volume)’이라는 메커니즘을 제공합니다. Dockerfile: 이미지 생성을 위한 코드 Dockerfile은 이미지를 어떻게 빌드할지를 명시하는 텍스트 기반의 스크립트 파일입니다. 이는 이미지 생성을 위한 ‘레시피’와 같으며, 모든 빌드 과정을 코드로 관리할 수 있게 해줍니다. 이를 통해 누구나, 언제, 어디서든 동일한 이미지를 재현할 수 있어, 빌드 과정의 자동화와 일관성을 보장합니다. 아래는 주요 Dockerfile 명령어와 그 역할에 대한 설명입니다. 도커 레지스트리: 이미지의 저장 및 배포 레지스트리는 도커 이미지를 저장하고 배포하는 중앙 집중식 저장소입니다. 개발자는 로컬에서 docker build 명령어로 이미지를 생성한 후, docker push 명령어로 이 이미지를 레지스트리에 업로드합니다. 그러면 운영 서버나 다른 개발자는 docker pull 명령어로 해당 이미지를 내려받아 사용할 수 있습니다. 가장 널리 알려진 공개 레지스트리는 도커 허브(Docker Hub)입니다. 하지만 대부분의 기업 환경에서는 보안, 접근 제어, 네트워크 성능 등의 이유로 자체적인 사설(private) 레지스트리를 구축하여 사용합니다. 대표적인 사설 레지스트리 서비스로는 Amazon Web Services(AWS)의 Elastic Container Registry (ECR), Google Cloud의 Container Registry (GCR), 그리고 Red Hat의 Quay 등이 있습니다. 2.2 컨테이너 vs. 가상 머신: 종합 분석 컨테이너와 가상 머신(VM)은 모두 애플리케이션을 격리된 환경에서 실행하기 위한 가상화 기술이지만, 그 접근 방식과 특성에는 근본적인 차이가 있습니다. 어떤 기술이 더 우월하다기보다는, 각 기술의 장단점과 트레이드오프를 이해하고 주어진 요구사항에 가장 적합한 도구를 선택하는 것이 중요합니다. 아키텍처 대조 가상 머신(VM): VM은 하이퍼바이저(Hypervisor) 위에서 동작합니다. 하이퍼바이저는 물리적인 하드웨어(CPU, 메모리, 스토리지)를 가상으로 에뮬레이션하여 여러 개의 독립적인 가상 컴퓨터를 생성합니다. 각 VM은 자신만의 완전한 게스트 운영체제(Guest OS)와 커널을 가지고 있으며, 그 위에서 애플리케이션과 관련 라이브러리들이 실행됩니다. 이 구조는 하드웨어 수준의 완전한 격리를 제공합니다. 컨테이너: 컨테이너는 호스트 운영체제(Host OS) 위에서 동작하는 컨테이너 엔진(예: 도커 엔진)에 의해 관리됩니다. 컨테이너들은 게스트 OS 없이 호스트 OS의 커널을 공유합니다. 격리는 커널의 네임스페이스(namespaces)와 Cgroups 기능을 통해 프로세스 수준에서 이루어집니다. 즉, 컨테이너는 OS를 가상화하는 것이 아니라, OS 위의 프로세스들을 격리하는 방식입니다. 이러한 아키텍처의 차이는 성능, 속도, 크기, 이식성 등 여러 측면에서 뚜렷한 차이를 만들어냅니다. 올바른 도구 선택하기 결론적으로, 컨테이너와 VM은 상호 배타적인 기술이 아니라, 서로 다른 문제 영역을 해결하는 도구입니다. VM을 사용해야 할 때: 리눅스 호스트에서 윈도우 서버를 실행하는 등, 호스트와 다른 종류의 OS가 필요한 경우. 여러 고객사에게 서비스를 제공하는 멀티 테넌트(multi-tenant) 환경과 같이, 커널 수준의 완전한 보안 격리가 최우선 순위일 때. 컨테이너화하기 어려운 거대한 모놀리식(monolithic) 레거시 애플리케이션을 그대로 운영해야 할 때. 컨테이너를 사용해야 할 때: 애플리케이션을 작고 독립적인 서비스로 분해하는 마이크로서비스 아키텍처를 구축할 때. 빠른 개발, 테스트, 배포 주기를 자동화하는 DevOps 및 CI/CD 문화를 도입하고자 할 때. 리소스 효율성을 극대화하고, 트래픽에 따라 애플리케이션을 신속하게 확장(scale-out)해야 할 때. 실제로는 이 두 기술을 함께 사용하는 하이브리드 접근 방식도 널리 쓰입니다. 예를 들어, 클라우드 환경에서 프로비저닝된 VM 내부에 여러 개의 컨테이너를 실행함으로써, VM이 제공하는 강력한 인프라 격리와 컨테이너가 제공하는 애플리케이션 배포의 유연성을 동시에 활용할 수 있습니다. 이러한 선택의 기저에는 ‘무엇을 가상화할 것인가’라는 근본적인 질문이 있습니다. VM은 물리적인 기계(machine)를 가상화하여 완전한 서버 환경을 제공하는 데 목적이 있습니다. 반면, 컨테이너는 운영체제(OS)를 가상화하여 애플리케이션 실행 환경을 제공하는 데 목적이 있습니다. 이 추상화 수준의 차이가 두 기술의 모든 특성을 결정하며, 개발자와 운영자는 해결하려는 문제의 성격에 따라 가장 적합한 추상화 도구를 선택해야 합니다. 3부: 실제 적용 및 생태계 통합 3.1 도커 워크플로우 마스터하기 도커의 이론적 개념을 이해했다면, 다음 단계는 이를 실제 개발 워크플로우에 적용하는 것입니다. 도커는 주로 커맨드 라인 인터페이스(CLI)를 통해 제어되며, 몇 가지 핵심적인 명령어만 익히면 기본적인 작업 흐름을 능숙하게 다룰 수 있습니다. 핵심 CLI 명령어 개발자가 일상적으로 사용하는 필수 도커 명령어는 다음과 같습니다. docker pull [이미지명:태그]: 원격 레지스트리(기본값은 도커 허브)에서 이미지를 로컬 시스템으로 다운로드합니다. 예를 들어, 최신 버전의 Ubuntu 이미지를 받으려면 docker pull ubuntu:latest를 실행합니다. docker build -t [태그명].: 현재 디렉터리에 있는 Dockerfile을 사용하여 새로운 이미지를 빌드하고, 지정된 태그(-t 옵션)를 붙입니다. 예를 들어, my-app:1.0이라는 이름으로 이미지를 빌드하려면 docker build -t my-app:1.0.을 실행합니다. docker images: 로컬 시스템에 저장된 이미지들의 목록을 보여줍니다. docker run [옵션][이미지명]: 지정된 이미지를 기반으로 새로운 컨테이너를 생성하고 실행하는 가장 중요한 명령어입니다. 다양한 옵션을 통해 컨테이너의 동작을 세밀하게 제어할 수 있습니다. -d: 컨테이너를 백그라운드에서 실행(detached mode)합니다. 이 옵션이 없으면 컨테이너는 포그라운드에서 실행되며, 터미널 세션이 컨테이너의 표준 입출력에 연결됩니다. -p [호스트_포트]:[컨테이너_포트]: 호스트의 특정 포트를 컨테이너의 포트로 매핑(포트 포워딩)합니다. 예를 들어 -p 8080:80은 호스트의 8080번 포트로 들어오는 트래픽을 컨테이너의 80번 포트로 전달합니다. 웹 서버와 같이 외부에서 접속해야 하는 서비스를 실행할 때 필수적입니다. -v [호스트_경로]:[컨테이너_경로]: 호스트의 디렉터리나 파일을 컨테이너 내부의 경로에 마운트(볼륨 마운트)합니다. 이는 컨테이너가 삭제되어도 데이터를 영구적으로 보존하거나, 개발 시 호스트에서 수정한 소스 코드를 즉시 컨테이너에 반영하는 데 사용됩니다. –name [컨테이너명]: 컨테이너에 사람이 식별하기 쉬운 이름을 부여합니다. 이름을 지정하지 않으면 도커가 무작위 이름을 생성합니다. –rm: 컨테이너가 종료될 때 자동으로 삭제되도록 설정합니다. 임시 테스트용 컨테이너를 실행할 때 유용합니다. docker ps: 현재 실행 중인 컨테이너들의 목록을 보여줍니다. -a 옵션을 추가하면(docker ps -a), 종료된 컨테이너를 포함한 모든 컨테이너 목록을 확인할 수 있습니다. docker stop: 실행 중인 컨테이너를 정상적으로 중지시킵니다. docker rm: 중지된 컨테이너를 삭제합니다. 실행 중인 컨테이너를 강제로 삭제하려면 -f 옵션을 사용합니다. 도커 컴포즈: 다중 컨테이너 애플리케이션 오케스트레이션 현대의 웹 애플리케이션은 웹 프론트엔드, API 백엔드, 데이터베이스, 캐시 서버 등 여러 개의 서비스가 유기적으로 연동되어 구성되는 경우가 많습니다. 이러한 다중 컨테이너 환경을 docker run 명령어로 하나씩 관리하는 것은 매우 번거롭고 오류가 발생하기 쉽습니다. 도커 컴포즈(Docker Compose)는 이러한 문제를 해결하기 위한 도구로, 여러 개의 컨테이너로 구성된 애플리케이션을 하나의 단위로 정의하고 실행할 수 있게 해줍니다. 도커 컴포즈는 docker-compose.yml이라는 YAML 형식의 설정 파일을 사용하며, 이 파일 안에 애플리케이션을 구성하는 모든 서비스(컨테이너), 네트워크, 볼륨 등을 코드로 정의합니다. docker-compose.yml 심층 분석 다음은 웹 서비스(Nginx)와 데이터베이스(PostgreSQL)로 구성된 간단한 애플리케이션의 docker-compose.yml 예시입니다. version: ‘3.8’ # YAML 파일 포맷 버전services: # 애플리케이션을 구성하는 서비스(컨테이너)들을 정의 db: # ‘db’라는 이름의 서비스 image: postgres:13 # 사용할 이미지 volumes: # 데이터 영속성을 위한 볼륨 마운트 – db_data:/var/lib/postgresql/data environment: # 환경 변수 설정 – POSTGRES_DB=mydb – POSTGRES_USER=user – POSTGRES_PASSWORD=password web: # ‘web’이라는 이름의 서비스 build:. # 현재 디렉터리의 Dockerfile을 사용하여 이미지 빌드 ports: # 포트 매핑 – “8000:80″ depends_on: # 의존성 정의 – dbvolumes: # 최상위 레벨에서 볼륨 정의 db_data: version: 사용할 도커 컴포즈 파일 포맷의 버전을 명시합니다. services: 애플리케이션을 구성하는 각 컨테이너를 서비스 단위로 정의합니다. 위 예시에서는 db와 web 두 개의 서비스가 있습니다. image / build: 서비스가 사용할 이미지를 지정합니다. image는 레지스트리에서 이미지를 가져오고, build는 Dockerfile을 통해 이미지를 직접 빌드합니다. ports, volumes, environment: docker run 명령어의 옵션들과 동일한 역할을 합니다. depends_on: 서비스 간의 시작 순서를 제어합니다. 위 예시에서 web 서비스는 db 서비스가 시작된 이후에 시작됩니다. 이는 웹 애플리케이션이 시작되기 전에 데이터베이스가 준비되어야 하는 일반적인 시나리오에 유용합니다. 핵심 컴포즈 명령어 docker-compose up: docker-compose.yml 파일에 정의된 모든 서비스를 생성하고 시작합니다. -d 옵션을 붙이면 백그라운드에서 실행됩니다. docker-compose down: up으로 생성된 컨테이너, 네트워크 등을 모두 중지하고 삭제합니다. -v 옵션을 추가하면 정의된 볼륨까지 함께 삭제합니다. 도커 컴포즈를 사용하면 단 두 개의 명령어로 복잡한 다중 컨테이너 애플리케이션의 전체 생명주기를 관리할 수 있어, 개발 환경 구성과 테스트 자동화에 혁신적인 편의성을 제공합니다. 3.2 현대 아키텍처의 촉매, 도커: 마이크로서비스 혁명 도커와 마이크로서비스 아키텍처(Microservice Architecture, MSA)는 서로를 위해 태어났다고 할 만큼 강력한 시너지를 발휘합니다. MSA는 하나의 거대한 애플리케이션(모놀리식 아키텍처)을 기능별로 잘게 쪼개어, 작고 독립적으로 배포 가능한 서비스들의 조합으로 만드는 설계 방식입니다. 도커의 핵심 특징들은 MSA를 구현할 때 발생하는 주요 기술적 과제들을 해결하는 완벽한 솔루션을 제공합니다. 도커와 MSA의 완벽한 조화 독립적인 배포(Independent Deployment): MSA의 핵심은 각 서비스가 다른 서비스에 영향을 주지 않고 독립적으로 개발, 테스트, 배포될 수 있어야 한다는 것입니다. 도커는 각 마이크로서비스를 자체적인 종속성을 모두 포함한 컨테이너로 패키징함으로써 이를 완벽하게 지원합니다. 이제 ‘사용자 관리’ 서비스의 버그를 수정하기 위해 전체 애플리케이션을 재배포할 필요 없이, 해당 서비스의 컨테이너만 새로 빌드하여 교체하면 됩니다. 기술적 이질성(Polyglot) 지원: 각 마이크로서비스는 저마다 다른 기술적 요구사항을 가질 수 있습니다. 예를 들어, 실시간 통신이 중요한 서비스는 Node.js로, 대규모 데이터 처리가 필요한 서비스는 Python으로, 고성능 연산이 필요한 서비스는 Go로 개발하는 것이 더 효율적일 수 있습니다. 도커 컨테이너는 각 서비스에 필요한 런타임과 라이브러리를 완벽하게 격리해주므로, 하나의 애플리케이션 내에서 다양한 프로그래밍 언어와 프레임워크를 자유롭게 혼용하는 ‘폴리글랏(polyglot)’ 아키텍처를 쉽게 구현할 수 있습니다. 장애 격리(Fault Isolation): 모놀리식 아키텍처에서는 한 기능의 오류가 전체 애플리케이션의 장애로 이어지기 쉽습니다. 반면, MSA에서는 각 서비스가 독립된 컨테이너에서 실행되므로, 특정 서비스에 장애가 발생하더라도 그 영향이 해당 컨테이너 내로 국한됩니다. 시스템의 다른 부분들은 정상적으로 동작을 계속할 수 있으며, 오케스트레이션 도구는 장애가 발생한 컨테이너를 자동으로 재시작하여 서비스의 복원력을 높일 수 있습니다. 유연한 확장성(Scalability): 애플리케이션의 부하가 특정 기능에 집중되는 경우가 많습니다. MSA에서는 트래픽이 몰리는 서비스의 컨테이너 인스턴스 수만 선택적으로 늘려 수평적 확장(horizontal scaling)을 할 수 있습니다. 예를 들어, 쇼핑몰에서 ‘상품 조회’ 서비스에 트래픽이 급증하면, 해당 서비스의 컨테이너만 수십 개로 늘리고, 상대적으로 사용량이 적은 ‘회원 정보 수정’ 서비스는 최소한의 컨테이너만 유지하여 자원을 효율적으로 사용할 수 있습니다. 이처럼 도커는 MSA의 개념을 현실 세계에서 운영 가능하게 만든 핵심 기술입니다. 마이크로서비스라는 개념 자체는 도커 이전에도 존재했지만, 수많은 작은 서비스들을 각각의 가상 머신(VM)에 배포하고 관리하는 것은 운영적, 비용적으로 거의 불가능에 가까웠습니다. 각 VM이 무거운 게스트 OS를 포함해야 했기 때문입니다. 도커는 경량의 표준화된 배포 단위인 ‘컨테이너’를 제공함으로써, 수백 개의 마이크로서비스를 상대적으로 적은 수의 호스트 머신 위에서 효율적으로 운영할 수 있는 길을 열었습니다. 즉, 도커는 MSA가 이론에서 벗어나 실용적인 주류 아키텍처로 자리 잡는 데 결정적인 역할을 한 것입니다. 3.3 소프트웨어 생명주기 자동화: CI/CD 파이프라인에서의 도커 도커의 가장 강력한 활용 사례 중 하나는 지속적인 통합(Continuous Integration, CI) 및 지속적인 배포(Continuous Deployment, CD) 파이프라인을 구축하는 것입니다. CI/CD는 소프트웨어의 빌드, 테스트, 배포 과정을 자동화하여 개발 생산성과 배포 안정성을 높이는 DevOps의 핵심 실천법입니다. 도커는 이 과정에서 ‘환경의 일관성’을 보장하는 중추적인 역할을 합니다. CI/CD에서 도커의 역할 전통적인 CI/CD 파이프라인에서는 CI 서버(예: Jenkins)의 환경과 실제 프로덕션 서버의 환경이 미묘하게 달라, “CI에서는 성공했는데 배포하니 실패했다”는 문제가 빈번하게 발생했습니다. 도커는 애플리케이션과 그 실행 환경을 하나의 이미지로 묶어버림으로써 이 문제를 근본적으로 해결합니다. 도커 이미지는 CI/CD 파이프라인 전체를 관통하는 단일하고 불변하는 결과물(artifact)이 되어, 빌드, 테스트, 배포의 모든 단계가 동일한 환경에서 수행됨을 보장합니다. GitHub Actions를 이용한 CI/CD 파이프라인 구축 예시 GitHub Actions는 GitHub 저장소에 내장된 CI/CD 자동화 도구입니다. 다음은 개발자가 develop 브랜치에 코드를 푸시(push)하면, 자동으로 애플리케이션을 빌드하여 도커 이미지로 만들고, 이를 AWS EC2 서버에 배포하는 CI/CD 파이프라인의 단계별 과정입니다. 트리거 (Trigger): 개발자가 로컬에서 작업한 코드를 git push origin develop 명령어로 GitHub 저장소의 develop 브랜치에 푸시합니다. 이것이 파이프라인을 시작하는 신호가 됩니다. CI 단계 (GitHub Actions Runner에서 실행): GitHub는 푸시 이벤트를 감지하고, 미리 정의된 워크플로우(.github/workflows/develop.yml)를 실행할 가상 머신(Runner)을 준비합니다. 코드 체크아웃: Runner는 actions/checkout 액션을 사용하여 develop 브랜치의 최신 소스 코드를 가져옵니다. 도커 이미지 빌드: Runner는 프로젝트 루트에 있는 Dockerfile을 사용하여 docker build 명령어로 애플리케이션의 도커 이미지를 생성합니다. 이 과정은 매번 깨끗하게 초기화된 환경에서 수행됩니다. (선택) 자동화 테스트: 새로 빌드된 컨테이너 내부에서 단위 테스트나 통합 테스트를 실행하여 코드의 무결성을 검증합니다. 레지스트리 로그인 및 푸시: 테스트를 통과한 이미지는 운영 환경에서 사용될 준비가 된 것입니다. Runner는 GitHub Secrets에 안전하게 저장된 인증 정보(DOCKER_HUB_USERNAME, DOCKER_HUB_ACCESS_TOKEN)를 사용하여 도커 허브와 같은 컨테이너 레지스트리에 로그인합니다. 그 후, 빌드된 이미지를 고유한 태그와 함께 레지스트리로 docker push합니다. CD 단계 (배포): CI 단계가 성공적으로 완료되면, CD 단계가 시작됩니다. 프로덕션 서버 접속: Runner는 appleboy/ssh-action과 같은 액션을 사용하여, 역시 GitHub Secrets에 저장된 SSH 키(EC2_SSH_KEY)와 서버 주소(EC2_HOST)를 이용해 프로덕션 EC2 서버에 안전하게 접속합니다. 애플리케이션 업데이트: EC2 서버에 접속한 Runner는 미리 준비된 셸 스크립트를 실행합니다. 이 스크립트는 일반적으로 다음과 같은 작업을 수행합니다 : 레지스트리에서 방금 푸시된 새로운 버전의 이미지를 docker pull합니다. 기존에 실행 중이던 구버전의 컨테이너를 docker stop 및 docker rm 명령어로 중지하고 제거합니다. (무중단 배포를 위해서는 블루/그린 배포 등의 고급 전략이 필요합니다.) 새로 받은 이미지로 새로운 컨테이너를 docker run 또는 docker-compose up -d 명령어를 통해 시작합니다. 이 모든 과정이 자동화되어, 개발자는 코드 작성에만 집중할 수 있고, 배포 과정에서 발생할 수 있는 인적 오류는 최소화됩니다. 아래는 이 파이프라인을 구현하는 GitHub Actions 워크플로우 파일(develop.yml)의 핵심 구조입니다. 3.4 고급 활용 사례와 더 넓은 생태계 도커의 활용 범위는 웹 애플리케이션 배포를 넘어 다양한 분야로 확장되며, 더 큰 기술 생태계 속에서 중요한 역할을 수행합니다. 재현 가능한 연구 환경 구축 데이터 과학 및 학술 연구 분야에서 ‘결과의 재현성’은 매우 중요한 원칙입니다. 과거에는 연구에 사용된 복잡한 소프트웨어 환경(특정 버전의 OS, 라이브러리, 프레임워크 등)을 다른 연구자가 똑같이 구성하기 어려워 연구 결과를 검증하는 데 큰 장벽이 있었습니다. 도커는 이러한 문제를 해결하는 이상적인 도구입니다. 연구에 사용된 모든 소프트웨어 스택과 종속성을 Dockerfile로 명시하고 하나의 도커 이미지로 패키징하면, 누구든지 해당 이미지를 실행하는 것만으로 원본 연구와 100% 동일한 환경을 즉시 구축할 수 있습니다. 특히 GPU를 사용하는 딥러닝 연구에서는 NVIDIA Container Toolkit을 사용하여 도커 컨테이너가 호스트의 NVIDIA GPU에 안전하게 접근하도록 할 수 있습니다. 이를 통해 복잡한 CUDA 및 cuDNN 버전 종속성 문제를 해결하고, 이식성 높은 GPU 가속 연구 환경을 만들 수 있습니다. 도커와 쿠버네티스: 상호 보완적 관계 도커가 대중화되면서 개발자들은 수많은 컨테이너를 더 효율적으로 관리해야 할 필요성을 느끼게 되었습니다. 도커는 단일 호스트에서 컨테이너를 생성하고 실행하는 데는 탁월하지만, 여러 서버(클러스터)에 걸쳐 분산된 컨테이너들을 관리하는 문제, 예를 들어 서비스 디스커버리, 로드 밸런싱, 자동 복구(self-healing), 스케일링, 무중단 업데이트 등의 ‘Day 2 운영’ 문제는 직접 해결해주지 않습니다. 이러한 문제를 해결하기 위해 등장한 것이 바로 컨테이너 오케스트레이션(Container Orchestration) 도구이며, 현재 업계의 사실상 표준은 쿠버네티스(Kubernetes)입니다. 도커와 쿠버네티스는 경쟁 관계가 아니라, 클라우드 네이티브 스택의 서로 다른 계층을 담당하는 상호 보완적인 관계입니다. 도커의 역할: 애플리케이션을 표준화된, 이식성 있는 컨테이너 이미지로 패키징하고 빌드하는 역할. 즉, ‘무엇을(what)’ 실행할 것인지를 정의합니다. 쿠버네티스의 역할: 도커로 만들어진 컨테이너 이미지들을 가져와, 클러스터 환경에서 대규모로 안정적으로 실행하고 관리하는 역할. 즉, ‘어떻게(how)’ 그리고 ‘어디서(where)’ 실행할 것인지를 담당합니다. 비유하자면, 도커는 화물을 담는 ‘표준 선적 컨테이너’를 제공하고, 쿠버네티스는 이 컨테이너들을 전 세계로 운송하고 관리하는 ‘거대한 물류 시스템(항구, 크레인, 화물선단)’을 제공하는 것과 같습니다. 개발자는 도커를 사용하여 애플리케이션을 만들고, 운영팀은 쿠버네티스를 사용하여 그 애플리케이션을 안정적으로 운영합니다. 이러한 기술의 발전은 인프라 관리의 추상화 수준을 지속적으로 높여왔습니다. 과거 운영자들은 물리적/가상 머신(Machine)을 관리했습니다. 도커의 등장으로 관심사는 개별 프로세스(Process)의 격리된 실행 환경으로 옮겨갔습니다. 그리고 쿠버네티스는 여기서 한 단계 더 나아가, 운영자가 개별 컨테이너가 아닌 전체 분산 애플리케이션(Application)의 원하는 상태(desired state)를 선언하고 관리할 수 있게 해주었습니다. 이처럼 도커는 개발자와 운영자가 인프라의 복잡성에서 벗어나 애플리케이션의 본질적인 가치에 더 집중할 수 있도록 만드는 클라우드 네이티브 시대로의 전환에 있어 필수적인 교두보 역할을 수행했습니다. 결론 도커는 현대 소프트웨어 개발과 배포의 패러다임을 근본적으로 변화시킨 혁신적인 기술입니다. 이는 단순히 새로운 가상화 도구의 등장을 넘어, 애플리케이션을 개발하고, 공유하며, 실행하는 방식 전반에 걸친 표준을 제시했습니다. 보고서에서 분석한 바와 같이, 도커의 성공은 기존의 배포 방식이 가졌던 고질적인 문제, 즉 ‘환경 불일치’에서 비롯된 비효율성과 불안정성을 해결한 데 있습니다. 리눅스 커널의 네임스페이스와 Cgroups와 같은 성숙한 기술을 기반으로, 도커는 가상 머신(VM)의 강력한 격리라는 장점과 네이티브 실행 환경의 효율성 및 속도라는 장점을 결합한 ‘컨테이너’라는 실용적인 해법을 제시했습니다. Dockerfile, 이미지, 컨테이너, 레지스트리로 이어지는 직관적인 워크플로우는 복잡했던 컨테이너 기술을 모든 개발자가 쉽게 접근하고 활용할 수 있도록 민주화했습니다. 주요 기능을 통해 살펴본 도커의 핵심 가치는 이식성, 일관성, 효율성으로 요약할 수 있습니다. 도커 이미지는 애플리케이션과 그 종속성을 하나의 불변하는 단위로 묶어, 개발자의 노트북에서부터 프로덕션 클라우드 서버에 이르기까지 어떤 환경에서든 동일한 실행을 보장합니다. 계층화된 파일 시스템은 빌드와 배포 속도를 획기적으로 향상시켰으며, 경량 구조는 서버 자원의 집적도를 극대화하여 비용 효율성을 높였습니다. 활용 방법 측면에서 도커는 특정 기술 영역에 국한되지 않고 소프트웨어 생명주기 전반에 깊숙이 통합되었습니다. 개발 단계에서는 도커 컴포즈를 통해 복잡한 로컬 개발 환경을 손쉽게 구성할 수 있게 해주었고, 운영 단계에서는 마이크로서비스 아키텍처(MSA)의 실질적인 구현을 가능케 하는 핵심 동력이 되었습니다. 또한, CI/CD 파이프라인에 통합되어 빌드-테스트-배포 과정을 자동화하고 신뢰성을 확보하는 데 중추적인 역할을 수행하며, 데이터 과학 분야에서는 재현 가능한 연구 환경을 구축하는 표준으로 자리 잡았습니다. 궁극적으로 도커는 더 높은 수준의 추상화를 향한 IT 인프라 발전의 중요한 이정표입니다. 도커가 컨테이너를 표준화함으로써, 쿠버네티스와 같은 오케스트레이션 플랫폼이 등장하여 개별 컨테이너가 아닌 전체 애플리케이션을 관리하는 시대를 열 수 있었습니다. 따라서 도커를 이해하는 것은 단순히 컨테이너 기술을 배우는 것을 넘어, 오늘날의 클라우드 네이티브 생태계와 DevOps 문화의 근간을 이해하는 것과 같습니다. 앞으로도 도커는 애플리케이션을 세상에 전달하는 가장 빠르고 안정적인 방법 중 하나로 그 중요성을 이어나갈 것입니다. 참고 자료
- Docker의 등장과 특징 – velog, https://velog.io/@rokwon_k/%EB%8F%84%EC%BB%A4%EC%9D%98-%EB%93%B1%EC%9E%A5%EA%B3%BC-%ED%8A%B9%EC%A7%95 2. [Docker]도커의 등장 배경 알아보기 – 개발개발 – 티스토리, https://ellie-dev.tistory.com/7 3. 컨테이너 vs 가상머신 – 태어난김에 개발자 – 티스토리, https://born-dev.tistory.com/39 4. 가상머신 vs 컨테이너 차이점 비교, 컨테이너를 사용해야 하는 이유 – 프린세스 다이어리, https://eunjinii.tistory.com/11 5. 컨테이너 기술 vs 가상화 기술 | 비교와 장단점 – OPENMARU APM – 오픈마루, https://www.openmaru.io/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B0%80%EC%83%81%ED%99%94/ 6. 컨테이너와 VM 비교 – Red Hat, https://www.redhat.com/ko/topics/containers/containers-vs-vms 7. [Docker] 도커란 무엇인가 – woo’^’chang – 티스토리, https://woo-chang.tistory.com/48 8. [Docker] 도커란 무엇인가? — 들숨에 건강을 날숨에 재력을, https://squirmm.tistory.com/entry/Docker-%EB%8F%84%EC%BB%A4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80 9. Docker란 무엇입니까? | AWS, https://aws.amazon.com/ko/docker/ 10. Docker – 도커란 무엇인가, https://wooody92.github.io/docker/Docker-%EB%8F%84%EC%BB%A4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80/ 11. LXC – 위키백과, 우리 모두의 백과사전, https://ko.wikipedia.org/wiki/LXC 12. [Linux] 리눅스 컨테이너(LXC)에 대해서 알아보자 – 기록하는 개발자 – 티스토리, https://dd-developer.tistory.com/64 13. Docker의 등장 배경과 구조 – TEN AI 공식블로그, https://ten1010.tistory.com/entry/Docker%EC%9D%98-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD%EA%B3%BC-%EA%B5%AC%EC%A1%B0 14. 컨테이너 설명: 컨테이너의 개념과 역할 – Red Hat, https://www.redhat.com/ko/topics/containers 15. Docker란? 도커 컨테이너(docker container) 뜻, 장점, 사용법 – Red Hat, https://www.redhat.com/ko/topics/containers/what-is-docker 16. 컨테이너와 가상 컴퓨터 비교 | Atlassian, https://www.atlassian.com/ko/microservices/cloud-computing/containers-vs-vms 17. 흔들리는 도커(Docker)의 위상 – OCI와 CRI 중심으로 재편되는 컨테이너 생태계, https://www.samsungsds.com/kr/insights/docker.html 18. Docker 레지스트리 연결 관리 방법 – LabEx, https://labex.io/ko/tutorials/docker-how-to-handle-docker-registry-connection-418048 19. 컨테이너 이미지를 빌드하고 컨테이너 레지스트리에 푸시하기, https://gitlab-docs.infograb.net/ee/user/packages/container_registry/build_and_push_images.html 20. Red Hat 컨테이너를 선택해야 하는 이유, https://www.redhat.com/ko/topics/containers/why-choose-red-hat-containers 21. 컨테이너 vs 가상 머신(VM) – 클루닉스, https://www.clunix.com/insight/it_trends.php?boardid=ittrend&mode=view&idx=773 22. docker 튜토리얼 도커 기본 사용법 익혀보기 – Laravel, https://qspblog.com/blog/docker-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EB%8F%84%EC%BB%A4-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%9D%B5%ED%98%80%EB%B3%B4%EA%B8%B0 23. 도커(Docker) 컴포즈를 활용하여 완벽한 개발 환경 구성하기 – 44BITS, https://www.44bits.io/ko/post/almost-perfect-development-environment-with-docker-and-docker-compose 24.
도커 컨테이너의 기본 사용법 : Run, https://haesoo9410.tistory.com/380 25. Docker – Tutorial – tyoon9781 – 티스토리, https://tyoon9781.tistory.com/entry/docker-tutorial 26. [Docker] 도커란 무엇인가? – 평생쓰는 IT 개발 노트, https://soonmin.tistory.com/89 27. [Container] 도커 알아보기(1) – 이미지와 컨테이너 – 길은 가면, 뒤에 있다., https://12bme.tistory.com/585 28. 도커 컴포즈(Docker Compose) – velog, https://velog.io/@dong5854/%EB%8F%84%EC%BB%A4-%EC%BB%B4%ED%8F%AC%EC%A6%88Docker-Compose 29. [Linux] 도커 컴포즈 (Docker Compose) 기초 개념과 설치부터 실행까지 – 탱크의 데이터분석, https://sseozytank.tistory.com/86 30. [Docker] 도커 컴포즈(Docker compose) – 개념 정리 및 사용법 – SH’s Devlog – 티스토리, https://seosh817.tistory.com/387 31. [Docker] Docker Compose의 개념, 사용법, 명령어 정리 – 찰나의 징니, https://devzzi.tistory.com/76 32. Docker compose란? – 개발일기 – 티스토리, https://phsun102.tistory.com/36 33. Micro Service Architecture(MSA)의 장단점 – 내가 보기 위한 기록, https://sunrise-min.tistory.com/entry/Micro-Service-ArchitectureMSA%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90 34. 마이크로서비스(Microservice) 정의, 구축, 장단점, 사례 – Red Hat, https://www.redhat.com/ko/topics/microservices/what-are-microservices 35. 마이크로서비스의 장점 및 알아야 할 단점 – Atlassian, https://www.atlassian.com/ko/microservices/cloud-computing/advantages-of-microservices 36. Container와 Docker 이해하기 & 쿠버네티스 차이 | by Yuni(Yuwon) Yoon – Medium, https://medium.com/@yyuni915/container%EC%99%80-docker-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%B0%A8%EC%9D%B4-4f1e13858dd4 37. GitHub Actions와 Docker를 이용한 CI/CD 파이프라인 구축하기 | by Mazleyou | Medium, https://medium.com/@mazleyou/github-actions%EC%99%80-docker%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-ci-cd-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-db447f4db3dd 38. [AWS & Github Actions] CI/CD 파이프라인 구축 (Spring + Docker), https://sjh9708.tistory.com/237 39. Docker를 활용한 연구환경 구성 방법 A to Z – omnicommerce, https://omnicommerce.ai/ko-kr/resources/research-team-docker-environment/ 40. Docker, 깔끔하고 빠른 분석.연구.개발 환경 세팅 – Product Analytics Playground, https://playinpap.github.io/docker-agile-development-environment-settings/ 41. 누가 도커랑 쿠버네티스 차이점을 쉽게 설명해 줄 수 있는 사람 있어? : r/selfhosted – Reddit, https://www.reddit.com/r/selfhosted/comments/r2a6og/is_someone_able_to_eli5_the_difference_between/?tl=ko 42. Kubernetes와 Docker 비교 – 컨테이너 기술 간의 차이점 – AWS, https://aws.amazon.com/ko/compare/the-difference-between-kubernetes-and-docker/ 43. Kubernetes vs Docker | Oracle 대한민국, https://www.oracle.com/kr/cloud/cloud-native/kubernetes-engine/what-is-kubernetes/kubernetes-vs-docker/ 44. 쿠버네티스 알아보기 1편: 쿠버네티스와 컨테이너, 도커에 대한 기본 개념 – Samsung SDS, https://www.samsungsds.com/kr/insights/220222_kubernetes1.html 45. [k8s] 도커와 쿠버네티스의 관계 – Developers Haven – 티스토리, https://developers-haven.tistory.com/76 46. Kubernetes와 Docker 비교 – Atlassian, https://www.atlassian.com/ko/microservices/microservices-architecture/kubernetes-vs-docker