26 October 2021
Manage Websites Like Docker
Author’s Note: This is based on experimental technologies that aren’t quite ready for production. Nonetheless, I believe the ideas presented in this article will be useful in the near future.
Did you know that you can manage static websites like you manage images with Docker? You can!
But I’m sure you have a few more questions, like:
- What do I mean by “like Docker”?
- Why would you want to do such a thing?
- How can you do it yourself?
To find answers, we will first take a simplified look at how management works with Docker. We will then learn how to create a similar process for static websites.
Workflow
At its most basic, Docker works like this:
- Create an image (an OCI Image)
— a single artifact that represents a file tree.
docker build -t registry/image:0.0.0 -f - . <<EOF FROM nginx:latest EOF
- Publish the image to a registry (an OCI Registry).
docker push registry/image:0.0.0
- When necessary, retrieve said image from the registry to use it.
docker run -it --rm -p 8080:80 registry/image:0.0.0 # Note:`docker run` pulls the image from the registry if not locally available
When we make static websites, the process is similarly simple:
-
Create a file tree.
npm run build ... # There are countless technologies -- atom, webpack, rollup, etc. -- # that can be used to create a static website.
-
When necessary, push file tree to a server.
npm run publish ... # There are similarly a number of options for publishing. # ftp and git (when configured with hooks) are viable options.
While both processes are simple, the idea of an image from the Docker workflow is key. An entire website as a single artifact to be versioned, shared, and deployed in like an image is enticing.
Tutorial
Now that we’ve covered the “what” and the “why”, let’s look at the “how”.
Prerequesites
In order complete do this tutorial, you’ll need the following installed:
go
Two CLI tools are based on go, so we might as well get this out of the way first.
Install go according to instructions here.
gen-bundle
Install gen-bundle according to instructions here.
# Install wasm-to-oci https://github.com/engineerd/wasm-to-oci
go get -u github.com/WICG/webpackage/go/bundle/cmd/...
wasm-to-oci
Install wasm-to-oci according to instructions here, or run:
# Install wasm-to-oci https://github.com/engineerd/wasm-to-oci
go get -u github.com/WICG/webpackage/go/bundle/cmd/...
Docker
This is silly; but to authenticate wasm-to-oci, you need Docker.
This will hopefully change soon — I said this was “experimental”!
Install docker according to instructions here.
Google Chrome
Install Google Chrome according to instructions here.
a Github Account
You’ll need a Github account to publish the artifact.
Sign up and set up your container registry if you haven’t already.
Plan
We want a process that mirrors the one outlined above for Docker:
- Convert a file tree into an “image-like” artifact. (using
gen-bungle
) - Publish said artifact to a registry. (Github via
wasm-to-oci
) - When necessary, retrieve said image from the registry and view it. (with
wasm-to-oci
and Google Chrome)
Artifact
In place of an image, we use a web bundle — an emerging format used to package entire websites.
Create an artifact
Create a empty folder named ‘hello-world’.
In it, place a file named ‘index.html’ with the following contents:
<!DOCTYPE html>
<html>
<head>
<title>My First Web Bundle</title>
</head>
<body>
Hello, World! This is the first page!
<a href="/next.html"> See what's next... </a>
</body>
</html>
Place another file named ‘next.html’ with the following contents:
<!DOCTYPE html>
<html>
<head>
<title>My first bundle</title>
</head>
<body>
Hello again, World! This is the second page!
<a href="/"> See what (or who? 🤔) has come before...</a>
</body>
</html>
Outside of the hello-world folder, run the following command:
gen-bundle -dir ./hello-world -baseURL http://localhost/ -o hello-world.wbn -primaryURL http://localhost/
This creates a bundle file
named “hello-world.wbn”.
This is equivalent to a Docker image.
(Note that when you interact with docker,
you use commands like build
, push
, and pull
.
You do not ususually interact directly
with image files themselves.)
View Artifact
To view the artifact, we need to enable the experimental “web bundles” browser feature.
Using Chrome, visit chrome://flags/#web-bundles
.
Enable the feature and restart your browser.
Using Chrome, open the hello-world.wbn
file
that you previoiusly created.
The url will look something like
file:///.../hello-world.wbn?http://localhost/
.
Notice that you can freely navigate between pages of the site — even though it’s just single file!
You can probably imagine a few uses for this; and here are a few:
-
Entire websites can be easily snapped-shotted and archived into a single file.
-
In the past, creating a test deployment for a site used to require spinning up a new server. Now you can do it by sharing a single file.
Registry
In place of a registry, how do we manage our bundles? The beautiful thing is that we can actually use an OCI registry!
OCI has a goal to enable the distribution of more cloud native artifacts.
There are no existing tools
to push bundles (specificaly)
to OCI registries.
Fortunately, we can abuse use WASM to OCI
— a tool for doing something
similar with web assembly —
for our purposes.
We’ll use GitHub as a OCI registry as it supports unknown images. Notably, Docker Hub does not support this.
Login to Github with Docker
We will use Docker )only_ to authenticate
First obtain a github access token with, permissions to read and write to the registry.
User docker login
to login to the github container registry at ghcr.io
,
echo $GITHUB_ACCESS_TOKEN | docker login ghcr.io -u $GITHUB_USERNAME --password-stdin
Pushing and Pulling
Push the bundle to the registry with:
wasm-to-oci push ./hello-world.wbn ghcr.io/$GITHUB_USERNAME/hello-world:0.0.0
If the registry at
https://github.com/users/$GITHUB_USERNAME/packages/container/package/hello-world
doest not exist, it will be created automatically.
You can view and manage artifacts in your repository at https://github.com/$GITHUB_USERNAME?tab=packages
.
wasm-to-oci pull ghcr.io/$GITHUB_USERNAME/hello-world:0.0.0 --out hello-world.test.wbn
What’s next?
There are still a few main things missing from this work flow:
-
The
docker images
command provides a way to manage docker images locally.I don’t currently have an equivalent for managing bundles or generic OCI images.
-
The
docker run
command provides way to to create a “running” container by combining an image with a linux kernel.The equivalent would be hosting a bundle as a static asset such that a user can interact with it.
-
The
docker run
command provides way to to create a “running” container by combining an image with a linux kernel.The equivalent would be hosting a bundle as a static asset such that a user can interact with it.
-
The
docker build
incrementally builds images from other, protypal images.The equivalent would be a templating system that can pull existing static in websites, and modify them to make new ones.