[Docker] Image + Container + Network

cup-wan·2025년 2월 16일
2

Docker

목록 보기
2/3

도커는 사용해본 사람 대비 "이게 왜 되지?" 싶은 기술 1위라 생각합니다. 저 또한 아직도 이게 왜 되는지 모르겠는데요? 그래도 한번 이해하기 위해 노력해보겠습니다.
우선 Docker를 이해하려면 이미지(Image)와 컨테이너(Container) 그리고 네트워크 개념을 제대로 아는 것이 중요합니다. 그러니 오늘은 이 세 주제에 대해 다뤄보겠습니다.

Docker 이미지

Docker 이미지는 컨테이너 실행을 위한 모든 요소를 포함한 불변(immutable) 패키지입니다.
운영체제, 애플리케이션 코드, 라이브러리, 실행 파일, 환경 설정 등이 포함되어 있으며, 이를 기반으로 컨테이너가 실행됩니다.

Docker 이미지의 계층 구조


Docker 이미지는 여러 개의 레이어(layer)로 구성됩니다. Docker는 UnionFS (Union File System)를 활용하여 여러 레이어를 겹쳐 하나의 이미지처럼 동작합니다.

  1. 베이스 이미지 (Base Image): 운영체제의 기본 환경을 제공하는 레이어
  2. 애플리케이션 종속성 (Dependencies): 실행에 필요한 라이브러리 및 패키지가 포함됨
  3. 애플리케이션 코드 (Application Code): 실제 애플리케이션 코드가 추가됨
  4. 환경 설정 및 실행 명령 (Configuration & CMD/ENTRYPOINT): 실행에 필요한 환경 변수를 정의하고, 실행될 명령어가 포함됨

Docker는 이러한 레이어를 활용하여 변경된 레이어만 다시 빌드하기 때문에 빠른 빌드 및 배포가 가능합니다. 각 레이어는 읽기 전용(Read-Only)이며, 컨테이너 실행 시 쓰기 가능한(Read-Write) 컨테이너 레이어가 추가됩니다.
➡️ 즉, 도커는 이미지의 레이어를 캐싱해서 빌드 및 배포를 최적화합니다.

Docker의 캐싱 메커니즘

Docker는 이미지 빌드 시 캐싱을 적극적으로 활용하여 속도를 최적화합니다.

  • Docker는 Dockerfile명령어 순서를 기반으로 레이어별 캐시를 재사용.
    ➡️Dockerfile 명령어 순서가 중요한 이유
  • 변경되지 않은 레이어는 다시 빌드되지 않고 캐시에서 불러오기 때문에 빌드 속도 상승
  • 캐시를 최적화하려면 변경이 적은 명령어를 위쪽에 배치하는 것이 유리함

예제)
아래와 같은 Dockerfile이 있다고 가정해봅시다.

FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
  • FROM python:3.9: 기본 이미지 레이어 (캐싱 가능)
  • WORKDIR /app: 작업 디렉토리 설정 (캐싱 가능)
  • COPY requirements.txt .: 파일 변경 시 새로 빌드
  • RUN pip install -r requirements.txt: 캐시 활용 가능 (파일이 변경되지 않으면 다시 실행되지 않음)
  • COPY . .: 소스 코드 변경 시 항상 다시 실행됨

이런 구조에서 requirements.txt가 변경되지 않는다면, pip install 단계는 캐시를 활용하여 다시 실행되지 않습니다. 하지만 COPY . .를 먼저 실행하면, 모든 파일이 변경된 것으로 인식되어 캐싱이 무효화됩니다.

Docker 컨테이너

컨테이너는 이미지를 기반으로 실행되는 독립적인 애플리케이션 환경입니다. 가상머신(VM)과 달리 호스트 운영체제(OS)의 커널을 공유하며, 리소스를 효율적으로 관리할 수 있습니다.

컨테이너는 실행 중인 애플리케이션을 감싸는 경량 프로세스로, 필요한 경우 격리된 환경에서 독립적으로 실행 가능합니다.

docker run -d --name my_container(컨테이너 이름) ubuntu:latest(이미지)

컨테이너 요소

리눅스 기반으로 가볍고 효율적인 가상화 환경을 제공하기 위해 컨테이너는 네임스페이스, Cgroups 등을 활용합니다.

네임스페이스 : 컨테이너가 독립적인 환경을 가지도록 하는 Linux 커널 기능

  • PID (프로세스 네임스페이스): 각 컨테이너에서 별도의 프로세스 ID 공간을 사용

  • NET (네트워크 네임스페이스): 컨테이너별로 독립적인 네트워크 인터페이스 제공

  • MNT (마운트 네임스페이스): 컨테이너마다 파일 시스템을 격리하여 사용 가능

  • UTS (호스트 및 도메인 네임스페이스): 컨테이너에서 별도의 호스트명을 설정 가능

  • IPC (인터프로세스 통신 네임스페이스): 컨테이너 내 프로세스 간 메모리 공유 격리

Cgroups : 컨테이너가 사용하는 CPU, 메모리, 디스크 I/O 등의 리소스를 제한하는 기능.

# cpu 사용량을 제한하는 명령 가능
docker run --cpus="1.5" ubuntu:latest

UnionFS (OverlayFS) : Unix File System 아님 위에 설명한 도커 이미지의 레이어 구조를 합치기 위한 개념입니다. 여러 개의 읽기 전용 레이어를 하나의 파일 시스템으로 조합 후 최상위에 쓰기 가능한 레이어를 추가하는 방식으로 작동합니다. (완전 도커 이미지와 똑같음. 당연한 것이 이 개념을 차용한 OverlayFS를 사용하기 때문)

Docker 네트워크

Docker는 컨테이너 간 통신을 관리하기 위해 다양한 네트워크 드라이버를 제공합니다. 1 컨테이너 1 어플리케이션을 권장하기 때문에 컨테이너 간 통신이 필수에 외부와도 통신이 가능해야합니다. 말만 들어도 네트워크가 매우 복잡할 거라고 예상이 됩니다.
그래도 알아보자 했으니 조금만 더 힘내봅시다.

도커 네트워크 개요

우선 흐름을 알아봅시다.

  1. 도커를 호스트 OS에 설치 시 다양한 네트워크 드라이버들이 설치됨 (Default = Bridge)
  2. 컨테이너에 IP 주소를 순차적으로 할당하고 해당 컨테이너는 네트워크를 구성할 때 원하는 네트워크 드라이버 선택 가능
    • 내부적으로 가상 이더넷 인터페이스 (Veth) 생성해 컨테이너와 호스트 간 통신을 가능하게 설정
    • veth 인터페이스를 통해 컨테이너와 Bridge 네트워크가 연결됨
  3. Bridge 네트워크 내부에서 컨테이너들이 서로 통신 가능
    • 같은 Bridge 네트워크에 속한 컨테이너들은 내부 IP를 사용해 통신 가능 (각 컨테이너에 부여된 IP)
    • iptables를 활용해 외부 네트워크와의 연결 관리
  4. 외부 네트워크와의 연결은 호스트 네트워크 인터페이스를 통해 수행
    • 컨테이너가 외부와 연결되려면 -p 옵션을 사용해 포트를 호스트에 매핑해야함

추가 내용) ubuntu 환경의 ufw가 docker에 적용 안돼요ㅠㅠ

  • ubuntu와 같은 os 사용 시 ufw를 사용하는 경우가 많음
  • docker는 iptables를 사용하기 때문에 ufw 설정^^해도 안먹는다
  • 어떻게 해결하는가? docker의 daemon.json 파일 만들어서 iptables :false 설정하기
sudo nano /etc/docker/daemon.json
{
    "iptables" : false
}
  • 그 후 docker 재시작 => 그러면 이제 docker 컨테이너 간 통신이 안됨 ^^
  • routed를 기본적으로 허용하는 iptables와 달리 ufw는 따로 설정해줘야함
sudo ufw default allow routed
sudo ufw reload
  • 이제 도커도 ufw로 사용가능해졌다~!

네트워크 드라이버

Docker는 다양한 환경에서 사용할 수 있도록 여러 가지 네트워크 드라이버를 제공 중 입니다. 각 드라이버는 특정한 목적과 사용 사례에 맞게 설계되었습니다.

드라이버설명
bridge기본 네트워크. 여러 컨테이너를 연결
host컨테이너가 호스트 네트워크를 공유
none네트워크를 비활성화 (격리)
overlay여러 호스트에 걸쳐 네트워크 구성
macvlan실제 물리 네트워크 인터페이스 사용

예를 들어, 컨테이너를 특정 브리지 네트워크에 연결하려면 다음과 같이 실행할 수 있습니다. (docker-compose 사용 시 docker-compose.yml 에서 network: 로 설정 가능)

docker network create my_bridge
docker run -d --name my_container --network my_bridge ubuntu:latest

이제 각 네트워크 드라이버에 대해 알아봅시다.

브리지 네트워크 (Bridge Network)

브리지 네트워크는 기본적으로 컨테이너 간 통신을 가능하게 하는 네트워크 유형입니다.

# 새로운 브리지 네트워크 생성
docker network create my_bridge

# 컨테이너 실행 시 특정 네트워크에 연결
docker run -d --name my_container --network my_bridge ubuntu:latest

브리지 네트워크 내의 컨테이너들은 컨테이너 이름을 이용해 서로 통신할 수 있습니다.

# container2 내부에서 container1에 접근
ping container1

그러나 브리지 네트워크는 기본적으로 외부 네트워크와 연결되지 않기 때문에 외부와의 통신을 위해서는 포트 매핑이 필요합니다.

# 컨테이너 실행 시 포트 매핑 설정
docker run -d -p 8080:80 --network my_bridge nginx

이 설정을 통해 외부에서 컨테이너 내부의 웹 서버(80번 포트)에 8080 포트를 통해 접근 가능해집니다!

호스트 네트워크 (Host Network)

호스트 네트워크는 컨테이너가 호스트 시스템의 네트워크를 직접 공유하는 방식으로, 별도의 네트워크 격리 없이 컨테이너가 호스트의 IP와 포트를 그대로 사용합니다.

# 호스트 네트워크 모드로 컨테이너 실행
docker run --network host nginx

이 방식은 네트워크 성능이 중요한 경우에 유리하지만, 컨테이너 간 격리가 없기 때문에 보안에 취약할 수 있습니다

논 네트워크 (None Network)

네트워크를 완전히 비활성화하여, 외부 및 다른 컨테이너와의 통신을 차단하는 방식입니다.

# 네트워크가 없는 컨테이너 실행
docker run --network none ubuntu:latest

이 방식은 완전히 격리된 환경에서 실행해야 하는 애플리케이션에 적합합니다. (도커 네트워크의 default는 bridge이기 때문에 네트워크 없는 컨테이너를 위해서는 논 네트워크 사용)

오버레이 네트워크 (Overlay Network)

오버레이 네트워크는 여러 개의 Docker 호스트(서버) 간에 컨테이너를 연결할 수 있도록 하는 네트워크입니다. Docker Swarm과 Kubernetes 같은 오케스트레이션 환경에서 사용합니다.

# 새로운 오버레이 네트워크 생성
docker network create -d overlay my_overlay_network

...오케스트레이션 환경은 다음에 다뤄보도록 하겠습니다

맥블란 네트워크 (Macvlan Network)

Macvlan 네트워크는 컨테이너가 독립적인 MAC 주소를 가지도록 설정해서 실제 네트워크 인터페이스를 사용할 수 있도록 합니다.

# 새로운 macvlan 네트워크 생성
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 my_macvlan_network

이 방식은 컨테이너를 기존 네트워크와 직접 연결해야 하는 경우에 적합합니다.

추가) TLS 인증을 통한 보안
Docker는 기본적으로 암호화되지 않기 때문에, 민감한 데이터 전송 시에는 TLS(Transport Layer Security)를 적용하기도 합니다.
저는 certbot으로 적용했으니 그에 대해서만 작성할게요..!


1. certbot 설치

sudo apt update
sudo apt install certbot
  1. 인증서 발급
sudo certbot certonly --standalone -d domain.com --email 이메일@email.com --agree-tos --no-eff-email

domain에 대한 인증서 발급, /etc/letsencrypt/live/domain.com/에 저장됨


3. Docker 컨테이너에 TLS 적용

docker run -d \
  --name my_secure_container \
  -v /etc/letsencrypt/live/domain.com/fullchain.pem:/etc/ssl/certs/fullchain.pem:ro \
  -v /etc/letsencrypt/live/domain.com/privkey.pem:/etc/ssl/private/privkey.pem:ro \
  -p 443:443 \
  nginx

nginx 컨테이너가 Let's Encrypt 인증서로 HTTPS 지원


3-1. docker 데몬에서 certbot 인증서 사용 (도커 엔진 자체가 TLS를 사용하게 하고 싶을 때 설정, 인증서 날라가면 난리나니 꼭 필요할 때만 설정합시다.)

{
  "tls": true,
  "tlscacert": "/etc/letsencrypt/live/yourdomain.com/fullchain.pem",
  "tlscert": "/etc/letsencrypt/live/yourdomain.com/cert.pem",
  "tlskey": "/etc/letsencrypt/live/yourdomain.com/privkey.pem",
  "hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"]
}

3-2. docker compose에 certbot tls 적용

version: '3'
services:
  web:
    image: nginx
    ports:
      - "443:443"
    volumes:
      - /etc/letsencrypt/live/yourdomain.com/fullchain.pem:/etc/ssl/certs/fullchain.pem:ro
      - /etc/letsencrypt/live/yourdomain.com/privkey.pem:/etc/ssl/private/privkey.pem:ro

킹 짱 컴포즈에도 물론 TLS 설정이 가능합니다.

마무리

도커는 배울수록 어렵습니다. 리눅스가 익숙한 개발자라면 굉장히 쉽게 배울 것 같은데 저는 학부생 시절에 배운 내용이 전부라 너무나도 어렵네요. 그래도 꾸준히 시리즈 완성해보겠습니다.

그리고 도커는 귀여운 이미지가 너무 많아요 여러분들도 귀여운 그림 보고 가세요 저는 뜬금없는 저 기차가 장어 닮아서 좋습니다.

출처

Docker가 Image Layer를 구성하는 방법 + 테스트
Deep Dive into Docker Containers | Architecture and Features
우분투에서 docker 컨테이너와 방화벽
Docker Network

profile
아무것도 안해서 유죄 판결 받음

0개의 댓글