2025. 3. 19. 14:53ㆍDocker
Docker의 격리가 완전한가?
Docker Container는 강력한 격리를 제공하지만 VM 만큼 완벽한 가상화 격리는 아니다.
1. 커널 공유
Cotainer은 Host Kernel을 공유하기 때문에 Kernel 을 취약점을 통해 Container Escape 이 발생할 가능성이 있다.
해결방안: gVisor, Kata Containers 같은 가상화 기반의 샌드박스 컨테이너 사용
2. Default 설정이 완전한 격리를 제공하지 않는다.
기본적으로 Docker는 User Namespace가 활성화되지 않았다.
(컨테이너 내 사용자/그룹 ID를 격리하여 Host 와 다른 권한 수준을 가지도록하는 격리 방법)
그래서 컨테이너 내부의 root 사용자는 Host 에서 root 로 동작할 수도 있다.
해결방안: --userns-remap 기능을 사용하여 User Namespace를 활성화한다.
3. 공유 네트워크 리소스
기본적으로 Docker 컨테이너는 Bridge Network 를 사용하여 서로 통신할 수 있다.
해결방안: docker network creat --driver=bridge --internal 같은 내부 네트워크 옵션을 사용하여 컨테이너를 격리한다.
Docker Architecture
Docker 는 다음과 같은 요소들로 구성되어있다.
- Docker Client → 사용자가 명령어 입력 (docker run, docker pull 등)
- Docker Daemon → 컨테이너를 실행 및 관리
- Docker Engine → Docker 환경을 제공
- Container Runtime → 컨테이너 실행 (containerd, runc)
- Docker Image & Registry → 컨테이너 실행을 위한 이미지 저장소
- Docker Network & Storage → 컨테이너 간 네트워크 및 볼륨 관리
- Namespace & cgroups → 리소스 및 프로세스 격리
- Linux Kernel & Host OS → 컨테이너의 기반이 되는 운영체제
Docker의 기본 구조는 Client - Server 모델이다.
기본적으로 Unix Socket (/var/run/docker.sock) 또는 TCP 포트 (2375 또는 2376 - 보안 TLS 포함)을 통해 연결 한다
Docker Client (도커 클라이언트)
Docker Client는 사용자가 Docker를 조작하는 인터페이스 역할을 한다.
사용자가 docker 명령어를 입력하면, 이는 Docker API를 통해 Docker Daemon과 통신하게 된다.
사용자가 명령을 내리고(Docker Client), 실제 작업을 수행하는 것은 Docker Daemon 이다.
- 사용자 요청을 docker daemon으로 전달하며 (docker run, docker build, docker ps 등)
- Docker는 REST API를 사용하여 클라이언트와 데몬 간 통신을 수행한다.
- 로컬 및 원격 Docker 서버와 연결이 가능하다. (원격 Docker Daemon을 제어하여 클라우드 환경에서도 컨테이너 관리 가능)
- GUI 기반 Docker 관리 툴 (ex: Docker Desktop, Portainer)도 내부적으로 Docker API를 사용하여 Docker Daemon과 소통한다.
Docker Daemon (도커 데몬)
Docker Daemon (dockerd) 은 Docker 의 핵심 Background Process 이다.
이 프로세스는 컨테이너를 실행하고 관리하는 모든 작업을 수행하며 사용자의 요청을 처리한다.
Linux 에서는 systemd 를 사용하여 관리한다.
/var/log/docker.log -> Docer Daemon의 Logs 파
1) Client 요청 처리
사용자가 docker 명령어 (CLI) 또는 Docker API를 사요하여 요청을 보내면 Docker Daemon 이 해당 요청을 처리한다.
예를 들어 사용자가 docker run 명령을 실행하면
- Docker Client 가 이를 Docker Daemon 에 전달한다
- Docker Daemon 이 적절한 image 를 찾고 Container 를 생성 및 실행한다.
- 실행 결과를 Docker Client에 반환한다.
2) Container 실행 및 관리
Docker Daemon은 Container의 생성, 시작, 중지, 삭제 등의 작업을 수행한다.
docker run -d -p 80:80 nginx
Docker Daemon이 nginx Container를 실행하고 내부적으로 해당 프로세스를 관리한다.
3) Image 관리
Docker Daemon은 Docker Hub 또는 프라이빗 레지스트리에서 이미지를 가져오고 저장하는 기능을 제공한다.
docker pull ubuntu:latest # Ubuntu 최신 이미지 다운로드
docker images # 다운로드된 이미지 목록 확인
docker build -t myapp . # Dockerfile을 사용하여 새로운 이미지 빌드
docker push myrepo/myapp # 빌드한 이미지를 Docker Hub에 푸시
4) 네트워크 관리
Docker Daemon은 Container 간 통신을 위해 네트워크를 생성하고 관리한다.
Docker 에는 기본적으로 여러 네트워크 드라이버를 제공한다.
docker network create mynetwork # 새로운 네트워크 생성
docker network ls # 네트워크 목록 확인
docker network inspect mynetwork # 네트워크 정보 확인
컨테이너가 동일한 네트워크에 있는 경우 내부적으로 DNS를 통해 서로 통신할 수 있다.
5) Container 상태 모니터링
실행 중인 컨테이너 상태를 지속적으로 모니터링하고 필요하면 logs 를 제공한다.
docker ps # 현재 실행 중인 컨테이너 목록 확인
docker logs myapp # 특정 컨테이너의 로그 확인
docker stats # 실행 중인 컨테이너의 CPU, 메모리 사용량 모니터링
6) 저장소 및 Volume 관리
Dcker Daemon은 컨테이너가 데이터를 영구적으로 저장할 수 있는 Volume 을 관리하낟.
docker volume create myvolume # 볼륨 생성
docker volume ls # 볼륨 목록 확인
docker volume inspect myvolume # 볼륨 정보 확인
Docker Engine (도커 엔진)
Docker Engine은 Docker의 실행 환경을 제공하는 핵심 소프트웨어로, 컨테이너 기반 애플리케이션을 효과적으로 빌드, 실행, 관리할 수 있게 해준다.
주요 역할
1) 컨테이너화된 애플리케이션 실행 환경 제공
Docker Engine은 컨테이너 기반 애플리케이션을 실행할 수 있는 통합 플랫폼을 제공한다.
개발자는 동일한 환경에서 애플리케이션을 개발, 테스트, 배포할 수 있어 환경 차이로 인한 문제를 최소화할 수 있다.
2) Docker CLI 및 REST API를 통한 상호작용
CLI: 사용자는 명령어 한 줄로 컨테이너 생성, 실행, 모니터링, 삭제 등을 수행할 수 있다.
REST API: 이를 통해 Docker의 기능을 다른 도구나 스크립트에서 제어할 수 있으며, GUI 기반 도구(예: Docker Desktop, Portainer)도 내부적으로 API를 사용하여 상호작용한다
3) 내부적으로 Container Runtime 호출
컨테이너를 실제로 실행하기 위해 Docker Engine은 Container Runtime(예: runc, containerd 등)을 호출한다.
이 과정을 통해, 격리된 환경(네임스페이스, cgroups 등)에서 컨테이너가 안전하게 실행된다.
이를 구성하는 주요 요소는
Docker Daemon, Docker CLI and Docer API 로 나눌 수 있으며 이 모든 요소가 함꼐 작동하는 Container Runtime 을 호출하는 역할까지 담당한다.
1) Docer Daemon (dockerd)
Backgorund에서 실행되며 컨테이너 생성, 실행, 중지, 삭제 등 모든 핵심 작업을 처리한다. 사용자 요청 (CLI나 API로 전달된)을 받아 실제 작업을 수행한다.
이미지, 네트웤, Volume 등의 관리 작업도 담당한다.
시스템 서비스로 동작하며 Unix Socket 또는 TCP Port (2375 또는 2376 - 보안 TLS 포함) 를 통해 클라이언트와 통신한다.
2) Docker CLI (Command Line interface)
사용자가 명령어(예: docker run, docker build, docker ps 등)를 입력하면 이를 해석하여 Docker Daemon에 전달한다.
사용하기 쉬운 텍스트 기반 인터페이스로, 복잡한 컨테이너 작업도 간단한 명령어로 실행할 수 있다.
3) Docker API (REST API)
Docker CLI 및 기타 도구가 Docker Daemon과 통신할 수 있도록 표준화된 인터페이스(RESTful)를 제공한다.
Docker Engine의 기능들을 프로그래밍 방식으로 제어하고 자동화할 수 있게 해준다.
네트워크를 통해 접근할 수 있어, 원격 Docker Daemon과의 상호작용도 가능하며, 다양한 언어나 툴에서 활용할 수 있다.
Container Runtime (컨테이너 런타임)
컨테이너를 실제로 실행하는 소프트웨어 계층으로 애플리케이션을 격리된 환경에서 실행하기 위한 필수적인 역할을 수행한다. Docker Engine의 구성 요소 중 하나로 Docker Daemon이 컨테이너를 생성하고 실행할 때 내부적으로 호출하는 Componenet 이다.
1) Container Runtime의 역할
- 프로세스 실행
Container Runtime은 Container 내부에서 애플리케이션 프로세스를 실행하고 관리한다. 실제로 컨테이너가 시작되면 Runtime은 해당 프로세스를 격리된 환경 (ex: Namespace, cgroups)를 사용하여 실행한다.
- 격리 및 자원 관리
각 컨테이너는 독립적인 파일 시스템, 네트워크, 프로세스 Tree 등을 가진다. 컨테이너 런타임은 이러한 Resource 의 격리를 위해 Linux Namespace, cgroups 등을 활용하여 컨테이너 간의 충돌을 방지하고 자원 사용을 관리한다.
- 이미지 실행
컨테이너 Runtime은 Docker Image 를 실제 실행 가능한 컨테이너 Instance로 변환한다.
- 컨테이너 라이프사이클 관리
컨테이너 생성, 시작, 정지, 삭제 등 컨테이너의 라이프사이클을 관리한다.
(이를 통해 애플리케이션은 Host 시스템의 다른 프로세스와 독립적으로 격리된 환경에서 안정적으로 실행될 수 있다.)
Docker Engine & Container Runtime
Docker Engine은 여러 구성 요소로 이루어져 있으며 그 중 Docker DaeMone은 사용자 명령을 받아 컨테이너 실행을 지시한다. 이때 Docker Daemon은 내부적으로 컨테이너 런타임을 호출하여 컨테이너를 실제로 실행한다.
대표적인 컨테이너 Runtime
runc:
Docker의 기본 컨테이너 런타임으로, OCI(Open Container Initiative) 표준을 준수합니다. 컨테이너 생성 및 실행에 필요한 낮은 수준의 작업(네임스페이스 설정, cgroups 관리 등)을 수행다.
containerd:
Docker Daemon 내부에서 runc와 함께 동작하며, 더 높은 수준의 컨테이너 관리 기능(이미지 전송, 스토리지, 네트워킹 등)을 제공합니다. 최근에는 Docker의 독립적인 프로젝트로도 발전했다.
CRI-O, gVisor 등:
Kubernetes 등 다양한 오케스트레이션 도구와 함께 사용하기 위해 개발된 대안 컨테이너 런타임들도 존재한다.
Docker Image (도커 이미지)
Docker Image 는 컨테이너 실행의 기반이 되는 Immutable File System SnapShot 이다. 이 Image 는 OS 뿐만 아니라 애플리케이션, 라이브러리, 설정 파일 등 컨테이너 실행에 필요한 모든 요소를 포함하고 있어 동일한 이미지로 어디서나 일관된 환경을 재현할 수 있다.
1. 특징
1) 캡슐화된 실행 환경
이미지 내 모든 요소는 종속적인 특성을 가지므로, 호스트 시스템과의 환경 차이로 인해 발생하는 문제를 줄여준다.
Image 자체가 변경되지 않기 때문에 업데이트나 수정이 필요하면 새로운 Image 를 Build 하는 방식이다.
2) Layer Structure
- Layer Based File System
Docker Image는 여러 Layer로 구성되어 있는 데 각 Layer는 파일 시스템의 변경 내역을 나타낸다. 상위 Layer가 하위 Layer 위에 덮어씌워지는 구조이다.
- 효율적인 Caching
이미지 빌드시, Docker는 기존의 변경되지 않는 Layer를 캐시하여 Build 속도를 크게 향상 시킨다.
- 공유 및 재사용
동일한 기본 Layer를 여러 이미지가 공유할 수 있기 때문에 저장 공간과 네트워크 대역폭을 절약할 수 있다.
3) Dockerfile 을 통한 Build
- 자동화된 Build Script
Dockerfile은 Image 를 어떻게 빌드할 지 단계별로 기술한 스크립트로 명령어(ex: COPY, RUN, WORKDIR 등)를 사용해 이미지의 최종 사앹를 정의한다.
- 일관성 있는 이미지 생성
Dockerfile을 사용하면 동일한 명령어를 재사용해 언제나 같은 결과의 이미지를 생성할 수 있다.
2. Docker Image 의 활용 및 관리
1) Image Registry
- Docker Hub 등 다양한 이미지 저장소에서 이미지 업로드와 다운로드가 가능하다
- 버전 관리와 태깅
각 이미지는 tag 를 통해서 버전 관리가 가능하다.
2) Image 생성 및 배포
Build :
docker build -t myapp . 명령어를 사용하여 Dockerfile 로부터 이미지를 생성한다.
실행
생성된 이미지는 docker run 명령어를 통해 컨테이너화되어 실행된다.
배포:
이미지가 저장된 Registry를 통해 여러 환경에서 배포할 수 있다. 이 과정은 CI / CD 파이프라인에서 매우 중요한 역할은 한다.
+ 추가적인 내용
1) Caching 및 효율성
- Build Cache
Docker는 Dockerfile의 각 명령어 실행 결과를 레이어로 캐싱한다. 예를 들어, apt-get update 같은 명령이 포함된 레이어는 소스 코드 변경에 영향을 받지 않으면 다시 빌드되지 않아 속도가 빠르다.
- 공유 Layer
동일한 기반 이미지 (ex: ubuntu) 를 사용하는 여러 컨테이너는 같은 레이어를 공유하여 디스크 사용량을 줄인다.
예를 들면 Docker 는 Dockerfile의 FROM 명령어에 지정된 기반 이미지 ubuntu:latest 를 한 번 다운로드한 후 해당 이미지의 레이러를 로컬에 저장한다. 이렇게 저장된 Layer는 이후 동일한 이미지를 사용하는 다른 컨테이너나 이미지 Build에서도 재사용되기 때문에 디스크 공간을 절약할 수 있다.
이는 별도의 설정없이 Docker 가 자동으로 캐싱하고 동일한 이미지 Layer 를 공유한다.
그렇다면 얼마나 오랫동안 저장할까??
기본적으로 Docker 는 한 번 다운로드 된 이미지를 삭제하지 않는다. 그래서 여러 종류의 기반 이미지를 사용하게 되면 로컬 저장소에 많은 이미지가 쌓이게 되고 이 경우 사용하지 않는 이미지나 중복된 이미지들을
docker image prune
docker system prune 등의 도구를 활용해서 제거하여 디스크 공간을 확보해야 한다.
2) Multistage Build
- Build 최적화
멀티스테이지 빌드를 사용하면 하나의 Dockerfile 내에서 여러 단계를 거쳐 불필요한 빌드 파일이나 의존성을 최종 이미지에 포함하지 않고 최종 실행에 필요한 최소한의 파일만 포함할 수 있다.
# 빌드 단계
FROM golang:1.18 AS builder # (1) 빌드에 필요한 환경 제공
WORKDIR /app
COPY . .
RUN go build -o myapp # (2) 실행 파일(myapp) 빌드
# 실행 단계
FROM alpine:latest # (3) 최적화된 경량 실행 환경
WORKDIR /app
COPY --from=builder /app/myapp . # (4) 빌드한 실행 파일만 복사
CMD ["./myapp"] # (5) 실행
위 처럼 빌드 단계와 실행 단계를 서로 불리해서 실행하는 데 필요한 것들만 최적화시켜 경량화된 실행이 가능하다.
핵심 개념
각 단계를 독립적인 컨테이너처럼 동작하게 한다
이전 단계(Build 단계)에서 생성된 파일 중 필요한 것만 최종 단계로 복사한다.
캐싱을 활용하여 빌드 성능을 향상시킨다.
이를 통해
최적화된 이미지 크기
빌드에 필요한 툴(예: golang, gcc, make)은 최종 이미지에서 제외된다.
최종 이미지는 경량화된 Alpine 기반으로 실행되어 크가기 줄어든다.
보안성 향상
( 빌드 환경 / 컴파일러, 디버거 등) 이 포함되지 않기 때문에 공격에 노출될 수 있는 요소가 줄어든다.
캐싱을 활용한 빠른 빌드
Docker는 레이어 단위로 캐싱을 하기 때문에, go build 과정이 변경되지 않으면 이전 빌드의 결과를 재사용하여 빌드 속도가 빨라진다.
유지 보수성 증가
빌드 환경과 실행 환경을 분리하여 관리가 편리해진다.
실행 환경을 가볍고 단순하게 유지할 수 있다.
3) Image Security and Scan
- 이미지 취약점 검사
이미지에 포함된 패키지나 라이브러리와 보안 취약점을 주기적으로 스캔하는 것이 중요하다. 여러 도구 (ex: Clair, Trivy 등)을 사용해 취약점 분석을 진행 할 수 있다.
- 서명 및 신뢰
Docker Content Trusht 를 활성화하면 이미지 서명을 통해 이미지 출처와 무결성을 검증 할 수 있다.
Docker Registry (도커 레지스트리)
Docker Image의 저장 및 배포를 위한 중앙 저장소 역할을 한다. 이를 통해 이미지를 쉽게 공유하고 배포할 수 있다.
이미지를 중앙에서 저장, 관리, 공유
버전 관리와 태깅을 통한 이미지 관리
인증, 암호화, 취약점 스캔 등을 통한 보안 기능 제공
- 분산 배포
여러 개발자나 시스템이 동일한 이미지를 활용할 수 있도록 중앙 집중화된 저장소를 제공하며 이를통해 일관된 실행 환경을 유지할 수 있다.
Docker Hub
AWS ECR
- Private Registry
자체 운영
조직 내에서 Docker Registry를 직접 운영할 수 있다. 이를 통해 민감한 데이터나 내부 이미지를 외부에 공개하지 않고 관리할 수 있다.
ocker는 docker registry 이미지로 프라이빗 레지스트리를 손쉽게 실행할 수 있으며, 인증, TLS 암호화 등 추가적인 보안 설정을 할 수 있다.
내부 인프라에 맞추어 커스터마이징 및 확장이 가능하며, CI/CD 파이프라인과도 원활하게 연동된다.
특정 브랜치나 태그에 따라 이미지를 자동으로 빌드하고 버전 관리하여, 릴리즈 프로세스를 자동화할 수 있다.
Docker Network & Storage (도커 네트워크 & 스토리지)
1. Docker Network
컨테이너들이 서로 통신하거나 외부와 연결할 수 있도록 Docker는 여러 네트워크 드라이버를 제공한다. 네트워크 드라이버는 컨테이너 연결 방식을 정의하며 각각의 특징과 사용 사례가 있다.
- bridge ( 기본 드라이버 )
기본적으로 Docker Daemone 이 생성하는 네트워크이다. 단일 호스트 내의 여러 컨테이너가 서로 통신할 수 있게 해준다.
외부 네트워크와의 연결은 Port Forwarding 을 통해 설정할 수 있다.
- host
컨테이너가 호스트의 Network Stack 을 공유한다. 네트워크 성능이 중요한 애플리케이션에 유리하지만 격리성이 떨어진다.
컨테이너 내부의 Port가 그대로 Host Port 와 매핑되어 충돌 가능성이 있으므로 주의가 필요하다.
- none
네트워크 기능을 완전히 비활성화하여 컨테이너를 격리한다
네트워크 통신이 전혀 필요없는 경우에 사용한다.
- overlay
여러 호스트에 걸쳐 컨테이너를 연결할 수 있는 네트워크이다.
Docker Swarm 이나 Kubernetes 같은 클러스트 환경에서 컨테이너 간의 통신을 지원한다.
분산 시스템에서 여러 노드에 분산된 컨테이너들이 서로 통신할 수 있도록 도와준다.
2. Docker Storage ( volume )
- Voiume
Docker 가 관리하는 독립된 저장 공간으로 컨테이너와 분리되어 데이터가 유지된다.
Docker 명령어를 통해 쉽게 생성, 관리, 백업할 수 있다. 여러 컨테이너 간에 공유하거나 데이터 일관성을 유지하기에 적합하다.
- Bind Mount
호스트의 특정 디렉토리를 컨테이너 내부에 마운트하는 방식
개발 중에 호스트와 컨테이너 간 파일 동기화가 필요한 경우에 유용하다.
호스트의 파일 시스템 상태에 직접 의존하기 떄문에 보안 및 권한 관리에 주의가 필요하다.
- tmpfs
컨테이너 메모리(RAM)에 데이터를 저장한다.
휘발성이며 빠른 I/O가 필요할 때 사용한다
컨테이너가 종료되면 데이터가 사라진다.
'Docker' 카테고리의 다른 글
Docker Compose (0) | 2025.03.25 |
---|---|
컨테이너 가상화 (0) | 2025.03.18 |