[{"data":1,"prerenderedAt":514},["ShallowReactive",2],{"mdc--j2rbbi-key":3,"mdc-6krd8f-key":25,"mdc--hlb0w6-key":128,"mdc--m4c7mn-key":158,"mdc--w7pbyj-key":170,"mdc-yocbf2-key":235,"mdc-xnhr6j-key":364,"mdc--5uqnau-key":410,"mdc-8msvk7-key":420,"mdc-7yd1a8-key":497},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8],{"type":9,"tag":10,"props":11,"children":12},"element","p",{},[13,16,23],{"type":14,"value":15},"text","The ",{"type":9,"tag":17,"props":18,"children":20},"code",{"className":19},[],[21],{"type":14,"value":22},"docker",{"type":14,"value":24}," command is a client. Everything it appears to do is done by a chain of programs behind it, and in production you eventually meet all of them — usually because one has gone wrong and the CLI is reporting it politely.",{"data":26,"body":27},{},{"type":6,"children":28},[29,36,48],{"type":9,"tag":30,"props":31,"children":33},"h2",{"id":32},"the-chain",[34],{"type":14,"value":35},"The chain",{"type":9,"tag":37,"props":38,"children":42},"pre",{"className":39,"code":41,"language":14},[40],"language-text","docker (CLI)\n   │  REST over /var/run/docker.sock\ndockerd (the daemon)\n   │  gRPC\ncontainerd\n   │  one shim per container\ncontainerd-shim-runc-v2\n   │  fork/exec\nrunc  ──►  clone(), setns(), cgroups, pivot_root  ──►  your process\n",[43],{"type":9,"tag":17,"props":44,"children":46},{"__ignoreMap":45},"",[47],{"type":14,"value":41},{"type":9,"tag":49,"props":50,"children":51},"ul",{},[52,68,82,106],{"type":9,"tag":53,"props":54,"children":55},"li",{},[56,66],{"type":9,"tag":57,"props":58,"children":59},"strong",{},[60],{"type":9,"tag":17,"props":61,"children":63},{"className":62},[],[64],{"type":14,"value":65},"dockerd",{"type":14,"value":67}," owns the Docker-shaped concepts: images with tags, networks, volumes, the build system, the API.",{"type":9,"tag":53,"props":69,"children":70},{},[71,80],{"type":9,"tag":57,"props":72,"children":73},{},[74],{"type":9,"tag":17,"props":75,"children":77},{"className":76},[],[78],{"type":14,"value":79},"containerd",{"type":14,"value":81}," owns container lifecycle and image distribution. It is a CNCF project used directly by Kubernetes, with Docker as one client among several.",{"type":9,"tag":53,"props":83,"children":84},{},[85,90,92,104],{"type":9,"tag":57,"props":86,"children":87},{},[88],{"type":14,"value":89},"The shim",{"type":14,"value":91}," is one process per container, and it is the piece that matters operationally: it holds the container's stdio and exit status, and it is ",{"type":9,"tag":57,"props":93,"children":94},{},[95,97,102],{"type":14,"value":96},"the reason you can restart ",{"type":9,"tag":17,"props":98,"children":100},{"className":99},[],[101],{"type":14,"value":65},{"type":14,"value":103}," without killing every running container",{"type":14,"value":105},".",{"type":9,"tag":53,"props":107,"children":108},{},[109,118,120,126],{"type":9,"tag":57,"props":110,"children":111},{},[112],{"type":9,"tag":17,"props":113,"children":115},{"className":114},[],[116],{"type":14,"value":117},"runc",{"type":14,"value":119}," does the actual work — namespaces, cgroups, ",{"type":9,"tag":17,"props":121,"children":123},{"className":122},[],[124],{"type":14,"value":125},"pivot_root",{"type":14,"value":127},", capabilities — then execs your process and exits. It is a short-lived program, not a supervisor.",{"data":129,"body":130},{},{"type":6,"children":131},[132,137],{"type":9,"tag":133,"props":134,"children":136},"terminal-teaser",{":lines":135},"[{\"cmd\":\"docker info --format '{{.ServerVersion}} / containerd {{.ContainerdCommit.ID}}'\",\"out\":\"29.7.2 / containerd 2.2.2\"},{\"cmd\":\"ps -ef | grep -c '[c]ontainerd-shim'\",\"out\":\"7\"},{\"cmd\":\"docker inspect -f '{{.State.Pid}}' api\",\"out\":\"48213\"},{\"cmd\":\"sudo readlink /proc/48213/ns/pid\",\"out\":\"pid:[4026533117]\"}]",[],{"type":9,"tag":10,"props":138,"children":139},{},[140,142,148,150,156],{"type":14,"value":141},"That last pair is the whole trick from ",{"type":9,"tag":143,"props":144,"children":145},"em",{},[146],{"type":14,"value":147},"Containers From Scratch",{"type":14,"value":149},": a container is a host process with different namespaces attached. ",{"type":9,"tag":17,"props":151,"children":153},{"className":152},[],[154],{"type":14,"value":155},"/proc/\u003Cpid>/",{"type":14,"value":157}," answers every question about it directly, without going through Docker at all — which is exactly what you want when Docker is the thing misbehaving.",{"data":159,"body":160},{},{"type":6,"children":161},[162],{"type":9,"tag":163,"props":164,"children":169},"quiz",{":answer":165,":options":166,"explanation":167,"question":168},"0","[\"Each container is held by its own containerd shim, which is not a child of dockerd\",\"dockerd checkpoints containers to disk before restarting\",\"They don't — a daemon restart stops all containers\"]","The shim owns the container's stdio and reaps its exit code, and it stays alive across a daemon restart. This is what makes `systemctl restart docker` survivable on a production host, and it is why `live-restore` exists as a daemon option to make it explicit.","Why do running containers survive a `dockerd` restart?",[],{"data":171,"body":172},{},{"type":6,"children":173},[174,180,185,223],{"type":9,"tag":30,"props":175,"children":177},{"id":176},"the-specifications-underneath",[178],{"type":14,"value":179},"The specifications underneath",{"type":9,"tag":10,"props":181,"children":182},{},[183],{"type":14,"value":184},"Two OCI specifications separate what Docker is from what a container is:",{"type":9,"tag":49,"props":186,"children":187},{},[188,198],{"type":9,"tag":53,"props":189,"children":190},{},[191,196],{"type":9,"tag":57,"props":192,"children":193},{},[194],{"type":14,"value":195},"The image spec",{"type":14,"value":197}," — a manifest, a config, and layers as content-addressed tarballs. Why an image built by Docker runs under Podman, containerd, or a Kubernetes node with no Docker on it.",{"type":9,"tag":53,"props":199,"children":200},{},[201,206,208,214,216,221],{"type":9,"tag":57,"props":202,"children":203},{},[204],{"type":14,"value":205},"The runtime spec",{"type":14,"value":207}," — the ",{"type":9,"tag":17,"props":209,"children":211},{"className":210},[],[212],{"type":14,"value":213},"config.json",{"type":14,"value":215}," describing namespaces, mounts, cgroup limits, capabilities, and a seccomp profile. ",{"type":9,"tag":17,"props":217,"children":219},{"className":218},[],[220],{"type":14,"value":117},{"type":14,"value":222}," consumes exactly this.",{"type":9,"tag":10,"props":224,"children":225},{},[226,228,233],{"type":14,"value":227},"Reading a real ",{"type":9,"tag":17,"props":229,"children":231},{"className":230},[],[232],{"type":14,"value":213},{"type":14,"value":234}," is the fastest way to see everything Docker configures on your behalf, and every field maps onto something in this course.",{"data":236,"body":237},{},{"type":6,"children":238},[239],{"type":9,"tag":240,"props":241,"children":243},"deep-dive",{"title":242},"The containerd image store, and looking underneath",[244,256,275,303,319,329,338,359],{"type":9,"tag":10,"props":245,"children":246},{},[247,249,254],{"type":14,"value":248},"Engine 29.0 made the ",{"type":9,"tag":57,"props":250,"children":251},{},[252],{"type":14,"value":253},"containerd image store the default on fresh installations",{"type":14,"value":255},", replacing the graph drivers Docker had used for a decade.",{"type":9,"tag":10,"props":257,"children":258},{},[259,261,266,268,273],{"type":14,"value":260},"What it buys, all of which this course depends on: ",{"type":9,"tag":57,"props":262,"children":263},{},[264],{"type":14,"value":265},"full multi-platform images held locally",{"type":14,"value":267},", ",{"type":9,"tag":57,"props":269,"children":270},{},[271],{"type":14,"value":272},"attestations and SBOMs stored as first-class objects",{"type":14,"value":274}," rather than dropped, and lazy-pulling snapshotters becoming possible.",{"type":9,"tag":10,"props":276,"children":277},{},[278,280,286,288,294,296,301],{"type":14,"value":279},"Two caveats. Existing installations that upgraded keep the old store — the change applies to fresh installs, and ",{"type":9,"tag":17,"props":281,"children":283},{"className":282},[],[284],{"type":14,"value":285},"docker info",{"type":14,"value":287}," tells you which you have. And daemons using ",{"type":9,"tag":17,"props":289,"children":291},{"className":290},[],[292],{"type":14,"value":293},"userns-remap",{"type":14,"value":295}," do ",{"type":9,"tag":57,"props":297,"children":298},{},[299],{"type":14,"value":300},"not",{"type":14,"value":302}," get it, because of an unresolved interaction between the two; a security lesson later in this course returns to that trade-off.",{"type":9,"tag":10,"props":304,"children":305},{},[306,308,317],{"type":14,"value":307},"Engine 29.7 also added an experimental ",{"type":9,"tag":57,"props":309,"children":310},{},[311],{"type":9,"tag":17,"props":312,"children":314},{"className":313},[],[315],{"type":14,"value":316},"embedded-containerd",{"type":14,"value":318}," mode, running containerd inside the daemon process rather than as a separate managed one. Worth knowing it exists; not worth adopting yet.",{"type":9,"tag":10,"props":320,"children":321},{},[322,327],{"type":9,"tag":57,"props":323,"children":324},{},[325],{"type":14,"value":326},"Looking directly at containerd",{"type":14,"value":328},", which is occasionally the only way to see what is happening:",{"type":9,"tag":37,"props":330,"children":333},{"className":331,"code":332,"language":14},[40],"sudo ctr --namespace moby containers list\nsudo ctr --namespace moby tasks list\n",[334],{"type":9,"tag":17,"props":335,"children":336},{"__ignoreMap":45},[337],{"type":14,"value":332},{"type":9,"tag":10,"props":339,"children":340},{},[341,343,349,351,357],{"type":14,"value":342},"Docker's containers live in containerd's ",{"type":9,"tag":17,"props":344,"children":346},{"className":345},[],[347],{"type":14,"value":348},"moby",{"type":14,"value":350}," namespace — a containerd namespace, unrelated to a kernel namespace, and an unfortunate collision of vocabulary. ",{"type":9,"tag":17,"props":352,"children":354},{"className":353},[],[355],{"type":14,"value":356},"nerdctl",{"type":14,"value":358}," is a more pleasant, Docker-compatible CLI over the same thing.",{"type":9,"tag":10,"props":360,"children":361},{},[362],{"type":14,"value":363},"Reach for these when the Docker API is unresponsive but containers are plainly still serving traffic. It happens, and it is survivable precisely because of the shim.",{"data":365,"body":366},{},{"type":6,"children":367},[368,374,383],{"type":9,"tag":30,"props":369,"children":371},{"id":370},"where-things-are-on-disk",[372],{"type":14,"value":373},"Where things are on disk",{"type":9,"tag":37,"props":375,"children":378},{"className":376,"code":377,"language":14},[40],"/var/lib/docker/            # images, containers, volumes, build cache\n/var/lib/containerd/        # containerd's own state\n/etc/docker/daemon.json     # daemon configuration\n/var/run/docker.sock        # the API socket\n",[379],{"type":9,"tag":17,"props":380,"children":381},{"__ignoreMap":45},[382],{"type":14,"value":377},{"type":9,"tag":10,"props":384,"children":385},{},[386,388,394,396,401,403,408],{"type":14,"value":387},"Two rules about ",{"type":9,"tag":17,"props":389,"children":391},{"className":390},[],[392],{"type":14,"value":393},"/var/lib/docker",{"type":14,"value":395},". ",{"type":9,"tag":57,"props":397,"children":398},{},[399],{"type":14,"value":400},"Do not modify it by hand",{"type":14,"value":402}," — the daemon holds state in memory and will not agree with your edits. And ",{"type":9,"tag":57,"props":404,"children":405},{},[406],{"type":14,"value":407},"give it its own filesystem",{"type":14,"value":409}," on any real host: a full root filesystem takes the whole machine down, where a full Docker filesystem only stops new builds.",{"data":411,"body":412},{},{"type":6,"children":413},[414],{"type":9,"tag":163,"props":415,"children":419},{":answer":165,":options":416,"explanation":417,"question":418},"[\"The API can start a privileged container mounting the host filesystem, so anyone who can reach it can take the machine\",\"The socket is owned by root and readable only by root\",\"It exposes the host's password database\"]","One API call runs `-v /:/host --privileged`, and now you have the host. This is why adding a user to the `docker` group is granting root, why mounting the socket into a container is a serious decision, and why rootless mode exists.","Why is access to `/var/run/docker.sock` equivalent to root on the host?",[],{"data":421,"body":422},{},{"type":6,"children":423},[424,430,441,452],{"type":9,"tag":30,"props":425,"children":427},{"id":426},"configuring-the-daemon",[428],{"type":14,"value":429},"Configuring the daemon",{"type":9,"tag":10,"props":431,"children":432},{},[433,439],{"type":9,"tag":17,"props":434,"children":436},{"className":435},[],[437],{"type":14,"value":438},"/etc/docker/daemon.json",{"type":14,"value":440},", applied on daemon restart:",{"type":9,"tag":37,"props":442,"children":447},{"className":443,"code":445,"language":446,"meta":45},[444],"language-json","{\n  \"log-driver\": \"local\",\n  \"log-opts\": { \"max-size\": \"10m\", \"max-file\": \"3\" },\n  \"live-restore\": true,\n  \"default-ulimits\": { \"nofile\": { \"Soft\": 65536, \"Hard\": 65536 } },\n  \"default-address-pools\": [\n    { \"base\": \"10.201.0.0/16\", \"size\": 24 }\n  ]\n}\n","json",[448],{"type":9,"tag":17,"props":449,"children":450},{"__ignoreMap":45},[451],{"type":14,"value":445},{"type":9,"tag":10,"props":453,"children":454},{},[455,457,462,464,473,475,480,482,487,489,495],{"type":14,"value":456},"Every one of those is a production lesson someone learned the hard way. ",{"type":9,"tag":57,"props":458,"children":459},{},[460],{"type":14,"value":461},"Log rotation",{"type":14,"value":463},", because the default is unbounded. ",{"type":9,"tag":57,"props":465,"children":466},{},[467],{"type":9,"tag":17,"props":468,"children":470},{"className":469},[],[471],{"type":14,"value":472},"live-restore",{"type":14,"value":474},", so containers survive a daemon restart explicitly. ",{"type":9,"tag":57,"props":476,"children":477},{},[478],{"type":14,"value":479},"File descriptor limits",{"type":14,"value":481},", because the default is low for a busy server. And ",{"type":9,"tag":57,"props":483,"children":484},{},[485],{"type":14,"value":486},"address pools",{"type":14,"value":488},", because Docker's default ",{"type":9,"tag":17,"props":490,"children":492},{"className":491},[],[493],{"type":14,"value":494},"172.17.0.0/16",{"type":14,"value":496}," collides with corporate VPN ranges more often than seems statistically reasonable, and the symptom is one subnet of the office becoming unreachable from containers.",{"data":498,"body":499},{},{"type":6,"children":500},[501,509],{"type":9,"tag":502,"props":503,"children":508},"fill-blank",{":answer":504,"hint":505,"placeholder":506,"prompt":507},"[\"docker inspect -f '{{.State.Pid}}' api\",\"docker inspect --format '{{.State.Pid}}' api\",\"docker inspect -f \\\"{{.State.Pid}}\\\" api\"]","Inspect with a format string reaching into the container's State.","docker inspect ...","Print the host PID of the main process in the container named `api`.",[],{"type":9,"tag":10,"props":510,"children":511},{},[512],{"type":14,"value":513},"Next up: security — dropping the privileges a container gets by default and does not need.",1787908868415]