doc: initial container documentation

Brief overview of configuration support.  Need to add admin-exec level
commands, but since operational and actions still are missing, we keep
it out until after the code review.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2024-02-25 19:49:27 +01:00
parent 580599f549
commit ed86a29bfc
+324 -254
View File
@@ -2,302 +2,372 @@ Containers in Infix
===================
* [Introduction](#introduction)
* [Docker Containers with Podman](#docker-containers-with-podman)
* [Multiple Networks](#multiple-networks)
* [Hybrid Mode](#hybrid-mode)
* [Enabling Container Support](#enabling-container-support)
* [Debugging Containers](#debugging-containers)
* [Caution](#caution)
* [Getting Started](#getting-started)
* [Examples](#examples)
* [Networking and Containers](#networking-and-containers)
* [CNI Bridge](#cni-bridge)
* [CNI Host](#cni-host)
* [Host Networking](#host-networking)
* [Example Containers](#example-containers)
* [System Container](#system-container)
* [Application Container: nftables](#application-container--nftables)
* [Application Container: ntpd](#application-container--ntpd)
* [Upgrading a Container Image](#upgradeing-a-container-image)
Introduction
------------
Default builds of Infix do not enable any container support. See below
section, [Enabling Container Support](#enabling-container-support), for
details on how to enable it using Podman.
Infix comes with native support for Docker containers using [podman][].
The [YANG model][1] describes the current level of support, complete
enough to run both system and application containers.
Networking in containers is provided by both Infix and the Container
Network Interface ([CNI](https://www.cni.dev/)) that Podman supports.
Key design features, like using Linux switchdev, allow users to assign
switch ports directly to containers, not just bridged VETH pairs, this
is a rare and in many cases *unique* feature of Infix.
> A convenience alias `docker=podman` is available in Infix, remember,
> not all features or syntax of docker is available in podman.
All network specific settings are done using the IETF interfaces YANG
model, with augments for containers to ensure smooth integration with
the Container Network Interface ([CNI][]) that podman supports.
Docker Containers with Podman
-----------------------------
Caution
-------
We assume you've booted into Infix and start with a familiar example.
This downloads the `hello-world` example container image and runs it:
A word of warning. Containers run on your system in privileged mode, as
`root`. Even though they are fenced with Linux namespaces, and resource
limited using Linux cgroups, which scope container applications from
seeing and accessing the complete system. There is no guarantee that an
application cannot ever break out of this confinement.
podman run -it --rm docker://hello-world
- If the system is compromised, containers can be used to easily
install malicious software in your system and over the network
- Your system is as secure as anything you run in the container
- If you run containers, there is no security guarantee of any kind
- Running 3rd party container images on your system could open a
security hole/attack vector/attack surface
- An expert with knowledge how to build exploits will be able to
jailbreak/elevate to root
We can also set up a port forward to a little web server:
This being said, a system suspected of being compromised can always be
restored to a safe state with a factory reset. Provided, of course,
that it has secure boot enabled.
podman run -d --rm -p 80:80 docker://nginx:alpine
In detached (`-d`) state you can check the status using `podman ps` and
try to connect to the web server:
Getting Started
---------------
curl http://localhost
In the CLI, containers can be run in one of two ways:
1. `container run IMAGE [COMMAND]`, and
2. enter `configure` context, then `edit container NAME`
The first is useful mostly for testing, or running single commands in an
image. It is a wrapper for `podman run -it --rm ...`, while the latter
is a wrapper and adaptation of `podman create ...`.
The second create a container with a semi-persistent writable layer that
survives container restarts and host system restarts. However, if you
change the container configuration or upgrade the image (see below), the
container will be recreated and the writable layer is lost. This is why
it is recommended to set up a named volume for directories, or use file
mounts, in your container you want truly persistent content.
In fact, in many cases the best way is to create a `read-only` container
and use file mounts and volumes only for the critical parts. Podman
ensures (using tmpfs) `read-only` containers still have writable
directories for certain critical file system paths: `/dev`, `/dev/shm`,
`/run`, `/tmp`, and `/var/tmp`. Meaning, what you most often need is
writable volumes for `/var/lib` and `/etc`, or only file mounts for a
few files in `/etc`. The actual needs depend on the container image and
application to run.
> **Note:** when running containers from public registries, double-check
> that they support the CPU architecture of your host system. Remember,
> unlike virtualization, containers reuse the host's CPU and kernel.
### Examples
Classic Hello World:
admin@example-c0-ff-ee:/> container run docker://hello-world
Persistent web server using nginx, sharing the host's network:
admin@example-c0-ff-ee:/> configure
admin@example-c0-ff-ee:/config> edit container web
admin@example-c0-ff-ee:/config/container/web> set image docker://nginx:alpine
admin@example-c0-ff-ee:/config/container/web> set publish 80:80
admin@example-c0-ff-ee:/config/container/web> set host-network
admin@example-c0-ff-ee:/config/container/web> leave
admin@example-c0-ff-ee:/> show container
Exit to the shell and verify the service with curl, or try to attach
to your device's IP address using your browser:
admin@example-c0-ff-ee:~$ curl http://localhost
or connect to port 80 of your running Infix system with a browser. See
the following sections for how to add more interfaces and start/stop the
container at boot/reboot.
> To add your own content to web server, place the HTML files in, e.g.,
> `/cfg/www/*.html` and add `-v /cfg/www:/usr/share/nginx/html:ro` to
> the command line (above). <https://hub.docker.com/_/nginx/>
the following sections for how to add more interfaces and manage your
container at runtime.
### Multiple Networks
Networking and Containers
-------------------------
It is also possible to start a container with multiple networks. The
approach shown here uses CNI profiles, which means the interfaces names
inside the container will always be: `eth0`, `eth1`, etc.
By default, unlike other systems, persistent[^1] containers have no
networking enabled. All network access has to be set up explicitly.
Currently two types of of [CNI][] networks are supported:
A common setup is to use a VETH pair, with one end in the container and
the other end routed, or bridged, to the rest of the world. The Infix
[CLI Guide](cli/introduction.md) provides examples of both. In either
case you need to create a matching CNI profile for one end of the VETH
pair before starting the container, here we use two network profiles,
the default podman bridge and the VETH profile:
- `cni-host`: one end of a VETH pair, or a physical Ethernet port
- `cni-bridge`: an IP masquerading bridge
cni create host net1 veth0a 192.168.0.42/24
podman run -d --rm --net=podman,net1 --entrypoint "/linuxrc" \
--privileged docker://troglobit/buildroot:latest
The first profile (`podman`) is a the default bridged profile. When a
container is started with that (default behavior), podman dynamically
creates a VETH pair which has one end attached as a bridge port in the
`cni-podman0` bridge managed by podman, and the other end is brought up
as `eth0` inside the container.
### CNI Bridge
The second profile is the one we created, it uses the `host-device`
profile and does not create anything, it simply lifts the peer end of
the pair into the container as `eth1`. This CNI profile can also be
used to hand over control of physical ports to a container.
All interface configuration is done in configure context. Let's start
by creating an IP masquerading bridge, a common default for containers:
> **Note:** here we start the container in `--privileged` mode. This
> allows the container guest unfiltered access to the host system and it
> might not be what you want for a production system. For that at least
> SECCOMP is recommended, which is out of scope for this tutorial.
admin@example-c0-ff-ee:/> configure
admin@example-c0-ff-ee:/config> edit interface docker0
admin@example-c0-ff-ee:/config/interface/docker0/> set type bridge
admin@example-c0-ff-ee:/config/interface/docker0/> set container-network type cni-bridge
admin@example-c0-ff-ee:/config/interface/docker0/> leave
### Real Example
We have to declare the interface type, and then also declare it as a
container network, ensuring the interface cannot be used by the system
for any other purpose. E.g., a `cni-host` interface is supposed to be
used by a container, by declaring it as such we can guarantee that it
would never accidentally be added as a bridge or lag port. Hence, to
move an interface currently set as a `bridge-port` it must be removed
from the bridge before being given to a container.
To be able to preserve state in containers between reboots we need a
writable layer, this is done with `podman create`, after which we can
use `podman start`. We build on the previous example:
The default subnet for a `cni-bridge` is 172.17.0.0/16, the bridge will
take the `.1` address and hand out the rest of the range to containers
in a round-robin like fashion. A container with this `network` get an
automatically created VETH pair connection to the bridge and a lot of
other networking parameters (DNS, default route) are set up.
cni create host net1 veth0a 192.168.0.42/24
podman create --name system --conmon-pidfile=/run/pod:system.pid \
--restart=no --systemd=false --tz=local --privileged \
--net=podman,net1 --entrypoint "/linuxrc" -p 222:22 \
docker://troglobit/buildroot:latest
Some of the defaults of a `cni-bridge` can be changed, e.g., instead of
`set container-network type cni-bridge`, above, do:
Here we map the host port 222 to the SSH port of the container, but one
can just as easily map the host's port 22 (SSH). Just make sure to
first disable the host's SSH service.
admin@example-c0-ff-ee:/config/interface/docker0/> edit container-network
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> set type cni-bridge
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> edit subnet 192.168.0.0/16
admin@example-c0-ff-ee:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> set gateway 192.168.255.254
admin@example-c0-ff-ee:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> end
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> edit route 10.0.10.0/24
admin@example-c0-ff-ee:/config/interface/docker0/container-network/route/10.0.10.0/24/> set gateway 192.168.10.254
admin@example-c0-ff-ee:/config/interface/docker0/container-network/route/10.0.10.0/24/> end
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> end
admin@example-c0-ff-ee:/config/interface/docker0/> leave
> **Note:** the new options used here are required for enabling
> monitoring and automate start/stop of containers at boot/reboot.
Other network settings, like DNS and domain, use built-in defaults in
CNI, but can be overridden from each container. Other common settings
per container is the IP address and name of the network interface inside
the container. The default, after each stop/start cycle, or reboot of
the host, is to name the interfaces `eth0`, `eth1`, in the order they
are given in the `network` list, and to give the container the next
address in a `cni-bridge`. Below an example of a system container calls
`set network docker0`, here we show how to set options for that network:
This creates the named container `system` which we can now start:
podman start system
and stop:
podman stop system
For this particular image[^1] we need to modify its defaults a bit,
because it is set up to run a DHCP client on the first Ethernet
interface, which in our case is the `podman` default CNI bridge. In
fact, we don't want the container to set up any networking since that is
handled by Infix and podman.
root@infix-12-34-56:~$ podman start system
root@infix-12-34-56:~$ podman exec -it system sh
/ # rm -rf /etc/network/interfaces
/ # exit
root@infix-12-34-56:~$ podman stop system
The change is now saved in the writable layer and the next time the
container is started it will look like this:
root@infix-12-34-56:~$ podman start system
root@infix-12-34-56:~$ podman exec -it system sh
/ # ifconfig
eth0 Link encap:Ethernet HWaddr A2:32:4C:2B:5E:51
inet addr:10.88.0.11 Bcast:10.88.255.255 Mask:255.255.0.0
inet6 addr: fe80::a032:4cff:fe2b:5e51/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:204 errors:0 dropped:107 overruns:0 frame:0
TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:48240 (47.1 KiB) TX bytes:1102 (1.0 KiB)
admin@example-c0-ff-ee:/config/container/ntpd/> edit network docker0
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/>
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> set option
<string> Options for CNI bridges.
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> help option
NAME
option <string>
eth1 Link encap:Ethernet HWaddr 56:B5:4E:D1:9C:E5
inet addr:192.168.0.42 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::54b5:4eff:fed1:9ce5/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:274 errors:0 dropped:213 overruns:0 frame:0
TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:60926 (59.4 KiB) TX bytes:1377 (1.3 KiB)
Automating start/stop at boot/reboot is documented in the next section.
[^1]: A common task one has to do for many other standard images, e.g.,
Alpine Linux and BusyBox available on Docker Hub.
### Hybrid Mode
Since container setup and configuration is not modeled in YANG yet, we
use the Infix *Hybrid mode*, described in [Infix Variants](variant.md).
To start containers in *Hybrid Mode*, provided the images have been
downloaded with `podman pull docker://troglobit/buildroot:latest` and
a container created (above):
```
root@infix:/cfg/start.d$ cat <<HERE >20-enable-container.sh
#!/bin/sh
# Remember to create the veth0a <--> vet0b pair in the CLI first!
cni create host net1 veth0a 192.168.0.42/24
cat <<EOF > /etc/finit.d/available/pod:system.conf
service name:pod :system pid:!/run/pod:system.pid podman --syslog start system -- System container
EOF
initctl enable pod:system
exit 0
HERE
root@infix:/cfg/start.d$ chmod +x 20-enable-container.sh
```
Reboot to activate the changes. To activate the changes without
rebooting, run the script and call `initctl reload`.
> **Note:** the `/etc` directory is a `tmpfs` ramdisk and contents will
> be lost on reboot, so to retain custom CNI profiles after reboot you
> need to either save them and restore in the script above, or recreate
> them on every boot.
DESCRIPTION
Options for CNI bridges.
Example: ip=1.2.3.4 to request a specific IP, both IPv4 and IPv6.
interface_name=foo0 name to set interface name inside container.
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> set option ip=172.17.0.2
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> set option interface_name=wan
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> leave
Enabling Container Support
--------------------------
### CNI Host
Container support is not enabled by default because it is not a common
customer feature, it also prolongs build times a lot due to bringing in
a build-time dependency on Go.
Another common use-case is to move a network interface into the network
namespace of a container. Which the CNI bridge network does behind the
scenes with one end of the automatically created VETH pair. This works
with regular Ethernet interfaces as well, but here we will use a VETH
pair as an example along with a regular bridge (where other Ethernet
interfaces may live as well).
However, customer specific builds may have it, and you can also roll
your own based on any of the available `defconfigs`. For example:
admin@example-c0-ff-ee:/config/> edit interface veth0
admin@example-c0-ff-ee:/config/interface/veth0a/> set veth peer ntpd
admin@example-c0-ff-ee:/config/interface/veth0a/> set ipv4 address 192.168.0.1 prefix-length 24
admin@example-c0-ff-ee:/config/interface/veth0a/> end
admin@example-c0-ff-ee:/config/> edit interface ntpd
admin@example-c0-ff-ee:/config/interface/ntpd/> set ipv4 address 192.168.0.2 prefix-length 24
admin@example-c0-ff-ee:/config/interface/ntpd/> set container-network
admin@example-c0-ff-ee:/config/interface/ntpd/container-network/> set
cd infix/
make x86_64_defconfig
Run menuconfig, search for `podman` using `/`, enable it and build:
make menuconfig
...
make
Enabling [podman][] select `crun`, `conmon`, and all other dependencies.
The build will take a while, but eventually you can:
make run
This is a routed setup, where we reserve 192.168.0.0/24 for the network
between the host and the `ntpd` container. A perhaps more common case
is to put `veth0` as a port in a bridge with other physical ports. The
point of the routed case is that port forwarding from the container in
this case is limited to a single interface, not *all interfaces* as is
the default in the CNI Bridge setup.
Debugging Containers
--------------------
### Host Networking
If the host system is not powered down or rebooted properly, containers
may not start up as they should on the following boot. Below is a very
common problem and solution shown.
The third use-case is host networking, this is where a container share
the network namespace of the host. An example here could be a nftables
or ntpd container -- single applications which add core functionality to
the host operating system.
```
root@infix-12-34-56:~$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@infix-12-34-56:~$ grep nginx /var/log/syslog
Jun 25 10:15:48 infix-12-34-56 finit[1]: Service pod:nginx[2376] died, restarting in 5000 msec (10/10)
Jun 25 10:15:48 infix-12-34-56 finit[1]: Starting pod:nginx[2408]
Jun 25 10:15:53 infix-12-34-56 finit[1]: Service pod:nginx keeps crashing, not restarting.
```
The host networking setup cannot be combined with any other network.
If this the system is isolated from remote network access, start by
verifying the image is downloaded:
```
root@infix-12-34-56:/cfg/start.d$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/nginx alpine 4937520ae206 10 days ago 43.2 MB
docker.io/troglobit/buildroot latest 68faf6b20f1a 6 weeks ago 41.4 MB
```
OK, let's see what the `podman-service` step (above) created:
```
root@infix-12-34-56:/cfg/start.d$ initctl show pod-nginx.conf
service name:pod :nginx podman run --name nginx --rm -p 80:80 nginx:alpine -- Nginx container
```
Try starting the container manually. Remember to add the `-d` flag to
emulate detached/background operation:
```
root@infix-12-34-56:/cfg/start.d$ podman run --name nginx --rm -d -p 8080:80 nginx:alpine
Error: creating container storage: the container name "nginx" is already in use by 9c73bd8d505b1585d241595bfadede361b87f6c1be9a5656253b5a4d73da57e0. You have to remove that container to be able to reuse that name: that name is already in use
```
Aha, a lingering image with the same name! Where is it?
```
root@infix-12-34-56:/cfg/start.d$ podman ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f3386ae9517f docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago ecstatic_panini
bf0c6178ea26 docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago determined_brown
385155f479c0 docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago vibrant_engelbart
99a1b3319d9e docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago dreamy_tesla
9c73bd8d505b docker.io/library/nginx:alpine nginx -g daemon o... 11 minutes ago Created 0.0.0.0:80->80/tcp nginx
8a5290504ebc docker.io/troglobit/buildroot:latest 10 minutes ago Created mystifying_liskov
```
Oh, we have two lingering containers that were created but did not stop
correctly. Let's remove them:
```
root@infix-12-34-56:/cfg/start.d$ docker rm -f 9c73bd8d505b
9c73bd8d505b
root@infix-12-34-56:/cfg/start.d$ docker rm -f 8a5290504ebc
8a5290504ebc
```
Now we can manually restart the (supervised) container:
```
root@infix-12-34-56:/cfg/start.d$ initctl restart pod:nginx
root@infix-12-34-56:/cfg/start.d$ initctl status pod:nginx
Status : running
Identity : pod:nginx
Description : Nginx container
Origin : /etc/finit.d/enabled/pod-nginx.conf
Command : podman run --name nginx --rm -p 80:80 nginx:alpine
PID file : none
PID : 2669
User : root
Group : root
Uptime : 15 sec
Restarts : 11 (0/10)
Runlevels : [---234-----]
Memory : 63.8M
CGroup : /system/pod-nginx cpu 0 [100, max] mem [0, max]
├─ 2669 podman run --name nginx --rm -p 80:80 nginx:alpine
└─ 2816 conmon --api-version 1 -c 44d24aa7e98b67ff811596984462b902af3b09a04b4f9bef86e11d246b8cc2ff -u 44d24aa7e98b67ff8
Jun 25 10:15:48 infix-12-34-56 finit[1]: Service pod:nginx[2376] died, restarting in 5000 msec (10/10)
Jun 25 10:15:48 infix-12-34-56 finit[1]: Starting pod:nginx[2408]
Jun 25 10:15:53 infix-12-34-56 finit[1]: Service pod:nginx keeps crashing, not restarting.
Jun 25 10:47:55 infix-12-34-56 finit[1]: Starting pod:nginx[2669]
```
For an example, see below.
Example Containers
------------------
### System Container
Let's try out what we've learned by setting up a system container, a
container providing multiple services, using the `docker0` interface
we created previously:
admin@example-c0-ff-ee:/> configure
admin@example-c0-ff-ee:/config> edit container system
admin@example-c0-ff-ee:/config/container/system/> set image ghcr.io/kernelkit/curios:edge
admin@example-c0-ff-ee:/config/container/system/> set network docker0
admin@example-c0-ff-ee:/config/container/system/> set publish 222:22
admin@example-c0-ff-ee:/config/container/system/> leave
> **Note:** ensure you have a network connection to the registry.
> If the image cannot be pulled, creation of the container will be
> put in a queue and be retried every time there is a change in the
> routing table, e.g., default route is added.
Provided the image is downloaded successfully, a new `system` container
now runs behind the docker0 interface, forwarding container port 22 to
port 222 on all of the host's interfaces. (See `help publish` in the
container configuration context for the full syntax.)
Available containers can be accessed from admin-exec:
admin@example-c0-ff-ee:/> show container
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
439af2917b44 ghcr.io/kernelkit/curios:edge 41 hours ago Up 16 hours 0.0.0.0:222->222/tcp system
This is a system container, so you can "attach" to it by starting a
shell (or logging in with SSH):
admin@example-c0-ff-ee:/> container shell system
root@439af2917b44:/#
Notice how the hostname inside the container changes. By default the
container ID (hash) is used, but this can be easily changed:
root@439af2917b44:/# exit
admin@infix-00-00-00:/> configure
admin@infix-00-00-00:/config/> edit container system
admin@infix-00-00-00:/config/container/system/> set hostname system1
admin@infix-00-00-00:/config/container/system/> leave
admin@infix-00-00-00:/> container shell system
root@system1:/#
[^1]: this does not apply to the admin-exec command `container run`.
This command is intended to be used for testing and evaluating
container images. Such containers are given a private network
behind an IP masquerading bridge.
### Application Container: nftables
Infix currently does not have a native firewall configuration, and even
when it does it will never expose the full capabilities of `nftables`.
For really advanced setups, the following will be the only alternative:
admin@example-c0-ff-ee:/> configure
admin@example-c0-ff-ee:/config> edit container nftables
admin@example-c0-ff-ee:/config/container/system/> set image ghcr.io/kernelkit/curios-nftables:edge
admin@example-c0-ff-ee:/config/container/system/> set host-network
admin@example-c0-ff-ee:/config/container/system/> edit file nftables.conf
admin@example-c0-ff-ee:/config/container/system/file/nftables.conf/> set path /etc/nftables.conf
admin@example-c0-ff-ee:/config/container/system/file/nftables.conf/> set content
... interactive editor starts up where you can paste your rules ...
admin@example-c0-ff-ee:/config/container/system/file/nftables.conf/> leave
### Application Container: ntpd
The default NTP server/client in Infix is Chrony, a fully working and
capable workhorse for most use-cases. However, it does not support a
feature like multicasting, for that you need ISC ntpd.
As we did with `nftables`, previously, we can use host networking and
set up a read-only config file that is bind-mounted into the container's
file system and store in the host's `startup-config`. However, `ntpd`
also saves clock drift information in `/var/lib/ntpd`, so we will also
use volumes in this example.
> Infix support named volumes (only), and it is not possible to share a
> volume between containers. All the tricks possible with volumes may
> be added in a later release.
A volume is an automatically created read-writable area that follows the
life of your container. They survive reboots and upgrading of the base
image, unlike the persistent writable layer you get by default, which
does not survive upgrades. The volume is created by podman when the
container first starts up, unlike a regular bind mount it synchronizes
with the contents of the underlying container image's path on the first
start. I.e., "bind-mount, if empty: then rsync".
admin@example-c0-ff-ee:/> configure
admin@example-c0-ff-ee:/config> edit container ntpd
admin@example-c0-ff-ee:/config/container/ntpd/> set image ghcr.io/kernelkit/curios-ntpd:edge
admin@example-c0-ff-ee:/config/container/ntpd/> set network ntpd # From veth0 above
admin@example-c0-ff-ee:/config/container/ntpd/> edit file ntp.conf
admin@example-c0-ff-ee:/config/container/ntpd/file/ntp.conf/> set path /etc/ntp.conf
admin@example-c0-ff-ee:/config/container/ntpd/file/ntp.conf/> set content
... interactive editor starts up where you can paste your rules ...
admin@example-c0-ff-ee:/config/container/ntpd/file/ntp.conf/> end
admin@example-c0-ff-ee:/config/container/ntpd/> edit volume varlib
admin@example-c0-ff-ee:/config/container/ntpd/volume/varlib/> set path /var/lib
admin@example-c0-ff-ee:/config/container/ntpd/volume/varlib/> leave
admin@example-c0-ff-ee:/> copy running-config startup-config
The `ntp.conf` file is stored in the host's `startup-config` and any
state data in the container's `/var/lib` is retained between reboots
and across image upgrades.
Upgrading a Container Image
---------------------------
All container configurations are locked to the image hash at the time of
first download, not just ones that use an `:edge` or `:latest` tag. An
upgrade of containers using versioned images is more obvious -- update
the configuration -- but the latter is a bit trickier. Either remove
the configuration and recreate it (leave/apply the changes between), or
use the admin-exec level command:
admin@example-c0-ff-ee:/> container upgrade NAME
Where `NAME` is the name of your container. This command stops your
container, does a `container pull IMAGE`, and then recreates the
container with the new image. Upgraded containers are not automatically
restarted.
admin@example-c0-ff-ee:/> container start NAME
> **Note:** the default writable layer is lost when upgrading the image
> Use named volumes for directories with writable content you wish to
> keep over an upgrade.
[1]: https://github.com/kernelkit/infix/blob/main/src/confd/yang/infix-containers%402023-12-14.yang
[CNI]: https://www.cni.dev/
[podman]: https://podman.io