Just some stuff I want to share with you
In the First there was Linux post I explained how to get going with a Linux system on a Windows laptop. Where this would be a difficult task many years ago, it’s a breeze nowadays with Windows Subsystem for Linux.
Running a Linux system only doesn’t add much value to my way of working. I need applications. But this whole journey started with messing up a Windows laptop with all sorts of applications, frameworks and add-ons to just try something. I don’t want to do the same thing on a Linux system.
The solution: containerization; and that doesn’t read: Docker. Let’s dive into it.
I am not going to introduce containerization; there is so much information about this already, just google for it or hop over to YouTube to learn more about it.
Mention containerization, people tend to immediately start to use the word “Docker”. And as explained in a previous post, yes Docker (the company) did amazing work, but it’s not the only player in the market anymore. Due to their change in licensing, I can’t/don’t want to use their software anymore. So, what’s the alternative? Well, there are a few, but there’s one that is interesting in the context of Development Containers (the dot on the horizon I am moving to with this series of articles): Podman.
Podman can be used to create, find, run and manage containers on a computer. Podman itself has no clue how to run a container; it interfaces with a runtime engine that takes care of that. In the context of Podman, this means that the underlaying (operating system) software layers will need to take care of that task.
With that in mind, using Podman on Windows would mean that Windows would need to act as the runtime engine for the containers. But it doesn’t… Hang on… What? Yep, that is correct. Podman on Windows is a command line tool that interacts with Podman running on a Linux system on your Windows computer. And the Linux system is used to run the containers.
This is why I first introduced how to setup a Linux system on a Windows laptop. Now it’s time to use it to work with containers.
For my experiments, I removed the Ubuntu system that I installed before. Meaning, that my Windows laptop does have WSL installed, but there’s no Linux system. I then followed the Windows installation instructions on the Podman website.
I am using the laptops as described in the First there was Linux post again. Let’s have a look at the software versions after installing Podman. The Windows 10 laptop:
C:\Users\WindowsUser>wsl -v
WSL version: 1.2.5.0
Kernel version: 5.15.90.1
WSLg version: 1.0.51
MSRDC version: 1.2.3770
Direct3D version: 1.608.2-61064218
DXCore version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windows version: 10.0.19045.3570
C:\Users\WindowsUser>podman -v
podman version 4.7.1
And the Windows 11 laptop:
C:\Users\WindowsUser>wsl -v
WSL version: 1.2.5.0
Kernel version: 5.15.90.1
WSLg version: 1.0.51
MSRDC version: 1.2.3770
Direct3D version: 1.608.2-61064218
DXCore version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windows version: 10.0.22621.2428
C:\Users\WindowsUser>podman -v
podman version 4.7.1
Once Podman is installed, we need to install the Linux system that Podman is going to use. This is fully covered by Podman itself. Entering a podman machine init
will take care of downloading and installing a Linux system on top of WSL:
C:\Users\WindowsUser>podman machine init
Downloading VM image: fedora-podman-amd64-v38.0.28.tar.xz: done
Extracting compressed file: podman-machine-default_fedora-podman-amd64-v38.0.28.tar: done
Importing operating system into WSL (this may take a few minutes on a new WSL install)...
Import in progress, this may take a few minutes.
The operation completed successfully.
Configuring system...
Generating public/private ed25519 key pair.
Your identification has been saved in podman-machine-default
Your public key has been saved in podman-machine-default.pub
The key fingerprint is:
SHA256:Khf7X7j8oJdtP88HIHMpFge7j8mOWC8Lkv6KtZJfqIE root@L14393-20200105
The key's randomart image is:
+--[ED25519 256]--+
| .. |
| ... |
| .o . |
| =.+ |
| . S..= . |
| . o + . = . |
|E ..* * . *oo . |
| o* * =.*o+o ...|
| oo=oo ==*o...o+|
+----[SHA256]-----+
Machine init complete
To start your machine run:
podman machine start
As part of the setup of the new VM, Podman also created matching ssh-keys and configured a Podman connection. Let’s start up the new system:
C:\Users\WindowsUser>podman machine start
Starting machine "podman-machine-default"
This machine is currently configured in rootless mode. If your containers
require root permissions (e.g. ports < 1024), or if you run into compatibility
issues with non-podman clients, you can switch using the following command:
podman machine set --rootful
API forwarding listening on: npipe:////./pipe/docker_engine
Docker API clients default to this address. You do not need to set DOCKER_HOST.
Machine "podman-machine-default" started successfully
And have a look at the connection:
C:\Users\WindowsUser>podman system connection list
Name URI Identity Default
podman-machine-default ssh://user@127.0.0.1:58465/run/user/1000/podman/podman.sock C:\Users\WindowsUser\.ssh\podman-machine-default true
podman-machine-default-root ssh://root@127.0.0.1:58465/run/podman/podman.sock C:\Users\WindowsUser\.ssh\podman-machine-default false
As we can see here, Podman on Windows is using an ssh-connection to a port on the Linux system to interact with a Podman socket. That Podman socket triggers the Podman API service that will act as a proxy to the local (on the Linux system) Podman software. Confused? Well, it took me some time to understand, but hopefully if you read this paragraph a few more times, it starts to make sense.
So, what’s the result of all this? Well, by now we can run Podman commands on the Windows laptop to work with containers in/on the Linux system. For example:
C:\Users\WindowsUser>podman run hello-world
Resolved "hello-world" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull quay.io/podman/hello:latest...
Getting image source signatures
Copying blob sha256:d08b40be68780d583e8c127f10228743e3e1beb520f987c0e32f4ef0c0ce8020
Copying config sha256:e2b3db5d4fdf670b56dd7138d53b5974f2893a965f7d37486fbb9fcbf5e91d9d
Writing manifest to image destination
!... Hello Podman World ...!
.--"--.
/ - - \
/ (O) (O) \
~~~| -=(,Y,)=- |
.---. /` \ |~~
~/ o o \~~~~.----. ~~
| =(X)= |~ / (O (O) \
~~~~~~~ ~| =(Y_)=- |
~~~~ ~~~| U |~~
Project: https://github.com/containers/podman
Website: https://podman.io
Documents: https://docs.podman.io
Twitter: @Podman_io
Voila, we have a working container environment. Now, let’s have a look at a real-life workload to validate this setup.
In my daily work, I am working as a product owner on a software product. One part of the product is a documentation website. The content for the site is stored in a git repository and we are using Hugo to generate the website. When content is added or updated, it is pushed to the git repository and that triggers build and deployment processes that validate the content and update the website. But before new content is pushed, we want to make sure that the content makes sense in the context of the whole website. Hugo provides a nice option to act as a local webserver, which allows the editor to see the updated site on their local computer while working on the content.
As an editor, I don’t want to install Hugo on my Windows laptop (anymore), instead, I will run it from within a container. By now we have setup Podman and everything should be in place to make this work. First step is to clone the documentation git repo to my local Windows computer.
C:\Users\WindowsUser\Local\documentation>git clone https://azure_repo_url/_git/documentation
Cloning into 'documentation'...
remote: Azure Repos
remote: Found 2261 objects to send. (276 ms)
Receiving objects: 100% (2261/2261), 11.72 MiB | 45.81 MiB/s, done.
Resolving deltas: 100% (992/992), done.
Before I start running Hugo using a container, let’s see how I used to do it when Hugo was installed on my Windows laptop: hugo server -s src --disableFastRender --buildDrafts --noHTTPCache --verbose --logLevel debug
would start Hugo as a server (using a default port: 1313) and monitoring the src
folder for changes. I can now start a browser and go to http://localhost:1313
to browse the documentation.
Let’s do the same thing, but now using a container:
C:\Users\WindowsUser\Local\documentation>podman run --rm -it -v src:/src -p 1313:1313 klakegg/hugo:ext-ubuntu server --disableFastRender --noHTTPCache --buildDrafts --verbose
hugo: downloading modules …
hugo: collected modules in 20591 ms
Start building sites …
hugo v0.111.3-5d4eb5154e1fed125ca8e9b5a0315c4180dab192+extended linux/amd64 BuildDate=2023-03-12T11:40:50Z VendorInfo=hugoguru
INFO 2023/10/27 09:32:24 syncing static files to /
| EN
-------------------+------
Pages | 122
Paginator pages | 0
Non-page files | 84
Static files | 45
Processed images | 7
Aliases | 3
Sitemaps | 0
Cleaned | 0
Built in 5393 ms
Watching for changes in /src/{archetypes,assets,content,i18n,layouts,package.json,static}
Watching for config changes in /src/config.toml, /src/go.mod
Environment: "DEV"
Serving pages from memory
Web Server is available at //localhost:1313/ (bind address 0.0.0.0)
Press Ctrl+C to stop
I can now browse to http://localhost:1313
and see the documentation site and everything works as it should.
When the build starts, Hugo needs to fetch a whole bunch of modules and that takes just over 20 seconds. This isn’t a big issue if you start the container and let it run for a few hours, but in certain scenario’s I can break the site and will have to restart the container. With every restart we will have to wait 20 seconds while Hugo is downloading modules. This is something we can improve, by mounting a persistent volume on the container. More on that later.
The next thing is the build time: just over 5 seconds. That’s not slow nor fast. It’s okay.
The deal breaker is the monitoring of the src
folder. That isn’t working anymore. This is caused by the fact that Hugo now runs in a container on the Linux VM and the src
folder (which is on the Windows filesystem) is mounted on the filesystem in that container. This breaks the ability for this Hugo container to monitor for filesystem changes on the Windows folder. In my workflow, I use a dual monitor setup, where the browser showing the site is on one monitor and I edit the content of the site on the other monitor. Every time when I hit save in the editor, I just need to look at the other monitor to see what the result is. But now I would have to restart the container after every change in the documentation to see the result, wait at least 5 seconds (if we fixed the 20 seconds of modules loading on every restart of the container) and hit refresh on the browser to see the result. That’s not going to work for me.
Let’s try another approach let’s run everything on the Linux system itself. The Linux system that Podman created with the machine init
is a minimal system. It doesn’t have anything like git on it, so I can not clone the repo. For now, I used the Windows Explorer to copy the content to \\wsl.localhost\podman-machine-default\home\user
. Now I can login using the wsl
command and go to the /home/user/documentation
folder on the Linux system.
Let’s do the same thing that we did before: start a Hugo container to serve the documentation:
[user@w10laptop documentation]$ podman run --rm -it -v ./src:/src -p 1313:1313 klakegg/hugo:ext-ubuntu server --disableFastRender --noHTTPCache --buildDrafts --verbose
hugo: downloading modules …
hugo: collected modules in 20900 ms
Start building sites …
hugo v0.111.3-5d4eb5154e1fed125ca8e9b5a0315c4180dab192+extended linux/amd64 BuildDate=2023-03-12T11:40:50Z VendorInfo=hugoguru
INFO 2023/10/27 10:16:50 syncing static files to /
| EN
-------------------+------
Pages | 122
Paginator pages | 0
Non-page files | 84
Static files | 45
Processed images | 7
Aliases | 3
Sitemaps | 0
Cleaned | 0
Built in 663 ms
Watching for changes in /src/{archetypes,assets,content,i18n,layouts,package.json,static}
Watching for config changes in /src/config.toml, /src/go.mod
Environment: "DEV"
Serving pages from memory
Web Server is available at //localhost:1313/ (bind address 0.0.0.0)
Press Ctrl+C to stop
INFO 2023/10/27 10:28:39 Received System Events: [WRITE "/src/content/en/docs/_index.md" WRITE "/src/content/en/docs/_index.md"]
Change detected, rebuilding site.
2023-10-27 10:28:39.576 +0000
Source changed WRITE "/src/content/en/docs/_index.md"
Total in 233 ms
Going to http://localhost:1313
on a web browser on Windows brings up the documentation site again. Everything is as expected.
The initial modules download again requires just over 20 seconds. But look at that build time: it went down from over 5 seconds to less than 1 second. This is the result of not having to access the files outside the Linux system anymore.
Even the monitoring of changes on the filesystem now works. It takes just over 200 ms to update the site; no need to restart the container. And due to the way Hugo works, the browser refreshes the page that I just modified, meaning that I don’t even need to “touch” the browser to see the result. This is the way of working that I used to have!
We can optimize the startup time by mounting a volume to store the modules as well. First, we need to create a directory to store the modules and then we will run the container using this directory. Here’s the first run:
[user@w10laptop documentation]$ mkdir hugo_cache
[user@w10laptop documentation]$ podman run --rm -it -v ./src:/src -v ./hugo_cache:/tmp -p 1313:1313 klakegg/hugo:ext-ubuntu server --disableFastRender --noHTTPCache --buildDrafts --verbose
hugo: downloading modules …
hugo: collected modules in 20705 ms
Start building sites …
hugo v0.111.3-5d4eb5154e1fed125ca8e9b5a0315c4180dab192+extended linux/amd64 BuildDate=2023-03-12T11:40:50Z VendorInfo=hugoguru
INFO 2023/10/27 10:36:43 syncing static files to /
| EN
-------------------+------
Pages | 122
Paginator pages | 0
Non-page files | 84
Static files | 45
Processed images | 7
Aliases | 3
Sitemaps | 0
Cleaned | 0
Built in 632 ms
Watching for changes in /src/{archetypes,assets,content,i18n,layouts,package.json,static}
Watching for config changes in /src/config.toml, /src/go.mod
Environment: "DEV"
Serving pages from memory
Web Server is available at //localhost:1313/ (bind address 0.0.0.0)
Press Ctrl+C to stop
And again, it takes just over 20 seconds to load the modules. But let’s see if we do another run:
[user@w10laptop documentation]$ podman run --rm -it -v ./src:/src -v ./hugo_cache:/tmp -p 1313:1313 klakegg/hugo:ext-ubuntu server --disableFastRender --noHTTPCache --buildDrafts --verbose
Start building sites …
hugo v0.111.3-5d4eb5154e1fed125ca8e9b5a0315c4180dab192+extended linux/amd64 BuildDate=2023-03-12T11:40:50Z VendorInfo=hugoguru
INFO 2023/10/27 10:38:36 syncing static files to /
| EN
-------------------+------
Pages | 122
Paginator pages | 0
Non-page files | 84
Static files | 45
Processed images | 7
Aliases | 3
Sitemaps | 0
Cleaned | 0
Built in 610 ms
Watching for changes in /src/{archetypes,assets,content,i18n,layouts,package.json,static}
Watching for config changes in /src/config.toml, /src/go.mod
Environment: "DEV"
Serving pages from memory
Web Server is available at //localhost:1313/ (bind address 0.0.0.0)
Press Ctrl+C to stop
No downloading at all and within a second, we have a running Hugo server. Brilliant.
Not yet… We made good progress, but the Linux distribution that Podman deployed is a bare minimal one. It will require tinkering to get git going and it will for sure not work like this with Development Containers. More on that in the next post.