Docker 이미지 심층 분석
James Reed
Infrastructure Engineer · Leapcell

Docker 이미지 심층 분석
I. Docker 이미지 개요
컨테이너의 기초로서 Docker 이미지는 기본적으로 컨테이너의 파일 시스템 내용을 나타냅니다. Docker 컨테이너를 생성하는 데 사용되는 읽기 전용 템플릿입니다. 기술적인 관점에서 Docker 이미지는 계층화된 구조 설계를 채택합니다. 기본 이미지를 제외하고 다른 이미지는 기존 이미지 위에 새로운 콘텐츠를 오버레이하여 생성됩니다. 이미지의 각 레이어에 대한 메타데이터는 json
파일에 저장됩니다. 이 메타데이터는 파일 시스템의 정적 콘텐츠를 설명할 뿐만 아니라 이미지 생성 시간, 빌드 지침 등과 같은 동적 데이터 정보도 포함합니다.
1.1 Docker 이미지 빌드
Docker 이미지는 일반적으로 Dockerfile
을 통해 빌드됩니다. Dockerfile
은 이미지의 기본 환경을 정의하고, 소프트웨어 패키지를 설치하고, 파일을 복사하는 등의 작업을 수행하기 위한 일련의 명령어가 포함된 텍스트 파일입니다. 빌드 과정에서 Docker는 Dockerfile
에 있는 명령어의 순서에 따라 이미지의 각 레이어를 단계별로 생성합니다. 각 명령어가 실행될 때마다 새로운 이미지 레이어가 생성되고, 이러한 레이어는 이후 빌드에서 재사용하기 위해 캐싱됩니다.
1.2 이미지 빌드 효율성을 높이는 방법
- 캐싱 메커니즘을 합리적으로 활용: 이미지를 빌드할 때 Docker는 현재 명령어로 생성된 이미지 레이어가 로컬 캐시에 이미 존재하는지 확인합니다. 존재하는 경우 특정 조건(예: 동일한 명령어, 변경되지 않은 파일 내용 등)을 충족하면 캐싱된 이미지 레이어를 직접 사용하여 반복적인 빌드를 피합니다. 예를 들어,
RUN
명령어를 실행할 때 이 명령어의 실행 결과가 이전에 캐싱된 결과와 동일한 경우(명령어 및 파일 시스템의 변경 사항 등을 비교하여 판단) 이미지 레이어가 재사용됩니다. - Dockerfile의 구조 최적화: 자주 변경되지 않는 명령어(예: 기본 소프트웨어 패키지 설치)를 앞에 둡니다. 이렇게 하면 나중에
Dockerfile
을 수정할 때 이러한 기본 명령어가 변경되지 않는 한 기존 이미지 레이어를 재사용할 수 있어 빌드 시간을 줄일 수 있습니다. - 레이어 단위로 빌드: 복잡한 빌드 프로세스를 여러 단계로 분할하고 각 단계는 특정 작업만 담당합니다. 예를 들어 컴파일 프로세스를 포함하는 이미지를 빌드할 때 컴파일을 먼저 한 단계에서 완료한 다음 컴파일된 결과를 다른 단계에서 최종 이미지로 복사하여 최종 이미지의 크기를 줄일 수 있습니다.
II. Docker 이미지 관련 명령어
Docker 클라이언트는 Docker 데몬과 상호 작용하여 이미지와 관련된 다양한 작업을 완료할 수 있는 풍부한 명령어 세트를 제공합니다.
- 이미지 나열:
docker images
명령어는 Docker 호스트에 있는 모든 이미지를 나열하는 데 사용됩니다. 필터링을 위해-f
매개변수를 사용할 수 있습니다. 예를 들어docker images -f dangling=true
는 태그가 없는 모든 이미지를 나열할 수 있습니다. - 이미지 빌드:
docker build
명령어는Dockerfile
에서 새 이미지를 빌드합니다. 예를 들어docker build -t myimage:latest .
, 여기서-t
는 이미지의 태그를 지정하는 데 사용되고.
는 현재 디렉터리의Dockerfile
을 나타냅니다. - 이미지 기록 보기:
docker history
명령어는 특정 이미지의 빌드 기록을 나열하여 이미지의 각 레이어의 생성 시간, 실행된 명령어, 이미지 크기 등의 정보를 보여줍니다. - 이미지 가져오기:
docker import
는tarball
파일에서 새 파일 시스템 이미지를 만듭니다. - 이미지 풀:
docker pull
은 Docker 이미지 레지스트리에서 로컬로 지정된 이미지를 가져옵니다. - 이미지 푸시:
docker push
는 로컬 이미지를 지정된 이미지 레지스트리로 푸시합니다. - 이미지 삭제:
docker rmi
는 로컬 이미지를 삭제하는 데 사용됩니다. 이미지가 여러 태그에서 참조되는 경우 먼저 모든 태그를 제거하거나-f
매개변수를 사용하여 강제 삭제해야 합니다. - 이미지 저장:
docker save
는 이미지를tar
파일로 저장하여 다른 환경에서 이미지를 마이그레이션하는 데 편리합니다. - 이미지 검색:
docker search
는 Docker Hub에서 조건을 충족하는 이미지를 검색합니다. - 이미지 태그:
docker tag
는 이미지에 태그를 지정하여 버전 관리 및 이미지 식별에 편리합니다.
III. Docker 이미지 다운로드 프로세스 (pull 작업)
Docker는 일반적인 C/S (클라이언트/서버) 아키텍처를 채택합니다. docker pull
과 같은 클라이언트 명령어는 결국 처리를 위해 Docker 데몬(서버 측)으로 전송됩니다. docker pull
이 실행되면 구체적인 프로세스는 다음과 같습니다.
- Docker 클라이언트는 구성 및 매개변수를 구성하고
pull
명령어를 Docker 서버로 보냅니다. - 서버 측에서 명령어를 받으면 해당 처리기에 전달합니다. 처리기는 Docker 데몬이 시작될 때 등록된
CmdPull
작업을 시작합니다. - 들어오는 이미지 레지스트리 주소(레지스트리 주소), 리포지토리 이름(repo name), 이미지 이름 및 태그(tag)에 따라 Docker 데몬은 다음 단계를 통해 이미지를 찾아 다운로드합니다.
- 리포지토리 아래의 모든 이미지 ID 가져오기:
GET /repositories/{repo}/images
인터페이스를 통해. - 리포지토리 아래의 모든 태그 정보 가져오기:
GET /repositories/{repo}/tags
인터페이스를 통해. - 태그에 따라 해당 이미지 UUID를 찾아 이미지를 다운로드합니다.
- 이미지의 기록 정보를 가져와 이러한 이미지 레이어를 하나씩 다운로드합니다:
GET /images/{image_id}/ancestry
인터페이스를 통해. 이미지 레이어가 로컬에 이미 있는 경우 다운로드가 건너뛰고, 그렇지 않은 경우 다운로드가 계속됩니다. - 이미지 레이어의
json
정보 가져오기:GET /images/{image_id}/json
인터페이스를 통해. - 이미지 콘텐츠 다운로드:
GET /images/{image_id}/layer
인터페이스를 통해.
- 리포지토리 아래의 모든 이미지 ID 가져오기:
- 다운로드가 완료되면 이미지 콘텐츠를 로컬 UnionFS(Union File System)에 저장하고 새로 다운로드한 이미지의 정보를 TagStore에 추가합니다.
IV. Docker 이미지 저장
4.1 UnionFS 및 aufs
UnionFS는 Docker가 계층적 이미지를 구현하기 위한 기반입니다. Linux, FreeBSD 및 NetBSD와 같은 시스템에서 여러 파일 시스템 분기를 투명하게 오버레이하여 통합 파일 시스템을 형성하는 파일 시스템 서비스입니다. Docker에서 이미지는 계층 형태로 저장됩니다. 애플리케이션 레이어는 완전한 파일 시스템을 보는 반면, 기본 레이어는 UnionFS를 통해 각 이미지 레이어의 콘텐츠 및 관계를 관리합니다.
aufs
(Another UnionFS)는 Docker에서 일반적으로 사용되는 스토리지 드라이버 중 하나입니다. 또한 devicemapper
등이 있습니다. 사용자는 필요에 따라 적절한 스토리지 드라이버를 선택하거나 자신의 드라이버를 구현할 수도 있습니다.
4.2 aufs 이미지의 스토리지 구조
ubuntu:20.04
이미지를 예로 들어 (현재 Docker 버전이 20.10.0이고 이미지 드라이버가 aufs
라고 가정), docker history
를 사용하여 이미지 기록을 봅니다.
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE myregistry/ubuntu 20.04 8b24f7a1cb23 2 months ago 256.3 MB $ docker history 8b24 IMAGE CREATED CREATED BY SIZE 8b24f7a1cb23 2 months ago /bin/sh -c #(nop) CMD ["bash"] 0 B b17ee223aa89 2 months ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.9 kB c18294cc5170 2 months ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 195.5 kB d4fd76b09ce9 2 months ago /bin/sh -c #(nop) ADD file:0018ff77d038472f52 256.1 MB 511136ea3c5a 3 years ago 0 B
ubuntu:20.04
이미지에는 여러 레이어가 포함되어 있음을 알 수 있습니다. aufs
데이터는 /var/lib/docker/aufs
디렉터리에 저장되며 여기에는 세 개의 주요 폴더가 포함됩니다.
- layers: 각 이미지가 구성된 레이어를 기록합니다.
- diff: 각 이미지와 이전 이미지 간의 차이점 콘텐츠, 즉 현재 이미지 레이어의 실제 데이터를 저장합니다.
- mnt: UnionFS가 외부에 제공하는 마운트 포인트로서 각 실행 중인 컨테이너에는 이 디렉터리에 해당 폴더가 있으며 통합된 파일 액세스 인터페이스를 제공하는 데 사용됩니다.
또한 Docker는 각 이미지 레이어에 대한 메타데이터를 json
형식으로 저장하며 이는 /var/lib/docker/graph/<image_id>/json
에 저장됩니다. 예를 들면 다음과 같습니다.
{ "id": "8b24f7a1cb23146e20erewtewtertewrwc0f82943f4ab8c097e7", "parent": "b17ee223aa89d1b136ea55eqweqweqwrewra6c88d93e1ad7c", "created": "2024-12-21T02:11:06.735146646Z", "container": "c9a3eda5951d28aa8dbe5qwrqwrewrtw886d0a8e7a710132a38ec", "container_config": { "Hostname": "43bd710ec89a", "Domainname": "", "User": "", "Memory": 0, "MemorySwap": 0, "CpuShares": 0, "Cpuset": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "PortSpecs": null, "ExposedPorts": null, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) CMD [\"bash\"]" ], "Image": "b17ee223aa89d1b136ea55e4421f4ce413dfc6c0cc6b2186dea6c88d93e1ad7c", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "NetworkDisabled": false, "MacAddress": "", "OnBuild": [], "Labels": null }, "docker_version": "20.10.0", "config": { "Hostname": "43bd710ec89a", "Domainname": "", "User": "", "Memory": 0, "MemorySwap": 0, "CpuShares": 0, "Cpuset": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "PortSpecs": null, "ExposedPorts": null, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "bash" ], "Image": "b17ee223aa89qwrewtretgertwerewrq6dea6c88d93e1ad7c", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "NetworkDisabled": false, "MacAddress": "", "OnBuild": [], "Labels": null }, "architecture": "amd64", "os": "linux", "Size": 0 }
동시에 /var/lib/docker/graph/<image_id>/layersize
파일은 이미지 레이어의 크기 정보를 저장합니다.
V. Docker 이미지의 생성 및 캐싱 메커니즘
docker build
를 사용하여 이미지를 만들 때 Docker는 캐싱 메커니즘을 사용하여 빌드 효율성을 향상시킵니다. 다음 Dockerfile
을 예로 들어 보겠습니다.
FROM ubuntu:20.04 RUN apt-get update ADD run.sh / VOLUME /data CMD ["./run.sh"]
빌드 과정에서 Docker는 명령어의 순서에 따라 실행합니다.
FROM
명령어 처리: Docker 데몬은 로컬에서ubuntu:20.04
이미지를 먼저 찾습니다. 존재하지 않으면 이미지 레지스트리에서 가져오고 메타데이터가 포함된json
파일을 가져옵니다.RUN
명령어 처리: 사용 가능한 캐시가 없으면 이 명령어는apt-get update
를 실행하고 파일 시스템의 변경 사항(예: 업데이트된 소프트웨어 패키지 목록 등)은/var/lib/docker/aufs/diff/<image_id>/
디렉터리에 저장됩니다. 동시에json
파일의container_config.Cmd
필드는 실행된 명령어를 기록합니다. 다음에 빌드할 때 새 이미지 레이어의parent
가 여전히ubuntu:20.04
이고json
파일의cmd
에서 변경할 내용이 동일하면 이미지 레이어가 동일하다고 간주되어 다시 빌드할 필요 없이 직접 재사용됩니다.ADD
및COPY
명령어 처리:ADD
또는COPY
명령어의 경우 Docker는 파일의 해시 값을 계산하여 이미지가 동일한지 확인합니다.json
파일에서ADD
명령어에 해당하는Cmd
필드는 파일의 해시 문자열을 기록합니다. 파일 내용, 파일 이름 등이 완전히 동일한 경우에만 이미지 레이어가 재사용됩니다.
그러나 캐싱 메커니즘에는 제한 사항이 있습니다. 외부 리소스에 의존하는 명령어(예: 외부 소프트웨어 소스에서 업데이트를 가져오는 apt-get update
, 외부 파일을 다운로드하는 curl
등)의 경우 외부 콘텐츠가 변경되면 Docker는 자동으로 감지할 수 없습니다. 이 경우 --no-cache
매개변수를 사용하여 캐시를 강제로 비활성화하고 이미지를 다시 빌드할 수 있습니다. 따라서 Dockerfile
을 작성할 때 개발자는 캐싱 메커니즘을 충분히 고려하고 이미지 빌드의 정확성과 효율성을 보장하기 위해 공식에서 제공하는 모범 사례를 따라야 합니다.
VI. Docker 이미지와 컨테이너 간의 관계
Docker 컨테이너는 이미지의 실행 중인 인스턴스입니다. 이미지는 정적 파일 시스템 콘텐츠를 포함하고 컨테이너는 이 기반에 동적 런타임 상태를 추가합니다. 컨테이너 실행 중 관련 정보(파일 시스템 콘텐츠 제외)는 이미지의 json
파일에 저장됩니다. 예를 들면 다음과 같습니다.
- 환경 변수: 예를 들어
ENV FOO=BAR
는 컨테이너 실행 중 환경 변수를 정의합니다. - 데이터 볼륨:
VOLUME /some/path
에 의해 선언된 컨테이너 데이터 볼륨은 컨테이너 실행 중에 동적으로 추가되며 이미지 레이어의 고정된 콘텐츠가 아닙니다. - 노출된 포트:
EXPOSE 80
은 컨테이너가 실행 중에 외부에 노출해야 하는 포트를 기록합니다. - 실행 진입점:
CMD ["./myscript.sh"]
는 컨테이너가 시작될 때 실행할 명령어를 정의합니다.
컨테이너를 시작할 때 Docker 데몬은 이미지 정보를 컨테이너의 루트 파일 시스템(rootfs)으로 읽음과 동시에 json
파일의 동적 정보를 읽어 컨테이너의 런타임 상태를 구성합니다. 각 실행 중인 컨테이너는 Docker 데몬의 하위 프로세스이며 Docker 데몬은 컨테이너의 수명 주기 및 리소스 할당을 관리합니다.
VII. Docker 이미지 삭제
이미지는 UnionFS 형식으로 로컬에 저장되며 docker rmi
명령어를 사용하여 이미지를 삭제할 수 있습니다. 삭제할 때는 다음 사항에 유의해야 합니다.
- 이미지 참조 관계: 이미지에는 "참조" 개념이 있습니다. 즉, 이미지는 여러 태그에서 참조될 수 있습니다. 태그가 있는 이미지를 삭제할 때 태그가 먼저 제거됩니다(태그 해제 작업). 이미지가 다른 태그에서 여전히 참조되는 경우 먼저 모든 태그를 삭제하거나
-f
매개변수를 사용하여 강제 삭제할 수 있습니다. - 다중 레이어 이미지 삭제: 이미지가 여러 레이어를 포함하고 중간 레이어가 다른 이미지에서 참조되지 않는 경우 이 이미지를 삭제하면 참조되지 않은 모든 이미지 레이어가 함께 삭제됩니다.
Leapcell: 최고의 서버리스 웹 호스팅
마지막으로 웹 서비스 배포에 가장 적합한 플랫폼인 **Leapcell**을 추천합니다.
🚀 좋아하는 언어로 빌드
JavaScript, Python, Go 또는 Rust로 손쉽게 개발하십시오.
🌍 무제한 프로젝트를 무료로 배포
사용한 만큼만 지불하십시오. 요청도 없고 요금도 없습니다.
⚡ 사용한 만큼 지불, 숨겨진 비용 없음
유휴 요금 없이 원활한 확장성만 제공합니다.
🔹 Twitter에서 팔로우하세요: @LeapcellHQ