github.com/lalkh/containerd@v1.4.3/docs/content-flow.md (about) 1 # Content Flow 2 3 A major goal of containerd is to create a system wherein content can be used for executing containers. 4 In order to execute on that flow, containerd requires content and to manage it. 5 6 This document describes how content flows into containerd, how it is managed, and where it exists 7 at each stage in the process. We use an example of going from a known image 8 [docker.io/library/redis:5.0.9](https://hub.docker.com/layers/redis/library/redis/5.0.9/images/sha256-4ff8940144391ecd5e1632d0c427d95f4a8d2bb4a72b7e3898733352350d9ab3?context=explore) to explore the 9 flow of content. 10 11 ## Content Areas 12 13 Content exists in several areas in the containerd lifecycle: 14 15 * OCI registry, for example [hub.docker.com](https://hub.docker.com) or [quay.io](https://quay.io) 16 * containerd content store, under containerd's local storage space, for example, on a standard Linux installation at `/var/lib/containerd/io.containerd.content.v1.content` 17 * snapshots, under containerd's local storage space, for example, on a standard Linux installation at `/var/lib/containerd/io.containerd.snapshotter.v1.<type>`. For an overlayfs snapshotter, that would be at `/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs` 18 19 In order to create a container, the following must occur: 20 21 1. The image and all its content must be loaded into the content store. This normally happens via download from the OCI registry, but you can load content in directly as well. 22 1. Committed snapshots must be created from each layer of content for the image. 23 1. An active snapshot must be created on top of the final layer of content for the image. 24 25 A container now can be created, with its root filesystem as the active snapshot. 26 27 The rest of this document looks at the content in each area in detail, and how they relate to one another. 28 29 ### Image Format 30 31 Images in a registry normally are stored in the following format. An "image" is comprised of a JSON document 32 known as a descriptor. A descriptor always contains an element, `mediaType`, which tells us which type it is. It is one of two options: 33 34 * a "manifest", which lists the hashes of the config file for running the image as a container, and the binary data layers that create the filesystem for the image 35 * an "index", which lists the hashes of manifests, one per platform, where a platform is a combination of architecture (e.g. amd64 or arm64) and operating system (e.g. linux) 36 37 The purpose of an index is to allow us to pick which manifest matches our target platform. 38 39 To convert an image reference, such as `redis:5.0.9`, from a registry to actual on-disk storage, we: 40 41 1. Retrieve the descriptor (JSON document) for the image 42 1. Determine from the `mediaType` if the descriptor is a manifest or an index: 43 * If the descriptor is an index, find in it the platform (architecture+os) that represents the platform on which we want to run the container, use that hash to retrieve the manifest 44 * If the descriptor already is a manifest, continue 45 1. For each element in the manifest - the config and one or more layers - use the hash listed to retrieve the components and save them 46 47 We use our example image, `redis:5.0.9`, to clarify the process. 48 49 When we first resolve `redis:5.0.9`, we get the following JSON document: 50 51 ```json 52 { 53 "manifests": [ 54 { 55 "digest": "sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a", 56 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 57 "platform": { 58 "architecture": "amd64", 59 "os": "linux" 60 }, 61 "size": 1572 62 }, 63 { 64 "digest": "sha256:4ff8940144391ecd5e1632d0c427d95f4a8d2bb4a72b7e3898733352350d9ab3", 65 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 66 "platform": { 67 "architecture": "arm", 68 "os": "linux", 69 "variant": "v5" 70 }, 71 "size": 1573 72 }, 73 { 74 "digest": "sha256:ce541c3e2570b5a05d40e7fc01f87fc1222a701c81f95e7e6f2ef6df1c6e25e7", 75 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 76 "platform": { 77 "architecture": "arm", 78 "os": "linux", 79 "variant": "v7" 80 }, 81 "size": 1573 82 }, 83 { 84 "digest": "sha256:535ee258100feeeb525d4793c16c7e58147c105231d7d05ffc9c84b56750f233", 85 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 86 "platform": { 87 "architecture": "arm64", 88 "os": "linux", 89 "variant": "v8" 90 }, 91 "size": 1573 92 }, 93 { 94 "digest": "sha256:0f3b047f2789547c58634ce88d71c7856999b2afc8b859b7adb5657043984b26", 95 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 96 "platform": { 97 "architecture": "386", 98 "os": "linux" 99 }, 100 "size": 1572 101 }, 102 { 103 "digest": "sha256:bfc45f499a9393aef091057f3d067ff7129ae9fb30d9f31054bafe96ca30b8d6", 104 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 105 "platform": { 106 "architecture": "mips64le", 107 "os": "linux" 108 }, 109 "size": 1572 110 }, 111 { 112 "digest": "sha256:3198e1f1707d977939154a57918d360a172c575bddeac875cb26ca6f4d30dc1c", 113 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 114 "platform": { 115 "architecture": "ppc64le", 116 "os": "linux" 117 }, 118 "size": 1573 119 }, 120 { 121 "digest": "sha256:24a15cc9366e1557db079a987e63b98a5abf4dee4356a096442f53ddc8b9c7e9", 122 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 123 "platform": { 124 "architecture": "s390x", 125 "os": "linux" 126 }, 127 "size": 1573 128 } 129 ], 130 "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", 131 "schemaVersion": 2 132 } 133 ``` 134 135 The descriptor above, towards the end, shows that the `mediaType` is a "manifest.list", or in OCI parlance, an index. 136 It has an array field called `manifests`, each element of which lists one platform and the hash of the manifest for that platform. 137 The "platform" is a combination of "architecture" and "os". Since we will be running on the common 138 linux on amd64, we look for an entry in `manifests` that has a `platform` entry as follows: 139 140 ```json 141 "platform": { 142 "architecture": "amd64", 143 "os": "linux" 144 } 145 ``` 146 147 This is the first one in the list, and it has the hash of `sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a`. 148 149 We then retrieve the item with that hash, specifically `docker.io/library/redis@sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a` 150 This gives us the manifest for the image on linux/amd64: 151 152 ```json 153 { 154 "schemaVersion": 2, 155 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 156 "config": { 157 "mediaType": "application/vnd.docker.container.image.v1+json", 158 "size": 6836, 159 "digest": "sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6" 160 }, 161 "layers": [ 162 { 163 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 164 "size": 27098147, 165 "digest": "sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b" 166 }, 167 { 168 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 169 "size": 1730, 170 "digest": "sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774" 171 }, 172 { 173 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 174 "size": 1417708, 175 "digest": "sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac" 176 }, 177 { 178 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 179 "size": 7345094, 180 "digest": "sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7" 181 }, 182 { 183 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 184 "size": 99, 185 "digest": "sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6" 186 }, 187 { 188 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 189 "size": 410, 190 "digest": "sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b" 191 } 192 ] 193 } 194 ``` 195 196 The `mediaType` tell us that this is a "manifest", and it fits the correct format: 197 198 * one `config`, whose hash is `sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6` 199 * one or more `layers`; in this example, there are 6 layers 200 201 Each of these elements - the index, the manifests, the config file and each of the layers - is stored 202 separately in the registry, and is downloaded independently. 203 204 ### Content Store 205 206 When content is loaded into containerd's content store, it stores them very similarly to how the registry does. 207 Each component is stored in a file whose name is the hash of it. 208 209 Continuing our redis example, if we do `client.Pull()` or `ctr pull`, we will get the following in our 210 content store: 211 212 * `sha256:1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728` - the index 213 * `sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a` - the manifest for `linux/amd64` 214 * `sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6` - the config 215 * `sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b` - layer 0 216 * `sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774` - layer 1 217 * `sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac` - layer 2 218 * `sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7` - layer 3 219 * `sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6` - layer 4 220 * `sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b` - layer 5 221 222 If we look in our content store, we see exactly these (I filtered and sorted to make it easier to read): 223 224 ```console 225 $ tree /var/lib/containerd/io.containerd.content.v1.content/blobs 226 /var/lib/containerd/io.containerd.content.v1.content/blobs 227 └── sha256 228 ├── 1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728 229 ├── a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a 230 ├── df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6 231 ├── 123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b 232 ├── f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774 233 ├── 66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac 234 ├── 79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7 235 ├── de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6 236 └── 602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b 237 ``` 238 239 We can see the same thing if we use the containerd interface. Again, we sorted it for consistent easier viewing. 240 241 ```console 242 ctr content ls 243 DIGEST SIZE AGE LABELS 244 sha256:1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728 1.862 kB 6 minutes containerd.io/gc.ref.content.0=sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a,containerd.io/gc.ref.content.1=sha256:4ff8940144391ecd5e1632d0c427d95f4a8d2bb4a72b7e3898733352350d9ab3,containerd.io/gc.ref.content.2=sha256:ce541c3e2570b5a05d40e7fc01f87fc1222a701c81f95e7e6f2ef6df1c6e25e7,containerd.io/gc.ref.content.3=sha256:535ee258100feeeb525d4793c16c7e58147c105231d7d05ffc9c84b56750f233,containerd.io/gc.ref.content.4=sha256:0f3b047f2789547c58634ce88d71c7856999b2afc8b859b7adb5657043984b26,containerd.io/gc.ref.content.5=sha256:bfc45f499a9393aef091057f3d067ff7129ae9fb30d9f31054bafe96ca30b8d6,containerd.io/gc.ref.content.6=sha256:3198e1f1707d977939154a57918d360a172c575bddeac875cb26ca6f4d30dc1c,containerd.io/gc.ref.content.7=sha256:24a15cc9366e1557db079a987e63b98a5abf4dee4356a096442f53ddc8b9c7e9 245 sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a 1.572 kB 6 minutes containerd.io/gc.ref.content.2=sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774,containerd.io/gc.ref.content.3=sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac,containerd.io/gc.ref.content.4=sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7,containerd.io/gc.ref.content.5=sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6,containerd.io/gc.ref.content.6=sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b,containerd.io/gc.ref.content.0=sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6,containerd.io/gc.ref.content.1=sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b 246 sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6 6.836 kB 6 minutes containerd.io/gc.ref.snapshot.overlayfs=sha256:87806a591ce894ff5c699c28fe02093d6cdadd6b1ad86819acea05ccb212ff3d 247 sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b 27.1 MB 6 minutes containerd.io/uncompressed=sha256:b60e5c3bcef2f42ec42648b3acf7baf6de1fa780ca16d9180f3b4a3f266fe7bc 248 sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774 1.73 kB 6 minutes containerd.io/uncompressed=sha256:b5a8df342567aa93d568b263b25c1eaf52655f0952e1911742ffb4f7a521e044 249 sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac 1.418 MB 6 minutes containerd.io/uncompressed=sha256:c03c7e9701eb61f1e2232f6d19faa699cd9d346207aaf4f50d84b1e37bbad3e2 250 sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7 7.345 MB 6 minutes containerd.io/uncompressed=sha256:367024e4e00618a9ada3203b5922d3186a0aa6136a1c4cbf5ed380171e1afe48 251 sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6 99 B 6 minutes containerd.io/uncompressed=sha256:60ef3ee42de712ef7748cc8e92192e926180b1be6fec9580933f1347fb6b2747 252 sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b 410 B 6 minutes containerd.io/uncompressed=sha256:bab68e5155b7010010964bf3aadc30e4a9c625701314ff6fa3c143c72f0aeb9c 253 ``` 254 255 #### Labels 256 257 Note that each chunk of content has several labels on it. This sub-section describes the labels. 258 This is not intended to be a comprehensive overview of labels. 259 260 ##### Layer Labels 261 262 We start with the layers themselves. These have only one label: `containerd.io/uncompressed`. These files are 263 gzipped tar files; the value of the label gives the hash of them when uncompressed. You can get the same value 264 by doing: 265 266 ```console 267 $ cat <file> | gunzip - | sha256sum - 268 ``` 269 270 For example: 271 272 ```console 273 $ cat /var/lib/containerd/io.containerd.content.v1.content/blobs/sha256/602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b | gunzip - | sha256sum - 274 bab68e5155b7010010964bf3aadc30e4a9c625701314ff6fa3c143c72f0aeb9c 275 ``` 276 277 That aligns precisely with the last layer: 278 279 ``` 280 sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b 410 B 6 minutes containerd.io/uncompressed=sha256:bab68e5155b7010010964bf3aadc30e4a9c625701314ff6fa3c143c72f0aeb9c 281 ``` 282 283 ##### Config Labels 284 285 We have a single config layer, `sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6`. It has a label prefixed with `containerd.io/gc.ref.` indicating 286 that it is a label that impacts garbage collection. 287 288 In this case, the label is `containerd.io/gc.ref.snapshot.overlayfs` and has a value of `sha256:87806a591ce894ff5c699c28fe02093d6cdadd6b1ad86819acea05ccb212ff3d`. 289 290 This is used to connect this config to a snapshot. We will look at that shortly when we discuss snapshots. 291 292 ##### Manifest Labels 293 294 The labels on the manifest also begin with `containerd.io/gc.ref`, indicating that they are used to control 295 garbage collection. A manifest has several "children". These normally are the config and the layers. We want 296 to ensure that as long as the image remains around, i.e. the manifest, the children do not get garbage collected. 297 Thus, we have labels referencing each child, `containerd.io/gc.ref.content.<index>`. 298 299 In our example, the manifest is `sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a`, and the labels are as follows. 300 301 ``` 302 containerd.io/gc.ref.content.0=sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6 303 containerd.io/gc.ref.content.1=sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b 304 containerd.io/gc.ref.content.2=sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774 305 containerd.io/gc.ref.content.3=sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac 306 containerd.io/gc.ref.content.4=sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7 307 containerd.io/gc.ref.content.5=sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6 308 containerd.io/gc.ref.content.6=sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b 309 ``` 310 311 These are precisely those children of the manifest - the config and layers - that are stored in our content store. 312 313 ##### Index Labels 314 315 The labels on the index also begin with `containerd.io/gc.ref`, indicating that they are used to control 316 garbage collection. An index has several "children", i.e. the manifests, one for each platform, as discussed above. 317 We want to ensure that as long as the index remains around, the children do not get garbage collected. 318 Thus, we have labels referencing each child, `containerd.io/gc.ref.content.<index>`. 319 320 In our example, the index is `sha256:1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728`, and the labels are as follows: 321 322 ``` 323 containerd.io/gc.ref.content.0=sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a 324 containerd.io/gc.ref.content.1=sha256:4ff8940144391ecd5e1632d0c427d95f4a8d2bb4a72b7e3898733352350d9ab3 325 containerd.io/gc.ref.content.2=sha256:ce541c3e2570b5a05d40e7fc01f87fc1222a701c81f95e7e6f2ef6df1c6e25e7 326 containerd.io/gc.ref.content.3=sha256:535ee258100feeeb525d4793c16c7e58147c105231d7d05ffc9c84b56750f233 327 containerd.io/gc.ref.content.4=sha256:0f3b047f2789547c58634ce88d71c7856999b2afc8b859b7adb5657043984b26 328 containerd.io/gc.ref.content.5=sha256:bfc45f499a9393aef091057f3d067ff7129ae9fb30d9f31054bafe96ca30b8d6 329 containerd.io/gc.ref.content.6=sha256:3198e1f1707d977939154a57918d360a172c575bddeac875cb26ca6f4d30dc1c 330 containerd.io/gc.ref.content.7=sha256:24a15cc9366e1557db079a987e63b98a5abf4dee4356a096442f53ddc8b9c7e9 331 ``` 332 333 Notice that there are 8 children to the index, but all of them are for platforms other than ours, `linux/amd64`, 334 and thus only one of them, `sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a` actually is 335 in our content store. That doesn't hurt; it just means that the others will not be garbage collected either. Since 336 they aren't there, they won't be removed. 337 338 ### Snapshots 339 340 The content in the content store is immutable, but also in formats that often are unusable. For example, 341 most container layers are in a tar-gzip format. One cannot simply mount a tar-gzip file. Even if one could, 342 we want to leave our immutable content not only unchanged, but unchangeable, even by accident, i.e. immutable. 343 344 In order to use it, we create snapshots of the content. 345 346 The process is as follows: 347 348 1. The snapshotter creates a snapshot from the parent. In the case of the first layer, that is blank. This is now an "active" snapshot. 349 1. The diff applier, which has knowledge of the internal format of the layer blob, applies the layer blob to the active snapshot. 350 1. The snapshotter commits the snapshot after the diff has been applied. This is now a "committed" snapshot. 351 1. The committed snapshot is used as the parent for the next layer. 352 353 Returning to our example, each layer will have a corresponding immutable snapshot layer. Recalling that 354 our example has 6 layers, we expect to see 6 committed snapshots. The output has been sorted to make viewing 355 easier; it matches the layers from the content store and manifest itself. 356 357 ``` 358 # ctr snapshot ls 359 KEY PARENT KIND 360 sha256:b60e5c3bcef2f42ec42648b3acf7baf6de1fa780ca16d9180f3b4a3f266fe7bc Committed 361 sha256:c2cba74b5b43db78068241279a3225ca4f9639c17a5f0ce019489ee71b4382a5 sha256:b60e5c3bcef2f42ec42648b3acf7baf6de1fa780ca16d9180f3b4a3f266fe7bc Committed 362 sha256:315768cd0d297e3cb707360f8dde646419940b42e055845a160880cf98b5a242 sha256:c2cba74b5b43db78068241279a3225ca4f9639c17a5f0ce019489ee71b4382a5 Committed 363 sha256:13aa829f25ce405c1c5f40e0449b9270ce162ac7e4c2a81359df6fe09f939afd sha256:315768cd0d297e3cb707360f8dde646419940b42e055845a160880cf98b5a242 Committed 364 sha256:814ff1c8753c9cd3942089a2401f1806a1133f27b6875bcad7b7e68846e205e4 sha256:13aa829f25ce405c1c5f40e0449b9270ce162ac7e4c2a81359df6fe09f939afd Committed 365 sha256:87806a591ce894ff5c699c28fe02093d6cdadd6b1ad86819acea05ccb212ff3d sha256:814ff1c8753c9cd3942089a2401f1806a1133f27b6875bcad7b7e68846e205e4 Committed 366 ``` 367 368 #### Parents 369 370 Each snapshot has a parent, except for the root. It is a tree, or a stacked cake, starting with the first layer. 371 This matches how the layers are built, as layers. 372 373 #### Name 374 375 The key, or name, for the snapshot does not match the hash from the content store. This is because the hash from the 376 content store is the hash of the _original_ content, in this case tar-gzipped. The snapshot expands it out into the 377 filesystem to make it useful. It also does not match the uncompressed content, i.e. the tar file without gzip, and as 378 given on the label `containerd.io/uncompressed`. 379 380 Rather the name is the result of applying the layer to the previous one and hashing it. By that logic, the very root 381 of the tree, the first layer, should have the same hash and name as the uncompressed value of the first layer blob. 382 Indeed, it does. The root layer is `sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b` 383 which, when uncompressed, has the value `sha256:b60e5c3bcef2f42ec42648b3acf7baf6de1fa780ca16d9180f3b4a3f266fe7bc`, 384 which is the first layer in the snapshot, and also the label on that layer in the content store: 385 386 ``` 387 sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b 27.1 MB 6 minutes containerd.io/uncompressed=sha256:b60e5c3bcef2f42ec42648b3acf7baf6de1fa780ca16d9180f3b4a3f266fe7bc 388 ``` 389 390 #### Final Layer 391 392 The final, or top, layer, is the point at which you would want to create an active snapshot to start a container. 393 Thus, we would need to track it. This is exactly the label that is placed on the config. In our example, the 394 config is at `sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6` and had the label 395 `containerd.io/gc.ref.snapshot.overlayfs=sha256:87806a591ce894ff5c699c28fe02093d6cdadd6b1ad86819acea05ccb212ff3d`. 396 397 Looking at our snapshots, the value of the final layer of the stack is, indeed, that: 398 399 ``` 400 sha256:87806a591ce894ff5c699c28fe02093d6cdadd6b1ad86819acea05ccb212ff3d sha256:814ff1c8753c9cd3942089a2401f1806a1133f27b6875bcad7b7e68846e205e4 Committed 401 ``` 402 403 Note as well, that the label on the config in the content store starts with `containerd.io/gc.ref`. This is 404 a garbage collection label. It is this label that keeps the garbage collector from removing the snapshot. 405 Because the config has a reference to it, the top layer is "protected" from garbage collection. This layer, 406 in turn, depends on the next layer down, so it is protected from collection, and so on until the root or base layer. 407 408 ### Container 409 410 With the above in place, we know how to create an active snapshot that is useful for the container. We simply 411 need to [https://godoc.org/github.com/containerd/containerd/snapshots#Snapshotter](Prepare()) the active snapshot, 412 passing it an ID and the parent, in this case the top layer of committed snapshots. 413 414 Thus, the steps are: 415 416 1. Get the content into the content store, either via [Pull()](https://godoc.org/github.com/containerd/containerd#Client.Pull), or via loading it in the [content.Store API](https://godoc.org/github.com/containerd/containerd/content#Store) 417 1. Unpack the image to create committed snapshots for each layer, using [image.Unpack()](https://godoc.org/github.com/containerd/containerd#Image). Alternatively, if you use [Pull()](https://godoc.org/github.com/containerd/containerd#Client.Pull), you can pass it an option to unpack when pulling, using [WithPullUnpack()](https://godoc.org/github.com/containerd/containerd#WithPullUnpack) 418 1. Create an active snapshot using [Prepare()](https://godoc.org/github.com/containerd/containerd/snapshots#Snapshotter). You can skip this step if you plan on creating a container, as you can pass it as an option to the next step. 419 1. Create a container using [NewContainer()](https://godoc.org/github.com/containerd/containerd#Client.NewContainer), optionally telling it to create a snapshot with [WithNewSnapshot()](https://godoc.org/github.com/containerd/containerd#WithNewSnapshot)