Words with Podman
I’ve installed Podman, and configured rootless. Let’s get Words generated inside a container.
We’ll be making use of Pods, which will be familiar to anyone who’s used Kubernetes before. A full-blown container orchestration system sounds unhinged but it’s the official way so let’s give it a whirl.
Create a pod
We’ll start by creating a pod to contain the JavaScript ecosystem (namely Parcel, which optimises the static content generated by a hodge-podge of ELisp I’ve cobbled together).
podman pod create --name words -p 1234:1234
Let’s take a look at that pod:
podman pod ls
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS c7efd4ede37a words Created 5 seconds ago e2b3df0b378c 1
Create a container
podman-run
podman run --help
Run a command in a new container Description: Runs a command in a new container from the given image Usage: podman run [options] IMAGE [COMMAND [ARG...]] Examples: podman run imageID ls -alF /etc podman run --network=host imageID dnf -y install java podman run --volume /var/hostdir:/var/ctrdir -i -t fedora /bin/bash Options: --add-host strings Add a custom host-to-IP mapping (host:ip) (default []) --annotation strings Add annotations to container (key:value) -a, --attach strings Attach to STDIN, STDOUT or STDERR --authfile string Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override --blkio-weight string Block IO weight (relative weight) accepts a weight value between 10 and 1000. --blkio-weight-device DEVICE_NAME:WEIGHT Block IO weight (relative device weight, format: DEVICE_NAME:WEIGHT) --cap-add strings Add capabilities to the container --cap-drop strings Drop capabilities from the container --cgroup-conf strings Configure cgroup v2 (key=value) --cgroup-parent string Optional parent cgroup for the container --cgroupns string cgroup namespace to use --cgroups string control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split") (default "enabled") --cidfile string Write the container ID to the file --conmon-pidfile string Path to the file that will receive the PID of conmon --cpu-period uint Limit the CPU CFS (Completely Fair Scheduler) period --cpu-quota int Limit the CPU CFS (Completely Fair Scheduler) quota --cpu-rt-period uint Limit the CPU real-time period in microseconds --cpu-rt-runtime int Limit the CPU real-time runtime in microseconds --cpu-shares uint CPU shares (relative weight) --cpus float Number of CPUs. The default is 0.000 which means no limit --cpuset-cpus string CPUs in which to allow execution (0-3, 0,1) --cpuset-mems string Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. -d, --detach Run container in background and print container ID --detach-keys [a-Z] Override the key sequence for detaching a container. Format is a single character [a-Z] or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\`, `]`, `^` or `_` (default "ctrl-p,ctrl-q") --device strings Add a host device to the container --device-cgroup-rule strings Add a rule to the cgroup allowed devices list --device-read-bps strings Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb) --device-read-iops strings Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000) --device-write-bps strings Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb) --device-write-iops strings Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000) --disable-content-trust This is a Docker specific option and is a NOOP --dns strings Set custom DNS servers --dns-opt strings Set custom DNS options --dns-search strings Set custom DNS search domains --entrypoint string Overwrite the default ENTRYPOINT of the image -e, --env stringArray Set environment variables in container (default [PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin,TERM=xterm]) --env-file strings Read in a file of environment variables --env-host Use all current host environment variables in container --expose strings Expose a port or a range of ports --gidmap strings GID map to use for the user namespace --group-add strings Add additional groups to join --health-cmd string set a healthcheck command for the container ('none' disables the existing healthcheck) --health-interval string set an interval for the healthchecks (a value of disable results in no automatic timer setup) (default "30s") --health-retries uint the number of retries allowed before a healthcheck is considered to be unhealthy (default 3) --health-start-period string the initialization time needed for a container to bootstrap (default "0s") --health-timeout string the maximum time allowed to complete the healthcheck before an interval is considered failed (default "30s") -h, --hostname string Set container hostname --http-proxy Set proxy environment variables in the container based on the host proxy vars (default true) --image-volume string Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore") (default "bind") --init Run an init binary inside the container that forwards signals and reaps processes --init-path string Path to the container-init binary -i, --interactive Keep STDIN open even if not attached --ip string Specify a static IPv4 address for the container --ipc string IPC namespace to use --kernel-memory <number>[<unit>] Kernel memory limit (format: <number>[<unit>], where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes)) -l, --label stringArray Set metadata on container --label-file strings Read in a line delimited file of labels --log-driver string Logging driver for the container --log-opt strings Logging driver options --mac-address string Container MAC address (e.g. 92:d0:c6:0a:29:33) -m, --memory <number>[<unit>] Memory limit (format: <number>[<unit>], where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes)) --memory-reservation <number>[<unit>] Memory soft limit (format: <number>[<unit>], where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes)) --memory-swap string Swap limit equal to memory plus swap: '-1' to enable unlimited swap --memory-swappiness int Tune container memory swappiness (0 to 100, or -1 for system default) (default -1) --mount stringArray Attach a filesystem mount to the container --name string Assign a name to the container --network string Connect a container to a network (default "slirp4netns") --network-alias strings Add network-scoped alias for the container --no-healthcheck Disable healthchecks on container --no-hosts Do not create /etc/hosts within the container, instead use the version from the image --oom-kill-disable Disable OOM Killer --oom-score-adj int Tune the host's OOM preferences (-1000 to 1000) --override-arch ARCH use ARCH instead of the architecture of the machine for choosing images --override-os OS use OS instead of the running OS for choosing images --override-variant string Use _VARIANT_ instead of the running architecture variant for choosing images --pid string PID namespace to use --pids-limit int Tune container pids limit (set 0 for unlimited, -1 for server defaults) --pod string Run container in an existing pod --pod-id-file string Read the pod ID from the file --preserve-fds uint Pass a number of additional file descriptors into the container --privileged Give extended privileges to container -p, --publish strings Publish a container's port, or a range of ports, to the host (default []) -P, --publish-all Publish all exposed ports to random ports on the host interface --pull string Pull image before creating ("always"|"missing"|"never") (default "missing") -q, --quiet Suppress output information when pulling images --read-only Make containers root filesystem read-only --read-only-tmpfs When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp (default true) --replace If a container with the same name exists, replace it --restart string Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped") --rm Remove container (and pod if created) after exit --rmi Remove container image unless used by other containers --rootfs The first argument is not an image but the rootfs to the exploded container --sdnotify string control sd-notify behavior ("container"|"conmon"|"ignore") (default "container") --seccomp-policy string Policy for selecting a seccomp profile (experimental) (default "default") --security-opt stringArray Security Options --shm-size <number>[<unit>] Size of /dev/shm (format: <number>[<unit>], where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes)) (default "65536k") --sig-proxy Proxy received signals to the process (default true) --stop-signal string Signal to stop a container. Default is SIGTERM --stop-timeout uint Timeout (in seconds) to stop a container. Default is 10 (default 10) --subgidname string Name of range listed in /etc/subgid for use in user namespace --subuidname string Name of range listed in /etc/subuid for use in user namespace --sysctl strings Sysctl options --systemd string Run container in systemd mode ("true"|"false"|"always") (default "true") --tmpfs tmpfs Mount a temporary filesystem (tmpfs) into a container -t, --tty Allocate a pseudo-TTY for container --tz string Set timezone in container --uidmap strings UID map to use for the user namespace --ulimit strings Ulimit options --umask string Set umask in container (default "0022") -u, --user string Username or UID (format: <name|uid>[:<group|gid>]) --userns string User namespace to use --uts string UTS namespace to use -v, --volume stringArray Bind mount a volume into the container --volumes-from stringArray Mount volumes from the specified container(s) -w, --workdir string Working directory inside the container
That’s a lot of options! Everything from network configuration to resource limits; just as you’d expect when you’re creating a container.
podman-rm
Given I’m running things with Podman, I need to be careful not to clobber things (without some careful massaging, publishing this file creates new containers over and over, and appends to the Kubernetes YAML we generate a little later).
podman-run
Now we can run a container inside our words
pod via podman-run(1)
.
podman run \ --detach \ --name=words-yarn-dev \ --pod=words \ --restart=on-failure \ --volume="$dir:/usr/src/app:rw" \ --workdir=/usr/src/app \ docker://docker.io/library/node:alpine \ yarn dev
a1a8c920b3248a0c527b99c8ce02ba8ff9989d848deb28aa2409d49895dc1352
podman pod inspect words | jq .
{ "Id": "c7efd4ede37aa765d7b9ceb551535c6001fba879c22ae385225f0ad167976635", "Name": "words", "Created": "2021-02-07T16:48:48.994240915Z", "CreateCommand": [ "podman", "pod", "create", "--name", "words", "-p", "1234:1234" ], "State": "Running", "Hostname": "words", "CreateCgroup": true, "CgroupParent": "/libpod_parent", "CgroupPath": "/libpod_parent/c7efd4ede37aa765d7b9ceb551535c6001fba879c22ae385225f0ad167976635", "CreateInfra": true, "InfraContainerID": "e2b3df0b378c08bc08887e6461340fa92b5f37c9b1ad24be990e5b80db075e1d", "InfraConfig": { "PortBindings": { "1234/tcp": [ { "HostIp": "", "HostPort": "1234" } ] }, "HostNetwork": false, "StaticIP": "", "StaticMAC": "", "NoManageResolvConf": false, "DNSServer": null, "DNSSearch": null, "DNSOption": null, "NoManageHosts": false, "HostAdd": null, "Networks": null, "NetworkOptions": null }, "SharedNamespaces": [ "uts", "ipc", "net" ], "NumContainers": 2, "Containers": [ { "Id": "a1a8c920b3248a0c527b99c8ce02ba8ff9989d848deb28aa2409d49895dc1352", "Name": "words-yarn-dev", "State": "running" }, { "Id": "e2b3df0b378c08bc08887e6461340fa92b5f37c9b1ad24be990e5b80db075e1d", "Name": "c7efd4ede37a-infra", "State": "running" } ] }
Making this reusable
Now we can export the configuration for our pod into YAML for the next time we want to see some words.
podman generate kube words
# Generation of Kubernetes YAML is still under development! # # Save the output of this file and use kubectl create -f to import # it into Kubernetes. # # Created with podman-2.2.1 apiVersion: v1 kind: Pod metadata: creationTimestamp: "2021-02-07T16:50:18Z" labels: app: words name: words spec: containers: - command: - yarn - dev env: - name: PATH value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - name: TERM value: xterm - name: container value: podman - name: YARN_VERSION value: 1.22.5 - name: NODE_VERSION value: 15.8.0 - name: HOSTNAME value: words image: docker.io/library/node:alpine name: words-yarn-dev ports: - containerPort: 1234 hostPort: 1234 protocol: TCP resources: {} securityContext: allowPrivilegeEscalation: true capabilities: drop: - CAP_MKNOD - CAP_NET_RAW - CAP_AUDIT_WRITE privileged: false readOnlyRootFilesystem: false seLinuxOptions: {} volumeMounts: - mountPath: /usr/src/app name: home-jcf-code-words workingDir: /usr/src/app restartPolicy: OnFailure volumes: - hostPath: path: /home/jcf/code/words type: Directory name: home-jcf-code-words status: {} --- metadata: creationTimestamp: null spec: {} status: loadBalancer: {}
Mention of my $HOME
in that configuration means it’s anything but portable unfortunately. That said, it will work next time I’m writing on this machine (or on any similar machine).