{"id":9020,"date":"2024-10-21T21:16:08","date_gmt":"2024-10-21T15:46:08","guid":{"rendered":"https:\/\/www.satup.xyz\/index.php\/2024\/10\/21\/inside-a-container-registry-the-mechanics-of-push-and-pull\/"},"modified":"2024-10-21T21:16:08","modified_gmt":"2024-10-21T15:46:08","slug":"inside-a-container-registry-the-mechanics-of-push-and-pull","status":"publish","type":"post","link":"https:\/\/www.satup.xyz\/index.php\/2024\/10\/21\/inside-a-container-registry-the-mechanics-of-push-and-pull\/","title":{"rendered":"Inside a container registry: The mechanics of push and pull"},"content":{"rendered":"<p><br \/>\n<\/p>\n<div>\n<p>If you have packaged your code and deployed it in a cloud or on-prem server, you must have come across containers. That\u2019s how ubiquitous containers have become today. You must have written that infamous Dockerfile, ran Docker build command to create the image and pushed it to a registry. Conversely, you must also have pulled that image (often using shell script or k8s manifests) and ran your image as a container. But have you ever thought about how the image is actually pushed or pulled from a registry? We are going to talk just about that in this blog. From here on out, \u201cclients\u201d will be used to refer to the cli tools which can pull images \u2013 like Docker, nerdctl, ctr etc. \u2013 and \u201cregistry\u201d to refer to the backend which serves these images. Also, we will take an example curlimages\/curl:8.9.1 image stored in DigitalOcean Container Registry. We will use nerdctl as the client to get a better understanding of the image internals.<\/p>\n<h3 id=\"use-a-linux-vm-_optional-_\"><a href=\"#use-a-linux-vm-_optional-_\" onclick=\"navigator.clipboard.writeText(this.href);\">Use a Linux VM (<em>Optional)<\/em><\/a><a class=\"hash-anchor\" href=\"#use-a-linux-vm-_optional-_\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>I have used an Ubuntu (24.04) AMD64 DigitalOcean Droplet for this blog. You can <a href=\"https:\/\/docs.digitalocean.com\/products\/droplets\/how-to\/create\/\" rel=\"ugc nofollow noopener\" target=\"_blank\">create such a Droplet by following the instructions<\/a> in our documentation. Using a Linux VM is optional and any platform such as Windows and MacOS can be used.<\/p>\n<p>If you have a DigitalOcean Managed Kubernetes cluster, you can use a debug pod on any one of the nodes as:<\/p>\n<pre class=\"language-bash\"><code><span class=\"token operator\">&gt;<\/span> k debug node\/<span class=\"token operator\">any node<span class=\"token operator\">&gt;<\/span> <span class=\"token parameter variable\">-it<\/span> <span class=\"token parameter variable\">--image<\/span><span class=\"token operator\">=<\/span>zmmdv\/nerdctl:1.7.6 -- \/bin\/bash\n<span class=\"token operator\">&gt;<\/span> <span class=\"token builtin class-name\">alias<\/span> <span class=\"token assign-left variable\">nerdctl<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">\"nerdctl -a \/host\/run\/containerd\/containerd.sock\"<\/span>\n<\/span><\/code><\/pre>\n<p>Also, you would have to use <code>\/host\/var\/lib\/containerd<\/code> in upcoming sections, if you choose this path, as the node\u2019s root filesystem is mounted in <code>\/host<\/code> path of the pod.<\/p>\n<h3 id=\"install-docker-containerd-and-nerdctl\"><a href=\"#install-docker-containerd-and-nerdctl\" onclick=\"navigator.clipboard.writeText(this.href);\">Install Docker\/containerd and nerdctl<\/a><a class=\"hash-anchor\" href=\"#install-docker-containerd-and-nerdctl\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>We can <a href=\"https:\/\/docs.docker.com\/engine\/install\/\" rel=\"ugc nofollow noopener\" target=\"_blank\">install Docker\/containerd by performing the steps<\/a> from Docker\u2019s official install page. For our blog, we can get by with installing just containerd since we will be using nerdctl as the image client. We can do so by <a href=\"https:\/\/docs.docker.com\/engine\/install\/ubuntu\/#install-using-the-repository\" rel=\"ugc nofollow noopener\" target=\"_blank\">adding the Docker\u2019s apt repository<\/a> and <code>do sudo apt-get install containerd.io<\/code>.<\/p>\n<p>Nerdctl can be installed from its official GitHub release page. I have used <a href=\"https:\/\/github.com\/containerd\/nerdctl\/releases\/tag\/v1.7.6\" rel=\"ugc nofollow noopener\" target=\"_blank\">version 1.7.6<\/a>. For this blog, the minimal nerdctl archive should be sufficient, but if you want to run containers, you will require the full nerdctl archive.<\/p>\n<h3 id=\"using-digitalocean-container-registry\"><a href=\"#using-digitalocean-container-registry\" onclick=\"navigator.clipboard.writeText(this.href);\">Using DigitalOcean Container Registry<\/a><a class=\"hash-anchor\" href=\"#using-digitalocean-container-registry\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>You can <a href=\"https:\/\/docs.digitalocean.com\/products\/container-registry\/getting-started\/quickstart\/#create-a-registry\" rel=\"ugc nofollow noopener\" target=\"_blank\">create a container registry in DigitalOcean by following the steps<\/a> or if you already have one you can <a href=\"https:\/\/docs.digitalocean.com\/products\/container-registry\/getting-started\/quickstart\/#push-to-your-registry\" rel=\"ugc nofollow noopener\" target=\"_blank\">follow the steps here<\/a> to push\/pull an image.<\/p>\n<p>It\u2019s important to talk about what an image consists of before finding out how it\u2019s transferred between the client and registry. First let\u2019s try to pull an image using nerdctl:<\/p>\n<pre class=\"language-bash\"><code><span class=\"token operator\">&gt;<\/span> nerdctl image pull <span class=\"token punctuation\">[<\/span>registry.digitalocean.com\/coolreg\/curlimages\/curl:8.9.1<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">(<\/span>http:\/\/registry.digitalocean.com\/coolreg\/curlimages\/curl:8.9.1<span class=\"token punctuation\">)<\/span>\n\nregistry.digitalocean.com\/coolreg\/curlimages\/curl:8.9.1:\nindex-sha256:4d3d08d1019a4b4507f18f5700f13dd7e106ed8214229b878417805094f21376:\n<span class=\"token keyword\">done<\/span>\nmanifest-sha256:d795b5d334f78dc8dbe55ba4332213a937b86ca193f4091e60963517f32340c4:done\nconfig-sha256:5f48a11a4b51dd9f8eddd97396069a65d9c8bd1ba2dbc4dffe98954a5078ad51:   <span class=\"token keyword\">done<\/span>\nlayer-sha256:4ca545ee6d5db5c1170386eeb39b2ffe3bd46e5d4a73a9acbebc805f19607eb3:    <span class=\"token keyword\">done<\/span>\nlayer-sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6:    <span class=\"token keyword\">done<\/span>\nlayer-sha256:4007b551d63a39f6c3235cb5af643f633cf6e0bf5a161465b074eaf60ab43f44:    <span class=\"token keyword\">done<\/span> \n<\/code><\/pre>\n<p>Container images consist of index, manifest, configs and layers as can be seen from the output of above command. These are described in the next sections in a top down manner.<\/p>\n<h3 id=\"index\"><a href=\"#index\" onclick=\"navigator.clipboard.writeText(this.href);\">Index<\/a><a class=\"hash-anchor\" href=\"#index\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>The index is the first component which is fetched from the registry when an image is pulled. It is a JSON file outlining the manifest digests for each platform in the format {os}\/{arch} such as linux\/amd64, linux\/arm64, and linux\/386 etc. For each of the platforms, the image contents would be different and this JSON file describes which manifest belongs to which platform. All the image contents are stored in <code>\/var\/lib\/containerd\/io.containerd.content.v1.content\/blobs<\/code> path (for Docker it is different). We can examine the index JSON file as:<\/p>\n<pre class=\"language-bash\"><code>\n<span class=\"token operator\">&gt;<\/span> <span class=\"token function\">cat<\/span> \/var\/lib\/containerd\/io.containerd.content.v1.content\/blobs\/sha256\/4d3d08d1019a4b4507f18f5700f13dd7e106ed8214229b878417805094f21376 <span class=\"token operator\">|<\/span> jq <span class=\"token string\">'.manifests'<\/span>\n\n<span class=\"token punctuation\">[<\/span>\n  <span class=\"token punctuation\">{<\/span>\n    <span class=\"token string\">\"mediaType\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"application\/vnd.oci.image.manifest.v1+json\"<\/span>,\n    <span class=\"token string\">\"digest\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"sha256:d795b5d334f78dc8dbe55ba4332213a937b86ca193f4091e60963517f32340c4\"<\/span>,\n    <span class=\"token string\">\"size\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token number\">860<\/span>,\n    <span class=\"token string\">\"platform\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token string\">\"architecture\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"amd64\"<\/span>,\n      <span class=\"token string\">\"os\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"linux\"<\/span>\n    <span class=\"token punctuation\">}<\/span>\n  <span class=\"token punctuation\">}<\/span>,\n  <span class=\"token punctuation\">{<\/span>\n    <span class=\"token string\">\"mediaType\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"application\/vnd.oci.image.manifest.v1+json\"<\/span>,\n    <span class=\"token string\">\"digest\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"sha256:7d3aecfe1d39dda61edefcdcead38a4eaa7c292734df00e9381504c0d699cab1\"<\/span>,\n    <span class=\"token string\">\"size\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token number\">860<\/span>,\n    <span class=\"token string\">\"platform\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token string\">\"architecture\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"arm64\"<\/span>,\n      <span class=\"token string\">\"os\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"linux\"<\/span>\n    <span class=\"token punctuation\">}<\/span>\n  <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>I am using a Droplet in DigitalOcean which is linux\/amd64, so the manifest to pull next is <code>sha256:d795b5d334f78dc8dbe55ba4332213a937b86ca193f4091e60963517f32340c4<\/code> which can be seen from the output of the nerdctl pull command before.<\/p>\n<p>Having an index is optional and is applicable only for multi-platform images. It is not used while uploading images for only one platform. In that case, only manifest is used (discussed in next section).<\/p>\n<h3 id=\"manifests\"><a href=\"#manifests\" onclick=\"navigator.clipboard.writeText(this.href);\">Manifests<\/a><a class=\"hash-anchor\" href=\"#manifests\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>Manifests are also JSON files which specify the layers (described next) and config comprising the image. Our manifest contains below contents:<\/p>\n<pre class=\"language-bash\"><code><span class=\"token operator\">&gt;<\/span> <span class=\"token function\">cat<\/span> \/var\/lib\/containerd\/io.containerd.content.v1.content\/blobs\/sha256\/d795b5d334f78dc8dbe55ba4332213a937b86ca193f4091e60963517f32340c4 <span class=\"token operator\">|<\/span> jq <span class=\"token builtin class-name\">.<\/span>\n\n<span class=\"token punctuation\">{<\/span>\n  <span class=\"token string\">\"schemaVersion\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token number\">2<\/span>,\n  <span class=\"token string\">\"mediaType\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"application\/vnd.oci.image.manifest.v1+json\"<\/span>,\n  <span class=\"token string\">\"config\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token string\">\"mediaType\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"application\/vnd.oci.image.config.v1+json\"<\/span>,\n    <span class=\"token string\">\"digest\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"sha256:5f48a11a4b51dd9f8eddd97396069a65d9c8bd1ba2dbc4dffe98954a5078ad51\"<\/span>,\n    <span class=\"token string\">\"size\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token number\">1486<\/span>\n  <span class=\"token punctuation\">}<\/span>,\n  <span class=\"token string\">\"layers\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">[<\/span>\n    <span class=\"token punctuation\">{<\/span>\n      <span class=\"token string\">\"mediaType\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"application\/vnd.oci.image.layer.v1.tar+gzip\"<\/span>,\n      <span class=\"token string\">\"digest\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6\"<\/span>,\n      <span class=\"token string\">\"size\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token number\">3622892<\/span>\n    <span class=\"token punctuation\">}<\/span>,\n    <span class=\"token punctuation\">{<\/span>\n      <span class=\"token string\">\"mediaType\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"application\/vnd.oci.image.layer.v1.tar+gzip\"<\/span>,\n      <span class=\"token string\">\"digest\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"sha256:4007b551d63a39f6c3235cb5af643f633cf6e0bf5a161465b074eaf60ab43f44\"<\/span>,\n      <span class=\"token string\">\"size\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token number\">5758798<\/span>\n    <span class=\"token punctuation\">}<\/span>,\n    <span class=\"token punctuation\">{<\/span>\n      <span class=\"token string\">\"mediaType\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"application\/vnd.oci.image.layer.v1.tar+gzip\"<\/span>,\n      <span class=\"token string\">\"digest\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"sha256:4ca545ee6d5db5c1170386eeb39b2ffe3bd46e5d4a73a9acbebc805f19607eb3\"<\/span>,\n      <span class=\"token string\">\"size\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token number\">42<\/span>\n    <span class=\"token punctuation\">}<\/span>\n  <span class=\"token punctuation\">]<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<h3 id=\"config\"><a href=\"#config\" onclick=\"navigator.clipboard.writeText(this.href);\">Config<\/a><a class=\"hash-anchor\" href=\"#config\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>The config is a JSON file describing how to run the image when we use commands like nerdctl run (or docker run). The <code>CMD, ENTRYPOINT, ENV, USER<\/code> etc directives in a Dockerfile makes its way to this config JSON file:<\/p>\n<pre class=\"language-bash\"><code><span class=\"token operator\">&gt;<\/span>cat \/var\/lib\/containerd\/io.containerd.content.v1.content\/blobs\/sha256\/5f48a11a4b51dd9f8eddd97396069a65d9c8bd1ba2dbc4dffe98954a5078ad51 <span class=\"token operator\">|<\/span> jq <span class=\"token string\">'{config: .config, rootfs: .rootfs}'<\/span>\n\n<span class=\"token punctuation\">{<\/span>\n  <span class=\"token string\">\"config\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token string\">\"User\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"curl_user\"<\/span>,\n    <span class=\"token string\">\"Env\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">[<\/span>\n      <span class=\"token string\">\"PATH=\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin\"<\/span>,\n      <span class=\"token string\">\"CURL_CA_BUNDLE=\/cacert.pem\"<\/span>\n    <span class=\"token punctuation\">]<\/span>,\n    <span class=\"token string\">\"Entrypoint\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">[<\/span>\n      <span class=\"token string\">\"\/entrypoint.sh\"<\/span>\n    <span class=\"token punctuation\">]<\/span>,\n    <span class=\"token string\">\"Cmd\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">[<\/span>\n      <span class=\"token string\">\"curl\"<\/span>\n    <span class=\"token punctuation\">]<\/span>,\n    <span class=\"token string\">\"WorkingDir\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"\/home\/curl_user\"<\/span>,\n    <span class=\"token string\">\"Labels\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token punctuation\">..<\/span>.\n    <span class=\"token punctuation\">}<\/span>,\n    <span class=\"token string\">\"OnBuild\"<\/span><span class=\"token builtin class-name\">:<\/span> null\n  <span class=\"token punctuation\">}<\/span>,\n  <span class=\"token string\">\"rootfs\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token string\">\"type\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token string\">\"layers\"<\/span>,\n    <span class=\"token string\">\"diff_ids\"<\/span><span class=\"token builtin class-name\">:<\/span> <span class=\"token punctuation\">[<\/span>\n     <span class=\"token string\">\"sha256:78561cef0761903dd2f7d09856150a6d4fb48967a8f113f3e33d79effbf59a07\"<\/span>,\n     <span class=\"token string\">\"sha256:b138805d27824c78d3d19b77cf24caf06839c64f54f70439f84c96160fb4d928\"<\/span>,\n     <span class=\"token string\">\"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef\"<\/span>\n    <span class=\"token punctuation\">]<\/span>\n  <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>Here, the layer digests are also somewhat different from what we saw in our manifest before and what the nerdctl client actually pulled. These are actually the uncompressed digest of the layers.<\/p>\n<h3 id=\"layers\"><a href=\"#layers\" onclick=\"navigator.clipboard.writeText(this.href);\">Layers<\/a><a class=\"hash-anchor\" href=\"#layers\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>A layer is a filesystem (or a directory structure in simple terms) containing the binaries and the required dependencies of the image such as system libs etc. in a compressed format (usually tar,gzip). An image might have one or many such layers. All of these layers are combined by the container runtime to create the final filesystem for the runnable container. The layers are referenced by their digests.<\/p>\n<p>Our example image curlimages\/curl:8.9.1 contains 3 layers: <code>c6a83fedfae6<\/code>, <code>4007b551d63a<\/code> and <code>4ca545ee6d5d<\/code>. We said these layers are actually compressed filesystems. Let\u2019s find out:<\/p>\n<pre class=\"language-bash\"><code><span class=\"token operator\">&gt;<\/span> <span class=\"token function\">rm<\/span> <span class=\"token parameter variable\">-rf<\/span> <span class=\"token builtin class-name\">test<\/span> <span class=\"token operator\">&amp;&amp;<\/span> <span class=\"token function\">mkdir<\/span> <span class=\"token builtin class-name\">test<\/span>\n<span class=\"token operator\">&gt;<\/span> <span class=\"token function\">tar<\/span> zxf \/var\/lib\/containerd\/io.containerd.content.v1.content\/blobs\/sha256\/c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6 <span class=\"token parameter variable\">-C<\/span> .\/test\n<span class=\"token operator\">&gt;<\/span> <span class=\"token function\">ls<\/span> .\/test\nbin  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var\n<\/code><\/pre>\n<p>We can see that the layer <code>c6a83fedfae6<\/code> is a compressed generic filesystem of a linux distro (alpine in our case).<\/p>\n<h4 id=\"bonus-content\">Bonus Content:<\/h4>\n<p>Layers are the compressed digests. To get the uncompressed digests we can use <code>ctr content ls<\/code>  and check the <code>containerd.io\/uncompressed<\/code> label for the layer:<\/p>\n<pre class=\"language-bash\"><code><span class=\"token operator\">&gt;<\/span> ctr content <span class=\"token function\">ls<\/span> <span class=\"token operator\">|<\/span> <span class=\"token function\">awk<\/span> <span class=\"token string\">'$1 ~ \/c6a83fedfae6|4007b551d63a|4ca545ee6d5d\/ { print }'<\/span> <span class=\"token operator\">|<\/span> <span class=\"token function\">sed<\/span> <span class=\"token string\">\"s\/^.*containerd.io\\\/uncompressed=\/\/\"<\/span>\n\nsha256:b138805d27824c78d3d19b77cf24caf06839c64f54f70439f84c96160fb4d928\nsha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef\nsha256:78561cef0761903dd2f7d09856150a6d4fb48967a8f113f3e33d79effbf59a07\n<\/code><\/pre>\n<p>This matches with that of <code>rootfs<\/code> entry in the config JSON file.<\/p>\n<p>Pulling an image consists of fetching the tag index digest, index itself, platform manifest, config and the layers in this order. But in the case of a private registry, where does the client get the authorization information (in other words, a token) from? There is a specific sequence to get each of the components of an image mentioned before, and it goes like this, when taking the example of fetching index digest for a tag:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/doimages.nyc3.cdn.digitaloceanspaces.com\/002Blog\/DOCR%20blog%20image1.png\" alt=\"DOCR blog diagram\"\/><\/p>\n<ol>\n<li>Client tries to get the manifest digest without any authorization token by doing a HEAD request. The url is something like: <code>https:\/\/registry.digitalocean.com\/v2\/coolreg\/curlimages\/curl\/manifests\/8.9.1<\/code><\/li>\n<\/ol>\n<ul>\n<li>The registry responds 401 Unauthorized with a response header <code>www-authenticate<\/code> which contains details about how the client can get the authorization token. This header is of format: <code><scheme> realm=\"<realm>\", service=\u201d<service>\u201d, scope=\u201d<repository scopes=\"\">\u201d<\/repository><\/service><\/realm><\/scheme><\/code>. The realm field tells which token server to reach out to further get the token and scheme field specifies what is the token type.<\/li>\n<\/ul>\n<ol start=\"2\">\n<li>Client uses the auth info stored during <code>nerdctl login<\/code> (or <code>docker login<\/code>) to authenticate (basic) to the token server endpoint obtained from step 1.a with the scopes and service.<\/li>\n<\/ol>\n<ul>\n<li>\n<p>If the basic auth info is valid, the token server returns a token.<\/p>\n<\/li>\n<li>\n<p>If the basic auth info is invalid, a 403 Forbidden is returned<\/p>\n<\/li>\n<\/ul>\n<ol start=\"3\">\n<li>Client tries to call the same url as in step 1 using the token obtained from 2.a and this time it fetches the index digest for the tag.<\/li>\n<\/ol>\n<p>These steps are repeated for fetching each of the tags, manifests and individual layers of the image. The client can reuse the token for the subsequent HTTPS fetch calls, thus reducing calls to the token server. We can use <code>--debug<\/code> flag in <code>nerdctl<\/code> to check those HTTPS calls in details:<\/p>\n<pre class=\"language-bash\"><code><span class=\"token operator\">&gt;<\/span> nerdctl image pull <span class=\"token parameter variable\">--debug<\/span> <span class=\"token parameter variable\">--quiet<\/span> <span class=\"token punctuation\">[<\/span>registry.digitalocean.com\/coolreg\/curlimages\/curl:8.9.1<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">(<\/span>http:\/\/registry.digitalocean.com\/coolreg\/curlimages\/curl:8.9.1<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p><code>--quiet<\/code> is to suppress the progress bars.<\/p>\n<p>Pushing an image is uploading the image components in the reverse order as that in image pull. That is, first all the layers and config (JSON) are pushed. Finally, the manifest and index (if present) are pushed at the end. The authentication mechanism remains the same as in pulling images. The part where it differs is in the layer upload process. The client might choose to do either of below:<\/p>\n<ol>\n<li>\n<p>Monolithic upload<\/p>\n<\/li>\n<li>\n<p>Chunked upload<\/p>\n<\/li>\n<\/ol>\n<p>Let\u2019s dig into them.<\/p>\n<h3 id=\"monolithic-upload\"><a href=\"#monolithic-upload\" onclick=\"navigator.clipboard.writeText(this.href);\">Monolithic upload<\/a><a class=\"hash-anchor\" href=\"#monolithic-upload\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>As the name suggests, a monolithic upload tries to upload the whole layer in one HTTPS call. But there are still a couple of other HTTPS calls to complete this upload.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/doimages.nyc3.cdn.digitaloceanspaces.com\/002Blog\/DOCR%20blog%20image2.png\" alt=\"monolithic diagram\"\/><\/p>\n<ol>\n<li>\n<p>Client first checks if the layer exists by doing a HEAD request for the layer.<\/p>\n<\/li>\n<li>\n<p>If the layer is not present i.e. a 404 Not found is received in step 1,<\/p>\n<\/li>\n<\/ol>\n<ul>\n<li>Start the upload process by doing a POST request to <registry host=\"\">\/v2\/<registry name=\"\">\/<image name=\"\">\/blobs\/uploads. This will return a Location header which will contain the url to which the actual layer should be uploaded.<\/image><\/registry><\/registry><\/li>\n<li>Upload the layer to the location url returned in step 2.a. This is a PUT request with Content-Type header application\/octet-stream with the layer binary data as body. Also, the digest of the layer should be sent as a digest query in the url. Upon successful upload, a 201 response will be returned. This means the registry server has the uploaded layer now.<\/li>\n<\/ul>\n<ol start=\"3\">\n<li>If the layer is present, do nothing.<\/li>\n<\/ol>\n<p>In this way, after all the layers have been uploaded, the manifest is uploaded referencing the uploaded layers in its JSON body and similarly the index as well.<\/p>\n<h3 id=\"chunked-upload\"><a href=\"#chunked-upload\" onclick=\"navigator.clipboard.writeText(this.href);\">Chunked upload<\/a><a class=\"hash-anchor\" href=\"#chunked-upload\" aria-hidden=\"true\" onclick=\"navigator.clipboard.writeText(this.href);\"\/><\/h3>\n<p>This kind of upload sends the layers by breaking it down into smaller parts. Each of the parts are sent in separate PATCH http calls. Also, the start and end of the upload process is similar to the monolithic upload, as we will see next.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/doimages.nyc3.cdn.digitaloceanspaces.com\/002Blog\/DOCR%20blog%20image3.png\" alt=\"image alt text\"\/><\/p>\n<p>As we can see in this sequence diagram, the only difference from the monolithic upload is the chunk uploads (outlined in red). The upload starts with a POST request which returns an upload UUID (same as monolithic). Then, for each chunk:<\/p>\n<ol>\n<li>Upload chunk with a PATCH request to <registry host=\"\">\/v2\/<registry name=\"\">\/<image name=\"\">\/blobs\/uploads\/<upload uuid=\"\">. The header Content-Length specifies the size of the chunk and Content-Range dictates which byte range of the whole layer this chunk belongs to. A 201 is returned after successful upload. The chunks <em>must<\/em> be sent in the correct order.<\/upload><\/image><\/registry><\/registry><\/li>\n<\/ol>\n<p>After the chunks are uploaded, a PUT request is made to mark the end of chunked upload.<\/p>\n<p>Containers have leveled up the \u201cBuild once and run everywhere\u201d paradigm. They are now used not only in production, but also in dev environments to facilitate compatibility across those environments. Although containers can be run everywhere, there are many intricate mechanisms which help them run everywhere such as cgroups, namespaces etc. We have not covered those but we have seen in detail the components which the images (which later become containers when run) are made of and how they are related to each other. We have gone through how each of those components are pulled\/pushed from the registry and the authentication mechanism. We have also seen how container clients can push images using a monolithic and chunked approach.<\/p>\n<\/div>\n<p><br \/>\n<br \/><a href=\"https:\/\/www.digitalocean.com\/blog\/inside-container-registry-mechanics-of-push-pull\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you have packaged your code and deployed it in a cloud or on-prem server, you must have come across containers. That\u2019s how ubiquitous containers have become today. You must have written that infamous Dockerfile, ran Docker build command to create the image and pushed it to a registry. Conversely, you must also have pulled [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":9021,"comment_status":"","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[16],"tags":[],"class_list":["post-9020","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-app-developer"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/posts\/9020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/comments?post=9020"}],"version-history":[{"count":0,"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/posts\/9020\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/media\/9021"}],"wp:attachment":[{"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/media?parent=9020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/categories?post=9020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.satup.xyz\/index.php\/wp-json\/wp\/v2\/tags?post=9020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}