개발언어 Back-End/Python

파이썬 기반 docker 라이브러리 정리

bluebamus 2025. 2. 9.

1. 설치 및 초기화

   1) 설치

pip install docker

 

   2) 클라이언트 초기화

      1. docker.from_env() 

         - 환경 변수에서 Docker 설정을 자동으로 가져와 클라이언트를 생성한다.
         - 내부적으로 DockerClient 객체를 반환한다.
         - Docker 데몬과 연결할 수 있도록 환경 변수를 자동 감지한다.
         - 일반적으로 로컬 개발 환경에서 사용된다.

 

      2. docker.DockerClient(...) 

         - Docker 데몬과의 직접적인 연결을 설정한다.
         - DockerClient 클래스의 인스턴스를 직접 생성하여 사용자가 원하는 설정을 지정할 수 있다.

 

import docker

# 기본 초기화 (로컬 Docker 데몬 연결)
client = docker.from_env()

# 고급 옵션 (TLS 인증, 원격 호스트)
client = docker.DockerClient(
    base_url='tcp://192.168.0.10:2376',
    tls=docker.tls.TLSConfig(
        ca_cert='ca.pem',
        client_cert=('cert.pem', 'key.pem'),
        verify=True
    )
)

 

      3. docker.DockerClient의 옵션

         - base_url='unix://var/run/docker.sock':

            - Unix 소켓을 사용하여 로컬 Docker 데몬과 통신한다.
            - Windows에서는 npipe:////./pipe/docker_engine을 사용해야 한다.

 

         - version='auto':

            - 현재 사용 가능한 Docker API 버전을 자동으로 감지한다.

 

         - tls=False: 

            - TLS 보안 연결을 사용하지 않도록 설정한다.
            - 원격 Docker 데몬과 통신하려면 tls=True로 설정하고 TLSConfig 객체를 제공해야 한다.

 

옵션명 타입 설명
base_url str Docker 데몬의 URL을 지정합니다. 기본적으로 unix://var/run/docker.sock(Linux/macOS) 또는 npipe:////./pipe/docker_engine(Windows) 사용
version str 또는 float 사용할 Docker API 버전을 지정합니다. "auto"로 설정하면 자동으로 감지됨
timeout int 요청의 타임아웃(초 단위)을 설정합니다. 기본값은 60초
tls bool 또는 TLSConfig TLS 보안 연결을 사용할지 여부 (기본값: False)
user_agent str 클라이언트의 User-Agent 값을 설정
credstore_env dict 사용자 인증 정보를 저장하는 환경 변수 전달

 

2. 명령어 레퍼런스

   1) 공통 명령어 (Common Commands)

      1. run: 컨테이너 생성 및 실행

옵션 설명 예제
detach=True 컨테이너를 백그라운드(분리 모드)로 실행 detach=True
remove=True 컨테이너 종료 후 자동 삭제 remove=True
ports 호스트와 컨테이너 간 포트 매핑 {"80/tcp": 8080}
volumes 호스트와 컨테이너 간 볼륨 매핑 {"host/path": {"bind": "/container/path", "mode": "rw"}}
environment 컨테이너 환경변수 설정 {"ENV_VAR": "value"}
name 컨테이너 이름 지정 "my_container"
container = client.containers.run(
    image="ubuntu:latest",
    command="echo Hello, World",
    detach=True,  # 백그라운드 실행
    environment={"ENV_VAR": "value"},
    ports={'8080/tcp': 80},  # 호스트:컨테이너 포트 바인딩
    volumes={'/host/path': {'bind': '/container/path', 'mode': 'rw'}},
    name="my-container",
    network="my-network"
)

 

       2. exec: 실행 중인 컨테이너에 명령어 전달

exec_output = container.exec_run("ls -l", workdir="/app")
print(exec_output.output.decode())

 

      3. pull(image, tag=None, stream=False, decode=False): 원격 레지스트리에서 이미지를 가져오기

ubuntu_image = client.images.pull("ubuntu", tag="latest")

 

       4. build: Dockerfile로 이미지 빌드

image, build_logs = client.images.build(
    path=".", dockerfile="Dockerfile", tag="my_image:latest",
    buildargs={"MY_ARG": "value"}, decode=True
)
for chunk in build_logs:
    if 'stream' in chunk:
        print(chunk['stream'].strip())
image, build_logs = client.images.build(
    path=".",  # Dockerfile 경로
    tag="my-image:1.0",
    buildargs={"BUILD_ENV": "production"},
    rm=True  # 빌드 후 중간 컨테이너 삭제
)
for line in build_logs:
    print(line.get('stream', ''))

 

       5. list(): 로컬에 있는 모든 이미지를 리스트로 반환

images = client.images.list()
for image in images:
    print(image.tags)

 

       6. remove(image, force=False, noprune=False): 지정한 이미지를 삭제

client.images.remove("my_image:latest", force=True)

 

        7. get(container_id_or_name): 특정 컨테이너 객체를 반환

container = client.containers.get("my_container")

 

        8. stop(): 컨테이너를 중지

container.stop()

 

         9. logs(stream=False, tail='all'): 컨테이너의 로그를 반환

logs = container.logs().decode('utf-8')

 

          10. exec_run(cmd, stdout=True, stderr=True, tty=False): 실행 중인 컨테이너 내부에서 명령어를 실행

exit_code, output = container.exec_run("echo '고급 실행 예제'")
print(output.decode('utf-8'))

 

          11. container.pause(): 현재 실행 중인 컨테이너를 일시 중지한다.

import docker

client = docker.from_env()
container = client.containers.get("container_name_or_id")
container.pause()

 

          12. container.unpause(): 일시 중지된 컨테이너를 다시 실행한다.

if container.status == "paused":
    container.unpause()

 

          13. container.wait(timeout=None): 컨테이너가 종료될 때까지 기다리고, 종료 코드를 반환한다.

              - timeout 값을 설정하면 해당 시간이 초과될 경우 예외가 발생할 수 있다.

import docker

client = docker.from_env()
container = client.containers.run("ubuntu", "sleep 5", detach=True)

# 컨테이너가 종료될 때까지 기다림
result = container.wait()

print(result)  # {'StatusCode': 0}

 

   2) 관리 명령어 (Management Commands)

      1. DockerClient.networks를 통한 네트워크 관리

         - client.networks.create() 메서드:
            - 이 메서드는 Docker에서 새로운 네트워크를 생성하는 데 사용됩니다. 기본적으로 브리지(bridge) 네트워크를 만들며, 컨테이너 간의 통신을 설정할 수 있다.

network = client.networks.create("my_bridge", driver="bridge")
# 네트워크 생성
network = client.networks.create(
    "my-network",
    driver="bridge",
    labels={"project": "my-app"}
)

# 네트워크에 컨테이너 연결
container.connect(network="my-network")

 

            - 사용 가능한 옵션

옵션 타입 설명
name str 생성할 네트워크의 이름
driver str 사용할 네트워크 드라이버 (예: "bridge", "overlay", "macvlan")
options dict 특정 네트워크 드라이버의 추가 옵션 (예: {"com.docker.network.bridge.enable_icc": "true"})
ipam IPAMPool IP 주소 관리(IPAM) 설정을 정의하는 객체
check_duplicate bool 같은 이름의 네트워크가 있을 경우 중복을 방지
internal bool True일 경우, 외부 네트워크에서 접근 불가
attachable bool True일 경우, 개별 컨테이너가 네트워크에 직접 연결 가능
ingress bool True일 경우, ingress 네트워크로 설정 (Swarm에서 사용)
enable_ipv6 bool True일 경우, IPv6 지원 활성화
labels dict 네트워크에 부착할 라벨 지정 (예: {"project": "my-app"})
scope str 네트워크의 범위 설정 ("local", "global" 등)
config_from dict 기존 네트워크 설정을 참조 (예: {"Network": "existing-network"})
config_only bool True일 경우, 네트워크를 구성 전용(config-only)으로 설정
driver_opts dict 네트워크 드라이버에 전달할 옵션 (예: {"com.docker.network.bridge.name": "my_custom_bridge"})

 

         - get(network_id, …):

            - 네트워크 ID를 사용하여 특정 네트워크 객체를 가져온다.

network = client.networks.get("abcdef123456")

 

            - 사용 가능한 옵션

매개변수 자료형 설명 기본값/필수 여부
network_id str 검색할 네트워크의 고유 ID입니다. 필수
verbose bool 스웜(Swarm) 모드 사용 시, 클러스터 내 서비스의 세부 정보를 포함하여 자세한 정보를 가져옵니다. 선택사항 (명시하지 않으면 기본값 사용)
scope str 네트워크를 특정 범위로 필터링합니다. 예를 들어, "swarm", "global", "local"과 같이 지정할 수 있습니다. 선택사항

 

         - list(…):

            - Docker 서버에 존재하는 네트워크 목록을 조회한다.

networks = client.networks.list(filters={"driver": "bridge"})

 

            - 사용 가능한 옵션

매개변수 자료형 설명 기본값/필수 여부
names list 조회할 네트워크의 이름 목록을 지정합니다. 지정된 이름과 일치하는 네트워크만 반환합니다. 선택사항
ids list 조회할 네트워크의 ID 목록을 지정합니다. 지정된 ID와 일치하는 네트워크만 반환합니다. 선택사항
filters dict 추가 필터 조건을 딕셔너리 형태로 지정합니다. 예를 들어, {"driver": "bridge"}와 같이 드라이버, 라벨, 유형 등으로 필터링할 수 있습니다. 선택사항
greedy bool 각 네트워크에 대해 더 상세한 정보를 개별적으로 조회할지 여부를 결정합니다. (예: 네트워크에 연결된 컨테이너 정보 등) 선택사항 (일반적으로 False)

 

         - prune(filters=None):

            - 사용되지 않는(오래된 혹은 사용 중이지 않은) 네트워크를 일괄 삭제한다.

result = client.networks.prune(filters={"label": "temp_network"})


            - 
사용 가능한 옵션

매개변수 자료형 설명 기본값/필수 여부
filters dict 삭제할 네트워크를 선택하기 위한 필터 옵션입니다. 예를 들어, {"label": "temp_network"}는 라벨이 "temp_network"인 네트워크만 대상으로 합니다. 선택사항 (기본값: None)

 

      2. Network 객체의 인스턴스 메소드

         - client.networks.create() 메서드:

            - 해당 네트워크에 컨테이너를 연결한다.

network.connect(container="my_container", aliases=["web"])

 

            - 사용 가능한 옵션

옵션 타입 설명
network str 연결할 네트워크의 이름 또는 ID
aliases list 컨테이너에 대한 네트워크 별칭 지정
links list 연결된 컨테이너와 직접 링크 설정 (링크 기능은 legacy 방식)
ipv4_address str 지정된 네트워크에서 사용할 IPv4 주소
ipv6_address str 지정된 네트워크에서 사용할 IPv6 주소
link_local_ips list 컨테이너에 할당할 링크 로컬 주소
priority int 네트워크 연결 우선순위 (Swarm에서 사용)
endpoint_config dict 컨테이너의 네트워크 엔드포인트 설정 (예: MAC 주소, 포트 매핑 등)
driver_opt dict 네트워크 드라이버에 전달할 추가 옵션
container str 또는 Container 객체 연결할 컨테이너 (컨테이너 ID, 이름 또는 Container 객체)

 

         - reload():

            - Docker 서버에서 최신 네트워크 정보를 다시 불러와 내부 속성(attrs)을 갱신한다.

# 예시: 네트워크 객체의 최신 정보를 갱신
network = client.networks.get("abcdef123456")
network.reload()  # 서버에서 최신 상태로 정보를 갱신

 

         - remove():

            - 네트워크를 삭제한다.

# 네트워크 객체를 가져와 삭제하는 예시
network = client.networks.get("abcdef123456")
network.remove()  # 해당 네트워크를 삭제합니다.

 

      3. 네트워크 관련 추가 구성

         3.1 create_networking_config(endpoints_config):

            - 여러 네트워크 연결 구성을 포함하는 네트워킹 설정을 생성한다. 각 네트워크에 연결할 엔드포인트(endpoint) 설정 정보를 포함하는 딕셔너리인 endpoints_config를 입력받아, Docker 엔진 API에 전달할 수 있는 네트워킹 구성 객체를 생성한다.

 

            - 주요 기능 및 사용 용도

                - 네트워킹 구성 생성:
                - 컨테이너를 생성할 때 여러 네트워크에 동시에 연결할 수 있도록 각 네트워크별 엔드포인트 설정(예: 별칭, IP 주소 등)을 정의하여 하나의 NetworkingConfig 객체로 결합한다.


                - 컨테이너 생성 시 활용:

                    - 생성된 네트워킹 구성은 client.create_container() 등 컨테이너 생성 메소드의 networking_config 인자로 전달되어, 컨테이너가 지정된 네트워크들에 올바르게 연결되도록 한다.


                - 유연한 네트워크 설정:

                    - 각 네트워크에 대해 별도의 설정(예: Aliases, Links, IPAMConfig 등)을 지정할 수 있어, 복잡한 네트워크 토폴로지 구성 시 유용하게 사용된다.

# 예시: 두 개의 네트워크(기본 'bridge'와 사용자 정의 네트워크 'custom_net')에 대해 엔드포인트 설정을 구성하는 경우
endpoints_config = {
    "bridge": {
        "Aliases": ["mycontainer_bridge"],
        "IPAMConfig": {"IPv4Address": "172.17.0.5"}
    },
    "custom_net": {
        "Aliases": ["mycontainer_custom"],
        "Links": ["other_container:alias"],
        "IPAMConfig": {"IPv4Address": "192.168.1.100"}
    }
}

# 네트워킹 구성 객체 생성
networking_config = client.create_networking_config(endpoints_config)

# 생성된 networking_config 객체를 컨테이너 생성 시에 전달하여,
# 컨테이너가 두 네트워크에 동시에 연결되도록 할 수 있습니다.
container = client.create_container(
    image="your-image",
    name="my_container",
    networking_config=networking_config
)

 

            - 매개변수 정보

매개변수 자료형 설명
endpoints_config dict 각 네트워크 이름을 키로, 해당 네트워크에 연결할 엔드포인트 설정을 값으로 갖는 딕셔너리입니다. 엔드포인트 설정은 다음과 같은 옵션들을 포함할 수 있다.

예시 옵션:
- Aliases: 해당 네트워크 내에서 사용할 별칭 목록 (리스트 형태)
- Links: 연결할 다른 컨테이너에 대한 링크 정보
- IPAMConfig: IP 주소 관리 설정 (예: IPv4Address, IPv6Address 등)

 

         3.2 create_networking_config(endpoints_config):

            - 개별 네트워크 연결에 대한 설정을 생성한다. 컨테이너가 네트워크에 연결될 때 사용되는 엔드포인트(endpoint) 구성 객체를 생성하는 헬퍼 함수이다.

            - 이 함수는 각 네트워크에 대해 컨테이너가 사용할 고정 IP 주소, 네트워크 내에서의 별칭(aliases), 다른 컨테이너와의 링크 정보, 링크 로컬 IP(link-local IP) 및 네트워크 드라이버에 특화된 옵션(driver_opt) 등을 지정할 수 있게 해준다.

            - 생성된 엔드포인트 구성 객체는 이후 컨테이너의 네트워킹 설정(예, NetworkingConfig의 엔트리)으로 전달되어 컨테이너가 해당 네트워크에 원하는 방식으로 연결되도록 한다.

 

            - 주요 기능 및 사용 용도
               - 엔드포인트 구성 생성:
                  - 네트워크에 연결할 때, 컨테이너별로 고정 IP, 별칭, 링크 정보 등 세부 설정을 하나의 구성 객체로 결합하여 Docker API에 전달할 수 있도록 한다.

 

               - 네트워크 연결 세부 제어:

                  - 여러 네트워크에 연결할 때, 각 네트워크별로 개별 엔드포인트 설정을 지정할 수 있으므로, 복잡한 네트워크 토폴로지 구성이나 특정 요구사항(예: 특정 IP 할당, 드라이버 옵션 전달 등)을 만족시킬 수 있다.

from docker.types import EndpointConfig

# 엔드포인트 구성 객체 생성 예시
endpoint_config = create_endpoint_config(
    aliases=["webserver"],
    links=["db:database"],
    ipv4_address="172.20.0.5",
    ipv6_address=None,
    link_local_ips=["169.254.1.5"],
    driver_opt={"custom_option": "value"}
)

# 생성된 endpoint_config 객체는 NetworkingConfig 구성의 일부로 사용될 수 있습니다.
# 예를 들어, 여러 네트워크에 연결할 경우 각 네트워크 이름을 키로 하여 엔드포인트 설정을 지정합니다:
endpoints_config = {
    "bridge": endpoint_config,
    "custom_net": create_endpoint_config(
        aliases=["web_custom"],
        ipv4_address="192.168.1.100",
        driver_opt={"opt1": "value1"}
    )
}

networking_config = client.create_networking_config(endpoints_config)
container = client.create_container(
    image="your-image",
    name="my_container",
    networking_config=networking_config
)

 

            - 매개변수 정보

매개변수 자료형 설명
aliases list of str 해당 네트워크 내에서 컨테이너가 사용할 추가 별칭(호스트명) 목록입니다.
links list 다른 컨테이너와의 링크 정보를 지정합니다. 일반적으로 "컨테이너명:별칭" 형식의 문자열 리스트로 제공됩니다.
ipv4_address str 컨테이너에 할당할 고정 IPv4 주소입니다.
ipv6_address str 컨테이너에 할당할 고정 IPv6 주소입니다. (없으면 None으로 설정)
link_local_ips list of str 컨테이너에 할당할 링크 로컬 IP 주소들의 리스트입니다.
driver_opt dict 네트워크 드라이버에 전달할 추가 옵션들을 담은 딕셔너리입니다.

 

      4. volume: 볼륨 관리

         - list()
         - 설명:
            - 현재 존재하는 모든 볼륨 목록을 조회한다.

 

         - list()의 매개변수

매개변수 타입 기본값 설명
filters dict None 특정 조건으로 볼륨을 필터링 (예: { "dangling": ["true"] })

 

         - create()
         - 설명:

            - 새로운 볼륨을 생성한다.

 

         - create()의 매개변수

매개변수 타입 기본값 설명
name str None 생성할 볼륨의 이름
driver str "local" 사용할 드라이버 (예: "local", "nfs")
driver_opts dict {} 드라이버에 전달할 추가 옵션
labels dict {} 볼륨에 추가할 레이블

 

         - get()
         - 설명:
            - 특정 볼륨의 정보를 가져온다.

 

         - get()의 매개변수

매개변수 타입 기본값 설명
name str 없음 조회할 볼륨의 이름

 

         - prune()
         - 설명:
            - 사용되지 않는 볼륨을 정리(삭제)한다.

 

         - prune()의 매개변수

매개변수 타입 기본값 설명
filters dict None 특정 조건으로 볼륨 정리 (예: { "until": "24h" })

 

         - remove()
         - 설명:
            - 특정 볼륨을 삭제한다.

 

         - remove()의 매개변수

매개변수 타입 기본값 설명
name str 없음 삭제할 볼륨의 이름
force bool FALSE 볼륨이 사용 중일 경우 강제 삭제 여부

 

volume = client.volumes.create(
    name="my-volume",
    driver="local",
    labels={"type": "database"}
)

# 볼륨 마운트로 컨테이너 실행
client.containers.run(
    "postgres:13",
    volumes={'my-volume': {'bind': '/var/lib/postgresql/data', 'mode': 'rw'}},
    detach=True
)

 

   3) 컨테이너 상태 모니터링

      1. 기능/메소드 정리

         1.1 컨테이너 목록 조회

            - client.containers.list(all=True)
               - 실행 중이거나 종료된 모든 컨테이너 목록을 가져온다.

         1.2 특정 컨테이너 객체 가져오기

            - client.containers.get(container_id_or_name)
               - 특정 컨테이너 객체를 얻어 상세 정보를 조회할 수 있다.

         1.3 컨테이너 속성 확인

            - container.status
               - 현재 상태(예: "running", "exited", "paused" 등)를 문자열로 반환한다.
            - container.attrs
               - 컨테이너에 대한 전체 정보를 딕셔너리 형태로 제공한다.
            - container.logs()
               - 컨테이너의 로그를 가져온다.


         1.4 컨테이너 정보 갱신

            - ontainer.reload()
               - 컨테이너의 내부 상태 정보를 최신 상태로 새로 고친다.

         1.5 이벤트 모니터링

            - client.events(decode=True)
               - Docker 데몬에서 발생하는 이벤트(컨테이너 생성, 시작, 종료 등)를 실시간으로 구독할 수 있다.

 

       2. 모니터링 방식

         2.1 이벤트 기반 모니터링

            - Docker 데몬에서 발생하는 이벤트를 실시간으로 구독하여, 컨테이너의 상태 변화(예: 생성, 시작, 종료, 재시작 등)를 감지할 수 있다.
            - 주요 메소드는 client.events(decode=True)이다.

 

         2.2 주기적 폴링(Polling) 방식

            - 정해진 간격으로 모든 컨테이너의 상태를 조회(예: client.containers.list(all=True))하고, 각 컨테이너의 상태(container.status)를 비교하여 변경 사항이 있으면 이를 기록하는 방식이다.

 

      3. 실무 레벨 샘플 코드

         3.1 이벤트 기반 모니터링

            - 이 코드는 Docker 데몬의 이벤트 스트림을 구독하여, 컨테이너 관련 이벤트가 발생할 때마다 해당 컨테이너의 이름과 상태를 로깅한다.

            - docker.from_env()를 사용하여 환경변수 기반의 클라이언트를 생성한다.
            - client.events(decode=True)를 통해 이벤트 스트림을 받아오고, 각 이벤트에서 "Type"이 "container"인 경우 상태(status)와 컨테이너 ID를 가져온다.
            - 컨테이너 이름은 추가 조회(get) 후 출력한다.

import docker
import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')

def monitor_container_events():
    client = docker.from_env()
    logging.info("Docker 컨테이너 이벤트 모니터링을 시작합니다.")
    try:
        for event in client.events(decode=True):
            # 이벤트 타입이 컨테이너인 경우만 처리
            if event.get("Type") == "container":
                status = event.get("status", "unknown")
                container_id = event.get("id", "")[:12]
                try:
                    container = client.containers.get(container_id)
                    name = container.name
                except Exception:
                    name = "N/A"
                logging.info("컨테이너 [%s] (%s): 이벤트 -> %s", name, container_id, status)
    except Exception as e:
        logging.error("이벤트 모니터링 중 오류 발생: %s", e)

if __name__ == "__main__":
    monitor_container_events()

 

          3.2 주기적 폴링 방식

            - 이 코드는 일정 주기로 모든 컨테이너의 상태를 조회하여, 이전 상태와 비교한 후 변화가 있으면 이를 로깅한다.

            - 주어진 간격(기본 5초)마다 모든 컨테이너 목록을 조회한다.
            - 각 컨테이너에 대해 container.reload()를 호출하여 최신 상태를 반영한 후 container.status를 확인한다.
            - 이전에 저장한 상태와 비교하여 변경이 있으면 로깅한다.

import docker
import logging
import time

logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')

def poll_containers_status(poll_interval=5):
    client = docker.from_env()
    known_status = {}

    while True:
        try:
            containers = client.containers.list(all=True)
            for container in containers:
                # 최신 상태를 위해 컨테이너 정보를 갱신
                container.reload()
                current_status = container.status
                if container.id not in known_status:
                    known_status[container.id] = current_status
                    logging.info("컨테이너 [%s] (%s)의 초기 상태: %s", container.name, container.id[:12], current_status)
                elif known_status[container.id] != current_status:
                    logging.info("컨테이너 [%s] (%s) 상태 변경: %s -> %s",
                                 container.name, container.id[:12], known_status[container.id], current_status)
                    known_status[container.id] = current_status
            time.sleep(poll_interval)
        except Exception as e:
            logging.error("폴링 중 오류 발생: %s", e)
            time.sleep(poll_interval)

if __name__ == "__main__":
    poll_containers_status()

 

   4) docker.APIClient를 이용한 저수준 API 사용 

      - Docker 엔진의 REST API와 1:1로 매핑되는 저수준 인터페이스를 제공한다.
      - 고수준 인터페이스(예: docker.from_env()를 통해 얻는 client)는 여러 작업을 한 번에 쉽게 처리할 수 있도록 래핑하지만, 보다 세밀한 제어가 필요할 때는 APIClient를 직접 사용하는 것이 유용하다.
         - 예를 들어, 이미지 빌드 과정에서 캐시 제어, 중간 컨테이너 제거 옵션, 빌드 아규먼트 전달 등 다양한 옵션을 세밀하게 조정할 수 있다.

 

      1. 기본 설정 및 연결

         - Docker 데몬과 통신하려면 APIClient 인스턴스를 생성할 때 데몬의 소켓(일반적으로 Unix 소켓 또는 TCP 주소)을 지정해야 한다.

 

         - APIClient 초기화

            - base_url에는 Docker 데몬의 주소를 지정한다.
            - version() 메서드를 통해 데몬의 API 버전, OS, 아키텍처 등 다양한 정보를 확인할 수 있다.

import docker

# Unix 소켓을 통한 연결 (리눅스나 macOS에서 사용)
client = docker.APIClient(base_url='unix://var/run/docker.sock')

# 또는 TCP 연결 (예: 도커 데몬이 TCP로 노출되어 있을 경우)
# client = docker.APIClient(base_url='tcp://127.0.0.1:2375')

# 데몬 버전 확인 (버전 정보는 딕셔너리 형태로 반환됨)
version_info = client.version()
print("Docker 서버 버전:", version_info.get('Version'))

 

      2. 주요 기능 및 사용 예제

         2.1 이미지 빌드 (Build)

            - 저수준 API를 사용하면 Dockerfile을 기반으로 이미지를 빌드할 수 있다.
            - 빌드 결과는 제너레이터(generator) 형태로 반환되며, 각 단계의 출력을 읽어야 한다.

            - 예제: 간단한 Dockerfile로 이미지 빌드

               - custom_context 옵션을 사용하면 Dockerfile 외의 컨텍스트(필요한 파일 등)를 함께 전달할 수 있다.
               - rm 옵션은 빌드 완료 후 중간 컨테이너를 자동 삭제한다.
               - decode=True를 사용하면 반환된 스트림이 JSON 디코딩되어 보다 쉽게 처리할 수 있다.

import io
from docker import APIClient

# Dockerfile 내용을 문자열로 작성
dockerfile_str = '''
# 베이스 이미지를 busybox로 설정
FROM busybox:latest
# /data 디렉토리를 볼륨으로 선언
VOLUME /data
# 기본 커맨드는 쉘 실행
CMD ["/bin/sh"]
'''

# 문자열을 바이트 스트림으로 변환 (BytesIO 객체 사용)
dockerfile_stream = io.BytesIO(dockerfile_str.encode('utf-8'))

# APIClient 생성 (Unix 소켓 사용)
client = APIClient(base_url='unix://var/run/docker.sock')

# 이미지 빌드: fileobj 인자에 Dockerfile의 파일 스트림을 전달, custom_context=True로 지정
build_output = client.build(
    fileobj=dockerfile_stream,
    custom_context=True,
    tag='my_busybox:latest',
    rm=True,          # 빌드 중간 컨테이너 제거 옵션
    decode=True       # 출력 스트림을 JSON 디코딩하여 dict 형태로 받음
)

# 빌드 과정 출력 (각 단계의 스트림 출력)
print("이미지 빌드 출력:")
for line in build_output:
    # 각 라인은 dict 형태이므로 'stream' 키의 값을 출력 (빌드 로그 메시지)
    print(line.get('stream', '').strip())

 

          2.2 컨테이너 생성 및 실행

            - 이미지로부터 컨테이너를 생성하고 실행하는 작업은 여러 API 호출로 구성된다.
            - 주요 메서드는 create_container(), start(), wait(), logs() 등이 있다.


            - 예제: 컨테이너 생성, 실행, 로그 확인 및 종료
               - create_container()에서는 Docker run 커맨드와 유사하게 이미지를 지정하고 실행할 커맨드를 설정한다.
               - start()로 컨테이너를 시작하고, wait()로 프로세스 종료를 대기한다.
               - logs()는 컨테이너에서 출력된 로그를 가져온다. (바이트 문자열로 반환되므로 decode 필요)

# 1. 컨테이너 생성: create_container() 메서드 사용
container_config = {
    'Image': 'my_busybox:latest',   # 빌드한 이미지 태그 사용
    'Cmd': ['/bin/sh', '-c', 'echo "Hello from container!"'],
    'Tty': False
}
container = client.create_container(**container_config)
container_id = container.get('Id')
print("생성된 컨테이너 ID:", container_id)

# 2. 컨테이너 시작: start() 메서드 사용
client.start(container=container_id)
print("컨테이너 시작됨.")

# 3. 컨테이너가 종료될 때까지 대기: wait() 메서드 사용
exit_status = client.wait(container=container_id)
print("컨테이너 종료 상태:", exit_status.get('StatusCode'))

# 4. 컨테이너 로그 확인: logs() 메서드 사용 (전체 로그 문자열 반환)
logs_output = client.logs(container=container_id, stdout=True, stderr=True)
print("컨테이너 로그:\n", logs_output.decode('utf-8'))

# 5. 컨테이너 삭제: remove_container() 메서드 사용 (컨테이너가 종료된 후)
client.remove_container(container=container_id)
print("컨테이너 삭제됨.")

 

         2.3 컨테이너 내 명령 실행 (Exec)

            - 실행 중인 컨테이너 내부에서 추가 명령을 실행하고자 할 때는 exec 인스턴스를 생성한 후 시작한다.

 

            - 예제: 컨테이너 내부에서 명령 실행

               - exec_create()로 실행할 명령과 옵션을 설정한 후, 생성된 exec 인스턴스의 ID를 이용해 명령을 실행한다.
               - exec 명령은 컨테이너 내부의 쉘 환경에서 실행된다.

# 1. 컨테이너 내부에서 실행할 명령 설정 (예: 'echo "Exec 명령 실행"' 실행)
exec_command = ['echo', 'Exec 명령 실행']

# 2. exec 인스턴스 생성: exec_create() 메서드 사용
exec_instance = client.exec_create(
    container=container_id,
    cmd=exec_command,
    stdout=True,
    stderr=True
)
exec_id = exec_instance.get('Id')
print("생성된 exec 인스턴스 ID:", exec_id)

# 3. exec 명령 시작: exec_start() 메서드 사용
# stream=False이면 전체 결과를 한 번에 반환, stream=True이면 제너레이터 반환
exec_output = client.exec_start(exec_id, stream=False)
print("Exec 결과:", exec_output.decode('utf-8'))

 

         2.4 컨테이너 커밋 (Commit)

            - 실행된 컨테이너의 상태를 이미지로 저장할 수 있습니다. 이는 docker commit과 유사하다.

 

            - 예제: 컨테이너를 커밋하여 새 이미지 생성

               - commit() 메서드는 컨테이너의 현재 상태를 스냅샷처럼 이미지로 저정한다.
               - 옵션으로 커밋 메시지, 작성자 정보 등을 포함할 수 있다.

commit_config = {
    'repository': 'my_committed_image',
    'tag': 'latest',
    'message': '컨테이너 상태 커밋',
    'author': '작성자 이름'
}

commit_response = client.commit(container=container_id, **commit_config)
new_image_id = commit_response.get('Id')
print("새로 커밋된 이미지 ID:", new_image_id)

 

      3. 추가 기능

         3.1 로그 스트리밍

실시간으로 로그를 모니터링하려면 logs() 메서드에 stream=True 옵션을 주어 제너레이터로 사용한다.

# 로그 스트리밍: 컨테이너 로그를 실시간으로 읽어오기
log_stream = client.logs(container=container_id, stdout=True, stderr=True, stream=True)
print("실시간 로그 출력:")
for log_line in log_stream:
    print(log_line.decode('utf-8').strip())
import docker

def exec_in_container():
    client = docker.from_env()
    container = client.containers.run("ubuntu", "sleep 60", detach=True)
    
    # 컨테이너 내부에서 명령어 실행
    exit_code, output = container.exec_run("echo '고급 실행 예제'")
    print("exec_run 출력:", output.decode('utf-8'))
    
    # 스트리밍 방식으로 로그 출력 (컨테이너가 출력하는 로그를 실시간 모니터링)
    for log_line in container.logs(stream=True):
        print(log_line.decode('utf-8').strip())
    
    container.stop()
    container.remove()

if __name__ == '__main__':
    exec_in_container()

 

         3.2 기타 유용한 메서드

            - get_archive() / put_archive()
              - 컨테이너 내부의 파일이나 폴더를 tar 아카이브 형태로 가져오거나 업로드할 수 있다.
            - diff()
               - 컨테이너의 파일 시스템 변경 사항을 확인할 수 있다.
            - resize()
               - TTY 세션의 크기를 변경할 수 있다.
            - stats()
               - 컨테이너의 리소스 사용량(메모리, CPU 등)을 스트리밍 방식으로 모니터링할 수 있다.

 

   5) Swarm 명령어 (Swarm Commands)

      1. service: Swarm 서비스 관리

service = client.services.create(
    image="nginx:alpine",
    name="web-service",
    replicas=3,
    networks=["my-overlay-network"],
    mounts=[
        docker.types.Mount(
            target="/usr/share/nginx/html",
            source="web-content",
            type="volume"
        )
    ],
    endpoint_spec=docker.types.EndpointSpec(ports={80: 8080})
)

 

3. 실무 프로젝트 시나리오

   1) 분산 마이크로서비스 아키텍처

      - 구성 요소:

         - Flask API 서버 (포트 5000)
         - Redis 캐시 (볼륨 마운트)
         - Nginx 로드 밸런서 (사용자 정의 네트워크)

# 1. 네트워크 생성
network = client.networks.create("msa-network", driver="bridge")

# 2. Redis 컨테이너 실행 (볼륨 포함)
redis_vol = client.volumes.create("redis-data")
redis = client.containers.run(
    "redis:6",
    volumes={redis_vol.name: {'bind': '/data', 'mode': 'rw'}},
    network="msa-network",
    detach=True
)

# 3. Flask API 실행
flask = client.containers.run(
    "my-flask-image:latest",
    environment={"REDIS_HOST": "redis"},
    ports={'5000/tcp': 5000},
    network="msa-network",
    detach=True
)

# 4. Nginx 실행 (커스텀 설정 파일 마운트)
nginx = client.containers.run(
    "nginx:latest",
    volumes={'/path/nginx.conf': {'bind': '/etc/nginx/nginx.conf', 'mode': 'ro'}},
    ports={'80/tcp': 8080},
    network="msa-network",
    detach=True
)

 

 

 - reference : 

https://docker-py.readthedocs.io/en/stable/

댓글