Docker Images For Arm Cluster

Docker Images For Arm Cluster

Now there's Kubernetes cluster on Raspberry Pis we needed to provide some docker images of our services. While working on Raspberry Pi as a Desktop Computer it is easy do build Docker image for Arm (arm64/aarch64 as my Raspberry Pi has Raspberry Pi OS 64bit version).

Such an image is then easily built, tagged and pushed to the cluster's repository by (as per Microk8s documentation):

docker build . -t <master-node-ip-or-dn>:32000/myimage:1.0.1
docker tag <image-sha> <master-node-ip-or-dn>:32000/myimage:1.0.1
docker push <master-node-ip-or-dn>:32000/myimage

But, what if the Raspberry Pi is not the development platform but an x86 system like MacBook Pro or a linux desktop?

Creating Docker Images on x86 Platforms (OSX)

Luckily Docker has come up with Multi Arch Support in a form of docker buildx command.

But, as with everything, there are gotchas...

First, making build image is not as straight-forward as above documentation said. Main issue is that Microk8s's local repository is, by default, insecure. So, pushing image from docker buildx need to allow for insecure repositories. And that's where all the problems are.

And it's all but easy to set up. If nothing else but because of some discrepancies in documentation and apparently a bug in the command itself. So, here are the steps I've done in order to work around it:

Setting Insecure Repository Access For Docker Buildx

This issue here is pointing to use of --config parameter (while creating build image!) and documentation here is pointing to example config file. But, the latest version of Docker for Mac (2.3.4.0 (46980) Edge - at the time of writing this blog post) expects a directory passed in --config:

$ docker buildx create --config ~/.docker/buildx/config.toml  --name mybuilder
WARNING: unable to read config file: open ~/.docker/buildx/config.toml/config.json: not a directory

So, to make create image happy, I've just dropped to directory where config image will reside:

docker buildx create --config ~/.docker/buildx/ --name mybuilder

And then amended ~/.docker/buildx/instances/mybuilder. With above create command instance definition file looks like this:

{"Name":"x","Driver":"docker-container","Nodes":[{"Name":"x0","Endpoint":"unix:///var/run/docker.sock","Platforms":null,"Flags":null,"ConfigFile":"~/.docker/buildx/","DriverOpts":{}}]}

See "ConfigFile":"~/.docker/buildx/". If I am to try to use that builder it is going to complain that config file ~/.docker/buildx/ does not exist. Well, it doesn't. So, I've changed it to:

"ConfigFile":"~/.docker/buildx/config.toml"

and added ~/.docker/buildx/config.toml as:

debug = true

[registry."<master-node-ip-or-dn>:32000"]
  http = true
  insecure = true

Note: in above I've replaced /Users/username with '~' in all instances.

Creating and Pushing Images

After the above - docker buildx command to build image was happy and was able to push to insecure Microk8s repo on my cluster with:

docker buildx build --platform linux/arm64,linux/arm/v7 -t <master-node-ip-or-dn>:32000/test:1.0.2 --push .

Such image can be inspected with:

docker manifest inspect --insecure -v <master-node-ip-or-dn>:32000/test:1.0.2

and the result will come up with something like following:

[
    {
        "Ref": "<master-node-ip-or-dn>:32000/test:1.0.2@sha256:c5535b8e086343a221dc4c48d136693303a7d378c4ace4847824471032c6fefd",
        "Descriptor": {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "digest": "sha256:c5535b8e086343a221dc4c48d136693303a7d378c4ace4847824471032c6fefd",
            "size": 6582,
            "platform": {
                "architecture": "arm64",
                "os": "linux"
            }
        },
        "SchemaV2Manifest": {
            ...
            "layers": [...]
        } 
    },
    {
        "Ref": "cluster-master.thenet:32000/test:1.0.2@sha256:01c3b06353e762a7af1234b8f9c83a63fd71f4a60166563f6b1eb9d0494994bc",
        "Descriptor": {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "digest": "sha256:01c3b06353e762a7af1234b8f9c83a63fd71f4a60166563f6b1eb9d0494994bc",
            "size": 6582,
            "platform": {
                "architecture": "arm",
                "os": "linux",
                "variant": "v7"
            }
        },
        "SchemaV2Manifest": {
            ...     
            "layers": [...]
        }
   }
]

To use such an image you can use version name, the 'latest' or just directly point to it with full sha. For instance:

<master-node-ip-or-dn>:32000/test:1.0.2@sha256:c5535b8e086343a221dc4c48d136693303a7d378c4ace4847824471032c6fefd

to select arm64 (aarch64) architecture.

Running Arm Image on x86 Platform

Fortunately it doesn't stop here. Docker had provided option to run such Arm architecture images easy locally, too:

docker run -p8082:8082 <master-node-ip-or-dn>:32000/test:1.0.2@sha256:c5535b8e086343a221dc4c48d136693303a7d378c4ace4847824471032c6fefd

Conclusion

This nicely rounds up whole Raspberry Pi cluster and development for it.

Make a code (Python, Java or whatever), pick generic arm (or arm64/aarch64) image for your Dockerfile, create Docker image and push it to the cluster's local (insecure!) repository quickly. Of course, not before you tested it locally (if appropriate).

Comments

Comments powered by Disqus