Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| docs:guide-user:virtualization:docker_host [2023/08/16 15:50] – Podman stokito | docs:guide-user:virtualization:docker_host [2024/10/08 16:59] (current) – [Docker Community Edition] luci-app-dockerman now depends on dockerd stokito | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ======= OpenWrt as Docker container host ======= | ======= OpenWrt as Docker container host ======= | ||
| - | OpenWrt can be a [[wp> | + | [[wp> |
| - | There are two ways to use Docker as a host, install Docker Community Edition, | + | To run containers, users may install Docker Community Edition, use native OpenWrt tools, or use Podman. While Docker |
| - | You will probably need to [[docs: | + | ===== Prerequisites ===== |
| - | Also in most cases you will be running the container as a specific user and will give it access to some folder outside the container, where it can store its configuration and the data. So you will probably | + | For devices with small flash partitions |
| - | ===== Install Docker Community Edition ===== | + | Also in many cases you will be running the container as a specific user that will need access to some folder outside the container |
| - | * Install **docker-ce** package | + | |
| - | * Install **luci-app-dockerman** package to get a control panel for containers in Luci | + | |
| - | The default folder for docker in the dockerman luci interface is **/opt/docker** so you want to mount your storage at **/opt** or change the folder in **Docker** > **Overview** > **Docker | + | ===== Docker Community Edition ===== |
| + | First you need to install | ||
| + | It provides an [[https://docs.docker.com/engine/ | ||
| - | ==== Add an image ==== | + | Then you need a //client// e.g. '' |
| - | To add an image, search it on [[https:// | + | To save space you can use the '' |
| + | |||
| + | As a GUI client install '' | ||
| + | |||
| + | The default folder for docker in the dockerman Luci interface is **/ | ||
| + | |||
| + | |||
| + | ==== Adding images | ||
| + | Search for an image on [[https:// | ||
| In Luci go to **Docker** > **Images** and paste that text in the **Pull Image** box, then click **Pull**. The page will show the download progress. | In Luci go to **Docker** > **Images** and paste that text in the **Pull Image** box, then click **Pull**. The page will show the download progress. | ||
| - | For longer | + | Note for larger |
| - | Then in Luci go to **Docker** > **Containers** > **Add**. In the new container page, select the docker image from the **Docker Image** menu, and then set all other parameters (usually the available/ | + | Once you have your images, |
| - | ==== Configure | + | ==== Configure |
| Config is located in ''/ | Config is located in ''/ | ||
| Line 46: | Line 54: | ||
| - | + | ===== Native | |
| - | ===== Use native | + | Instead of running Docker CE users may want to use the procd init system |
| - | Procd init system | + | |
| - | The uxc command line tool handles the basic operations on containers as defined by the spec.\\ | + | |
| - | This allows to use it as a drop-in replacement for Docker' | + | |
| Detailed but possibly outdated info available on https:// | Detailed but possibly outdated info available on https:// | ||
| - | ==== install packages ==== | ||
| - | For 20.0x install the following: | ||
| - | < | ||
| - | opkg install kmod-veth uxc ujail-console | ||
| - | </ | ||
| - | |||
| - | For newer snapshots: | ||
| + | ==== Install packages ==== | ||
| + | Install the following: | ||
| < | < | ||
| opkg install kmod-veth uxc procd-ujail procd-ujail-console | opkg install kmod-veth uxc procd-ujail procd-ujail-console | ||
| </ | </ | ||
| - | ==== create | + | |
| + | ==== Create | ||
| < | < | ||
| uci batch <<EOF | uci batch <<EOF | ||
| Line 83: | Line 84: | ||
| </ | </ | ||
| - | ====creating | + | ==== Creating |
| To create an OCI run-time bundle, which is needed for uxc, follow these steps. | To create an OCI run-time bundle, which is needed for uxc, follow these steps. | ||
| Line 101: | Line 102: | ||
| This is quite cumbersome. If someone knows a better way, please do update this page. | This is quite cumbersome. If someone knows a better way, please do update this page. | ||
| - | ====import | + | ==== Import |
| (assuming OCI run-time bundle with config.json in / | (assuming OCI run-time bundle with config.json in / | ||
| < | < | ||
| Line 117: | Line 118: | ||
| ===== Podman ===== | ===== Podman ===== | ||
| - | https:// | + | https:// |
| - | It has a smaller size than the Docker so you may consider to use it instead. | + | Here is example setup using podman |
| - | There is also [[https:// | + | |
| + | Install necessary packages: | ||
| + | <code bash> | ||
| + | opkg install conmon crun catatonit netavark podman external-protocol | ||
| + | </ | ||
| + | |||
| + | If you want to use rootless containers, you need additional packages, such as slirp4netns. | ||
| + | There' | ||
| + | This guide excludes their setup currently. | ||
| + | |||
| + | **Network** | ||
| + | |||
| + | Let's start by reviewing our container network' | ||
| + | / | ||
| + | < | ||
| + | { | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | { | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | ], | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | I have a rather large network (10.0.0.0/ | ||
| + | In this file you define your network named podman. You can have multiple networks. | ||
| + | |||
| + | **Firewall/ | ||
| + | |||
| + | Next, we make sure internal port forwarding/ | ||
| + | < | ||
| + | config firewall | ||
| + | option driver ' | ||
| + | </ | ||
| + | |||
| + | We do this to rather use openwrt' | ||
| + | when you start your first container network, when podman0 interface comes up. | ||
| + | |||
| + | Next is time to setup network and firewall on the openwrt' | ||
| + | < | ||
| + | config interface ' | ||
| + | option proto ' | ||
| + | option device ' | ||
| + | </ | ||
| + | |||
| + | And we also want to use firewall, in this setup we allow access from lan to podman, but not the other way around, we also | ||
| + | grant access from wan, and to wan. / | ||
| + | < | ||
| + | config zone | ||
| + | option name ' | ||
| + | option input ' | ||
| + | option output ' | ||
| + | option forward ' | ||
| + | option mtu_fix ' | ||
| + | list network ' | ||
| + | |||
| + | config forwarding | ||
| + | option src ' | ||
| + | option dest ' | ||
| + | |||
| + | config forwarding | ||
| + | option src ' | ||
| + | option dest ' | ||
| + | |||
| + | config forwarding | ||
| + | option src ' | ||
| + | option dest ' | ||
| + | </ | ||
| + | |||
| + | Now that we have blocked access to LAN, our containers are missing access to DNS, unless we configure them to use something else, | ||
| + | such as 8.8.8.8 - so we make a exception, containers can connect to lan, but only on port 53 (DNS): | ||
| + | < | ||
| + | config rule | ||
| + | option name ' | ||
| + | option src ' | ||
| + | option dest_port ' | ||
| + | option target ' | ||
| + | </ | ||
| + | |||
| + | Now initial network setup is complete. | ||
| + | |||
| + | **Service** | ||
| + | |||
| + | Next we make sure that podman service is started on boot. This is optional, but if you want to follow this guide, it comes handy later. | ||
| + | Podman service does not create/ | ||
| + | This socket is located at / | ||
| + | podman-docker-compatibility package usually only contains a link to this socket to standard docker' | ||
| + | script that forwards it's command-line to podman command. Service actually starts when you use podman, but we want to make sure it's listed | ||
| + | as openwrt instance, and for further more advanced setup, this is helpful. | ||
| + | |||
| + | <code bash> | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | After reboot, you can start using your podman setup. Guide continues, we make a web server and caddy proxy as example projects | ||
| + | and handle forwarding of traffic to them. We will store our container data in /srv. | ||
| + | |||
| + | **Container storage (optional) ** | ||
| + | |||
| + | I have a lot of disk space available, so I set my graphroot to hard drive, instead of | ||
| + | storing container data in RAM, such as /tmp or /var which it defaults. First make a proper path: | ||
| + | <code bash> | ||
| + | mkdir -p / | ||
| + | </ | ||
| + | |||
| + | And edit your / | ||
| + | < | ||
| + | graphroot = "/ | ||
| + | </ | ||
| + | default was: "/// | ||
| + | |||
| + | Without setting graphroot, your setup works also, it's just that I happen to have a lot of disk | ||
| + | space in my setups, I rather store them on hard drive instead of memory, such as /tmp or /var. | ||
| + | |||
| + | **Local image storage (optional)** | ||
| + | |||
| + | My setup builds my containers on every boot and I want to speed up that process, so I want images for Caddy proxy and nginx web server to | ||
| + | be stored in permanent storage. But beware, every time you want to make changes to these images, you need to restart this process completely | ||
| + | from beginning, first by resetting // | ||
| + | all this, you must remove containers using these images, along the images, from Podman' | ||
| + | restore // | ||
| + | necessary as well. Complicated? | ||
| + | To begin with, reboot your computer and make sure any containers are not created, started or even exist, this will help to avoid problems. | ||
| + | |||
| + | Create directory / | ||
| + | < | ||
| + | mkdir -p / | ||
| + | </ | ||
| + | |||
| + | Then check your / | ||
| + | < | ||
| + | additionalimagestores = [] | ||
| + | </ | ||
| + | |||
| + | If you have to change this line, you must reboot to podman service restart so this takes effect. | ||
| + | Following line should be found, it is default setting. With this line, we do not have images locally stored in permanent store. | ||
| + | Next issue following commands: | ||
| + | < | ||
| + | podman --root / | ||
| + | podman --root / | ||
| + | podman --root / | ||
| + | </ | ||
| + | replace image urls with your own, first command sets up path images as local image store, | ||
| + | and next commands pull your images to local image store. You can also pull your pause | ||
| + | image, for example: // | ||
| + | |||
| + | After this, make changes to your / | ||
| + | < | ||
| + | additionalimagestores = [ | ||
| + | "/ | ||
| + | ] | ||
| + | </ | ||
| + | and reboot. Now when creating containers that use locally stored images, they do not need | ||
| + | to be pulled from internet, they are instantly available. If you use not locally stored | ||
| + | images, they work fine; they just are pulled from internet. | ||
| + | |||
| + | This is a one time operation; changes, removals, adds of locally stored images cannot | ||
| + | be modified very easily. To restart process; remove containers using locally stored images, | ||
| + | remove then these images from podman (podman image rm < | ||
| + | your // | ||
| + | service doesn' | ||
| + | from / | ||
| + | < | ||
| + | rm -rf / | ||
| + | </ | ||
| + | |||
| + | reboot again, and begin this again from start of this section of guide. | ||
| + | |||
| + | **Pod** | ||
| + | We will start by creating a pod. Pod can hold multiple containers, they share some attributes, | ||
| + | such as ip address. As we are trying to build a web server setup, we want IP address to be | ||
| + | always same for this pod. I have created a script / | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | |||
| + | podman pod create \ | ||
| + | --replace \ | ||
| + | --name servers \ | ||
| + | --hostname srv \ | ||
| + | --ip 10.129.0.2 | ||
| + | |||
| + | podman pod start servers | ||
| + | </ | ||
| + | This creates, or replaces if one exists, pod named servers, gives it a hostname srv (not important) and a static | ||
| + | IP address 10.129.0.2. | ||
| + | |||
| + | **Containers** | ||
| + | All configurations and statically exported data is also in /srv. In /srv/caddy I have all needed to build my | ||
| + | caddy container, such as configurations and what ever caddy container of your choice needs. I also have a | ||
| + | build script there, / | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | |||
| + | podman create \ | ||
| + | --name caddy \ | ||
| + | --pod servers \ | ||
| + | --replace \ | ||
| + | --systemd false \ | ||
| + | --label app=caddy \ | ||
| + | --volume / | ||
| + | --volume / | ||
| + | --volume / | ||
| + | --volume / | ||
| + | --mount=" | ||
| + | --mount=" | ||
| + | docker.io/ | ||
| + | |||
| + | podman start caddy | ||
| + | </ | ||
| + | |||
| + | In this guide I do not review configuration of Caddy, look it up from caddy' | ||
| + | setup are uid 82 and gid 82, acme is used to fetch certificates, | ||
| + | those 2 files for user www: | ||
| + | those files to be available for reading to everyone, or at least for user and/or group 82. Or copy them locally and chown them in that | ||
| + | location statically. | ||
| + | |||
| + | And I have a similar script for nginx: | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | |||
| + | podman create \ | ||
| + | --name nginx \ | ||
| + | --pod servers \ | ||
| + | --replace \ | ||
| + | --systemd false \ | ||
| + | --label app=nginx \ | ||
| + | --volume / | ||
| + | --volume / | ||
| + | --volume / | ||
| + | --volume / | ||
| + | docker.io/ | ||
| + | |||
| + | podman start nginx | ||
| + | </ | ||
| + | |||
| + | Now after you have configured properly your caddy and nginx, we should have a server properly running. | ||
| + | We need to setup redirections from wan. | ||
| + | |||
| + | **Expose to wan** | ||
| + | |||
| + | Now that we have caddy serving at 10.129.0.2, ports 80 and 443, we edit / | ||
| + | < | ||
| + | config redirect | ||
| + | option name ' | ||
| + | option src ' | ||
| + | option dest ' | ||
| + | option src_dport ' | ||
| + | option dest_ip ' | ||
| + | option dest_port ' | ||
| + | option proto ' | ||
| + | option reflection ' | ||
| + | option target ' | ||
| + | option enabled ' | ||
| + | |||
| + | config redirect | ||
| + | option name ' | ||
| + | option src ' | ||
| + | option dest ' | ||
| + | option src_dport ' | ||
| + | option dest_ip ' | ||
| + | option dest_port ' | ||
| + | option proto ' | ||
| + | option reflection ' | ||
| + | option target ' | ||
| + | option enabled ' | ||
| + | </ | ||
| + | |||
| + | **Automation** | ||
| + | |||
| + | Finally, we want our pod and containers to build and start during boot, we also have | ||
| + | acme handling our certificates, | ||
| + | |||
| + | I added / | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | |||
| + | / | ||
| + | logger -t acme -p daemon.info "SSL certificates renewed, restarting container servers: | ||
| + | |||
| + | podman stop caddy | ||
| + | sleep 1 | ||
| + | podman start caddy | ||
| + | </ | ||
| + | |||
| + | And then rest is handled by / | ||
| + | <code bash> | ||
| + | # Put your custom commands here that should be executed once | ||
| + | # the system init finished. By default this file does nothing. | ||
| + | |||
| + | add_podman_trigger() { | ||
| + | |||
| + | local counter=10 | ||
| + | local running=0 | ||
| + | |||
| + | [ -x "/ | ||
| + | / | ||
| + | |||
| + | while [ " | ||
| + | [ " | ||
| + | running=1 | ||
| + | counter=0 | ||
| + | } || { | ||
| + | sleep 1 | ||
| + | counter=$(($counter-1)) | ||
| + | } | ||
| + | done | ||
| + | |||
| + | [ " | ||
| + | ubus call service set '{ " | ||
| + | logger -t podman -p daemon.info " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | start_podman_services() { | ||
| + | |||
| + | / | ||
| + | [ -f / | ||
| + | |||
| + | touch / | ||
| + | |||
| + | sleep 1 | ||
| + | / | ||
| + | sleep 2 | ||
| + | / | ||
| + | sleep 2 | ||
| + | / | ||
| + | |||
| + | add_podman_trigger & | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | This is why starting podman service with / | ||
| + | all container related during boot, by just simply disabling service as nothing podman | ||
| + | related is started if service is disabled. This builds our pod and both containers and | ||
| + | then adds a trigger for podman service to restart caddy when SSL certificates are renewed. | ||
| + | There' | ||
| + | added AFTER podman service has started. | ||