This is Why You Doubt so Much

The mind is the seat of our imagination — it is where we exercise our will. It determines the magnitude and direction of our life. Moses sent 12 spies to Canaan, to evaluate the land God had given to…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




How to implement a Docker Desktop alternative in macOS with Podman

On August 31 2021 Docker Inc. announced a change in the licensing model for Docker Desktop. As of January 31 2022, Docker Desktop is no longer free and users need to pay a monthly subscription fee of $5 per month.

However, chances are that this doesn’t apply to you. Docker Desktop still remains free for:

With January 31 around the corner, it’s time for users that don’t belong to any of the above categories to either transition to the paid plan or seek an alternative to Docker Desktop.

Docker is a containerization platform that uses unique features of the Linux kernel to run our containers. This means that on systems where there is no Linux kernel such as macOS, we need some Linux virtual machine that will run our Docker containers for us.

This is the most important feature that Docker for Desktop brings to the table. Under the hood it runs a Linux based virtual machine. It makes network port forwarding and filesystem volume sharing completely transparent to provide us with the same experience as if we were running Docker natively.

To replace Docker Desktop, we need something else to manage this virtual machine.

In search for a replacement, I wrote down these requirements:

In this article I will explain how I managed to implement all of these using Podman. The examples assume that Homebrew is installed and that there are no Docker related packages present.

To install podman, simply execute:

After installation we need to setup the Podman virtual machine that will run our containers. First we initialize the machine with:

This will create CoreOS based virtual machine with default settings: 1 CPU, 2GB RAM and 10GB disk space. While this might be enough for a casual user running only a couple of containers, I like to spice it up a little:

When finished, the virtual machine will have been created with a default name ‘podman-machine-default’:

To use it, we need to start it:

That’s it, we have our Podman installation all setup to run our Docker containers. The Podman developers did a terrific job of supporting the Docker API.

The podman executable is a drop-in replacement for the docker command. Because I’m so used to the docker command, I created this alias:

Let’s try it by starting a MySQL container:

Everything should go smooth and a brand new MySQL will be running:

Now that Podman is configured, it’s time to setup docker-compose. Since docker-compose is a Docker specific solution there’s no equivalent included with Podman and we need to install it separately:

For docker-compose to communicate with Docker, it needs access to the Unix socket the Docker daemon is listening at. Although Podman unlike Docker is daemonless, it does have support for listening to a Unix domain socket as of version 3.0.

The Unix socket is enabled by default. However, the socket is only available inside the virtual machine. To use it on our host, we can open a forwarding tunnel over SSH using the SSH key pair podman has generated for us when we initialized our virtual machine.

First, we need to find out on what port our virtual machine listens for SSH connections (output adjusted for display purposes):

Using port number 54961, we can create our tunnel:

Now that docker-compose knows where to find the socket, it’s ready for use.

Let’s try it out with a simple docker-compose.yml file:

Our container should start nicely:

Of course having a database with no data is no fun. We can create some by mounting an SQL script to initialize a database. I have created such a script in my personal temp directory as /Users/verhage/tmp/init.sql and added it to the docker-compose.yml :

This will create a database called ‘testdb’ and run our script in this database.

However, when we try to start it we get an error message looking like this:

Although this seems like an innocent permissions problem, it’s actually a problem with Podman not able to mount the file at all. What’s happening?

Well, Podman is unable to mount files or folders directly from the host system. Instead, it tries to resolve the file on the filesystem of the VM that is running our container…

Luckily for us, there’s a workaround to this problem. We have to get dirty with Podman’s virtual machine though…

Before we start, in the macOS system preferences we need to open our Firewall for SSH and allow remote login for our user:

Open our firewall for SSH
Allow remote login for our user

We’ll be needing this later.

Let’s login to the virtual machine, but this time with one additional argument:

The new argument -R 10000:$(hostname):22 will create a reverse tunnel through which we are going to mount our host filesystem!

Next, let’s create a mount point where we’ll be mounting our host’s /Users folder:

The /Users symbolic link we create on the second line enables us to use the exact same paths in the VM as on our host.

Now we’re ready to proceed with mounting the file system.

We’ll be mounting the filesystem using a tool called sshfs . For a password-less login, we create an SSH key-pair first and copy it to our host through the reverse tunnel:

Our mount we then create with:

To verify that it all works:

Time to take docker-compose for a test drive!

Remember on our host we have this docker-compose.yml :

First we have to clean-up anything from our previous attempt:

When we try again we should see that it can now successfully load our script:

It works!

Docker-compose first expands ./init.sql to its full path and because of the symlink /Users that we created in the VM Podman is able to resolve it in the same place.

As a Java developer, I frequently use Testcontainers for integration testing. Like with docker-compose, we need to tell Testcontainers where to find the Unix socket:

What we didn’t mention before is that unlike Docker, Podman runs our containers in an unpriviliged mode. While this is a good thing, it will disallow Ryuk from cleaning up any hanging containers.

We have to disable Ryuk to make Testcontainers work in unprivileged mode:

Setting both environment variables will enable us to run our integration tests from the command line with Maven or Gradle.

To make it work in our IDE we need to make sure to pass these variables to the JVM. If you are like me and use IntelliJ IDEA you can add them as environment variables in the JUnit run template.

While not an ideal solution because of the SSH session with all the tunnels that needs to be open for it to work, for me this is the best alternative to Docker Desktop I could come up with.

Hopefully in the near future we will have some better out of the box solution with Podman when they solve the remote mounting issues.

Until then, happy tunnelling!

Add a comment

Related posts:

What is software coding?

Software is a set of instructions, data or programs used to operate computers and execute specific tasks. It is the opposite of hardware, which describes the physical aspects of a computer. Software…

Hungary No More

This is the story of one of the least appealing things I have ever eaten. And also, of friendship. But mostly it’s about the unappealing thing. Read at your own risk. The November rain in Budapest…

Dealing With Pharaoh Ants and Bait That Works

Could you imagine waking up littered with tiny insects every morning? For centuries, and in every corner of the world, Pharaoh ants have ravaged the homes and lives of many. In some cases, medical…