[{"data":1,"prerenderedAt":505},["ShallowReactive",2],{"post-document:\u002Fdevops\u002Fdocker\u002F2026\u002F08\u002F17\u002Fdocker-02-ecosystem\u002F":3},{"id":4,"title":5,"body":6,"categories":483,"date":486,"description":487,"extension":488,"image":489,"key_concepts":489,"last_modified_at":489,"legacyPath":490,"meta":491,"navigation":44,"part":493,"path":494,"published":44,"robots":489,"seo":495,"series":496,"stem":497,"strengths":489,"summary":489,"tags":498,"tradeoffs":489,"__hash__":504},"posts\u002Fposts\u002FCI-CD-Docker\u002FDocker\u002F2026-08-17-docker-02-ecosystem.md","Docker 기초 2편: Layer, Registry, Volume과 Network",{"type":7,"value":8,"toc":475},"minimark",[9,14,21,63,66,74,77,80,83,89,92,95,99,102,105,108,114,117,120,126,131,135,138,144,150,153,157,160,163,166,172,175,178,181,184,187,190,195,198,201,207,210,213,219,222,226,229,235,238,241,247,250,253,256,259,263,266,269,275,278,281,325,328,331,448,451,457,463,466,471],[10,11,13],"h2",{"id":12},"_1-docker-image는-layer-구조","1. Docker Image는 Layer 구조",[15,16,17],"blockquote",{},[18,19,20],"p",{},"Docker Image 내부는 하나의 거대한 파일이라기보다 여러 Layer로 구성되어있다.",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-dockerfile shiki shiki-themes github-light github-dark","FROM eclipse-temurin:21-jre\n\nWORKDIR \u002Fapp\n\nCOPY app.jar app.jar\n","dockerfile","",[29,30,31,39,46,52,57],"code",{"__ignoreMap":27},[32,33,36],"span",{"class":34,"line":35},"line",1,[32,37,38],{},"FROM eclipse-temurin:21-jre\n",[32,40,42],{"class":34,"line":41},2,[32,43,45],{"emptyLinePlaceholder":44},true,"\n",[32,47,49],{"class":34,"line":48},3,[32,50,51],{},"WORKDIR \u002Fapp\n",[32,53,55],{"class":34,"line":54},4,[32,56,45],{"emptyLinePlaceholder":44},[32,58,60],{"class":34,"line":59},5,[32,61,62],{},"COPY app.jar app.jar\n",[18,64,65],{},"개념적으로",[22,67,72],{"className":68,"code":70,"language":71,"meta":27},[69],"language-text","Layer 3\n\napp.jar\n\nLayer 2\n\nWORKDIR \u002Fapp\n\nLayer\n\nJAVA 21 Runtime\n","text",[29,73,70],{"__ignoreMap":27},[18,75,76],{},"그래서 변경되지 않은 Layer는 재사용할 수 있다.\n(재탕이 가능하다!)",[18,78,79],{},"Docker Layer Cache",[18,81,82],{},"예를 들어",[22,84,87],{"className":85,"code":86,"language":71,"meta":27},[69],"Java Runtime -> 변경 없음\n\napp.jar -> 변경됨\n",[29,88,86],{"__ignoreMap":27},[18,90,91],{},"이라면 전체를 처음부터 다시 만들지 않고, 변경된 위주로만 Build하면 된다.\n(리소스를 덜 잡아먹는 좋은 설계이다.)",[18,93,94],{},"이 특성을 이용해서 CI\u002FCD에서 Build 속도를 높일 수 있다.",[10,96,98],{"id":97},"_2-docker-registry","2. Docker Registry",[18,100,101],{},"Image를 내 컴퓨터에만 가지고 있으면 서버가 재사용할 수가 없다.",[18,103,104],{},"따라서 Image를 저장하는 중앙 저장소가 필요하다.",[18,106,107],{},"이를 위한 위한 저장소가 다행히도 Docker Registry 라고 존재한다.",[22,109,112],{"className":110,"code":111,"language":71,"meta":27},[69],"Docker Hub\nGitHub Container Registry\nAWS ECR\nGoogle Artifact Registry\n",[29,113,111],{"__ignoreMap":27},[18,115,116],{},"이게 목록이다.",[18,118,119],{},"구조는 아래와 같다",[22,121,124],{"className":122,"code":123,"language":71,"meta":27},[69],"Developer\n    ↓\ndocker build\n    ↓\nDocker Image\n    ↓\ndocker push\n    ↓\nDocker Registry\n    ↓\ndocker pull\n    ↓\nProduction Server\n",[29,125,123],{"__ignoreMap":27},[15,127,128],{},[18,129,130],{},"Registry >> Repository 이므로 잘 기억하자.",[10,132,134],{"id":133},"_3-github와-docker-registry-차이","3. GitHub와 Docker Registry 차이",[18,136,137],{},"이 둘을 구분해야할 필요가 있다.",[22,139,142],{"className":140,"code":141,"language":71,"meta":27},[69],"GitHub\n    ↓\nSource Code 저장\n\nDocker Registry\n    ↓\nDocker Image 저장\n",[29,143,141],{"__ignoreMap":27},[22,145,148],{"className":146,"code":147,"language":71,"meta":27},[69],"GitHub\n= 코드 저장소\n\nDocker Hub\n= 실행 패키지 저장소\n",[29,149,147],{"__ignoreMap":27},[18,151,152],{},"이렇게 정리해서 생각하면 된다.",[10,154,156],{"id":155},"_4-volume","4. Volume",[18,158,159],{},"근데 저장은 어떻게 하는 걸까?",[18,161,162],{},"Container 안에 파일을 저장했다고 하자.",[18,164,165],{},"만약에 Container 안에 저장할 경우에는",[22,167,170],{"className":168,"code":169,"language":71,"meta":27},[69],"Container\n   ↓\ndata.txt\n",[29,171,169],{"__ignoreMap":27},[18,173,174],{},"이런식으로 하면 Container가 작동하는 동안에는 저장이 되겠지만,",[18,176,177],{},"Container가 삭제되는 경우에는, 내부 데이터가 삭제될 것이다.",[18,179,180],{},"근데 여기에 DB 데이터가 담겨있다면?",[18,182,183],{},"음",[18,185,186],{},"유감이라고 할 수 있다.",[18,188,189],{},"하지만 개발자성님들은 최강이기 때문에",[15,191,192],{},[18,193,194],{},"VOULME",[18,196,197],{},"이라는 기능을 구현해놓았다.",[18,199,200],{},"그래서 사용하는 것이 Volume",[22,202,205],{"className":203,"code":204,"language":71,"meta":27},[69],"Container\n     │\n     ▼\nDocker Volume\n     │\n     ▼\nData\n",[29,206,204],{"__ignoreMap":27},[18,208,209],{},"이런 식으로 데이터를 유지할 수 있다.",[18,211,212],{},"예를 들어 MySQL를 Docker로 실행한다고 하자.",[22,214,217],{"className":215,"code":216,"language":71,"meta":27},[69],"MySQL Container\n       ↓\nVolume\n       ↓\nDatabase Files\n",[29,218,216],{"__ignoreMap":27},[18,220,221],{},"이런식으로 구조를 유지할 수 있다.",[10,223,225],{"id":224},"_5-docker-network","5. Docker Network",[18,227,228],{},"Container 끼리 통신도 된다!",[22,230,233],{"className":231,"code":232,"language":71,"meta":27},[69],"Spring Boot\n     ↓\nMySQL\n",[29,234,232],{"__ignoreMap":27},[18,236,237],{},"이렇게 구현했는데",[18,239,240],{},"둘 다 Container라면",[22,242,245],{"className":243,"code":244,"language":71,"meta":27},[69],"Docker Network\n\nSpring Container\n        │\n        ↓\nMySQL Container\n",[29,246,244],{"__ignoreMap":27},[18,248,249],{},"이런식으롤 연결한다.",[18,251,252],{},"다만, 컨테이너끼리 통신에서는 localhost는 사용 불가하다.",[18,254,255],{},"각 컨테이너의 localhost는 자기 자신을 가리키기 때문이다.",[18,257,258],{},"따라서 네트워크 이름을 Container 또는 Service 이름으로 접근할 수 있다.",[10,260,262],{"id":261},"_6-docker-compose","6. Docker Compose",[18,264,265],{},"Container가 하나뿐이라면 명령어로 실행할 수 있다.",[18,267,268],{},"근데 아시다시피 프로젝트에서는 여러가지 기능을 구현해야하고, 그로 인해 컨테이너를 여러개 구동해야 한다.",[22,270,273],{"className":271,"code":272,"language":71,"meta":27},[69],"Spring Boot\nMySQL\nRedis\nNginx\n",[29,274,272],{"__ignoreMap":27},[18,276,277],{},"처럼 여러 개가 된다.",[18,279,280],{},"매번",[22,282,286],{"className":283,"code":284,"language":285,"meta":27,"style":27},"language-bash shiki shiki-themes github-light github-dark","docker run ...\ndocker run ...\ndocker run ...\ndocker run ...\n","bash",[29,287,288,301,309,317],{"__ignoreMap":27},[32,289,290,294,298],{"class":34,"line":35},[32,291,293],{"class":292},"sScJk","docker",[32,295,297],{"class":296},"sZZnC"," run",[32,299,300],{"class":296}," ...\n",[32,302,303,305,307],{"class":34,"line":41},[32,304,293],{"class":292},[32,306,297],{"class":296},[32,308,300],{"class":296},[32,310,311,313,315],{"class":34,"line":48},[32,312,293],{"class":292},[32,314,297],{"class":296},[32,316,300],{"class":296},[32,318,319,321,323],{"class":34,"line":54},[32,320,293],{"class":292},[32,322,297],{"class":296},[32,324,300],{"class":296},[18,326,327],{},"이거를 언제 치고 있겠음?",[18,329,330],{},"그래서 Docker Compose를 사용한다 .",[22,332,336],{"className":333,"code":334,"language":335,"meta":27,"style":27},"language-yaml shiki shiki-themes github-light github-dark","services:\n  backend:\n    build: .\n    ports:\n      - \"8080:8080\"\n\n  mysql:\n    image: mysql:8\n    environment:\n      MYSQL_ROOT_PASSWORD: password\n\n  redis:\n    image: redis:7\n","yaml",[29,337,338,348,355,367,374,382,387,395,406,414,425,430,438],{"__ignoreMap":27},[32,339,340,344],{"class":34,"line":35},[32,341,343],{"class":342},"s9eBZ","services",[32,345,347],{"class":346},"sVt8B",":\n",[32,349,350,353],{"class":34,"line":41},[32,351,352],{"class":342},"  backend",[32,354,347],{"class":346},[32,356,357,360,363],{"class":34,"line":48},[32,358,359],{"class":342},"    build",[32,361,362],{"class":346},": ",[32,364,366],{"class":365},"sj4cs",".\n",[32,368,369,372],{"class":34,"line":54},[32,370,371],{"class":342},"    ports",[32,373,347],{"class":346},[32,375,376,379],{"class":34,"line":59},[32,377,378],{"class":346},"      - ",[32,380,381],{"class":296},"\"8080:8080\"\n",[32,383,385],{"class":34,"line":384},6,[32,386,45],{"emptyLinePlaceholder":44},[32,388,390,393],{"class":34,"line":389},7,[32,391,392],{"class":342},"  mysql",[32,394,347],{"class":346},[32,396,398,401,403],{"class":34,"line":397},8,[32,399,400],{"class":342},"    image",[32,402,362],{"class":346},[32,404,405],{"class":296},"mysql:8\n",[32,407,409,412],{"class":34,"line":408},9,[32,410,411],{"class":342},"    environment",[32,413,347],{"class":346},[32,415,417,420,422],{"class":34,"line":416},10,[32,418,419],{"class":342},"      MYSQL_ROOT_PASSWORD",[32,421,362],{"class":346},[32,423,424],{"class":296},"password\n",[32,426,428],{"class":34,"line":427},11,[32,429,45],{"emptyLinePlaceholder":44},[32,431,433,436],{"class":34,"line":432},12,[32,434,435],{"class":342},"  redis",[32,437,347],{"class":346},[32,439,441,443,445],{"class":34,"line":440},13,[32,442,400],{"class":342},[32,444,362],{"class":346},[32,446,447],{"class":296},"redis:7\n",[18,449,450],{},"이렇게 해놓고",[18,452,453,456],{},[29,454,455],{},"docker compose up"," 때리면",[22,458,461],{"className":459,"code":460,"language":71,"meta":27},[69],"Spring Boot\nMySQL\nRedis\n",[29,462,460],{"__ignoreMap":27},[18,464,465],{},"가 한꺼번에 실행되서 매우 편리하다.",[15,467,468],{},[18,469,470],{},"Docker Compose를 통해서 여러 Container의 실행 방법과 연결 관계를 하나의 파일로 정의한다.",[472,473,474],"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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":27,"searchDepth":41,"depth":41,"links":476},[477,478,479,480,481,482],{"id":12,"depth":41,"text":13},{"id":97,"depth":41,"text":98},{"id":133,"depth":41,"text":134},{"id":155,"depth":41,"text":156},{"id":224,"depth":41,"text":225},{"id":261,"depth":41,"text":262},[484,485],"DevOps","Docker","2026-08-17 12:05:00 +0900","Docker Image Layer와 Registry, Volume, Network, Docker Compose의 기본 역할을 정리한다.","md",null,"\u002Fdevops\u002Fdocker\u002F2026\u002F08\u002F17\u002Fdocker-02-ecosystem\u002F",{"layout":492},"post","2","\u002Fposts\u002Fci-cd-docker\u002Fdocker\u002F2026-08-17-docker-02-ecosystem",{"title":5,"description":487},"Docker 기초","posts\u002FCI-CD-Docker\u002FDocker\u002F2026-08-17-docker-02-ecosystem",[485,499,500,501,502,503],"Layer","Registry","Volume","Network","Docker Compose","kVnMBCOZqkKI2rcK9Qn9vNjj9DlnaMtyE0aGKIQJqPE",1788744787533]