Docker는 ‘항만에서 일하는 부두 노동자’라는 뜻이며, 이 노동자(도커 호스트)는 부두의 컨테이너를 관리한다.
도커(Docker)는 컨테이너 기반 가상화 플랫폼으로, 애플리케이션과 그 실행 환경(라이브러리, 의존성, 설정 등)을 하나의 패키지(컨테이너 이미지)로 묶어 어디서나 동일하게 실행할 수 있도록 해준다.
- 알아야 할 기본 용어
- 프로세스: 운영 체제에서 실행 중인 프로그램의 인스턴스
- 커널: 운영 체제의 핵심 부분으로, 하드웨어와 소프트웨어 간의 통신을 관리하며, 시스템 자원과 다양한 컴퓨터 작업을 효율적으로 조정하고 제어한다.
🤔개념
컨테이너
한마디로 ‘샌드박스화된 런타임 환경’라고 할 수 있겠다.
컨테이너는 애플리케이션 소프트웨어를 운영체제로부터 분리시켜, 사용자에게 애플리케이션이 동작할 때 필요한 최소한의 실행환경을 만든 것이다.
애플리케이션이 한 컴퓨팅 환경에서 다른 컴퓨팅 환경으로 빠르고 안정적으로 실행될 수 있도록 코드와 모든 종속성을 패키지화하는 소프트웨어의 표준 단위로써 작용하게 된다. 이를 통해 개발, 배송 및 배포(CI/CD를 의미)를 위해 소프트웨어를 표준화된 단위로 패키징할 수 있게 되었다.
- 컨테이너기술 = 패키징기술(packaging technology)
- 패키징대상 = 애플리케이션 + 애플리케이션 동작에 필요한 환경
- 동작에 필요한 환경 = 애플리케이션에 필요한 라이브러리 + 다른 프로세스로부터 완전히 독립적으로 분리된 환경
- 장점
- 확장/축소가 쉽고 MSA, DevOps에 적합하다.
- 컨테이너는 런타임 환경을 일관적으로 유지하며 설정에 따라 개발, 테스트, 프로덕션 모드를 손쉽게 선택하고 실행할 수 있다.
- 환경 자체를 애플리케이션과 함께 컨테이너 이미지에 탑재하기 때문에 의존성 누락 등 환경적 요인으로 서비스나 애플리케이션이 다르게 작동할 위험을 크게 줄일 수 있다. ⇒ 컨테이너는 이식성(portability)이 높아 개발자가 로컬 환경에서 온전성(sanity) 테스트를 수행 하기 쉽다.
- 버그 발견 시 프로덕션과 동일한 버전의 코드를 간단히 배포할 수 있어 문제를 재현하기 쉽다.
- 컨테이너는 가상 머신과 달리 운영 체제 수준에서 격리되며, 하나의 호스트 시스템에서 여러 컨테이너를 실행할 수 있어 자원을 효율적으로 사용할 수 있다.
역사
- Unix V7의 chroot (1979): 프로세스의 루트 디렉터리를 새로운 곳으로 변경하는 기술이자, 컨테이너들이 각자 실행 파일과 라이브러리를 지닐 수 있게 하는 기술이다.
- FreeBSD Jail (2000): FreeBSD로 구동되는 컴퓨터 시스템을 여러 개의 독립된 작은 시스템으로 분리하는 기술
- LinuxVserver (2001)
- SolarisContainers (2004): 존(zone)의 개념을 도입하여 자원을 분리
- Open VZ (2005)
- Google의 Process Container (2006): CPU, 메모리, 디스크 I/O, 네트워크와 같은 컴퓨터 자원에 대한 프로세스의 사용량을 제한. 컨테이너와 용어 혼동을 피하기 위해 cgroup(controlgroup)으로 변경
- LXC(Linux Container) (2008): 리눅스 기반의 운영체제 수준의 가상화 기술로, cgroup과 네임스페이스 분리(namespace isolation) 기술을 이용하였다.
- Docker (2013): PyCon에서LXC를 이용한 컨테이너를 쉽게 활용할 수 있는 기술을 소개했다.
- Kubernetes (2015)
- Container Security Issue (2016): 시스템의 복잡도 증가 → DirtyCOW(copy on write) 컨테이너 보안 문제 발생
- OCI(Open Container Initiative) (2017)
기반 리눅스 기술
리눅스의 핵심 격리 기술을 기반으로 하여, 하나의 리눅스 커널 위에서 여러 개의 독립적인 사용자 공간 인스턴스를 실행할 수 있게 했다.
- Cgroups(Control Groups): 프로세스 그룹의 리소스 사용량(예: CPU 시간, 시스템 메모리, 네트워크 대역폭)을 모니터링하고 제한하는 기능을 제공한다. 이는 각 컨테이너가 할당된 자원을 초과하여 사용하는 것을 방지함으로써, 시스템의 안정성을 유지하고 다른 컨테이너 또는 시스템 전체에 미치는 영향을 최소화함을 목적으로 한다. 이를 통해, 시스템 자원이 공정하게 분배되도록 보장하고, 한 애플리케이션이 전체 시스템을 점유하는 것을 방지할 수 있다.
- Namespace: 프로세스에게 제한된 시스템 뷰를 제공하여, 프로세스가 자신의 네임스페이스 내에서만 시스템 리소스를 볼 수 있도록 한다. 즉, 파일 시스템 마운트, 네트워크, 사용자 ID, 호스트 이름 등을 각 컨테이너마다 독립적으로 할당하고 관리할 수 있게 되는 것이다. 컨테이너를 격리된 환경으로 만들어 주는 핵심 기술로, 컨테이너 내부에서 실행되는 프로세스가 호스트 시스템이나 다른 컨테이너의 리소스와 충돌하지 않도록 한다. 이는 컨테이너 내부의 애플리케이션은 자신이 전체 시스템에서 유일하게 실행되고 있는 것처럼 동작할 수 있으며, 이는 보안과 격리를 강화한다.
컨테이너 vs 가상화

[출처: https://www.docker.com/blog/containers-and-vms-together/]
VM과는 다른 방법을 사용해서 효율 성능이 더 나아진 게 컨테이너!
독립된 환경 및 다른 컨테이너에 영향을 주지 않는다는 점은 가상화와 동일하지만, 컨테이너는 독립된 운영체제를 갖지 않는다는 점에서 가상화와 차이를 보인다.
- 컨테이너가 유리한 경우
- 애플리케이션이 멀티 서비스(multi service) 아키텍처인 경우
- 애플리케이션이 동작하는 서버의 수를 최소화하고자 하는 경우
- 클라우드 네이티브 애플리케이션(cloud native application)개발
- 작고, 독립적이고, 상호 결합도가 낮은 서비스들의 모음
- 애플리케이션 개발환경이 배포환경과 유사한 경우
- 가상화가 유리한 경우
- 애플리케이션이 모놀리식(monolithic) 아키텍처인 경우
- 서로 다른 운영체제가 필요한 경우
- 플랫폼에 고정된 스토리지 시스템이 필요한 경우
- 운영체제의 많은 기능이 필요한 경우
도커 이미지
한마디로 ‘방(컨테이너) 구성을 위한 설계 도면’이라 할 수 있겠다.
도커 이미지란 컨테이너를 실행하기 위한 불변의 템플릿이다. 이 이미지는 코드, 런타임, 애플리케이션과 관련된 라이브러리, 환경 변수 및 설정 파일 등 애플리케이션 실행에 필요한 모든 것을 포함한다. 도커 이미지는 여러 계층(layer)으로 구성되어 있으며, 각 계층은 변경 사항을 저장한다. 이미지는 컨테이너를 생성하는 데 사용되며, 일반적으로 docker hub와 같은 레지스트리에서 가져오거나 Dockerfile
을 사용해 직접 빌드할 수 있다.
- 관련 파일:
/var/lib/docker/overlay2
도커 이미지 최적화
도커 이미지의 크기를 줄임으로써, 이미지 효율성 증대, 배포시간 단축, 보안 강화를 꾀할 수 있다. 이미지 크기를 줄이기 위해 다음과 같은 작업을 수행할 수 있다.
- 경량 베이스 이미지 사용: 가능한 가장 경량의 베이스 이미지를 사용한다. 예를 들어,
alpine
이미지는 매우 작은 크기로 필요한 최소한의 기능만 포함하기 때문에 용량 최소화에 유리하다.
- 멀티 스테이지 빌드 사용하기:
Dockerfile
에서 멀티 스테이지 빌드를 사용하여 빌드 단계에만 필요한 도구를 최종 이미지에서 제외시킨다. 이 방식을 사용하면 최종 이미지에는 애플리케이션 실행에 필요한 파일과 dependencies만 포함된다. 여러 개의 FROM
명령어를 사용하여 구현할 수 있으며, 각 스테이지는 독립적인 베이스 이미지를 가질 수 있다. 첫 번째 스테이지에서는 빌드에 필요한 도구와 소스 코드를 컴파일하는 데 필요한 작업을 수행한다. 이후 스테이지에서는 첫 번째 스테이지에서 생성된 ‘아티팩트’만을 가져와서 최종 이미지를 생성한다. 이는 불필요한 빌드 도구나 중간 생성물을 최종 이미지에서 제외할 수 있어 이미지 크기가 상당히 줄어드는 효과를 준다.
- 필요 없는 파일 제거하기: 빌드 과정에서 생성되는 임시 파일, 캐시 파일 등 필요 없는 파일은
RUN
명령어에서 && rm -rf /path/to/temporary/files
와 같이 제거하여 이미지 크기를 줄일 수 있다.
- 레이어 수 최소화하기:
RUN
, COPY
, ADD
명령어는 새로운 레이어를 생성하므로, 이러한 명령어를 적절히 조합하여 가능한 한 적은 수의 레이어를 생성하도록 Dockerfile
을 최적화하며, COPY
와 ADD
를 사용할 때에는 필요한 파일만 이미지에 추가하도록 구성한다. 또한, .dockerignore
파일을 사용하여 불필요한 파일이 이미지에 포함되지 않도록 설정할 수도 있다.
- 환경 변수를 이용한 설정하기: 가능한 설정 파일 대신 환경 변수를 사용하여 애플리케이션을 구성한다. 이 방법은 설정 변경이 필요할 때 이미지를 다시 빌드하지 않아도 되므로 이미지 크기를 줄이는 데 도움을 준다.
- 적절한 태그 사용하기: 필요한 소프트웨어의 적절한 버전을 지정하여 불필요한 업데이트로 인한 크기 증가를 피한다.
도커 컨테이너
한마디로 ‘설계 도면(이미지)로부터 설계된 방’이라 할 수 있겠다.
도커 컨테이너는 도커 이미지의 실행 인스턴스로, 이미지를 기반으로 하여 실행되며 격리된 환경에서 애플리케이션과 그 의존성을 캡슐화한다.
- 특징
- 가벼우며, 시스템 리소스를 효율적으로 사용한다.
- 시작될 때 생성되고, 종료될 때 제거된다.
- 동일한 이미지를 사용하여 여러 컨테이너를 동시에 실행할 수 있으며, 각 컨테이너는 독립적으로 실행되고 관리된다.
도커 엔진
컨테이너를 생성하고 관리하는 기능을 제공하는 클라이언트-서버 형태의 애플리케이션이다.
구성요소

- CLI 클라이언트: 사용자가 커맨드 라인을 통해 도커 엔진과 상호작용할 수 있게 하는 인터페이스로, 이를 통해 컨테이너를 생성하고 관리할 수 있다. 이미지를 빌드하고 docker hub와 같은 이미지 저장소와 상호작용할 수 있다.
- REST API: 도커 데몬과 통신하는 인터페이스로, 이 API를 통해 다양한 도커 클라이언트가 도커 데몬과 상호작용할 수 있다. 이를 통해 애플리케이션의 배포, 관리, 확장을 위한 다양한 명령을 수행할 수 있다.
- 도커 데몬: 컨테이너 관리를 위한 백그라운드 프로세스로 이미지 생성, 컨테이너 실행, 네트워크 설정 등의 작업을 수행한다. 또한 컨테이너의 생성, 실행, 중지와 같은 생명 주기를 관리하는 역할과 이미지를 빌드하고 저장소에서 이미지를 가져오는 역할을 수행한다. 도커 클라이언트를 통해 REST API를 사용하여 접근할 수 있다.
동작방식
컨테이너 생명주기: create - start - stop - rm(remove)
- 이미지 다운로드 또는 빌드: 컨테이너를 실행하기 전에, 도커 엔진은 해당 애플리케이션을 실행하는 데 필요한 모든 파일과 설정이 포함된 도커 이미지를 필요로 한다. 이를 위해 사용자는 도커 허브나 다른 이미지 저장소에서 이미지를 다운로드하거나, Dockerfile을 정의하고 이를 통해 새로운 이미지를 빌드할 수 있다.
- 컨테이너 생성: 이미지가 준비되면 사용자는 도커 데몬에게 컨테이너 생성을 요청한다. 이때, 네트워크 설정이나 볼륨 마운트와 같은 추가적인 옵션을 지정할 수 있다. 도커 데몬은 이러한 정보를 바탕으로 새로운 컨테이너의 인스턴스를 생성한다.
- 컨테이너 실행: 컨테이너가 생성되면, 도커 데몬은 컨테이너 내부에서 정의된 애플리케이션을 실행한다. 이 과정에서 컨테이너는 독립된 파일 시스템, 네트워크 인터페이스, 프로세스 ID 공간 등을 갖게 되며, 이는 컨테이너를 호스트 시스템과 격리된 환경에서 실행되게 한다.
- 컨테이너 관리: 도커 엔진은 실행 중인 컨테이너의 상태를 모니터링하고 관리한다. 사용자는 도커 CLI를 통해 실행 중인 컨테이너를 조회하고 로그를 확인하며, 필요한 경우 컨테이너를 중지, 재시작 또는 삭제할 수 있다.
- 리소스 관리: 도커 엔진은 Cgroups와 네임스페이스와 같은 리눅스의 핵심 기능을 사용하여 컨테이너의 리소스 사용량을 제한하고, 컨테이너 간의 격리를 유지한다. 이를 통해 시스템의 안정성을 보장하고, 다른 컨테이너나 시스템 전체에 부정적인 영향을 미치는 것을 방지한다.
볼륨
Docker 컨테이너는 기본적으로 일시적인 환경에서 실행되므로, 컨테이너가 종료되거나 삭제되면 그 안에 있는 데이터도 사라지게 된다. 볼륨 메커니즘을 적용함으로써, 파일 수정은 호스트에서 진행하고, 실행환경은 컨테이너에서 알아서 관리하게 할 수 있다.

[출처: 생활코딩]

[출처: 도커 공식 홈페이지]
컨테이너와 호스트 시스템 간의 데이터를 지속적으로 저장하고 공유하기 위한 메커니즘으로, 컨테이너 내의 데이터를 지속성 있게 보관하기 위해서 사용된다.
- 특징
- 높은 이식성: 볼륨은 도커 엔진이 관리하며, 컨테이너의 생명주기와 독립적으로 존재한다.
- 데이터 보안: 호스트 파일 시스템의 특정 디렉토리에 데이터를 저장하는 대신, 도커가 관리하는 디렉토리(Docker area)에 데이터를 저장한다.
- 데이터 공유: 여러 컨테이너 간에 데이터를 공유하거나 데이터의 지속적인 저장이 필요한 경우에 적합하다. (개발 환경)
- 볼륨 지정 방법
네트워크
각 도커 설치에는 하나의 가상 브리지 네트워크가 존재하며, 컨테이너를 실행할 때 이 네트워크에 자동으로 연결된다. 이 브릿지 네트워크는 컨테이너와 호스트의 네트워크 대역을 연결해주는 역할을 한다. 사용자는 여러 개의 사용자 정의 브리지 네트워크를 생성하여 컨테이너를 논리적으로 분리할 수 있다.
- 특징
- L2 통신을 기반으로 한다.
- 컨테이너 생성 시 veth 인터페이스를 생성한다. (sandbox)
- 모든 컨테이너는 외부 통신을 docker0를 통해 진행한다.
- 컨테이너 실행 시 IP 주소를 순차적으로 할당한다.