To reduce bot traffic you must login to view /lordwelch/gokrazy/commit/a68485791abc3f461300cfe012f68e99943a4be8?files=docs%2Fuserguide.
The GitHub login only links via username.

Files
gokrazy/website/content/packages/docker-containers.markdown

159 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Docker containers"
weight: 60
---
gokrazys goal is to make it easy to build Go appliances. In an ideal world, all
building blocks you need would be available in Go. In reality, that is not
entirely the case. Perhaps you need to run a C program next to your Go
programs. Docker containers make incremental (or partial) adoption of gokrazy
easy.
Were going to use podman, a drop-in replacement for Docker, because there is a
statically compiled version for amd64 and arm64 available that we could easily
re-package into https://github.com/gokrazy/podman.
## Step 1: Install podman to your gokrazy device
Include the following packages in your gokrazy installation:
```bash
gok add github.com/gokrazy/nsenter
gok add github.com/gokrazy/podman
gok add github.com/spod/nftables
```
Then, deploy as usual:
```bash
gok update
```
**Note:** Podman 5+ uses netavark for container networking instead of CNI. If you are migrating from podman < 5, you can remove `github.com/gokrazy/iptables` and any CNI plugin packages, and replace them with `github.com/spod/nftables`.
## Step 2: Verify podman works
Use [breakglass](https://github.com/gokrazy/breakglass) to login to your gokrazy
instance and run a container manually:
```shell
/tmp/breakglass187145741 $ podman run --rm -ti docker.io/library/debian:sid
root@gokrazy:/# cat /etc/debian_version
12.0
```
## Step 3: Use podman programmatically
Now that you have the required tools, there are a couple of decisions you have
to make depending on what you want to run in your container(s):
1. Should container data be stored ephemerally in `tmpfs` (lost with the next
reboot), on the permanent partition of your SD card, or somewhere else
entirely (e.g. network storage)?
1. Do you want to pull new container versions automatically before each run, or
manually on demand only?
1. Should your container be started as a one-off job only ([→ detached
mode](https://docs.docker.com/engine/reference/run/#detached--d)), or
supervised continuously (restarted when it exits)?
1. Should your container use a deterministic name (so that you can `exec`
commands in it easily), or use a fresh name for each run (so that there never
are conflicts)?
Aside from these broad questions, you very likely need to set a bunch of detail
options for your container, such as additional environment variables, volume
mounts, networking flags, or command line arguments.
The following program is an example for how this could look like. I use this
program to run [irssi](https://irssi.org/).
```go
package main
import (
"fmt"
"log"
"os"
"os/exec"
"strings"
"github.com/gokrazy/gokrazy"
)
func podman(args ...string) error {
podman := exec.Command("/usr/local/bin/podman", args...)
podman.Env = expandPath(os.Environ())
podman.Env = append(podman.Env, "TMPDIR=/tmp")
podman.Stdin = os.Stdin
podman.Stdout = os.Stdout
podman.Stderr = os.Stderr
if err := podman.Run(); err != nil {
return fmt.Errorf("%v: %v", podman.Args, err)
}
return nil
}
func irssi() error {
// Ensure we have an up-to-date clock, which in turn also means that
// networking is up. This is relevant because podman takes whats in
// /etc/resolv.conf (nothing at boot) and holds on to it, meaning your
// container will never have working networking if it starts too early.
gokrazy.WaitForClock()
if err := podman("kill", "irssi"); err != nil {
log.Print(err)
}
if err := podman("rm", "irssi"); err != nil {
log.Print(err)
}
// You could podman pull here.
if err := podman("run",
"-td",
"-v", "/perm/irssi:/home/michael/.irssi",
"-v", "/perm/irclogs:/home/michael/irclogs",
"-e", "TERM=rxvt-unicode",
"-e", "LANG=C.UTF-8",
"--network", "host",
"--name", "irssi",
"docker.io/stapelberg/irssi:latest",
"screen", "-S", "irssi", "irssi"); err != nil {
return err
}
return nil
}
func main() {
if err := irssi(); err != nil {
log.Fatal(err)
}
}
// expandPath returns env, but with PATH= modified or added
// such that both /user and /usr/local/bin are included, which podman needs.
func expandPath(env []string) []string {
extra := "/user:/usr/local/bin"
found := false
for idx, val := range env {
parts := strings.Split(val, "=")
if len(parts) < 2 {
continue // malformed entry
}
key := parts[0]
if key != "PATH" {
continue
}
val := strings.Join(parts[1:], "=")
env[idx] = fmt.Sprintf("%s=%s:%s", key, extra, val)
found = true
}
if !found {
const busyboxDefaultPATH = "/usr/local/sbin:/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin"
env = append(env, fmt.Sprintf("PATH=%s:%s", extra, busyboxDefaultPATH))
}
return env
}
```