Docker is cool. Lets do Rust with Docker
version: 1.0 date: 2020-08-26 author: bestia.dev repository: GitHub
Hashtags: #rustlang #tutorial #docker
My projects on Github are more like a tutorial than a finished product: bestia-dev tutorials.
On my Win10 I have WSL2 - Windows subsystem for Linux.
I had WSL 1 for a while, but it was not a true Linux kernel and Docker did not work with WSL 1.
WSL2 is a revolution. With it I have a lightweight virtual machine with a true Linux kernel. Great !
Win10 must be version 1903 or higher. My machine did not automatically update to that yet, so I needed the workaround. I needed to register in the Microsoft Insiders program.
It uses the same account you use for other Microsoft service.
Check for updates I can see the update to version 19041. Just do it!
# Open PowerShell as Administrator dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart # restart and update to WSL2 dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart # restart your machine. Set WSL2 as default wsl --set-default-version 2 # I get the error: WSL 2 requires an update to its kernel component. # visit https://aka.ms/wsl2kernel and do the update # I choose to install the Debian distro: https://www.microsoft.com/sl-si/p/debian/9msvkqc78pk6?rtc=1 # give it a password you will remember # then in linux bash $ sudo apt update $ sudo apt dist-upgrade
I will try to compile all my Rust code to Linux or Wasm.
The Windows OS is less and less interesting to me. It is really good for GUI applications. But I am not interested in those, because it limits the use of the program to only that OS. This kind of platform vendor-lock-in because of the GUI is stupid.
I will try to make applications that are separated into server/backend and client/frontend. The server will work on Linux. It means everywhere. The server can be on the same machine, in a virtual machine, in the WSL2, on another machine in the local network or anywhere on the internet. So everywhere.
The client/frontend will work inside the browser with Wasm. It means everywhere.
All will be written in Rust. Full stack Rust.
It sounds like a good cross-platform solution. To me.
Install Docker in windows with WSL2 support
Download and install the stable version from
Use the whale icon in the notification area to see the Docker containers.
# run in powershell to check the installation docker version docker run hello-world
Docker engine is inside WSL2. Great!
Windows is only a GUI. Everything really is running in Linux.
This is the future.
Container for Rust building
For now we don't need to install Rust on our Debian.
Just pull the image for Rust building standalone 100% statically linked app with musl.
It already has installed everything we need for Rust. Easy.
# bash commands $ docker pull ekidd/rust-musl-builder # create an alias to be used easily to run the container $ alias rust-musl-builder='docker run --rm -it -v "$(pwd)":/home/rust/src ekidd/rust-musl-builder' # docker container can be run for only one execution and then removed # --rm means the container is removed after execution # -it means interactive + pseudo-tty (not in background) # -v Volume links a read/write host directory to the container file system # $(pwd) means "Present Working Directory" and has nothing to do with passwords # docker commands finish with the image name
We will need git in Debian for our example program.
sudo apt install git git --version git config --global user.name "Your Name" git config --global user.email "firstname.lastname@example.org"
Clone this repo:
cd ~ mkdir rustprojects cd rustprojects git clone email@example.com:bestia-dev/docker_rust_minimal.git
Building a CLI program
Our pwd Present Working Directory is the cloned project.
We will use the alias to run a docker container. And after that we will give the command to execute in the container. We want to build the Rust project.
rust-musl-builder cargo build --release
That's it. The docker container was spinned, the volume was linked, the project was built. We can find the result in
target/x86_64-unknown-linux-musl/release/hello on the host OS Debian.
Make it small
Now we have a binary (executable) and we want to deploy it somewhere. We will try to make it as small as possible. Maybe that is not smart for every scenario out there, but let's have a look.
# let "strip" the binary for minimal size $ strip target/x86_64-unknown-linux-musl/release/hello # from 3MB to 300kB ?!
Build the Docker image
We want to use docker to encapsulate all the needed files and configuration so the person on the other side does not need to worry about it. We need to have a Dockerfile in our Working Directory. It is already in this git repo. It looks simple like this:
FROM scratch COPY target/x86_64-unknown-linux-musl/release/hello / CMD ["/hello"]
scratch is the smallest possible docker image base. We can use it because our binary is statically linked to everything including
musl. The next smallest image base is Alpine Linux. Only 5 MB. The next can be Debian Slim 88 MB. You see the difference in size.
We need to copy only our binary file to the image.
When the container is run, it will execute our binary.
Now build this image:
alias utc='date -u +"%Y-%m-%dT%H-%M-%S"' docker build --no-cache -t scratch_hello -t scratch_hello:$(utc) .
For deployment reasons we want to give a version to the image. It is difficult to make this smart. I will just use the UTC date and time.
--no-cache for this example we avoid caching
-t means tag or name. There can be more than one tag curiously.
. means the Dockerfile is here in the Working Directory
We can see it now in our list of local docker images:
It is only 305kB. Woohoo!
Run the containerized CLI
We can now run the docker container with our CLI just as we would run the app itself:
docker run --rm scratch_hello
--rm means remove the container after execution
We can run it also from the
Windows Command Prompt and
Docker Hub is a website to share docker images easily. Alternatively it can be saved to a tar file and then transfered, copied and finally loaded.
docker save --output scratch_hello-2020-08-26T11-45-51.tar scratch_hello:2020-08-26T11-45-51
On the other side:
docker load --input scratch_hello-2020-08-26T11-45-51.tar docker tag scratch_hello:2020-08-26T11-45-51 scratch_hello:latest docker run scratch_hello
Adding the tag
latest makes it easier to call that image later.