[{"data":1,"prerenderedAt":1529},["ShallowReactive",2],{"post-document:\u002Fdevops\u002Fdocker\u002F2026\u002F08\u002F17\u002Fdocker-07-image-layer-archive\u002F":3},{"id":4,"title":5,"body":6,"categories":1512,"date":1515,"description":1516,"extension":1517,"image":1518,"key_concepts":1518,"last_modified_at":1518,"legacyPath":1519,"meta":1520,"navigation":640,"part":1522,"path":1523,"published":640,"robots":1518,"seo":1524,"series":1525,"stem":1526,"strengths":1518,"summary":1518,"tags":1527,"tradeoffs":1518,"__hash__":1528},"posts\u002Fposts\u002FCI-CD-Docker\u002FDocker\u002F2026-08-17-docker-07-image-layer-archive.md","Docker 기초 7편: 이미지 Layer와 tar 내부 구조",{"type":7,"value":8,"toc":1495},"minimark",[9,13,49,78,95,100,103,108,113,194,198,201,223,226,244,248,273,276,315,353,368,372,428,431,446,450,453,482,485,496,498,502,505,574,579,583,601,727,730,803,842,846,849,879,895,899,1015,1064,1068,1172,1178,1192,1195,1283,1286,1355,1374,1378,1442,1445,1482,1489,1491],[10,11,12],"p",{},"Docker를 이해할 때는 다음 흐름을 먼저 잡으면 쉽다.",[14,15,20],"pre",{"className":16,"code":17,"language":18,"meta":19,"style":19},"language-mermaid shiki shiki-themes github-light github-dark","flowchart LR\n    A[\"Dockerfile\u003Cbr\u002F>실행 환경 설명서\"] -->|docker build| B[\"Image\u003Cbr\u002F>배포 가능한 설계도\"]\n    B -->|docker run| C[\"Container\u003Cbr\u002F>격리된 프로세스\"]\n    C --> D[\"Linux Kernel\u003Cbr\u002F>실제 실행과 격리 담당\"]\n","mermaid","",[21,22,23,31,37,43],"code",{"__ignoreMap":19},[24,25,28],"span",{"class":26,"line":27},"line",1,[24,29,30],{},"flowchart LR\n",[24,32,34],{"class":26,"line":33},2,[24,35,36],{},"    A[\"Dockerfile\u003Cbr\u002F>실행 환경 설명서\"] -->|docker build| B[\"Image\u003Cbr\u002F>배포 가능한 설계도\"]\n",[24,38,40],{"class":26,"line":39},3,[24,41,42],{},"    B -->|docker run| C[\"Container\u003Cbr\u002F>격리된 프로세스\"]\n",[24,44,46],{"class":26,"line":45},4,[24,47,48],{},"    C --> D[\"Linux Kernel\u003Cbr\u002F>실제 실행과 격리 담당\"]\n",[50,51,52,60,66,72],"ul",{},[53,54,55,59],"li",{},[56,57,58],"strong",{},"Dockerfile"," 은 이미지를 만드는 방법을 적은 문서.",[53,61,62,65],{},[56,63,64],{},"이미지(Image)"," 는 애플리케이션 실행에 필요한 파일과 설정을 묶은 읽기 전용 패키지.",[53,67,68,71],{},[56,69,70],{},"컨테이너(Container)"," 는 이미지를 바탕으로 실행된 격리된 프로세스 또는 프로세스 그룹.",[53,73,74,77],{},[56,75,76],{},"Linux Kernel"," 은 Namespace, cgroups 등의 기능으로 컨테이너를 실제로 격리하고 제한.",[79,80,81],"blockquote",{},[10,82,83,84,86,87,90,91,94],{},"쉬운 비유: ",[21,85,58],{},"은 요리법, ",[21,88,89],{},"Image","는 밀키트, ",[21,92,93],{},"Container","는 밀키트로 실제 조리한 한 접시. 같은 이미지로 여러 컨테이너를 제작 가능.",[79,96,97],{},[10,98,99],{},"다시 한 번 말하지만, 도커의 핵심 철학 중 하나는 재탕이다.",[101,102],"hr",{},[104,105,107],"h2",{"id":106},"_1-docker-이미지와-레이어","1. Docker 이미지와 레이어",[109,110,112],"h3",{"id":111},"먼저-알아둘-단어","먼저 알아둘 단어",[114,115,116,132],"table",{},[117,118,119],"thead",{},[120,121,122,126,129],"tr",{},[123,124,125],"th",{},"단어",[123,127,128],{},"뜻",[123,130,131],{},"쉬운 비유",[133,134,135,146,157,172,183],"tbody",{},[120,136,137,140,143],{},[138,139,89],"td",{},[138,141,142],{},"애플리케이션 실행 파일, 라이브러리, 설정을 묶은 읽기 전용 패키지",[138,144,145],{},"프로그램을 찍어내는 설계도",[120,147,148,151,154],{},[138,149,150],{},"Layer",[138,152,153],{},"이전 상태에서 추가,수정,삭제된 파일의 묶음",[138,155,156],{},"투명 필름 한 장",[120,158,159,162,169],{},[138,160,161],{},"rootfs",[138,163,164,165,168],{},"컨테이너가 ",[21,166,167],{},"\u002F","로 보는 루트 파일 시스템",[138,170,171],{},"컨테이너 전용 서랍장",[120,173,174,177,180],{},[138,175,176],{},"Metadata",[138,178,179],{},"실행 명령, 환경 변수, 포트 등 이미지의 설정 정보",[138,181,182],{},"제품 사용 설명서",[120,184,185,188,191],{},[138,186,187],{},"Digest",[138,189,190],{},"콘텐츠를 해시한 고유 식별값",[138,192,193],{},"내용이 바뀌면 함께 바뀌는 디지털 지문",[109,195,197],{"id":196},"_11-이미지는-무엇인가","1.1 이미지는 무엇인가?",[10,199,200],{},"Docker 이미지는 애플리케이션을 실행하는 데 필요한 다음 요소를 묶은 배포 단위.",[50,202,203,206,217,220],{},[53,204,205],{},"여러 개의 파일 시스템 레이어",[53,207,208,209,212,213,216],{},"실행 명령(",[21,210,211],{},"CMD",", ",[21,214,215],{},"ENTRYPOINT",")",[53,218,219],{},"환경 변수, 작업 디렉터리, 사용자 등의 설정",[53,221,222],{},"레이어의 순서와 Digest를 가리키는 Manifest",[10,224,225],{},"레이어는 일반적으로 파일 시스템의 변경분을 tar 형식으로 직렬화해 저장한다. 이미지 전체가 단일 거대한 파일인 것이 아니라, 여러 변경분과 메타데이터가 조합된 구조.",[79,227,228],{},[10,229,230,231,234,235,212,237,212,240,243],{},"주의: 레이어에는 주로 ",[56,232,233],{},"파일 시스템 변경분","이 들어가고, ",[21,236,211],{},[21,238,239],{},"LABEL",[21,241,242],{},"EXPOSE"," 같은 실행 설정은 이미지 설정 메타데이터에 기록된다.",[109,245,247],{"id":246},"_12-레이어는-어떻게-쌓이는가","1.2 레이어는 어떻게 쌓이는가?",[14,249,251],{"className":16,"code":250,"language":18,"meta":19,"style":19},"flowchart BT\n    L1[\"Layer 1\u003Cbr\u002F>Base OS 파일\"] --> L2[\"Layer 2\u003Cbr\u002F>패키지 설치\"]\n    L2 --> L3[\"Layer 3\u003Cbr\u002F>애플리케이션 복사\"]\n    L3 --> CFG[\"Image Config\u003Cbr\u002F>CMD,환경 변수,포트\"]\n",[21,252,253,258,263,268],{"__ignoreMap":19},[24,254,255],{"class":26,"line":27},[24,256,257],{},"flowchart BT\n",[24,259,260],{"class":26,"line":33},[24,261,262],{},"    L1[\"Layer 1\u003Cbr\u002F>Base OS 파일\"] --> L2[\"Layer 2\u003Cbr\u002F>패키지 설치\"]\n",[24,264,265],{"class":26,"line":39},[24,266,267],{},"    L2 --> L3[\"Layer 3\u003Cbr\u002F>애플리케이션 복사\"]\n",[24,269,270],{"class":26,"line":45},[24,271,272],{},"    L3 --> CFG[\"Image Config\u003Cbr\u002F>CMD,환경 변수,포트\"]\n",[10,274,275],{},"다음 Dockerfile을 예로 들어 보자.",[14,277,281],{"className":278,"code":279,"language":280,"meta":19,"style":19},"language-dockerfile shiki shiki-themes github-light github-dark","FROM python:3.12-slim\nWORKDIR \u002Fapp\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\nCOPY . .\nCMD [\"python\", \"webserver.py\"]\n","dockerfile",[21,282,283,288,293,298,303,309],{"__ignoreMap":19},[24,284,285],{"class":26,"line":27},[24,286,287],{},"FROM python:3.12-slim\n",[24,289,290],{"class":26,"line":33},[24,291,292],{},"WORKDIR \u002Fapp\n",[24,294,295],{"class":26,"line":39},[24,296,297],{},"COPY requirements.txt .\n",[24,299,300],{"class":26,"line":45},[24,301,302],{},"RUN pip install --no-cache-dir -r requirements.txt\n",[24,304,306],{"class":26,"line":305},5,[24,307,308],{},"COPY . .\n",[24,310,312],{"class":26,"line":311},6,[24,313,314],{},"CMD [\"python\", \"webserver.py\"]\n",[50,316,317,323,335,347],{},[53,318,319,322],{},[21,320,321],{},"FROM",": 부모 이미지의 기존 레이어를 가져온다.",[53,324,325,212,328,212,331,334],{},[21,326,327],{},"COPY",[21,329,330],{},"RUN",[21,332,333],{},"ADD",": 보통 파일 시스템 변경분을 새 레이어에 기록.",[53,336,337,212,339,212,341,212,344,346],{},[21,338,211],{},[21,340,215],{},[21,342,343],{},"ENV",[21,345,242],{},": 주로 실행 설정 메타데이터를 기록.",[53,348,349,352],{},[21,350,351],{},"WORKDIR"," 등 일부 명령은 빌더와 상황에 따라 파일 시스템 변경과 이미지 이력의 표현이 달라질 수 있다.",[10,354,355,356,359,360,363,364,367],{},"따라서 ",[56,357,358],{},"Dockerfile 명령어 하나가 항상 실제 파일 시스템 레이어 하나와 정확히 대응하는 것은 아니다."," ",[21,361,362],{},"docker image history","에는 파일 변경이 없는 ",[21,365,366],{},"0B"," 단계도 표시될 수 있습니다.",[109,369,371],{"id":370},"_13-레이어의-특징","1.3 레이어의 특징",[50,373,374,391,401,415],{},[53,375,376,379],{},[56,377,378],{},"이미지 레이어는 기본적으로 읽기 전용입.",[50,380,381,384],{},[53,382,383],{},"여러 이미지와 컨테이너가 동일한 레이어를 안전하게 공유하고, 원본 이미지가 변경되는 것을 방지하기 위해 읽기 전용으로 사용.",[53,385,386,387,390],{},"컨테이너에서 파일을 수정하면 이미지 레이어를 직접 변경하지 않고, 컨테이너 위에 생성된 별도의 ",[56,388,389],{},"쓰기 가능한 레이어","에 변경 사항을 저장.",[53,392,393,396],{},[56,394,395],{},"부모에서 자식 순서로 쌓인다.",[50,397,398],{},[53,399,400],{},"위 레이어의 파일이 아래 레이어의 같은 경로에 있는 파일을 가릴 수 있다.",[53,402,403,406],{},[56,404,405],{},"파일 삭제도 아래 레이어를 실제로 지우지 않는다.",[50,407,408],{},[53,409,410,411,414],{},"읽기 전용인 아래 레이어를 변경할 수 없으므로, 위 레이어에 ",[21,412,413],{},"whiteout","이라는 삭제 표시를 남겨 파일이 삭제된 것처럼 보이게 한다.",[53,416,417,420],{},[56,418,419],{},"동일한 Digest의 레이어는 여러 이미지와 컨테이너가 공유할 수 있다.",[50,421,422,425],{},[53,423,424],{},"레이어가 읽기 전용이므로 여러 곳에서 함께 사용해도 내용이 바뀌거나 서로의 작업에 영향을 주지 않는다.",[53,426,427],{},"이를 통해 디스크 사용량과 이미지 다운로드 시간을 줄일 수 있다.",[10,429,430],{},"레이어 공유 덕분에 다음 효과가 생긴다.",[50,432,433,436,443],{},[53,434,435],{},"같은 베이스 이미지를 중복 저장하지 않아 디스크를 절약한다.",[53,437,438,439,442],{},"이미 보유한 레이어는 다시 받지 않아 ",[21,440,441],{},"pull","이 빨라진다.",[53,444,445],{},"동일한 이미지를 사용하면 서버가 달라도 비슷한 실행 환경을 재현할 수 있다.",[109,447,449],{"id":448},"_14-컨테이너를-실행하면-무엇이-달라지는가","1.4 컨테이너를 실행하면 무엇이 달라지는가?",[10,451,452],{},"이미지를 실행하면 읽기 전용 이미지 레이어 위에 컨테이너 전용 쓰기 레이어가 추가된다.",[14,454,456],{"className":16,"code":455,"language":18,"meta":19,"style":19},"flowchart BT\n    B[\"Base image layer\u003Cbr\u002F>읽기 전용\"] --> L1[\"Package layer\u003Cbr\u002F>읽기 전용\"]\n    L1 --> L2[\"Application layer\u003Cbr\u002F>읽기 전용\"]\n    L2 --> U[\"Container writable layer\u003Cbr\u002F>컨테이너별 읽기,쓰기\"]\n    V[\"Volume\u003Cbr\u002F>영구 데이터\"] -. 별도 마운트 .-> U\n",[21,457,458,462,467,472,477],{"__ignoreMap":19},[24,459,460],{"class":26,"line":27},[24,461,257],{},[24,463,464],{"class":26,"line":33},[24,465,466],{},"    B[\"Base image layer\u003Cbr\u002F>읽기 전용\"] --> L1[\"Package layer\u003Cbr\u002F>읽기 전용\"]\n",[24,468,469],{"class":26,"line":39},[24,470,471],{},"    L1 --> L2[\"Application layer\u003Cbr\u002F>읽기 전용\"]\n",[24,473,474],{"class":26,"line":45},[24,475,476],{},"    L2 --> U[\"Container writable layer\u003Cbr\u002F>컨테이너별 읽기,쓰기\"]\n",[24,478,479],{"class":26,"line":305},[24,480,481],{},"    V[\"Volume\u003Cbr\u002F>영구 데이터\"] -. 별도 마운트 .-> U\n",[10,483,484],{},"컨테이너 안에서 파일을 수정해도 원본 이미지가 바뀌지는 않는다. 변경 내용은 해당 컨테이너의 쓰기 레이어에 저장된다.",[10,486,487,488,491,492,495],{},"컨테이너를 삭제하면 쓰기 레이어도 함께 사라진다. 데이터베이스 파일처럼 유지해야 할 데이터는 ",[56,489,490],{},"Volume","이나 ",[56,493,494],{},"Bind Mount","에 저장해야 합니다.",[101,497],{},[104,499,501],{"id":500},"_2-tar와-이미지-내부-구조","2. tar와 이미지 내부 구조",[109,503,112],{"id":504},"먼저-알아둘-단어-1",[114,506,507,517],{},[117,508,509],{},[120,510,511,513,515],{},[123,512,125],{},[123,514,128],{},[123,516,131],{},[133,518,519,530,541,552,563],{},[120,520,521,524,527],{},[138,522,523],{},"Archive",[138,525,526],{},"여러 파일과 디렉터리를 하나로 묶은 파일",[138,528,529],{},"이삿짐 상자",[120,531,532,535,538],{},[138,533,534],{},"tar",[138,536,537],{},"아카이브를 만들고 푸는 형식이자 명령어",[138,539,540],{},"물건을 한 상자에 포장하는 도구",[120,542,543,546,549],{},[138,544,545],{},"gzip",[138,547,548],{},"데이터 크기를 줄이는 압축 방식",[138,550,551],{},"상자 속 진공 압축팩",[120,553,554,557,560],{},[138,555,556],{},"Manifest",[138,558,559],{},"이미지 구성과 레이어 순서를 기록한 문서",[138,561,562],{},"상자 안 물품 목록",[120,564,565,568,571],{},[138,566,567],{},"Blob",[138,569,570],{},"Digest로 이름 붙여 저장한 실제 데이터 덩어리",[138,572,573],{},"번호표가 붙은 포장물",[79,575,576],{},[10,577,578],{},"쉬운 비유: tar는 여러 서류를 한 파일철에 모으는 작업이고, gzip은 그 파일철을 눌러 부피까지 줄이는 작업.",[109,580,582],{"id":581},"_21-tar-기본-사용법","2.1 tar 기본 사용법",[10,584,585,588,589,592,593,596,597,600],{},[21,586,587],{},".tar","는 여러 파일을 하나로 ",[56,590,591],{},"묶은 것","이다. tar 자체와 압축은 별개이며, gzip을 함께 사용하면 보통 ",[21,594,595],{},".tar.gz"," 또는 ",[21,598,599],{},".tgz","가 된다.",[14,602,606],{"className":603,"code":604,"language":605,"meta":19,"style":19},"language-bash shiki shiki-themes github-light github-dark","# 여러 파일과 디렉터리를 files.tar로 묶기\ntar -cvf files.tar file1.txt file2.txt directory\u002F\n\n# .tar 내부 목록 확인\ntar -tvf files.tar\n\n# .tar 풀기\ntar -xvf files.tar\n\n# gzip으로 압축된 .tar.gz 만들기\ntar -czvf files.tar.gz file1.txt file2.txt directory\u002F\n\n# .tar.gz 풀기\ntar -xzvf files.tar.gz\n","bash",[21,607,608,614,636,642,647,657,661,667,677,682,688,705,710,716],{"__ignoreMap":19},[24,609,610],{"class":26,"line":27},[24,611,613],{"class":612},"sJ8bj","# 여러 파일과 디렉터리를 files.tar로 묶기\n",[24,615,616,619,623,627,630,633],{"class":26,"line":33},[24,617,534],{"class":618},"sScJk",[24,620,622],{"class":621},"sj4cs"," -cvf",[24,624,626],{"class":625},"sZZnC"," files.tar",[24,628,629],{"class":625}," file1.txt",[24,631,632],{"class":625}," file2.txt",[24,634,635],{"class":625}," directory\u002F\n",[24,637,638],{"class":26,"line":39},[24,639,641],{"emptyLinePlaceholder":640},true,"\n",[24,643,644],{"class":26,"line":45},[24,645,646],{"class":612},"# .tar 내부 목록 확인\n",[24,648,649,651,654],{"class":26,"line":305},[24,650,534],{"class":618},[24,652,653],{"class":621}," -tvf",[24,655,656],{"class":625}," files.tar\n",[24,658,659],{"class":26,"line":311},[24,660,641],{"emptyLinePlaceholder":640},[24,662,664],{"class":26,"line":663},7,[24,665,666],{"class":612},"# .tar 풀기\n",[24,668,670,672,675],{"class":26,"line":669},8,[24,671,534],{"class":618},[24,673,674],{"class":621}," -xvf",[24,676,656],{"class":625},[24,678,680],{"class":26,"line":679},9,[24,681,641],{"emptyLinePlaceholder":640},[24,683,685],{"class":26,"line":684},10,[24,686,687],{"class":612},"# gzip으로 압축된 .tar.gz 만들기\n",[24,689,691,693,696,699,701,703],{"class":26,"line":690},11,[24,692,534],{"class":618},[24,694,695],{"class":621}," -czvf",[24,697,698],{"class":625}," files.tar.gz",[24,700,629],{"class":625},[24,702,632],{"class":625},[24,704,635],{"class":625},[24,706,708],{"class":26,"line":707},12,[24,709,641],{"emptyLinePlaceholder":640},[24,711,713],{"class":26,"line":712},13,[24,714,715],{"class":612},"# .tar.gz 풀기\n",[24,717,719,721,724],{"class":26,"line":718},14,[24,720,534],{"class":618},[24,722,723],{"class":621}," -xzvf",[24,725,726],{"class":625}," files.tar.gz\n",[10,728,729],{},"주요 옵션은 다음과 같습니다.",[114,731,732,741],{},[117,733,734],{},[120,735,736,739],{},[123,737,738],{},"옵션",[123,740,128],{},[133,742,743,753,763,773,783,793],{},[120,744,745,750],{},[138,746,747],{},[21,748,749],{},"c",[138,751,752],{},"새 아카이브 생성(Create)",[120,754,755,760],{},[138,756,757],{},[21,758,759],{},"x",[138,761,762],{},"아카이브 해제(Extract)",[120,764,765,770],{},[138,766,767],{},[21,768,769],{},"t",[138,771,772],{},"내부 목록 조회(List)",[120,774,775,780],{},[138,776,777],{},[21,778,779],{},"v",[138,781,782],{},"처리 과정을 자세히 출력(Verbose)",[120,784,785,790],{},[138,786,787],{},[21,788,789],{},"f",[138,791,792],{},"뒤에 오는 값을 아카이브 파일명으로 사용(File)",[120,794,795,800],{},[138,796,797],{},[21,798,799],{},"z",[138,801,802],{},"gzip 압축 또는 해제",[14,804,806],{"className":16,"code":805,"language":18,"meta":19,"style":19},"flowchart LR\n    F1[\"file1\"] --> TAR[\"tar\"]\n    F2[\"file2\"] --> TAR\n    D[\"directory\u002F\"] --> TAR\n    TAR --> A[\"files.tar\"]\n    A --> G[\"gzip\"]\n    G --> TGZ[\"files.tar.gz\"]\n",[21,807,808,812,817,822,827,832,837],{"__ignoreMap":19},[24,809,810],{"class":26,"line":27},[24,811,30],{},[24,813,814],{"class":26,"line":33},[24,815,816],{},"    F1[\"file1\"] --> TAR[\"tar\"]\n",[24,818,819],{"class":26,"line":39},[24,820,821],{},"    F2[\"file2\"] --> TAR\n",[24,823,824],{"class":26,"line":45},[24,825,826],{},"    D[\"directory\u002F\"] --> TAR\n",[24,828,829],{"class":26,"line":305},[24,830,831],{},"    TAR --> A[\"files.tar\"]\n",[24,833,834],{"class":26,"line":311},[24,835,836],{},"    A --> G[\"gzip\"]\n",[24,838,839],{"class":26,"line":663},[24,840,841],{},"    G --> TGZ[\"files.tar.gz\"]\n",[109,843,845],{"id":844},"_22-docker-이미지가-tar와-닮은-이유","2.2 Docker 이미지가 tar와 닮은 이유",[10,847,848],{},"컨테이너 이미지도 실행 파일들을 레이어별 묶음으로 저장. 각 레이어가 파일 시스템 변경분을 담고, Manifest가 레이어의 순서를 연결.",[14,850,852],{"className":16,"code":851,"language":18,"meta":19,"style":19},"flowchart TD\n    M[\"manifest\u003Cbr\u002F>레이어 순서\"] --> C[\"config\u003Cbr\u002F>실행 설정과 이력\"]\n    M --> L1[\"layer blob 1\u003Cbr\u002F>Base OS\"]\n    M --> L2[\"layer blob 2\u003Cbr\u002F>패키지\"]\n    M --> L3[\"layer blob 3\u003Cbr\u002F>앱 파일\"]\n",[21,853,854,859,864,869,874],{"__ignoreMap":19},[24,855,856],{"class":26,"line":27},[24,857,858],{},"flowchart TD\n",[24,860,861],{"class":26,"line":33},[24,862,863],{},"    M[\"manifest\u003Cbr\u002F>레이어 순서\"] --> C[\"config\u003Cbr\u002F>실행 설정과 이력\"]\n",[24,865,866],{"class":26,"line":39},[24,867,868],{},"    M --> L1[\"layer blob 1\u003Cbr\u002F>Base OS\"]\n",[24,870,871],{"class":26,"line":45},[24,872,873],{},"    M --> L2[\"layer blob 2\u003Cbr\u002F>패키지\"]\n",[24,875,876],{"class":26,"line":305},[24,877,878],{},"    M --> L3[\"layer blob 3\u003Cbr\u002F>앱 파일\"]\n",[10,880,881,882,885,886,212,889,212,892,894],{},"다만 ",[21,883,884],{},"docker save"," 결과의 정확한 디렉터리 형태와 Blob 압축 여부는 Docker 버전과 이미지 저장 방식에 따라 달라질 수 있다. 구조를 추측하기보다 ",[21,887,888],{},"file",[21,890,891],{},"jq",[21,893,534],{},"로 실제 형식을 확인하는 것이 안전.",[109,896,898],{"id":897},"_23-예제-이미지-만들기","2.3 예제 이미지 만들기",[14,900,902],{"className":278,"code":901,"language":280,"meta":19,"style":19},"ARG UBUNTU_VERSION=22.04\nFROM ubuntu:${UBUNTU_VERSION}\n\n# FROM 앞에서 선언한 ARG를 빌드 단계 안에서 다시 사용하려면 재선언.\nARG UBUNTU_VERSION\n\nRUN apt-get update \\\n    && apt-get install -y --no-install-recommends curl lsb-release nginx \\\n    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n\nRUN echo \"현재 빌드에 사용된 Ubuntu 버전: ${UBUNTU_VERSION}\"\n\nLABEL maintainer=\"sample@example.com\"\nLABEL description=\"Linux version example\"\n\nEXPOSE 8080\u002Ftcp\nEXPOSE 80\u002Ftcp\n\nWORKDIR \u002Fvar\u002Fwww\u002Fhtml\nCOPY index.html .\n\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n",[21,903,904,909,914,918,923,928,932,937,942,947,951,956,960,965,970,975,981,987,992,998,1004,1009],{"__ignoreMap":19},[24,905,906],{"class":26,"line":27},[24,907,908],{},"ARG UBUNTU_VERSION=22.04\n",[24,910,911],{"class":26,"line":33},[24,912,913],{},"FROM ubuntu:${UBUNTU_VERSION}\n",[24,915,916],{"class":26,"line":39},[24,917,641],{"emptyLinePlaceholder":640},[24,919,920],{"class":26,"line":45},[24,921,922],{},"# FROM 앞에서 선언한 ARG를 빌드 단계 안에서 다시 사용하려면 재선언.\n",[24,924,925],{"class":26,"line":305},[24,926,927],{},"ARG UBUNTU_VERSION\n",[24,929,930],{"class":26,"line":311},[24,931,641],{"emptyLinePlaceholder":640},[24,933,934],{"class":26,"line":663},[24,935,936],{},"RUN apt-get update \\\n",[24,938,939],{"class":26,"line":669},[24,940,941],{},"    && apt-get install -y --no-install-recommends curl lsb-release nginx \\\n",[24,943,944],{"class":26,"line":679},[24,945,946],{},"    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[24,948,949],{"class":26,"line":684},[24,950,641],{"emptyLinePlaceholder":640},[24,952,953],{"class":26,"line":690},[24,954,955],{},"RUN echo \"현재 빌드에 사용된 Ubuntu 버전: ${UBUNTU_VERSION}\"\n",[24,957,958],{"class":26,"line":707},[24,959,641],{"emptyLinePlaceholder":640},[24,961,962],{"class":26,"line":712},[24,963,964],{},"LABEL maintainer=\"sample@example.com\"\n",[24,966,967],{"class":26,"line":718},[24,968,969],{},"LABEL description=\"Linux version example\"\n",[24,971,973],{"class":26,"line":972},15,[24,974,641],{"emptyLinePlaceholder":640},[24,976,978],{"class":26,"line":977},16,[24,979,980],{},"EXPOSE 8080\u002Ftcp\n",[24,982,984],{"class":26,"line":983},17,[24,985,986],{},"EXPOSE 80\u002Ftcp\n",[24,988,990],{"class":26,"line":989},18,[24,991,641],{"emptyLinePlaceholder":640},[24,993,995],{"class":26,"line":994},19,[24,996,997],{},"WORKDIR \u002Fvar\u002Fwww\u002Fhtml\n",[24,999,1001],{"class":26,"line":1000},20,[24,1002,1003],{},"COPY index.html .\n",[24,1005,1007],{"class":26,"line":1006},21,[24,1008,641],{"emptyLinePlaceholder":640},[24,1010,1012],{"class":26,"line":1011},22,[24,1013,1014],{},"CMD [\"nginx\", \"-g\", \"daemon off;\"]\n",[14,1016,1018],{"className":603,"code":1017,"language":605,"meta":19,"style":19},"# 마지막의 점(.)은 현재 디렉터리를 빌드 컨텍스트로 전달한다는 뜻.\ndocker build --tag indepth-container:1.0 .\n\n# Dockerfile 단계와 이미지 이력 확인\ndocker image history indepth-container:1.0\n",[21,1019,1020,1025,1042,1046,1051],{"__ignoreMap":19},[24,1021,1022],{"class":26,"line":27},[24,1023,1024],{"class":612},"# 마지막의 점(.)은 현재 디렉터리를 빌드 컨텍스트로 전달한다는 뜻.\n",[24,1026,1027,1030,1033,1036,1039],{"class":26,"line":33},[24,1028,1029],{"class":618},"docker",[24,1031,1032],{"class":625}," build",[24,1034,1035],{"class":621}," --tag",[24,1037,1038],{"class":625}," indepth-container:1.0",[24,1040,1041],{"class":625}," .\n",[24,1043,1044],{"class":26,"line":39},[24,1045,641],{"emptyLinePlaceholder":640},[24,1047,1048],{"class":26,"line":45},[24,1049,1050],{"class":612},"# Dockerfile 단계와 이미지 이력 확인\n",[24,1052,1053,1055,1058,1061],{"class":26,"line":305},[24,1054,1029],{"class":618},[24,1056,1057],{"class":625}," image",[24,1059,1060],{"class":625}," history",[24,1062,1063],{"class":625}," indepth-container:1.0\n",[109,1065,1067],{"id":1066},"_24-이미지를-저장하고-분해하기","2.4 이미지를 저장하고 분해하기",[14,1069,1071],{"className":603,"code":1070,"language":605,"meta":19,"style":19},"mkdir -p indepth-container\ncd indepth-container\n\ndocker save --output indepth-container.tar indepth-container:1.0\n\n# 먼저 목록만 확인\ntar -tvf indepth-container.tar\n\n# 현재 디렉터리에 해제\ntar -xvf indepth-container.tar\nfind . -maxdepth 3 -type f | sort\n",[21,1072,1073,1084,1091,1095,1110,1114,1119,1128,1132,1137,1145],{"__ignoreMap":19},[24,1074,1075,1078,1081],{"class":26,"line":27},[24,1076,1077],{"class":618},"mkdir",[24,1079,1080],{"class":621}," -p",[24,1082,1083],{"class":625}," indepth-container\n",[24,1085,1086,1089],{"class":26,"line":33},[24,1087,1088],{"class":621},"cd",[24,1090,1083],{"class":625},[24,1092,1093],{"class":26,"line":39},[24,1094,641],{"emptyLinePlaceholder":640},[24,1096,1097,1099,1102,1105,1108],{"class":26,"line":45},[24,1098,1029],{"class":618},[24,1100,1101],{"class":625}," save",[24,1103,1104],{"class":621}," --output",[24,1106,1107],{"class":625}," indepth-container.tar",[24,1109,1063],{"class":625},[24,1111,1112],{"class":26,"line":305},[24,1113,641],{"emptyLinePlaceholder":640},[24,1115,1116],{"class":26,"line":311},[24,1117,1118],{"class":612},"# 먼저 목록만 확인\n",[24,1120,1121,1123,1125],{"class":26,"line":663},[24,1122,534],{"class":618},[24,1124,653],{"class":621},[24,1126,1127],{"class":625}," indepth-container.tar\n",[24,1129,1130],{"class":26,"line":669},[24,1131,641],{"emptyLinePlaceholder":640},[24,1133,1134],{"class":26,"line":679},[24,1135,1136],{"class":612},"# 현재 디렉터리에 해제\n",[24,1138,1139,1141,1143],{"class":26,"line":684},[24,1140,534],{"class":618},[24,1142,674],{"class":621},[24,1144,1127],{"class":625},[24,1146,1147,1150,1153,1156,1159,1162,1165,1169],{"class":26,"line":690},[24,1148,1149],{"class":618},"find",[24,1151,1152],{"class":625}," .",[24,1154,1155],{"class":621}," -maxdepth",[24,1157,1158],{"class":621}," 3",[24,1160,1161],{"class":621}," -type",[24,1163,1164],{"class":625}," f",[24,1166,1168],{"class":1167},"szBVR"," |",[24,1170,1171],{"class":618}," sort\n",[10,1173,1174,1177],{},[21,1175,1176],{},"manifest.json","이 있다면 다음처럼 확인.",[14,1179,1181],{"className":603,"code":1180,"language":605,"meta":19,"style":19},"jq . manifest.json\n",[21,1182,1183],{"__ignoreMap":19},[24,1184,1185,1187,1189],{"class":26,"line":27},[24,1186,891],{"class":618},[24,1188,1152],{"class":625},[24,1190,1191],{"class":625}," manifest.json\n",[10,1193,1194],{},"개념적인 내용은 다음과 같다. Digest 값은 빌드할 때마다 달라질 수 있다.",[14,1196,1200],{"className":1197,"code":1198,"language":1199,"meta":19,"style":19},"language-json shiki shiki-themes github-light github-dark","[\n  {\n    \"Config\": \"blobs\u002Fsha256\u002F\u003Cconfig-digest>\",\n    \"RepoTags\": [\"indepth-container:1.0\"],\n    \"Layers\": [\n      \"blobs\u002Fsha256\u002F\u003Cbase-layer-digest>\",\n      \"blobs\u002Fsha256\u002F\u003Cpackage-layer-digest>\",\n      \"blobs\u002Fsha256\u002F\u003Capp-layer-digest>\"\n    ]\n  }\n]\n","json",[21,1201,1202,1208,1213,1227,1241,1249,1256,1263,1268,1273,1278],{"__ignoreMap":19},[24,1203,1204],{"class":26,"line":27},[24,1205,1207],{"class":1206},"sVt8B","[\n",[24,1209,1210],{"class":26,"line":33},[24,1211,1212],{"class":1206},"  {\n",[24,1214,1215,1218,1221,1224],{"class":26,"line":39},[24,1216,1217],{"class":621},"    \"Config\"",[24,1219,1220],{"class":1206},": ",[24,1222,1223],{"class":625},"\"blobs\u002Fsha256\u002F\u003Cconfig-digest>\"",[24,1225,1226],{"class":1206},",\n",[24,1228,1229,1232,1235,1238],{"class":26,"line":45},[24,1230,1231],{"class":621},"    \"RepoTags\"",[24,1233,1234],{"class":1206},": [",[24,1236,1237],{"class":625},"\"indepth-container:1.0\"",[24,1239,1240],{"class":1206},"],\n",[24,1242,1243,1246],{"class":26,"line":305},[24,1244,1245],{"class":621},"    \"Layers\"",[24,1247,1248],{"class":1206},": [\n",[24,1250,1251,1254],{"class":26,"line":311},[24,1252,1253],{"class":625},"      \"blobs\u002Fsha256\u002F\u003Cbase-layer-digest>\"",[24,1255,1226],{"class":1206},[24,1257,1258,1261],{"class":26,"line":663},[24,1259,1260],{"class":625},"      \"blobs\u002Fsha256\u002F\u003Cpackage-layer-digest>\"",[24,1262,1226],{"class":1206},[24,1264,1265],{"class":26,"line":669},[24,1266,1267],{"class":625},"      \"blobs\u002Fsha256\u002F\u003Capp-layer-digest>\"\n",[24,1269,1270],{"class":26,"line":679},[24,1271,1272],{"class":1206},"    ]\n",[24,1274,1275],{"class":26,"line":684},[24,1276,1277],{"class":1206},"  }\n",[24,1279,1280],{"class":26,"line":690},[24,1281,1282],{"class":1206},"]\n",[10,1284,1285],{},"Blob의 실제 유형과 파일 목록은 다음처럼 확인.",[14,1287,1289],{"className":603,"code":1288,"language":605,"meta":19,"style":19},"cd blobs\u002Fsha256\nfile *\n\n# tar 형식 Blob\ntar -tvf \u003Clayer-digest>\n\n# gzip으로 압축된 tar Blob\ntar -tzvf \u003Clayer-digest>\n",[21,1290,1291,1298,1305,1309,1314,1331,1335,1340],{"__ignoreMap":19},[24,1292,1293,1295],{"class":26,"line":27},[24,1294,1088],{"class":621},[24,1296,1297],{"class":625}," blobs\u002Fsha256\n",[24,1299,1300,1302],{"class":26,"line":33},[24,1301,888],{"class":618},[24,1303,1304],{"class":621}," *\n",[24,1306,1307],{"class":26,"line":39},[24,1308,641],{"emptyLinePlaceholder":640},[24,1310,1311],{"class":26,"line":45},[24,1312,1313],{"class":612},"# tar 형식 Blob\n",[24,1315,1316,1318,1320,1323,1326,1328],{"class":26,"line":305},[24,1317,534],{"class":618},[24,1319,653],{"class":621},[24,1321,1322],{"class":1167}," \u003C",[24,1324,1325],{"class":625},"layer-diges",[24,1327,769],{"class":1206},[24,1329,1330],{"class":1167},">\n",[24,1332,1333],{"class":26,"line":311},[24,1334,641],{"emptyLinePlaceholder":640},[24,1336,1337],{"class":26,"line":663},[24,1338,1339],{"class":612},"# gzip으로 압축된 tar Blob\n",[24,1341,1342,1344,1347,1349,1351,1353],{"class":26,"line":669},[24,1343,534],{"class":618},[24,1345,1346],{"class":621}," -tzvf",[24,1348,1322],{"class":1167},[24,1350,1325],{"class":625},[24,1352,769],{"class":1206},[24,1354,1330],{"class":1167},[79,1356,1357],{},[10,1358,1359,1360,1363,1364,212,1366,212,1368,1370,1371,1373],{},"Manifest의 ",[21,1361,1362],{},"Layers"," 순서는 이미지의 파일 시스템을 구성하는 순서입니다. ",[21,1365,211],{},[21,1367,239],{},[21,1369,242],{},"처럼 파일을 바꾸지 않는 명령은 별도의 파일 시스템 레이어가 없을 수 있으므로, Dockerfile의 모든 줄과 ",[21,1372,1362],{}," 배열을 일대일로 맞추면 안 됨.",[109,1375,1377],{"id":1376},"_25-mariadb-이미지로-확인하기","2.5 MariaDB 이미지로 확인하기",[14,1379,1381],{"className":603,"code":1380,"language":605,"meta":19,"style":19},"docker pull mariadb:10.11\nmkdir -p indepth-mariadb\ncd indepth-mariadb\n\ndocker save --output mariadb.tar mariadb:10.11\ntar -xvf mariadb.tar\njq . manifest.json\n",[21,1382,1383,1393,1402,1408,1412,1425,1434],{"__ignoreMap":19},[24,1384,1385,1387,1390],{"class":26,"line":27},[24,1386,1029],{"class":618},[24,1388,1389],{"class":625}," pull",[24,1391,1392],{"class":625}," mariadb:10.11\n",[24,1394,1395,1397,1399],{"class":26,"line":33},[24,1396,1077],{"class":618},[24,1398,1080],{"class":621},[24,1400,1401],{"class":625}," indepth-mariadb\n",[24,1403,1404,1406],{"class":26,"line":39},[24,1405,1088],{"class":621},[24,1407,1401],{"class":625},[24,1409,1410],{"class":26,"line":45},[24,1411,641],{"emptyLinePlaceholder":640},[24,1413,1414,1416,1418,1420,1423],{"class":26,"line":305},[24,1415,1029],{"class":618},[24,1417,1101],{"class":625},[24,1419,1104],{"class":621},[24,1421,1422],{"class":625}," mariadb.tar",[24,1424,1392],{"class":625},[24,1426,1427,1429,1431],{"class":26,"line":311},[24,1428,534],{"class":618},[24,1430,674],{"class":621},[24,1432,1433],{"class":625}," mariadb.tar\n",[24,1435,1436,1438,1440],{"class":26,"line":663},[24,1437,891],{"class":618},[24,1439,1152],{"class":625},[24,1441,1191],{"class":625},[10,1443,1444],{},"특정 레이어의 내용은 다음과 같이 확인할 수 있다.",[14,1446,1448],{"className":603,"code":1447,"language":605,"meta":19,"style":19},"file blobs\u002Fsha256\u002F\u003Clayer-digest>\ntar -tvf blobs\u002Fsha256\u002F\u003Clayer-digest>\n",[21,1449,1450,1466],{"__ignoreMap":19},[24,1451,1452,1454,1457,1460,1462,1464],{"class":26,"line":27},[24,1453,888],{"class":618},[24,1455,1456],{"class":625}," blobs\u002Fsha256\u002F",[24,1458,1459],{"class":1167},"\u003C",[24,1461,1325],{"class":625},[24,1463,769],{"class":1206},[24,1465,1330],{"class":1167},[24,1467,1468,1470,1472,1474,1476,1478,1480],{"class":26,"line":33},[24,1469,534],{"class":618},[24,1471,653],{"class":621},[24,1473,1456],{"class":625},[24,1475,1459],{"class":1167},[24,1477,1325],{"class":625},[24,1479,769],{"class":1206},[24,1481,1330],{"class":1167},[10,1483,1484,1485,1488],{},"예를 들어 엔트리포인트가 추가된 레이어라면 ",[21,1486,1487],{},"usr\u002Flocal\u002Fbin\u002Fdocker-entrypoint.sh"," 같은 파일이 나타날 수 있다. 정확한 Digest와 레이어 수는 이미지 버전에 따라 달라진다.",[101,1490],{},[1492,1493,1494],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":19,"searchDepth":33,"depth":33,"links":1496},[1497,1504],{"id":106,"depth":33,"text":107,"children":1498},[1499,1500,1501,1502,1503],{"id":111,"depth":39,"text":112},{"id":196,"depth":39,"text":197},{"id":246,"depth":39,"text":247},{"id":370,"depth":39,"text":371},{"id":448,"depth":39,"text":449},{"id":500,"depth":33,"text":501,"children":1505},[1506,1507,1508,1509,1510,1511],{"id":504,"depth":39,"text":112},{"id":581,"depth":39,"text":582},{"id":844,"depth":39,"text":845},{"id":897,"depth":39,"text":898},{"id":1066,"depth":39,"text":1067},{"id":1376,"depth":39,"text":1377},[1513,1514],"DevOps","Docker","2026-08-17 12:30:00 +0900","Docker 이미지와 Layer의 특징, tar 명령어와 이미지 아카이브 내부 구조 확인 방법을 정리한다.","md",null,"\u002Fdevops\u002Fdocker\u002F2026\u002F08\u002F17\u002Fdocker-07-image-layer-archive\u002F",{"layout":1521},"post","7","\u002Fposts\u002Fci-cd-docker\u002Fdocker\u002F2026-08-17-docker-07-image-layer-archive",{"title":5,"description":1516},"Docker 기초","posts\u002FCI-CD-Docker\u002FDocker\u002F2026-08-17-docker-07-image-layer-archive",[1514,89,150,534,556,567],"DyM2QrqVq5bd0Tyx33X43_lkYGVHWpuVJlRyyyp4u2I",1788744787698]