[{"data":1,"prerenderedAt":2841},["ShallowReactive",2],{"content:\u002F07-infrastructure\u002F01-docker-basics":3},{"title":4,"description":5,"path":6,"body":7},"Docker — основы контейнеризации","Docker — платформа контейнеризации, которая решает одну из самых старых проблем в разработке: «у меня на машине работает». Она упаковывает приложение вместе со всеми его зависимостями — системными библиотеками, средой исполнения, конфигурациями — в изолированный контейнер. Этот контейнер ведёт себя одинаково на ноутбуке разработчика, CI-сервере и продакшн-кластере.","\u002F07-infrastructure\u002F01-docker-basics",{"type":8,"value":9,"toc":2788},"minimark",[10,14,17,25,28,33,36,43,49,55,61,63,67,70,75,82,92,95,99,106,112,116,217,220,222,226,229,233,236,331,337,341,344,347,375,412,418,422,425,433,436,438,442,445,451,457,460,463,474,478,485,547,550,557,559,563,570,590,594,600,606,612,616,728,732,753,759,762,764,768,785,789,795,802,806,809,816,886,908,912,918,965,974,978,988,990,994,1001,1005,1011,1017,1023,1029,1033,1039,1046,1050,1242,1261,1263,1267,1270,1273,1277,1280,1332,1335,1339,1346,1428,1431,1434,1437,1456,1459,1463,1521,1525,1632,1643,1645,1649,1652,1656,1775,1778,1975,1978,2071,2075,2133,2135,2139,2142,2196,2202,2204,2208,2213,2216,2221,2224,2229,2232,2237,2240,2245,2248,2253,2256,2261,2267,2269,2273,2277,2283,2312,2315,2319,2326,2352,2356,2367,2387,2390,2392,2396,2427,2597,2784],[11,12,4],"h1",{"id":13},"docker-основы-контейнеризации",[15,16,5],"p",{},[15,18,19,20,24],{},"Для Go-разработчика Docker — это не абстрактный инструмент из мира DevOps, а повседневная реальность. Вы будете собирать Go-бинарники в Docker, поднимать базы и зависимости через Compose, описывать деплой в Kubernetes. Поэтому начнём с фундамента: что на самом деле происходит, когда вы запускаете ",[21,22,23],"code",{},"docker run",".",[26,27],"hr",{},[29,30,32],"h2",{"id":31},"что-такое-docker-и-зачем-он-нужен","Что такое Docker и зачем он нужен",[15,34,35],{},"На первый взгляд Docker решает простую задачу — воспроизводимость окружения. Но за этим стоит целый набор практических проблем, с которыми сталкивается любая команда:",[15,37,38,42],{},[39,40,41],"strong",{},"Проблема окружения."," Приложение зависит не только от своего кода. Ему нужна конкретная версия Go (или другого рантайма), определённые системные библиотеки, переменные окружения, файлы конфигурации. На одной машине стоит PostgreSQL 14, на другой — 15. На одной — Ubuntu, на другой — Alpine. Docker фиксирует всё окружение целиком.",[15,44,45,48],{},[39,46,47],{},"Проблема изоляции."," На одном сервере могут работать несколько приложений, и они не должны мешать друг другу. Один сервис слушает порт 8080, другому тоже нужен 8080. Один требует определённую версию glibc, другой — другую. Docker изолирует каждое приложение в собственном пространстве.",[15,50,51,54],{},[39,52,53],{},"Проблема масштабирования."," Когда нагрузка растёт, нужно быстро поднимать новые экземпляры сервиса. Docker-контейнеры запускаются за секунды, в отличие от виртуальных машин, которым нужны минуты на загрузку полноценной ОС.",[15,56,57,60],{},[39,58,59],{},"Проблема CI\u002FCD."," Сборка, тестирование и деплой должны происходить в одинаковом окружении. Docker-образ — это артефакт, который один раз собрали, протестировали и развернули в продакшн без изменений.",[26,62],{},[29,64,66],{"id":65},"контейнеры-vs-виртуальные-машины","Контейнеры vs виртуальные машины",[15,68,69],{},"Контейнеры и виртуальные машины решают схожую задачу — изоляцию приложений — но принципиально разными способами. Понимание этой разницы важно не только для собеседований, но и для принятия архитектурных решений.",[71,72,74],"h3",{"id":73},"виртуальные-машины","Виртуальные машины",[15,76,77,78,81],{},"VM виртуализирует ",[39,79,80],{},"железо",". Гипервизор (VMware, KVM, Hyper-V) создаёт виртуальное оборудование — процессор, память, диск, сетевой адаптер — и внутри него запускается полноценная гостевая ОС со своим ядром:",[83,84,89],"pre",{"className":85,"code":87,"language":88},[86],"language-text","┌──────────────────────────────────────────────────┐\n│               Хостовая ОС \u002F Гипервизор           │\n│  ┌──────────────┐  ┌──────────────┐              │\n│  │  Гостевая ОС │  │  Гостевая ОС │              │\n│  │  (ядро Linux) │  │ (ядро Windows)│             │\n│  │  ┌──────────┐│  │  ┌──────────┐│              │\n│  │  │   App A  ││  │  │   App B  ││              │\n│  │  └──────────┘│  │  └──────────┘│              │\n│  └──────────────┘  └──────────────┘              │\n└──────────────────────────────────────────────────┘\n","text",[21,90,87],{"__ignoreMap":91},"",[15,93,94],{},"Каждая VM — это гигабайты данных (ОС + приложение) и минуты на старт.",[71,96,98],{"id":97},"контейнеры","Контейнеры",[15,100,101,102,105],{},"Контейнер виртуализирует ",[39,103,104],{},"операционную систему",". Все контейнеры используют одно и то же ядро хоста, но каждый видит собственное изолированное пространство процессов, сети и файловой системы:",[83,107,110],{"className":108,"code":109,"language":88},[86],"┌──────────────────────────────────────────────────┐\n│               Хостовая ОС (Linux kernel)         │\n│  ┌──────────────┐  ┌──────────────┐              │\n│  │  Контейнер A │  │  Контейнер B │              │\n│  │  (userspace) │  │  (userspace) │              │\n│  │  ┌──────────┐│  │  ┌──────────┐│              │\n│  │  │   App A  ││  │  │   App B  ││              │\n│  │  └──────────┘│  │  └──────────┘│              │\n│  └──────────────┘  └──────────────┘              │\n└──────────────────────────────────────────────────┘\n",[21,111,109],{"__ignoreMap":91},[71,113,115],{"id":114},"сравнение","Сравнение",[117,118,119,136],"table",{},[120,121,122],"thead",{},[123,124,125,130,133],"tr",{},[126,127,129],"th",{"align":128},"left","Характеристика",[126,131,132],{"align":128},"Контейнер",[126,134,135],{"align":128},"Виртуальная машина",[137,138,139,151,162,173,184,195,206],"tbody",{},[123,140,141,145,148],{},[142,143,144],"td",{"align":128},"Уровень виртуализации",[142,146,147],{"align":128},"ОС (userspace)",[142,149,150],{"align":128},"Железо (hardware)",[123,152,153,156,159],{},[142,154,155],{"align":128},"Ядро",[142,157,158],{"align":128},"Общее с хостом",[142,160,161],{"align":128},"Собственное",[123,163,164,167,170],{},[142,165,166],{"align":128},"Размер",[142,168,169],{"align":128},"Мегабайты",[142,171,172],{"align":128},"Гигабайты",[123,174,175,178,181],{},[142,176,177],{"align":128},"Время старта",[142,179,180],{"align":128},"Секунды",[142,182,183],{"align":128},"Минуты",[123,185,186,189,192],{},[142,187,188],{"align":128},"Накладные расходы",[142,190,191],{"align":128},"Минимальные",[142,193,194],{"align":128},"Значительные",[123,196,197,200,203],{},[142,198,199],{"align":128},"Изоляция",[142,201,202],{"align":128},"Процессная (namespaces)",[142,204,205],{"align":128},"Полная (гипервизор)",[123,207,208,211,214],{},[142,209,210],{"align":128},"Разные ОС на одном хосте",[142,212,213],{"align":128},"Нет",[142,215,216],{"align":128},"Да",[15,218,219],{},"Контейнеры выигрывают в скорости и эффективности, а виртуальные машины — в безопасности и гибкости. Но это не значит, что одно вытесняет другое — они часто работают вместе. Kubernetes-кластер, например, обычно работает на виртуальных машинах, внутри которых бегут контейнеры.",[26,221],{},[29,223,225],{"id":224},"linux-under-the-hood-namespaces-cgroups-overlay-fs","Linux under the hood: namespaces, cgroups, overlay fs",[15,227,228],{},"Docker — это не магия. Под капотом он использует три механизма ядра Linux, которые существовали задолго до Docker. Docker просто сделал их удобными.",[71,230,232],{"id":231},"namespaces-изоляция","Namespaces — изоляция",[15,234,235],{},"Namespaces создают иллюзию того, что контейнер — отдельная машина. Каждый namespace изолирует определённый аспект системы:",[117,237,238,251],{},[120,239,240],{},[123,241,242,245,248],{},[126,243,244],{"align":128},"Namespace",[126,246,247],{"align":128},"Что изолирует",[126,249,250],{"align":128},"Эффект",[137,252,253,266,279,292,305,318],{},[123,254,255,260,263],{},[142,256,257],{"align":128},[39,258,259],{},"PID",[142,261,262],{"align":128},"Процессы",[142,264,265],{"align":128},"Контейнер видит только свои процессы, PID 1 — его init",[123,267,268,273,276],{},[142,269,270],{"align":128},[39,271,272],{},"NET",[142,274,275],{"align":128},"Сеть",[142,277,278],{"align":128},"Собственный сетевой стек, IP-адрес, порты",[123,280,281,286,289],{},[142,282,283],{"align":128},[39,284,285],{},"MNT",[142,287,288],{"align":128},"Файловая система",[142,290,291],{"align":128},"Собственная корневая ФС, точки монтирования",[123,293,294,299,302],{},[142,295,296],{"align":128},[39,297,298],{},"UTS",[142,300,301],{"align":128},"Hostname",[142,303,304],{"align":128},"Собственное имя хоста",[123,306,307,312,315],{},[142,308,309],{"align":128},[39,310,311],{},"IPC",[142,313,314],{"align":128},"Межпроцессное общение",[142,316,317],{"align":128},"Изолированные очереди сообщений, shared memory",[123,319,320,325,328],{},[142,321,322],{"align":128},[39,323,324],{},"USER",[142,326,327],{"align":128},"Пользователи",[142,329,330],{"align":128},"Маппинг UID\u002FGID — root в контейнере != root на хосте",[15,332,333,334,336],{},"Когда вы запускаете ",[21,335,23],{},", Docker создаёт набор namespaces для контейнера. Процесс внутри контейнера думает, что он единственный в системе — у него PID 1, свой IP-адрес, своя файловая система. Но на самом деле это обычный процесс хоста, просто с ограниченной видимостью.",[71,338,340],{"id":339},"cgroups-лимиты-ресурсов","Cgroups — лимиты ресурсов",[15,342,343],{},"Namespaces изолируют видимость, но не ограничивают потребление. Если контейнер начнёт есть всю память хоста — другие контейнеры упадут. Здесь вступают cgroups (control groups).",[15,345,346],{},"Cgroups позволяют задать лимиты:",[348,349,350,357,363,369],"ul",{},[351,352,353,356],"li",{},[39,354,355],{},"CPU"," — сколько процессорного времени может использовать контейнер",[351,358,359,362],{},[39,360,361],{},"Memory"," — максимальный объём оперативной памяти",[351,364,365,368],{},[39,366,367],{},"Disk I\u002FO"," — скорость чтения\u002Fзаписи на диск",[351,370,371,374],{},[39,372,373],{},"Network"," — пропускная способность сети",[83,376,380],{"className":377,"code":378,"language":379,"meta":91,"style":91},"language-bash shiki shiki-themes github-dark","# Запуск контейнера с ограничением: максимум 512 МБ RAM и 1 ядро CPU\ndocker run --memory=512m --cpus=1.0 my-app\n","bash",[21,381,382,391],{"__ignoreMap":91},[383,384,387],"span",{"class":385,"line":386},"line",1,[383,388,390],{"class":389},"sAwPA","# Запуск контейнера с ограничением: максимум 512 МБ RAM и 1 ядро CPU\n",[383,392,394,398,402,406,409],{"class":385,"line":393},2,[383,395,397],{"class":396},"svObZ","docker",[383,399,401],{"class":400},"sU2Wk"," run",[383,403,405],{"class":404},"sDLfK"," --memory=512m",[383,407,408],{"class":404}," --cpus=1.0",[383,410,411],{"class":400}," my-app\n",[15,413,414,415,24],{},"Если контейнер превышает лимит памяти, ядро Linux отправляет ему OOM-сигнал (Out of Memory) и завершает процесс. Это поведение критично для Go-приложений: рантайм Go должен знать о лимитах cgroups, иначе GC будет работать неэффективно. Начиная с Go 1.19, рантайм автоматически читает лимиты cgroups через ",[21,416,417],{},"GOMEMLIMIT",[71,419,421],{"id":420},"overlay-fs-слоистая-файловая-система","Overlay FS — слоистая файловая система",[15,423,424],{},"Overlay FS — это механизм, который позволяет «наложить» несколько файловых систем друг на друга. Docker использует его для создания слоёв образа:",[348,426,427,430],{},[351,428,429],{},"Нижние слои — read-only (базовый образ, зависимости)",[351,431,432],{},"Верхний слой — writable (изменения контейнера)",[15,434,435],{},"Если контейнер хочет изменить файл из нижнего слоя, overlay fs создаёт копию файла в верхнем слое (copy-on-write). Оригинал остаётся нетронутым. Это позволяет множеству контейнеров делить одни и те же базовые слои, экономя место на диске.",[26,437],{},[29,439,441],{"id":440},"docker-на-mac-и-windows-почему-нужна-vm","Docker на Mac и Windows — почему нужна VM",[15,443,444],{},"Это один из самых частых вопросов: «если контейнер использует ядро хоста, а на Mac нет Linux-ядра — как Docker вообще работает?»",[15,446,447,448,24],{},"Ответ прост: ",[39,449,450],{},"на Mac и Windows Docker запускает скрытую Linux VM",[83,452,455],{"className":453,"code":454,"language":88},[86],"Linux-хост:\n  Приложение --> контейнер --> Linux kernel хоста (напрямую)\n\nMac\u002FWindows:\n  Приложение --> контейнер --> Linux kernel --> LinuxKit VM --> хост ОС\n",[21,456,454],{"__ignoreMap":91},[15,458,459],{},"Docker Desktop устанавливает легковесную виртуальную машину LinuxKit, внутри которой работает настоящее Linux-ядро. Все контейнеры бегут внутри этой VM. На Mac для виртуализации используется встроенный фреймворк Apple Hypervisor (или ранее xhyve), на Windows — WSL 2 или Hyper-V.",[15,461,462],{},"Из-за этого на Mac\u002FWindows есть дополнительные накладные расходы по сравнению с нативным Linux:",[348,464,465,468,471],{},[351,466,467],{},"Потребление памяти (VM забирает фиксированный объём)",[351,469,470],{},"Скорость файловой системы (bind mounts работают через виртуальный FS)",[351,472,473],{},"Networking (порты пробрасываются через VM)",[71,475,477],{"id":476},"почему-vm-не-вымерли","Почему VM не вымерли",[15,479,480,481,484],{},"Раз контейнеры такие лёгкие и удобные, зачем нужны VM? Потому что у контейнеров есть фундаментальное ограничение — ",[39,482,483],{},"они разделяют ядро хоста",":",[117,486,487,499],{},[120,488,489],{},[123,490,491,494,496],{},[126,492,493],{"align":128},"Сценарий",[126,495,132],{"align":128},[126,497,498],{"align":128},"VM",[137,500,501,509,518,528,537],{},[123,502,503,505,507],{},[142,504,210],{"align":128},[142,506,213],{"align":128},[142,508,216],{"align":128},[123,510,511,514,516],{},[142,512,513],{"align":128},"Полная изоляция ядра (безопасность)",[142,515,213],{"align":128},[142,517,216],{"align":128},[123,519,520,523,526],{},[142,521,522],{"align":128},"Мультитенантность (чужой код)",[142,524,525],{"align":128},"Рискованно",[142,527,216],{"align":128},[123,529,530,533,535],{},[142,531,532],{"align":128},"Legacy-приложения (Windows Server)",[142,534,213],{"align":128},[142,536,216],{"align":128},[123,538,539,542,544],{},[142,540,541],{"align":128},"Микросервисы, CI\u002FCD, dev-окружение",[142,543,216],{"align":128},[142,545,546],{"align":128},"Избыточно",[15,548,549],{},"Если эксплойт в ядре Linux — все контейнеры на хосте уязвимы. VM обеспечивает полную изоляцию на уровне гипервизора. Поэтому в облаках (AWS, GCP) каждый клиент получает свои VM, а внутри VM уже бегут контейнеры.",[15,551,552,553,556],{},"Также существуют ",[39,554,555],{},"Windows-контейнеры"," — они работают только на Windows-хосте. Linux-контейнер на Windows запускается через VM (WSL 2). А вот Windows-контейнер на Linux — невозможно в принципе, потому что ему нужно Windows-ядро.",[26,558],{},[29,560,562],{"id":561},"docker-image-неизменяемый-шаблон","Docker Image — неизменяемый шаблон",[15,564,565,566,569],{},"Docker Image (образ) — это ",[39,567,568],{},"read-only шаблон",", который содержит всё необходимое для запуска приложения:",[348,571,572,575,578,581,584,587],{},[351,573,574],{},"Легковесную операционную систему (обычно урезанный Linux)",[351,576,577],{},"Среду исполнения (если нужна)",[351,579,580],{},"Код приложения",[351,582,583],{},"Зависимости",[351,585,586],{},"Конфигурации",[351,588,589],{},"Инструкции по запуску",[71,591,593],{"id":592},"ключевые-характеристики-образа","Ключевые характеристики образа",[15,595,596,599],{},[39,597,598],{},"Неизменяемость (immutability)."," После сборки образ невозможно изменить. Любое изменение — это новый образ с новым хешем. Это фундаментальное свойство, которое обеспечивает воспроизводимость: если образ работает на staging — он будет работать точно так же на production.",[15,601,602,605],{},[39,603,604],{},"Легковесность."," Образ содержит только минимальный набор компонентов. Go-приложение в multi-stage build может уместиться в 10-20 МБ (alpine + бинарник), в отличие от VM-образа на несколько гигабайт.",[15,607,608,611],{},[39,609,610],{},"Портативность."," Один раз собранный образ будет работать одинаково на любом сервере, где установлен Docker. Не важно — Ubuntu, CentOS, или Amazon Linux.",[71,613,615],{"id":614},"основные-команды-для-работы-с-образами","Основные команды для работы с образами",[83,617,619],{"className":377,"code":618,"language":379,"meta":91,"style":91},"# Сборка образа из Dockerfile (точка — контекст сборки, текущая директория)\ndocker build -t my-backend-app:v1.0 .\n\n# Получение готового образа из реестра (Docker Hub, GitLab Registry, etc.)\ndocker pull postgres:15-alpine\n\n# Список всех образов на локальной машине\ndocker images\n\n# Удаление образа\ndocker rmi my-backend-app:v1.0\n\n# Просмотр истории слоёв образа\ndocker history my-backend-app:v1.0\n",[21,620,621,626,642,649,655,666,671,677,685,690,696,707,712,718],{"__ignoreMap":91},[383,622,623],{"class":385,"line":386},[383,624,625],{"class":389},"# Сборка образа из Dockerfile (точка — контекст сборки, текущая директория)\n",[383,627,628,630,633,636,639],{"class":385,"line":393},[383,629,397],{"class":396},[383,631,632],{"class":400}," build",[383,634,635],{"class":404}," -t",[383,637,638],{"class":400}," my-backend-app:v1.0",[383,640,641],{"class":400}," .\n",[383,643,645],{"class":385,"line":644},3,[383,646,648],{"emptyLinePlaceholder":647},true,"\n",[383,650,652],{"class":385,"line":651},4,[383,653,654],{"class":389},"# Получение готового образа из реестра (Docker Hub, GitLab Registry, etc.)\n",[383,656,658,660,663],{"class":385,"line":657},5,[383,659,397],{"class":396},[383,661,662],{"class":400}," pull",[383,664,665],{"class":400}," postgres:15-alpine\n",[383,667,669],{"class":385,"line":668},6,[383,670,648],{"emptyLinePlaceholder":647},[383,672,674],{"class":385,"line":673},7,[383,675,676],{"class":389},"# Список всех образов на локальной машине\n",[383,678,680,682],{"class":385,"line":679},8,[383,681,397],{"class":396},[383,683,684],{"class":400}," images\n",[383,686,688],{"class":385,"line":687},9,[383,689,648],{"emptyLinePlaceholder":647},[383,691,693],{"class":385,"line":692},10,[383,694,695],{"class":389},"# Удаление образа\n",[383,697,699,701,704],{"class":385,"line":698},11,[383,700,397],{"class":396},[383,702,703],{"class":400}," rmi",[383,705,706],{"class":400}," my-backend-app:v1.0\n",[383,708,710],{"class":385,"line":709},12,[383,711,648],{"emptyLinePlaceholder":647},[383,713,715],{"class":385,"line":714},13,[383,716,717],{"class":389},"# Просмотр истории слоёв образа\n",[383,719,721,723,726],{"class":385,"line":720},14,[383,722,397],{"class":396},[383,724,725],{"class":400}," history",[383,727,706],{"class":400},[71,729,731],{"id":730},"теги-и-реестры","Теги и реестры",[15,733,734,735,738,739,738,742,745,746,749,750,752],{},"Каждый образ имеет имя и тег: ",[21,736,737],{},"postgres:15-alpine",", ",[21,740,741],{},"golang:1.26-bookworm",[21,743,744],{},"my-app:v2.3.1",". Тег ",[21,747,748],{},"latest"," используется по умолчанию, если тег не указан явно — но полагаться на ",[21,751,748],{}," в production опасно, потому что он указывает на разные версии в разное время.",[15,754,755,756,758],{},"Версии базовых образов в примерах нужно регулярно ревизовать: Go поддерживает только две последние major\u002Fminor ветки, а Alpine release branches имеют ограниченный срок security support. Старый фиксированный тег лучше ",[21,757,748],{},", но он всё равно устаревает.",[15,760,761],{},"Образы хранятся в реестрах (registries). Docker Hub — публичный реестр по умолчанию. В компаниях обычно используют приватные реестры: GitLab Container Registry, AWS ECR, Google Artifact Registry.",[26,763],{},[29,765,767],{"id":766},"слои-docker-механизм-кэширования","Слои Docker — механизм кэширования",[15,769,770,771,738,774,738,777,780,781,784],{},"Каждая инструкция в Dockerfile (",[21,772,773],{},"RUN",[21,775,776],{},"COPY",[21,778,779],{},"ADD",") создаёт новый ",[39,782,783],{},"read-only слой",". Слои — это то, что делает Docker быстрым и экономным.",[71,786,788],{"id":787},"как-устроены-слои","Как устроены слои",[83,790,793],{"className":791,"code":792,"language":88},[86],"Образ (read-only слои):\n┌─────────────────────┐\n│ ENTRYPOINT [\"\u002Fapp\"] │  слой 4\n├─────────────────────┤\n│ RUN go build        │  слой 3\n├─────────────────────┤\n│ COPY . .            │  слой 2\n├─────────────────────┤\n│ FROM golang:1.26    │  слой 1 (базовый)\n└─────────────────────┘\n\nКонтейнер:\n┌─────────────────────┐\n│ writable layer      │  \u003C-- логи, tmp файлы (удаляется с контейнером)\n├─────────────────────┤\n│ образ (read-only)   │\n└─────────────────────┘\n",[21,794,792],{"__ignoreMap":91},[15,796,797,798,801],{},"Когда из образа создаётся контейнер, сверху добавляется тонкий ",[39,799,800],{},"writable layer",". Все изменения, которые происходят в контейнере (создание файлов, запись логов, модификация конфигов), попадают в этот слой. Когда контейнер удаляется — writable layer удаляется вместе с ним. Образ остаётся нетронутым.",[71,803,805],{"id":804},"кэширование-слоёв","Кэширование слоёв",[15,807,808],{},"Docker кэширует каждый слой по его содержимому. Если инструкция и её входные данные не изменились — слой берётся из кэша. Это критически важно для скорости сборки.",[15,810,811,812,815],{},"Но есть правило: ",[39,813,814],{},"если слой изменился — все последующие слои пересобираются",". Именно поэтому порядок инструкций в Dockerfile имеет огромное значение.",[83,817,821],{"className":818,"code":819,"language":820,"meta":91,"style":91},"language-dockerfile shiki shiki-themes github-dark","# Плохо: любое изменение кода инвалидирует кэш зависимостей\nCOPY . .\nRUN go mod download\nRUN go build\n\n# Хорошо: зависимости кэшируются отдельно\nCOPY go.mod go.sum .\u002F\nRUN go mod download       # кэшируется, пока go.mod не изменился\nCOPY . .\nRUN go build\n","dockerfile",[21,822,823,828,837,844,851,855,860,867,874,880],{"__ignoreMap":91},[383,824,825],{"class":385,"line":386},[383,826,827],{"class":389},"# Плохо: любое изменение кода инвалидирует кэш зависимостей\n",[383,829,830,833],{"class":385,"line":393},[383,831,776],{"class":832},"snl16",[383,834,836],{"class":835},"s95oV"," . .\n",[383,838,839,841],{"class":385,"line":644},[383,840,773],{"class":832},[383,842,843],{"class":835}," go mod download\n",[383,845,846,848],{"class":385,"line":651},[383,847,773],{"class":832},[383,849,850],{"class":835}," go build\n",[383,852,853],{"class":385,"line":657},[383,854,648],{"emptyLinePlaceholder":647},[383,856,857],{"class":385,"line":668},[383,858,859],{"class":389},"# Хорошо: зависимости кэшируются отдельно\n",[383,861,862,864],{"class":385,"line":673},[383,863,776],{"class":832},[383,865,866],{"class":835}," go.mod go.sum .\u002F\n",[383,868,869,871],{"class":385,"line":679},[383,870,773],{"class":832},[383,872,873],{"class":835}," go mod download       # кэшируется, пока go.mod не изменился\n",[383,875,876,878],{"class":385,"line":687},[383,877,776],{"class":832},[383,879,836],{"class":835},[383,881,882,884],{"class":385,"line":692},[383,883,773],{"class":832},[383,885,850],{"class":835},[15,887,888,889,892,893,896,897,900,901,904,905,24],{},"В первом варианте каждое изменение в любом ",[21,890,891],{},".go"," файле инвалидирует слой ",[21,894,895],{},"COPY . .",", а значит — и ",[21,898,899],{},"go mod download"," будет выполняться заново. Во втором варианте зависимости скачиваются только когда изменился ",[21,902,903],{},"go.mod"," или ",[21,906,907],{},"go.sum",[71,909,911],{"id":910},"группировка-команд-run","Группировка команд RUN",[15,913,914,915,917],{},"Каждый ",[21,916,773],{}," создаёт отдельный слой. Связанные команды стоит объединять, чтобы уменьшить количество слоёв и размер образа:",[83,919,921],{"className":818,"code":920,"language":820,"meta":91,"style":91},"# Плохо: 3 слоя, промежуточные данные (apt cache) остаются в слоях\nRUN apt-get update\nRUN apt-get install -y curl\nRUN rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n\n# Хорошо: 1 слой, чище и меньше по размеру\nRUN apt-get update && apt-get install -y curl && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[21,922,923,928,935,942,949,953,958],{"__ignoreMap":91},[383,924,925],{"class":385,"line":386},[383,926,927],{"class":389},"# Плохо: 3 слоя, промежуточные данные (apt cache) остаются в слоях\n",[383,929,930,932],{"class":385,"line":393},[383,931,773],{"class":832},[383,933,934],{"class":835}," apt-get update\n",[383,936,937,939],{"class":385,"line":644},[383,938,773],{"class":832},[383,940,941],{"class":835}," apt-get install -y curl\n",[383,943,944,946],{"class":385,"line":651},[383,945,773],{"class":832},[383,947,948],{"class":835}," rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[383,950,951],{"class":385,"line":657},[383,952,648],{"emptyLinePlaceholder":647},[383,954,955],{"class":385,"line":668},[383,956,957],{"class":389},"# Хорошо: 1 слой, чище и меньше по размеру\n",[383,959,960,962],{"class":385,"line":673},[383,961,773],{"class":832},[383,963,964],{"class":835}," apt-get update && apt-get install -y curl && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[15,966,967,968,971,972,24],{},"Важно понимать: удаление файла в следующем слое ",[39,969,970],{},"не уменьшает"," размер образа. Файл всё ещё существует в предыдущем слое (слои неизменяемы). Поэтому создание и удаление временных файлов нужно делать в одном ",[21,973,773],{},[71,975,977],{"id":976},"переиспользование-слоёв-между-образами","Переиспользование слоёв между образами",[15,979,980,981,984,985,987],{},"Если два образа используют один и тот же базовый образ (например, ",[21,982,983],{},"alpine:3.23","), Docker хранит его слои только один раз. Десять сервисов на базе ",[21,986,983],{}," не занимают 10x места — базовые слои разделяются. Это справедливо и для docker pull — скачиваются только те слои, которых ещё нет локально.",[26,989],{},[29,991,993],{"id":992},"docker-container-запущенный-экземпляр-образа","Docker Container — запущенный экземпляр образа",[15,995,996,997,1000],{},"Docker Container (контейнер) — это ",[39,998,999],{},"запущенный и работающий экземпляр образа",". Если образ — это пассивный шаблон, «архив» с файлами и инструкциями, то контейнер — это активный процесс, изолированный от хостовой ОС и других контейнеров.",[71,1002,1004],{"id":1003},"ключевые-особенности-контейнера","Ключевые особенности контейнера",[15,1006,1007,1010],{},[39,1008,1009],{},"Изоляция."," Контейнер работает внутри собственного набора namespaces. У него свой PID-namespace (процессы), NET-namespace (сетевой стек), MNT-namespace (файловая система). Он не видит процессы других контейнеров и не конфликтует с ними.",[15,1012,1013,1016],{},[39,1014,1015],{},"Эффективность."," В отличие от VM, контейнеру не нужна полная гостевая ОС. Он использует ядро хоста и содержит только минимальный userspace. Типичный контейнер с Go-приложением весит десятки мегабайт и запускается за секунды.",[15,1018,1019,1022],{},[39,1020,1021],{},"Контроль ресурсов."," При запуске можно явно ограничить, сколько CPU и памяти может использовать контейнер. Это защищает другие контейнеры и хост от «прожорливых» процессов.",[15,1024,1025,1028],{},[39,1026,1027],{},"Эфемерность."," Контейнеры проектируются как временные сущности. Их можно остановить, удалить, пересоздать из того же образа — и получить идентичный результат. Данные, которые должны пережить контейнер, хранятся в volumes (о них ниже).",[71,1030,1032],{"id":1031},"жизненный-цикл-контейнера","Жизненный цикл контейнера",[83,1034,1037],{"className":1035,"code":1036,"language":88},[86],"docker create   -->  Created\ndocker start    -->  Running\ndocker pause    -->  Paused\ndocker unpause  -->  Running\ndocker stop     -->  Stopped (Exited)\ndocker rm       -->  Removed\n",[21,1038,1036],{"__ignoreMap":91},[15,1040,1041,1042,1045],{},"Когда контейнер останавливается, его writable layer сохраняется до явного удаления (",[21,1043,1044],{},"docker rm","). Это позволяет посмотреть логи или скопировать файлы из остановленного контейнера.",[71,1047,1049],{"id":1048},"основные-команды-для-работы-с-контейнерами","Основные команды для работы с контейнерами",[83,1051,1053],{"className":377,"code":1052,"language":379,"meta":91,"style":91},"# Запуск контейнера из образа с пробросом порта\ndocker run -p 8088:8088 my-backend-app\n\n# Запуск в фоновом режиме (detached)\ndocker run -d -p 8088:8088 --name my-app my-backend-app\n\n# Список запущенных контейнеров\ndocker ps\n\n# Список всех контейнеров (включая остановленные)\ndocker ps -a\n\n# Просмотр логов контейнера\ndocker logs my-app\ndocker logs -f my-app    # в режиме follow (аналог tail -f)\n\n# Выполнение команды внутри работающего контейнера\ndocker exec -it my-app \u002Fbin\u002Fsh\n\n# Остановка контейнера (SIGTERM, затем SIGKILL через 10 секунд)\ndocker stop my-app\n\n# Удаление остановленного контейнера\ndocker rm my-app\n",[21,1054,1055,1060,1075,1079,1084,1105,1109,1114,1121,1125,1130,1140,1144,1149,1158,1173,1178,1184,1200,1205,1211,1221,1226,1232],{"__ignoreMap":91},[383,1056,1057],{"class":385,"line":386},[383,1058,1059],{"class":389},"# Запуск контейнера из образа с пробросом порта\n",[383,1061,1062,1064,1066,1069,1072],{"class":385,"line":393},[383,1063,397],{"class":396},[383,1065,401],{"class":400},[383,1067,1068],{"class":404}," -p",[383,1070,1071],{"class":400}," 8088:8088",[383,1073,1074],{"class":400}," my-backend-app\n",[383,1076,1077],{"class":385,"line":644},[383,1078,648],{"emptyLinePlaceholder":647},[383,1080,1081],{"class":385,"line":651},[383,1082,1083],{"class":389},"# Запуск в фоновом режиме (detached)\n",[383,1085,1086,1088,1090,1093,1095,1097,1100,1103],{"class":385,"line":657},[383,1087,397],{"class":396},[383,1089,401],{"class":400},[383,1091,1092],{"class":404}," -d",[383,1094,1068],{"class":404},[383,1096,1071],{"class":400},[383,1098,1099],{"class":404}," --name",[383,1101,1102],{"class":400}," my-app",[383,1104,1074],{"class":400},[383,1106,1107],{"class":385,"line":668},[383,1108,648],{"emptyLinePlaceholder":647},[383,1110,1111],{"class":385,"line":673},[383,1112,1113],{"class":389},"# Список запущенных контейнеров\n",[383,1115,1116,1118],{"class":385,"line":679},[383,1117,397],{"class":396},[383,1119,1120],{"class":400}," ps\n",[383,1122,1123],{"class":385,"line":687},[383,1124,648],{"emptyLinePlaceholder":647},[383,1126,1127],{"class":385,"line":692},[383,1128,1129],{"class":389},"# Список всех контейнеров (включая остановленные)\n",[383,1131,1132,1134,1137],{"class":385,"line":698},[383,1133,397],{"class":396},[383,1135,1136],{"class":400}," ps",[383,1138,1139],{"class":404}," -a\n",[383,1141,1142],{"class":385,"line":709},[383,1143,648],{"emptyLinePlaceholder":647},[383,1145,1146],{"class":385,"line":714},[383,1147,1148],{"class":389},"# Просмотр логов контейнера\n",[383,1150,1151,1153,1156],{"class":385,"line":720},[383,1152,397],{"class":396},[383,1154,1155],{"class":400}," logs",[383,1157,411],{"class":400},[383,1159,1161,1163,1165,1168,1170],{"class":385,"line":1160},15,[383,1162,397],{"class":396},[383,1164,1155],{"class":400},[383,1166,1167],{"class":404}," -f",[383,1169,1102],{"class":400},[383,1171,1172],{"class":389},"    # в режиме follow (аналог tail -f)\n",[383,1174,1176],{"class":385,"line":1175},16,[383,1177,648],{"emptyLinePlaceholder":647},[383,1179,1181],{"class":385,"line":1180},17,[383,1182,1183],{"class":389},"# Выполнение команды внутри работающего контейнера\n",[383,1185,1187,1189,1192,1195,1197],{"class":385,"line":1186},18,[383,1188,397],{"class":396},[383,1190,1191],{"class":400}," exec",[383,1193,1194],{"class":404}," -it",[383,1196,1102],{"class":400},[383,1198,1199],{"class":400}," \u002Fbin\u002Fsh\n",[383,1201,1203],{"class":385,"line":1202},19,[383,1204,648],{"emptyLinePlaceholder":647},[383,1206,1208],{"class":385,"line":1207},20,[383,1209,1210],{"class":389},"# Остановка контейнера (SIGTERM, затем SIGKILL через 10 секунд)\n",[383,1212,1214,1216,1219],{"class":385,"line":1213},21,[383,1215,397],{"class":396},[383,1217,1218],{"class":400}," stop",[383,1220,411],{"class":400},[383,1222,1224],{"class":385,"line":1223},22,[383,1225,648],{"emptyLinePlaceholder":647},[383,1227,1229],{"class":385,"line":1228},23,[383,1230,1231],{"class":389},"# Удаление остановленного контейнера\n",[383,1233,1235,1237,1240],{"class":385,"line":1234},24,[383,1236,397],{"class":396},[383,1238,1239],{"class":400}," rm",[383,1241,411],{"class":400},[15,1243,1244,1245,1248,1249,1252,1253,1256,1257,1260],{},"Флаг ",[21,1246,1247],{},"-it"," в ",[21,1250,1251],{},"docker exec"," — это комбинация ",[21,1254,1255],{},"-i"," (interactive, stdin остаётся открытым) и ",[21,1258,1259],{},"-t"," (tty, выделяется псевдотерминал). Без них вы не сможете интерактивно работать в shell контейнера.",[26,1262],{},[29,1264,1266],{"id":1265},"docker-volumes-персистентное-хранилище","Docker Volumes — персистентное хранилище",[15,1268,1269],{},"Контейнеры по своей природе эфемерны — когда контейнер удаляется, его writable layer исчезает вместе со всеми данными. Но базе данных нужно хранить файлы между перезапусками. Volumes решают эту проблему.",[15,1271,1272],{},"Существует три способа прокинуть данные в контейнер:",[71,1274,1276],{"id":1275},"bind-mount","Bind mount",[15,1278,1279],{},"Bind mount монтирует конкретную папку хоста внутрь контейнера. Контейнер видит содержимое этой папки как своё собственное:",[83,1281,1283],{"className":377,"code":1282,"language":379,"meta":91,"style":91},"# Через командную строку\ndocker run -v \u002Fhost\u002Fpath:\u002Fcontainer\u002Fpath myapp\n\n# Монтирование конфига и данных\ndocker run -v .\u002Fconfig:\u002Fapp\u002Fconfig -v .\u002Fdata:\u002Fvar\u002Flib\u002Fpostgresql myapp\n",[21,1284,1285,1290,1305,1309,1314],{"__ignoreMap":91},[383,1286,1287],{"class":385,"line":386},[383,1288,1289],{"class":389},"# Через командную строку\n",[383,1291,1292,1294,1296,1299,1302],{"class":385,"line":393},[383,1293,397],{"class":396},[383,1295,401],{"class":400},[383,1297,1298],{"class":404}," -v",[383,1300,1301],{"class":400}," \u002Fhost\u002Fpath:\u002Fcontainer\u002Fpath",[383,1303,1304],{"class":400}," myapp\n",[383,1306,1307],{"class":385,"line":644},[383,1308,648],{"emptyLinePlaceholder":647},[383,1310,1311],{"class":385,"line":651},[383,1312,1313],{"class":389},"# Монтирование конфига и данных\n",[383,1315,1316,1318,1320,1322,1325,1327,1330],{"class":385,"line":657},[383,1317,397],{"class":396},[383,1319,401],{"class":400},[383,1321,1298],{"class":404},[383,1323,1324],{"class":400}," .\u002Fconfig:\u002Fapp\u002Fconfig",[383,1326,1298],{"class":404},[383,1328,1329],{"class":400}," .\u002Fdata:\u002Fvar\u002Flib\u002Fpostgresql",[383,1331,1304],{"class":400},[15,1333,1334],{},"Bind mount привязывает контейнер к конкретной структуре файловой системы хоста. Это удобно для разработки — можно монтировать каталог с исходниками и видеть изменения в реальном времени (hot reload). Но на production это анти-паттерн: пути на разных серверах могут отличаться.",[71,1336,1338],{"id":1337},"named-volume","Named volume",[15,1340,1341,1342,1345],{},"Named volume — Docker сам управляет хранилищем. Данные хранятся в специальной директории Docker (",[21,1343,1344],{},"\u002Fvar\u002Flib\u002Fdocker\u002Fvolumes\u002F",") и не зависят от файловой системы хоста:",[83,1347,1349],{"className":377,"code":1348,"language":379,"meta":91,"style":91},"# Создание именованного тома\ndocker volume create pgdata\n\n# Использование при запуске контейнера\ndocker run -v pgdata:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata postgres:15-alpine\n\n# Список томов\ndocker volume ls\n\n# Удаление тома\ndocker volume rm pgdata\n",[21,1350,1351,1356,1369,1373,1378,1391,1395,1400,1409,1413,1418],{"__ignoreMap":91},[383,1352,1353],{"class":385,"line":386},[383,1354,1355],{"class":389},"# Создание именованного тома\n",[383,1357,1358,1360,1363,1366],{"class":385,"line":393},[383,1359,397],{"class":396},[383,1361,1362],{"class":400}," volume",[383,1364,1365],{"class":400}," create",[383,1367,1368],{"class":400}," pgdata\n",[383,1370,1371],{"class":385,"line":644},[383,1372,648],{"emptyLinePlaceholder":647},[383,1374,1375],{"class":385,"line":651},[383,1376,1377],{"class":389},"# Использование при запуске контейнера\n",[383,1379,1380,1382,1384,1386,1389],{"class":385,"line":657},[383,1381,397],{"class":396},[383,1383,401],{"class":400},[383,1385,1298],{"class":404},[383,1387,1388],{"class":400}," pgdata:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata",[383,1390,665],{"class":400},[383,1392,1393],{"class":385,"line":668},[383,1394,648],{"emptyLinePlaceholder":647},[383,1396,1397],{"class":385,"line":673},[383,1398,1399],{"class":389},"# Список томов\n",[383,1401,1402,1404,1406],{"class":385,"line":679},[383,1403,397],{"class":396},[383,1405,1362],{"class":400},[383,1407,1408],{"class":400}," ls\n",[383,1410,1411],{"class":385,"line":687},[383,1412,648],{"emptyLinePlaceholder":647},[383,1414,1415],{"class":385,"line":692},[383,1416,1417],{"class":389},"# Удаление тома\n",[383,1419,1420,1422,1424,1426],{"class":385,"line":698},[383,1421,397],{"class":396},[383,1423,1362],{"class":400},[383,1425,1239],{"class":400},[383,1427,1368],{"class":400},[15,1429,1430],{},"Named volumes — предпочтительный способ для production. Они портативны, Docker управляет их жизненным циклом, и они могут быть подключены к разным контейнерам.",[71,1432,1433],{"id":1433},"tmpfs",[15,1435,1436],{},"tmpfs — данные хранятся в оперативной памяти и исчезают при остановке контейнера:",[83,1438,1440],{"className":377,"code":1439,"language":379,"meta":91,"style":91},"docker run --tmpfs \u002Ftmp myapp\n",[21,1441,1442],{"__ignoreMap":91},[383,1443,1444,1446,1448,1451,1454],{"class":385,"line":386},[383,1445,397],{"class":396},[383,1447,401],{"class":400},[383,1449,1450],{"class":404}," --tmpfs",[383,1452,1453],{"class":400}," \u002Ftmp",[383,1455,1304],{"class":400},[15,1457,1458],{},"Подходит для временных файлов и секретов, которые не должны попадать на диск.",[71,1460,1462],{"id":1461},"сравнение-типов","Сравнение типов",[117,1464,1465,1481],{},[120,1466,1467],{},[123,1468,1469,1472,1475,1478],{},[126,1470,1471],{"align":128},"Тип",[126,1473,1474],{"align":128},"Персистентность",[126,1476,1477],{"align":128},"Кто управляет",[126,1479,1480],{"align":128},"Когда использовать",[137,1482,1483,1496,1509],{},[123,1484,1485,1487,1490,1493],{},[142,1486,1276],{"align":128},[142,1488,1489],{"align":128},"Да, на хосте",[142,1491,1492],{"align":128},"Разработчик",[142,1494,1495],{"align":128},"Dev: конфиги, код, hot reload",[123,1497,1498,1500,1503,1506],{},[142,1499,1338],{"align":128},[142,1501,1502],{"align":128},"Да, в Docker",[142,1504,1505],{"align":128},"Docker",[142,1507,1508],{"align":128},"Prod: данные БД, файлы",[123,1510,1511,1513,1515,1518],{},[142,1512,1433],{"align":128},[142,1514,213],{"align":128},[142,1516,1517],{"align":128},"--",[142,1519,1520],{"align":128},"Секреты, временные файлы",[71,1522,1524],{"id":1523},"пример-postgresql-с-named-volume","Пример: PostgreSQL с named volume",[83,1526,1530],{"className":1527,"code":1528,"language":1529,"meta":91,"style":91},"language-yaml shiki shiki-themes github-dark","# docker-compose.yaml\nservices:\n  postgres:\n    image: postgres:15-alpine\n    environment:\n      POSTGRES_PASSWORD: secret\n    volumes:\n      - pgdata:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n    ports:\n      - \"5432:5432\"\n\nvolumes:\n  pgdata:  # Docker создаёт и хранит в \u002Fvar\u002Flib\u002Fdocker\u002Fvolumes\u002F\n","yaml",[21,1531,1532,1537,1546,1553,1564,1571,1581,1588,1596,1603,1610,1614,1621],{"__ignoreMap":91},[383,1533,1534],{"class":385,"line":386},[383,1535,1536],{"class":389},"# docker-compose.yaml\n",[383,1538,1539,1543],{"class":385,"line":393},[383,1540,1542],{"class":1541},"s4JwU","services",[383,1544,1545],{"class":835},":\n",[383,1547,1548,1551],{"class":385,"line":644},[383,1549,1550],{"class":1541},"  postgres",[383,1552,1545],{"class":835},[383,1554,1555,1558,1561],{"class":385,"line":651},[383,1556,1557],{"class":1541},"    image",[383,1559,1560],{"class":835},": ",[383,1562,1563],{"class":400},"postgres:15-alpine\n",[383,1565,1566,1569],{"class":385,"line":657},[383,1567,1568],{"class":1541},"    environment",[383,1570,1545],{"class":835},[383,1572,1573,1576,1578],{"class":385,"line":668},[383,1574,1575],{"class":1541},"      POSTGRES_PASSWORD",[383,1577,1560],{"class":835},[383,1579,1580],{"class":400},"secret\n",[383,1582,1583,1586],{"class":385,"line":673},[383,1584,1585],{"class":1541},"    volumes",[383,1587,1545],{"class":835},[383,1589,1590,1593],{"class":385,"line":679},[383,1591,1592],{"class":835},"      - ",[383,1594,1595],{"class":400},"pgdata:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n",[383,1597,1598,1601],{"class":385,"line":687},[383,1599,1600],{"class":1541},"    ports",[383,1602,1545],{"class":835},[383,1604,1605,1607],{"class":385,"line":692},[383,1606,1592],{"class":835},[383,1608,1609],{"class":400},"\"5432:5432\"\n",[383,1611,1612],{"class":385,"line":698},[383,1613,648],{"emptyLinePlaceholder":647},[383,1615,1616,1619],{"class":385,"line":709},[383,1617,1618],{"class":1541},"volumes",[383,1620,1545],{"class":835},[383,1622,1623,1626,1629],{"class":385,"line":714},[383,1624,1625],{"class":1541},"  pgdata",[383,1627,1628],{"class":835},":  ",[383,1630,1631],{"class":389},"# Docker создаёт и хранит в \u002Fvar\u002Flib\u002Fdocker\u002Fvolumes\u002F\n",[15,1633,1634,1635,1638,1639,1642],{},"Даже если контейнер ",[21,1636,1637],{},"postgres"," удалить и создать заново — данные в ",[21,1640,1641],{},"pgdata"," сохранятся. Это фундаментальный паттерн для stateful-сервисов в Docker.",[26,1644],{},[29,1646,1648],{"id":1647},"основные-команды-docker","Основные команды Docker",[15,1650,1651],{},"Сводная таблица команд, которые вы будете использовать ежедневно:",[71,1653,1655],{"id":1654},"образы","Образы",[83,1657,1659],{"className":377,"code":1658,"language":379,"meta":91,"style":91},"# Сборка образа из Dockerfile в текущей директории\ndocker build -t my-app:v1.0 .\n\n# Сборка с указанием конкретного Dockerfile\ndocker build -f Dockerfile.prod -t my-app:prod .\n\n# Скачать образ из реестра\ndocker pull postgres:15-alpine\n\n# Список локальных образов\ndocker images\n\n# Удалить образ\ndocker rmi my-app:v1.0\n\n# Удалить неиспользуемые образы\ndocker image prune\n",[21,1660,1661,1666,1679,1683,1688,1706,1710,1715,1723,1727,1732,1738,1742,1747,1756,1760,1765],{"__ignoreMap":91},[383,1662,1663],{"class":385,"line":386},[383,1664,1665],{"class":389},"# Сборка образа из Dockerfile в текущей директории\n",[383,1667,1668,1670,1672,1674,1677],{"class":385,"line":393},[383,1669,397],{"class":396},[383,1671,632],{"class":400},[383,1673,635],{"class":404},[383,1675,1676],{"class":400}," my-app:v1.0",[383,1678,641],{"class":400},[383,1680,1681],{"class":385,"line":644},[383,1682,648],{"emptyLinePlaceholder":647},[383,1684,1685],{"class":385,"line":651},[383,1686,1687],{"class":389},"# Сборка с указанием конкретного Dockerfile\n",[383,1689,1690,1692,1694,1696,1699,1701,1704],{"class":385,"line":657},[383,1691,397],{"class":396},[383,1693,632],{"class":400},[383,1695,1167],{"class":404},[383,1697,1698],{"class":400}," Dockerfile.prod",[383,1700,635],{"class":404},[383,1702,1703],{"class":400}," my-app:prod",[383,1705,641],{"class":400},[383,1707,1708],{"class":385,"line":668},[383,1709,648],{"emptyLinePlaceholder":647},[383,1711,1712],{"class":385,"line":673},[383,1713,1714],{"class":389},"# Скачать образ из реестра\n",[383,1716,1717,1719,1721],{"class":385,"line":679},[383,1718,397],{"class":396},[383,1720,662],{"class":400},[383,1722,665],{"class":400},[383,1724,1725],{"class":385,"line":687},[383,1726,648],{"emptyLinePlaceholder":647},[383,1728,1729],{"class":385,"line":692},[383,1730,1731],{"class":389},"# Список локальных образов\n",[383,1733,1734,1736],{"class":385,"line":698},[383,1735,397],{"class":396},[383,1737,684],{"class":400},[383,1739,1740],{"class":385,"line":709},[383,1741,648],{"emptyLinePlaceholder":647},[383,1743,1744],{"class":385,"line":714},[383,1745,1746],{"class":389},"# Удалить образ\n",[383,1748,1749,1751,1753],{"class":385,"line":720},[383,1750,397],{"class":396},[383,1752,703],{"class":400},[383,1754,1755],{"class":400}," my-app:v1.0\n",[383,1757,1758],{"class":385,"line":1160},[383,1759,648],{"emptyLinePlaceholder":647},[383,1761,1762],{"class":385,"line":1175},[383,1763,1764],{"class":389},"# Удалить неиспользуемые образы\n",[383,1766,1767,1769,1772],{"class":385,"line":1180},[383,1768,397],{"class":396},[383,1770,1771],{"class":400}," image",[383,1773,1774],{"class":400}," prune\n",[71,1776,98],{"id":1777},"контейнеры-1",[83,1779,1781],{"className":377,"code":1780,"language":379,"meta":91,"style":91},"# Запуск с пробросом порта и именем\ndocker run -d -p 8080:8080 --name api my-app:v1.0\n\n# Запуск с переменными окружения\ndocker run -d -e DB_HOST=localhost -e DB_PORT=5432 my-app:v1.0\n\n# Запуск с ограничением ресурсов\ndocker run -d --memory=256m --cpus=0.5 my-app:v1.0\n\n# Список запущенных контейнеров\ndocker ps\n\n# Логи\ndocker logs -f api\n\n# Зайти внутрь контейнера\ndocker exec -it api \u002Fbin\u002Fsh\n\n# Остановить и удалить\ndocker stop api && docker rm api\n\n# Остановить все контейнеры\ndocker stop $(docker ps -q)\n",[21,1782,1783,1788,1808,1812,1817,1841,1845,1850,1866,1870,1874,1880,1884,1889,1900,1904,1909,1921,1925,1930,1947,1951,1956],{"__ignoreMap":91},[383,1784,1785],{"class":385,"line":386},[383,1786,1787],{"class":389},"# Запуск с пробросом порта и именем\n",[383,1789,1790,1792,1794,1796,1798,1801,1803,1806],{"class":385,"line":393},[383,1791,397],{"class":396},[383,1793,401],{"class":400},[383,1795,1092],{"class":404},[383,1797,1068],{"class":404},[383,1799,1800],{"class":400}," 8080:8080",[383,1802,1099],{"class":404},[383,1804,1805],{"class":400}," api",[383,1807,1755],{"class":400},[383,1809,1810],{"class":385,"line":644},[383,1811,648],{"emptyLinePlaceholder":647},[383,1813,1814],{"class":385,"line":651},[383,1815,1816],{"class":389},"# Запуск с переменными окружения\n",[383,1818,1819,1821,1823,1825,1828,1831,1833,1836,1839],{"class":385,"line":657},[383,1820,397],{"class":396},[383,1822,401],{"class":400},[383,1824,1092],{"class":404},[383,1826,1827],{"class":404}," -e",[383,1829,1830],{"class":400}," DB_HOST=localhost",[383,1832,1827],{"class":404},[383,1834,1835],{"class":400}," DB_PORT=",[383,1837,1838],{"class":404},"5432",[383,1840,1755],{"class":400},[383,1842,1843],{"class":385,"line":668},[383,1844,648],{"emptyLinePlaceholder":647},[383,1846,1847],{"class":385,"line":673},[383,1848,1849],{"class":389},"# Запуск с ограничением ресурсов\n",[383,1851,1852,1854,1856,1858,1861,1864],{"class":385,"line":679},[383,1853,397],{"class":396},[383,1855,401],{"class":400},[383,1857,1092],{"class":404},[383,1859,1860],{"class":404}," --memory=256m",[383,1862,1863],{"class":404}," --cpus=0.5",[383,1865,1755],{"class":400},[383,1867,1868],{"class":385,"line":687},[383,1869,648],{"emptyLinePlaceholder":647},[383,1871,1872],{"class":385,"line":692},[383,1873,1113],{"class":389},[383,1875,1876,1878],{"class":385,"line":698},[383,1877,397],{"class":396},[383,1879,1120],{"class":400},[383,1881,1882],{"class":385,"line":709},[383,1883,648],{"emptyLinePlaceholder":647},[383,1885,1886],{"class":385,"line":714},[383,1887,1888],{"class":389},"# Логи\n",[383,1890,1891,1893,1895,1897],{"class":385,"line":720},[383,1892,397],{"class":396},[383,1894,1155],{"class":400},[383,1896,1167],{"class":404},[383,1898,1899],{"class":400}," api\n",[383,1901,1902],{"class":385,"line":1160},[383,1903,648],{"emptyLinePlaceholder":647},[383,1905,1906],{"class":385,"line":1175},[383,1907,1908],{"class":389},"# Зайти внутрь контейнера\n",[383,1910,1911,1913,1915,1917,1919],{"class":385,"line":1180},[383,1912,397],{"class":396},[383,1914,1191],{"class":400},[383,1916,1194],{"class":404},[383,1918,1805],{"class":400},[383,1920,1199],{"class":400},[383,1922,1923],{"class":385,"line":1186},[383,1924,648],{"emptyLinePlaceholder":647},[383,1926,1927],{"class":385,"line":1202},[383,1928,1929],{"class":389},"# Остановить и удалить\n",[383,1931,1932,1934,1936,1938,1941,1943,1945],{"class":385,"line":1207},[383,1933,397],{"class":396},[383,1935,1218],{"class":400},[383,1937,1805],{"class":400},[383,1939,1940],{"class":835}," && ",[383,1942,397],{"class":396},[383,1944,1239],{"class":400},[383,1946,1899],{"class":400},[383,1948,1949],{"class":385,"line":1213},[383,1950,648],{"emptyLinePlaceholder":647},[383,1952,1953],{"class":385,"line":1223},[383,1954,1955],{"class":389},"# Остановить все контейнеры\n",[383,1957,1958,1960,1962,1965,1967,1969,1972],{"class":385,"line":1228},[383,1959,397],{"class":396},[383,1961,1218],{"class":400},[383,1963,1964],{"class":835}," $(",[383,1966,397],{"class":396},[383,1968,1136],{"class":400},[383,1970,1971],{"class":404}," -q",[383,1973,1974],{"class":835},")\n",[71,1976,1977],{"id":1618},"Volumes",[83,1979,1981],{"className":377,"code":1980,"language":379,"meta":91,"style":91},"# Создать том\ndocker volume create mydata\n\n# Список томов\ndocker volume ls\n\n# Информация о томе\ndocker volume inspect mydata\n\n# Удалить том\ndocker volume rm mydata\n\n# Удалить неиспользуемые тома\ndocker volume prune\n",[21,1982,1983,1988,1999,2003,2007,2015,2019,2024,2035,2039,2044,2054,2058,2063],{"__ignoreMap":91},[383,1984,1985],{"class":385,"line":386},[383,1986,1987],{"class":389},"# Создать том\n",[383,1989,1990,1992,1994,1996],{"class":385,"line":393},[383,1991,397],{"class":396},[383,1993,1362],{"class":400},[383,1995,1365],{"class":400},[383,1997,1998],{"class":400}," mydata\n",[383,2000,2001],{"class":385,"line":644},[383,2002,648],{"emptyLinePlaceholder":647},[383,2004,2005],{"class":385,"line":651},[383,2006,1399],{"class":389},[383,2008,2009,2011,2013],{"class":385,"line":657},[383,2010,397],{"class":396},[383,2012,1362],{"class":400},[383,2014,1408],{"class":400},[383,2016,2017],{"class":385,"line":668},[383,2018,648],{"emptyLinePlaceholder":647},[383,2020,2021],{"class":385,"line":673},[383,2022,2023],{"class":389},"# Информация о томе\n",[383,2025,2026,2028,2030,2033],{"class":385,"line":679},[383,2027,397],{"class":396},[383,2029,1362],{"class":400},[383,2031,2032],{"class":400}," inspect",[383,2034,1998],{"class":400},[383,2036,2037],{"class":385,"line":687},[383,2038,648],{"emptyLinePlaceholder":647},[383,2040,2041],{"class":385,"line":692},[383,2042,2043],{"class":389},"# Удалить том\n",[383,2045,2046,2048,2050,2052],{"class":385,"line":698},[383,2047,397],{"class":396},[383,2049,1362],{"class":400},[383,2051,1239],{"class":400},[383,2053,1998],{"class":400},[383,2055,2056],{"class":385,"line":709},[383,2057,648],{"emptyLinePlaceholder":647},[383,2059,2060],{"class":385,"line":714},[383,2061,2062],{"class":389},"# Удалить неиспользуемые тома\n",[383,2064,2065,2067,2069],{"class":385,"line":720},[383,2066,397],{"class":396},[383,2068,1362],{"class":400},[383,2070,1774],{"class":400},[71,2072,2074],{"id":2073},"общая-очистка","Общая очистка",[83,2076,2078],{"className":377,"code":2077,"language":379,"meta":91,"style":91},"# Удалить все остановленные контейнеры, неиспользуемые сети и образы\ndocker system prune\n\n# То же + volumes (осторожно!)\ndocker system prune --volumes\n\n# Показать использование дискового пространства\ndocker system df\n",[21,2079,2080,2085,2094,2098,2103,2115,2119,2124],{"__ignoreMap":91},[383,2081,2082],{"class":385,"line":386},[383,2083,2084],{"class":389},"# Удалить все остановленные контейнеры, неиспользуемые сети и образы\n",[383,2086,2087,2089,2092],{"class":385,"line":393},[383,2088,397],{"class":396},[383,2090,2091],{"class":400}," system",[383,2093,1774],{"class":400},[383,2095,2096],{"class":385,"line":644},[383,2097,648],{"emptyLinePlaceholder":647},[383,2099,2100],{"class":385,"line":651},[383,2101,2102],{"class":389},"# То же + volumes (осторожно!)\n",[383,2104,2105,2107,2109,2112],{"class":385,"line":657},[383,2106,397],{"class":396},[383,2108,2091],{"class":400},[383,2110,2111],{"class":400}," prune",[383,2113,2114],{"class":404}," --volumes\n",[383,2116,2117],{"class":385,"line":668},[383,2118,648],{"emptyLinePlaceholder":647},[383,2120,2121],{"class":385,"line":673},[383,2122,2123],{"class":389},"# Показать использование дискового пространства\n",[383,2125,2126,2128,2130],{"class":385,"line":679},[383,2127,397],{"class":396},[383,2129,2091],{"class":400},[383,2131,2132],{"class":400}," df\n",[26,2134],{},[29,2136,2138],{"id":2137},"как-всё-связано-вместе","Как всё связано вместе",[15,2140,2141],{},"Подведём итог, чтобы собрать полную картину:",[2143,2144,2145,2151,2161,2170,2184,2189],"ol",{},[351,2146,2147,2150],{},[39,2148,2149],{},"Dockerfile"," описывает шаги сборки образа",[351,2152,2153,2156,2157,2160],{},[39,2154,2155],{},"docker build"," выполняет эти шаги и создаёт ",[39,2158,2159],{},"Image"," — набор read-only слоёв",[351,2162,2163,2165,2166,2169],{},[39,2164,23],{}," берёт Image, добавляет writable layer и создаёт ",[39,2167,2168],{},"Container"," — изолированный процесс",[351,2171,2172,2173,2176,2177,2180,2181],{},"Контейнер изолирован через ",[39,2174,2175],{},"namespaces",", ограничен через ",[39,2178,2179],{},"cgroups",", его файловая система построена на ",[39,2182,2183],{},"overlay fs",[351,2185,2186,2187],{},"Данные, которые должны пережить контейнер, хранятся в ",[39,2188,1977],{},[351,2190,2191,2192,2195],{},"На Mac\u002FWindows всё это работает внутри скрытой ",[39,2193,2194],{},"Linux VM"," (LinuxKit)",[83,2197,2200],{"className":2198,"code":2199,"language":88},[86],"Dockerfile  --build-->  Image (read-only слои)\n                            |\n                         run |\n                            v\n                        Container (image + writable layer)\n                            |\n                     ┌------┼------┐\n                     |      |      |\n                namespaces cgroups overlay fs\n                     |      |      |\n                     └------┼------┘\n                            |\n                        Linux Kernel\n",[21,2201,2199],{"__ignoreMap":91},[26,2203],{},[29,2205,2207],{"id":2206},"вопросы-на-собеседовании","Вопросы на собеседовании",[15,2209,2210],{},[39,2211,2212],{},"1. Чем контейнер отличается от виртуальной машины?",[15,2214,2215],{},"Контейнер виртуализирует ОС (userspace), VM виртуализирует железо. Контейнер использует ядро хоста через namespaces и cgroups, а VM имеет собственное ядро. Контейнеры легче (мегабайты vs гигабайты), быстрее запускаются (секунды vs минуты), но обеспечивают менее строгую изоляцию — уязвимость в ядре затрагивает все контейнеры.",[15,2217,2218],{},[39,2219,2220],{},"2. Что такое namespaces и cgroups? Какую роль они играют в Docker?",[15,2222,2223],{},"Namespaces обеспечивают изоляцию: PID (процессы), NET (сеть), MNT (файловая система), UTS (hostname), IPC (межпроцессное общение), USER (пользователи). Cgroups ограничивают потребление ресурсов: CPU, память, disk I\u002FO. Docker использует оба механизма: namespaces создают иллюзию отдельной машины, cgroups не дают контейнеру «съесть» все ресурсы хоста.",[15,2225,2226],{},[39,2227,2228],{},"3. Что произойдёт с данными при удалении контейнера?",[15,2230,2231],{},"Writable layer контейнера удаляется вместе с ним — все данные, записанные внутри контейнера, теряются. Чтобы данные сохранялись, нужно использовать volumes: bind mount (монтирование папки хоста), named volume (управляемый Docker), или tmpfs (в памяти, для временных данных).",[15,2233,2234],{},[39,2235,2236],{},"4. Чем Docker Image отличается от Docker Container?",[15,2238,2239],{},"Image — неизменяемый (immutable) шаблон, набор read-only слоёв. Container — запущенный экземпляр образа с добавленным writable layer. Из одного образа можно создать множество контейнеров. Образ нельзя изменить после сборки, контейнер — можно (но изменения живут только в его writable layer).",[15,2241,2242],{},[39,2243,2244],{},"5. Как Docker работает на macOS?",[15,2246,2247],{},"На macOS нет Linux-ядра, а контейнерам оно необходимо. Docker Desktop запускает легковесную Linux VM (LinuxKit) через Apple Hypervisor framework. Все контейнеры работают внутри этой VM. Из-за этого есть дополнительные накладные расходы: медленнее файловая система (bind mounts через виртуальный FS), больше потребление памяти (VM занимает фиксированный объём).",[15,2249,2250],{},[39,2251,2252],{},"6. Как работает кэширование слоёв Docker? Почему порядок инструкций в Dockerfile важен?",[15,2254,2255],{},"Каждая инструкция (RUN, COPY, ADD) создаёт слой. Docker кэширует слои по содержимому. Если инструкция и входные данные не изменились — слой берётся из кэша. Но если один слой инвалидируется — все последующие слои тоже пересобираются. Поэтому редко меняющиеся инструкции (установка зависимостей) ставят раньше, а часто меняющиеся (копирование кода) — позже.",[15,2257,2258],{},[39,2259,2260],{},"7. В чём разница между bind mount и named volume?",[15,2262,2263,2264,2266],{},"Bind mount привязывает конкретную директорию хоста к контейнеру — разработчик сам управляет путями и содержимым. Named volume управляется Docker, хранится в ",[21,2265,1344],{},". Bind mount удобен для разработки (монтирование исходников, hot reload), named volume — для production (портативность, управление жизненным циклом).",[26,2268],{},[29,2270,2272],{"id":2271},"задачи","Задачи",[71,2274,2276],{"id":2275},"задача-1-запуск-postgresql-с-персистентными-данными","Задача 1: Запуск PostgreSQL с персистентными данными",[15,2278,2279,2280,2282],{},"Запустите контейнер PostgreSQL 15 (образ ",[21,2281,737],{},") со следующими требованиями:",[348,2284,2285,2291,2297,2303,2309],{},[351,2286,2287,2288],{},"Пароль ",[21,2289,2290],{},"mysecret",[351,2292,2293,2294],{},"Порт проброшен на хост ",[21,2295,2296],{},"5432:5432",[351,2298,2299,2300],{},"Данные хранятся в named volume ",[21,2301,2302],{},"pg-data",[351,2304,2305,2306],{},"Контейнер называется ",[21,2307,2308],{},"my-postgres",[351,2310,2311],{},"Ограничение памяти: 512 МБ",[15,2313,2314],{},"Убедитесь, что после остановки и удаления контейнера, а затем повторного запуска с тем же volume — данные сохраняются.",[71,2316,2318],{"id":2317},"задача-2-исследование-слоёв-образа","Задача 2: Исследование слоёв образа",[15,2320,2321,2322,2325],{},"Возьмите любой Docker-образ (например, ",[21,2323,2324],{},"golang:1.26-alpine3.23",") и выполните:",[2143,2327,2328,2334,2337,2349],{},[351,2329,2330,2331],{},"Посмотрите его слои через ",[21,2332,2333],{},"docker history",[351,2335,2336],{},"Определите, какой слой занимает больше всего места",[351,2338,2339,2340,2342,2343,2345,2346],{},"Сравните размер ",[21,2341,2324],{}," и ",[21,2344,983],{}," через ",[21,2347,2348],{},"docker images",[351,2350,2351],{},"Объясните, почему разница именно такая",[71,2353,2355],{"id":2354},"задача-3-namespaces-на-практике","Задача 3: Namespaces на практике",[15,2357,2358,2359,2362,2363,2366],{},"Запустите контейнер с ",[21,2360,2361],{},"alpine"," в интерактивном режиме (",[21,2364,2365],{},"docker run -it alpine \u002Fbin\u002Fsh",") и выполните внутри:",[2143,2368,2369,2375,2381],{},[351,2370,2371,2374],{},[21,2372,2373],{},"ps aux"," — какие процессы видит контейнер? Какой PID у вашего shell?",[351,2376,2377,2380],{},[21,2378,2379],{},"hostname"," — какое имя хоста у контейнера?",[351,2382,2383,2386],{},[21,2384,2385],{},"ip addr"," — какой IP-адрес у контейнера?",[15,2388,2389],{},"Сравните результаты с выводом тех же команд на хосте. Объясните, какие namespaces обеспечивают каждое из наблюдаемых отличий.",[26,2391],{},[29,2393,2395],{"id":2394},"интерактивная-практика","Интерактивная практика",[2397,2398,2402,2405,2422],"quiz",{"answer":2399,"id":2400,"xp":2401},"2","infra-docker-basics-q1","10",[15,2403,2404],{},"Что точнее всего описывает разницу между Docker image и container?",[2406,2407,2408],"template",{"v-slot:options":91},[348,2409,2410,2413,2416,2419],{},[351,2411,2412],{},"Image — это запущенный процесс, container — архив с файловой системой",[351,2414,2415],{},"Image — неизменяемый шаблон, container — запущенный экземпляр с собственным состоянием",[351,2417,2418],{},"Image хранит только переменные окружения, container хранит только сетевые правила",[351,2420,2421],{},"Между ними нет практической разницы",[2406,2423,2424],{"v-slot:explanation":91},[15,2425,2426],{},"Image задаёт слои файловой системы и metadata для запуска. Container создаётся из image и добавляет runtime-состояние: процессы, сеть, writable layer, mounts и limits.",[2428,2429,2433,2436,2592],"predict",{"answer":2430,"id":2431,"xp":2432},"image\\ncontainer","infra-docker-basics-p1","15",[15,2434,2435],{},"Что выведет программа?",[2406,2437,2438],{"v-slot:code":91},[83,2439,2443],{"className":2440,"code":2441,"language":2442,"meta":91,"style":91},"language-go shiki shiki-themes github-dark","package main\n\nimport \"fmt\"\n\nfunc dockerObject(running bool) string {\n    if running {\n        return \"container\"\n    }\n    return \"image\"\n}\n\nfunc main() {\n    fmt.Println(dockerObject(false))\n    fmt.Println(dockerObject(true))\n}\n","go",[21,2444,2445,2453,2457,2471,2475,2502,2510,2518,2523,2531,2536,2540,2550,2571,2588],{"__ignoreMap":91},[383,2446,2447,2450],{"class":385,"line":386},[383,2448,2449],{"class":832},"package",[383,2451,2452],{"class":396}," main\n",[383,2454,2455],{"class":385,"line":393},[383,2456,648],{"emptyLinePlaceholder":647},[383,2458,2459,2462,2465,2468],{"class":385,"line":644},[383,2460,2461],{"class":832},"import",[383,2463,2464],{"class":400}," \"",[383,2466,2467],{"class":396},"fmt",[383,2469,2470],{"class":400},"\"\n",[383,2472,2473],{"class":385,"line":651},[383,2474,648],{"emptyLinePlaceholder":647},[383,2476,2477,2480,2483,2486,2490,2493,2496,2499],{"class":385,"line":657},[383,2478,2479],{"class":832},"func",[383,2481,2482],{"class":396}," dockerObject",[383,2484,2485],{"class":835},"(",[383,2487,2489],{"class":2488},"s9osk","running",[383,2491,2492],{"class":832}," bool",[383,2494,2495],{"class":835},") ",[383,2497,2498],{"class":832},"string",[383,2500,2501],{"class":835}," {\n",[383,2503,2504,2507],{"class":385,"line":668},[383,2505,2506],{"class":832},"    if",[383,2508,2509],{"class":835}," running {\n",[383,2511,2512,2515],{"class":385,"line":673},[383,2513,2514],{"class":832},"        return",[383,2516,2517],{"class":400}," \"container\"\n",[383,2519,2520],{"class":385,"line":679},[383,2521,2522],{"class":835},"    }\n",[383,2524,2525,2528],{"class":385,"line":687},[383,2526,2527],{"class":832},"    return",[383,2529,2530],{"class":400}," \"image\"\n",[383,2532,2533],{"class":385,"line":692},[383,2534,2535],{"class":835},"}\n",[383,2537,2538],{"class":385,"line":698},[383,2539,648],{"emptyLinePlaceholder":647},[383,2541,2542,2544,2547],{"class":385,"line":709},[383,2543,2479],{"class":832},[383,2545,2546],{"class":396}," main",[383,2548,2549],{"class":835},"() {\n",[383,2551,2552,2555,2558,2560,2563,2565,2568],{"class":385,"line":714},[383,2553,2554],{"class":835},"    fmt.",[383,2556,2557],{"class":396},"Println",[383,2559,2485],{"class":835},[383,2561,2562],{"class":396},"dockerObject",[383,2564,2485],{"class":835},[383,2566,2567],{"class":404},"false",[383,2569,2570],{"class":835},"))\n",[383,2572,2573,2575,2577,2579,2581,2583,2586],{"class":385,"line":720},[383,2574,2554],{"class":835},[383,2576,2557],{"class":396},[383,2578,2485],{"class":835},[383,2580,2562],{"class":396},[383,2582,2485],{"class":835},[383,2584,2585],{"class":404},"true",[383,2587,2570],{"class":835},[383,2589,2590],{"class":385,"line":1160},[383,2591,2535],{"class":835},[2406,2593,2594],{"v-slot:hint":91},[15,2595,2596],{},"Образ можно считать шаблоном до запуска, а контейнером — runtime-экземпляр.",[2598,2599,2603,2620,2765],"code-task",{"expected":2600,"id":2601,"xp":2602},"volume\\ntmpfs\\nbind","infra-docker-basics-ct1","20",[15,2604,2605,2606,2609,2610,2613,2614,2617,2618,24],{},"Реализуй ",[21,2607,2608],{},"StorageKind",": для host path нужен ",[21,2611,2612],{},"bind",", для постоянного managed-хранилища — ",[21,2615,2616],{},"volume",", для временного in-memory хранилища — ",[21,2619,1433],{},[2406,2621,2622],{"v-slot:template":91},[83,2623,2625],{"className":2440,"code":2624,"language":2442,"meta":91,"style":91},"package main\n\nimport \"fmt\"\n\nfunc StorageKind(persistent bool, hostPath bool) string {\n    return \"todo\"\n}\n\nfunc main() {\n    fmt.Println(StorageKind(true, false))\n    fmt.Println(StorageKind(false, false))\n    fmt.Println(StorageKind(true, true))\n}\n",[21,2626,2627,2633,2637,2647,2651,2678,2685,2689,2693,2701,2721,2741,2761],{"__ignoreMap":91},[383,2628,2629,2631],{"class":385,"line":386},[383,2630,2449],{"class":832},[383,2632,2452],{"class":396},[383,2634,2635],{"class":385,"line":393},[383,2636,648],{"emptyLinePlaceholder":647},[383,2638,2639,2641,2643,2645],{"class":385,"line":644},[383,2640,2461],{"class":832},[383,2642,2464],{"class":400},[383,2644,2467],{"class":396},[383,2646,2470],{"class":400},[383,2648,2649],{"class":385,"line":651},[383,2650,648],{"emptyLinePlaceholder":647},[383,2652,2653,2655,2658,2660,2663,2665,2667,2670,2672,2674,2676],{"class":385,"line":657},[383,2654,2479],{"class":832},[383,2656,2657],{"class":396}," StorageKind",[383,2659,2485],{"class":835},[383,2661,2662],{"class":2488},"persistent",[383,2664,2492],{"class":832},[383,2666,738],{"class":835},[383,2668,2669],{"class":2488},"hostPath",[383,2671,2492],{"class":832},[383,2673,2495],{"class":835},[383,2675,2498],{"class":832},[383,2677,2501],{"class":835},[383,2679,2680,2682],{"class":385,"line":668},[383,2681,2527],{"class":832},[383,2683,2684],{"class":400}," \"todo\"\n",[383,2686,2687],{"class":385,"line":673},[383,2688,2535],{"class":835},[383,2690,2691],{"class":385,"line":679},[383,2692,648],{"emptyLinePlaceholder":647},[383,2694,2695,2697,2699],{"class":385,"line":687},[383,2696,2479],{"class":832},[383,2698,2546],{"class":396},[383,2700,2549],{"class":835},[383,2702,2703,2705,2707,2709,2711,2713,2715,2717,2719],{"class":385,"line":692},[383,2704,2554],{"class":835},[383,2706,2557],{"class":396},[383,2708,2485],{"class":835},[383,2710,2608],{"class":396},[383,2712,2485],{"class":835},[383,2714,2585],{"class":404},[383,2716,738],{"class":835},[383,2718,2567],{"class":404},[383,2720,2570],{"class":835},[383,2722,2723,2725,2727,2729,2731,2733,2735,2737,2739],{"class":385,"line":698},[383,2724,2554],{"class":835},[383,2726,2557],{"class":396},[383,2728,2485],{"class":835},[383,2730,2608],{"class":396},[383,2732,2485],{"class":835},[383,2734,2567],{"class":404},[383,2736,738],{"class":835},[383,2738,2567],{"class":404},[383,2740,2570],{"class":835},[383,2742,2743,2745,2747,2749,2751,2753,2755,2757,2759],{"class":385,"line":709},[383,2744,2554],{"class":835},[383,2746,2557],{"class":396},[383,2748,2485],{"class":835},[383,2750,2608],{"class":396},[383,2752,2485],{"class":835},[383,2754,2585],{"class":404},[383,2756,738],{"class":835},[383,2758,2585],{"class":404},[383,2760,2570],{"class":835},[383,2762,2763],{"class":385,"line":714},[383,2764,2535],{"class":835},[2406,2766,2767],{"v-slot:hints":91},[348,2768,2769,2774,2779],{},[351,2770,2771,2773],{},[21,2772,2669],{}," должен иметь приоритет: это bind mount.",[351,2775,2776,2777,24],{},"Если данные должны переживать пересоздание контейнера, выбирай ",[21,2778,2616],{},[351,2780,2781,2782,24],{},"Временное хранилище без persistence — ",[21,2783,1433],{},[2785,2786,2787],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}",{"title":91,"searchDepth":393,"depth":393,"links":2789},[2790,2791,2796,2801,2804,2809,2815,2820,2827,2833,2834,2835,2840],{"id":31,"depth":393,"text":32},{"id":65,"depth":393,"text":66,"children":2792},[2793,2794,2795],{"id":73,"depth":644,"text":74},{"id":97,"depth":644,"text":98},{"id":114,"depth":644,"text":115},{"id":224,"depth":393,"text":225,"children":2797},[2798,2799,2800],{"id":231,"depth":644,"text":232},{"id":339,"depth":644,"text":340},{"id":420,"depth":644,"text":421},{"id":440,"depth":393,"text":441,"children":2802},[2803],{"id":476,"depth":644,"text":477},{"id":561,"depth":393,"text":562,"children":2805},[2806,2807,2808],{"id":592,"depth":644,"text":593},{"id":614,"depth":644,"text":615},{"id":730,"depth":644,"text":731},{"id":766,"depth":393,"text":767,"children":2810},[2811,2812,2813,2814],{"id":787,"depth":644,"text":788},{"id":804,"depth":644,"text":805},{"id":910,"depth":644,"text":911},{"id":976,"depth":644,"text":977},{"id":992,"depth":393,"text":993,"children":2816},[2817,2818,2819],{"id":1003,"depth":644,"text":1004},{"id":1031,"depth":644,"text":1032},{"id":1048,"depth":644,"text":1049},{"id":1265,"depth":393,"text":1266,"children":2821},[2822,2823,2824,2825,2826],{"id":1275,"depth":644,"text":1276},{"id":1337,"depth":644,"text":1338},{"id":1433,"depth":644,"text":1433},{"id":1461,"depth":644,"text":1462},{"id":1523,"depth":644,"text":1524},{"id":1647,"depth":393,"text":1648,"children":2828},[2829,2830,2831,2832],{"id":1654,"depth":644,"text":1655},{"id":1777,"depth":644,"text":98},{"id":1618,"depth":644,"text":1977},{"id":2073,"depth":644,"text":2074},{"id":2137,"depth":393,"text":2138},{"id":2206,"depth":393,"text":2207},{"id":2271,"depth":393,"text":2272,"children":2836},[2837,2838,2839],{"id":2275,"depth":644,"text":2276},{"id":2317,"depth":644,"text":2318},{"id":2354,"depth":644,"text":2355},{"id":2394,"depth":393,"text":2395},1781022065107]