OpenWrt como host de contenedor Docker

OpenWrt puede ser un host Docker en x86-64, Aarch64 y otras arquitecturas compatibles.

Hay dos formas de utilizar Docker como host: instalar Docker Community Edition o utilizar herramientas nativas OpenWrt que admitan la especificación del contenedor Docker.

Probablemente, primero necesitará configurar el almacenamiento como lugar para almacenar los contenedores y los datos.

Además, en la mayoría de los casos ejecutará el contenedor como un usuario específico y le dará acceso a alguna carpeta fuera del contenedor, donde podrá almacenar su configuración y los datos. Por lo tanto, probablemente necesitará crear nuevos usuarios y grupos para aplicaciones o servicios del sistema, crear las carpetas para la configuración y los datos, y luego cambiar el propietario de estas carpetas al usuario con el que ejecutará el contenedor.

  • Instala el paquete docker-ce para herramientas de línea de comandos
  • Instala el paquete luci-app-dockerman para obtener un panel de control para contenedores en Luci

La carpeta predeterminada para Docker en la interfaz Dockerman de Luci es /opt/docker, por lo que es deseable montar su almacenamiento en /opt o cambiar la carpeta en Docker > Overview > Docker Root Dir y luego reiniciar el servicio Dockerd.

Para agregar una imagen, búsquela en Docker Hub y luego copie el nombre de imagen desde el cuadro de texto Docker Pull Command. Por ejemplo, si el texto es docker pull linuxserver/transmission, copie linuxserver/transmission.

En Luci, vaya a Docker > Images y pegue ese texto en el cuadro Pull Image, luego haga clic en Pull. La página mostrará el progreso de la descarga.

Para extracciones de contenedores más largas, Luci puede agotar el tiempo de espera, por lo que deberá usar la línea de comando. Por ejemplo, las imágenes del controlador unifi incluyen el entorno de ejecución de Java y se acercan a los 500 MB, por lo que puede acceder mediante SSH e ingresar: docker pull linuxserver/unifi-controller.

Luego, en Luci, vaya a Docker > Containers > Add. En la página del nuevo contenedor, seleccione la imagen de docker en el menú Docker Image, y luego configure todos los demás parámetros (normalmente los parámetros disponibles/útiles se describen en la descripción del contenedor en Docker Hub), luego presione Enviar (Submit) para crear el contenedor.

La configuración está ubicada en /etc/config/dockerd.

  • data_root es una carpeta donde almacenar imágenes y contenedores. Esta también es montada por un docker. Puede que desee cambiarla a un disco USB. Su sistema de archivos no puede ser fat o ntfs. De manera predeterminada es /opt/docker/
  • log_level predeterminado warn.
  • hosts es un detector de API. De manera predeterminada se utiliza un socket UNIX /var/run/docker.sock.
  • iptables Habilita las reglas de iptables. Predeterminado 1
  • bip puente de red IP. Predeterminado 172.18.0.1/24
  • fixed_cidr Asigna IPs desde un rango. Predeterminado 172.17.0.0/16
  • fixed_cidr_v6 igual que fixed_cidr para IPv6. Predeterminado 'fc00:1::/80'
  • ipv6 Habilita redes IPv6. Predeterminado 1
  • ip Predeterminado ::ffff:0.0.0.0
  • dns Servidores DNS. Predeterminado 172.17.0.1
  • registry_mirrors URL de un registro. Predeterminado https://hub.docker.com

Las siguientes configuraciones requieren reiniciar (restart) docker para que surta efecto completo. Una recarga (reload) solo tendrá efecto parcial o ningún efecto:

  • bip
  • blocked_interfaces
  • extra_iptables_args
  • device

El sistema Procd init ahora admite la especificación de tiempo de ejecución de Open Container Initiative, ampliando su capacidad de contenedores delgados (“ujail”).
La herramienta de línea de comandos uxc maneja las operaciones básicas en contenedores según lo definido por la especificación.
Esto permite usarlo como reemplazo directo del 'runc' (o 'crun') de Docker en hosts OpenWrt con un espacio significativamente reducido.

Información detallada pero posiblemente desactualizada disponible en https://gitlab.com/prpl-foundation/prplos/prplos/-/wikis/uxc

Para 20.0x instale lo siguiente:

opkg install kmod-veth uxc ujail-console

Para nuevas instantáneas:

opkg install kmod-veth uxc procd-ujail procd-ujail-console
uci batch <<EOF
set network.veth0=device
set network.veth0.type='veth'
set network.veth0.name='vhost0'
set network.veth0.peer_name='virt0'
add_list network.lan.ifname='vhost0'
set network.virt0=interface
set network.virt0.ifname='virt0'
set network.virt0.proto='none'
# set proto='none' assuming DHCP client inside container
# use 'static' otherwise and also set ipaddr, gateway and dns
set network.virt0.jail='container1'
set network.virt0.jail_ifname='host0'
commit network
EOF

Para crear un paquete de tiempo de ejecución de OCI, necesario para uxc, siga estos pasos.

Primero compile una imagen de contenedor.

docker build -t container1 .

Tenga en cuenta la identificación de la imagen que se imprime al final y úsela después de @ en el siguiente comando.

skopeo copy containers-storage:[overlay@$HOME/.local/share/containers/storage+/run/user/1000/containers]@b0897a4ee285938413663f4c7b2b06d21e45c4358cebb04093ac9de9de118bf2 oci:container1:latest
sudo umoci unpack --image container1 container1-bundle
sudo rsync -aH container1-bundle root@192.168.0.1:/mnt/sda3/debian

Esto es bastante engorroso. Si alguien conoce una manera mejor, actualice esta página.

(asumiendo el paquete de tiempo de ejecución OCI con config.json en /mnt/sda3/debian)

uxc create container1 /mnt/sda3/debian true
uxc start container1

uxc list
uxc state container

Si el contenedor usa una consola stdio, puede conectarla usando

ujail-console -c container1

(no hay búfer, por lo que si desea ver el registro de inicio completo de un contenedor, asegúrese de conectar una consola después de la llamada 'create' pero antes de iniciarlo)

https://podman.io/ es otra alternativa a Docker y es compatible con los comandos del cliente Docker. A continuación se muestra un ejemplo de configuración utilizando podman para crear un contenedor de servidor web con proxy.

Instalar paquetes necesarios:

opkg install conmon crun catatonit netavark podman external-protocol

Si desea utilizar contenedores 'rootless', necesita paquetes adicionales, como slirp4netns. También hay un servidor DNS autoritario para netavark disponible como paquete aardvark-dns. Esta guía excluye su configuración actual.

Red

Comencemos revisando la configuración de nuestra red de contenedores. /etc/containers/networks/podman.json:

{
     "name": "podman",
     "id": "5ef894788befd4d42498314b6e66282ca730aa2e1e82f9b9597bf4d1725ca074",
     "driver": "bridge",
     "network_interface": "podman0",
     "created": "2023-02-20T08:56:34.652030952Z",
     "subnets": [
          {
               "subnet": "10.129.0.0/24",
               "gateway": "10.129.0.1"
          }
     ],
     "ipv6_enabled": false,
     "internal": false,
     "dns_enabled": true,
     "ipam_options": {
          "driver": "host-local"
     }
}

Tengo una red bastante grande (10.0.0.0/9), por eso la subred rara: puede elegir la suya propia, pero este es un ejemplo de configuración basado en mi configuración. En este archivo usted define su red llamada podman. Puede tener múltiples redes.

Cortafuegos/Zona

A continuación, nos aseguramos de que el reenvío de puertos internos/cortafuegos de podman esté deshabilitado, verifique /etc/config/netavark:

config firewall
	option driver 'none'

Hacemos esto para usar el propio cortafuegos de openwrt, ya que cuando se usa podman/netavark, las reglas se pierden cada vez que se recarga el cortafuegos, incluso cuando inicia su primera red de contenedores, cuando aparece la interfaz podman0.

A continuación es momento de configurar la red y el cortafuegos en el lado de openwrt, agregue esto a /etc/config/network:

config interface 'podman'
	option proto 'external'
	option device 'podman0'

Y también queremos usar cortafuegos, en esta configuración permitimos el acceso desde lan a podman, pero no al revés, también otorgamos acceso desde wan y hacia wan. /etc/config/firewall:

config zone
	option name 'podman'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	option mtu_fix '1'
	list network 'podman'

config forwarding
	option src 'lan'
	option dest 'podman'

config forwarding
	option src 'podman'
	option dest 'wan'

config forwarding
	option src 'wan'
	option dest 'podman'

Ahora que hemos bloqueado el acceso a la LAN, a nuestros contenedores les falta acceso al DNS, a menos que los configuremos para usar otra cosa, como 8.8.8.8; por lo tanto, hacemos una excepción, los contenedores pueden conectarse a la LAN, pero solo en el puerto 53 ( DNS):

config rule
	option name 'Allow-Podman-DNS'
	option src 'podman'
	option dest_port '53'
	option target 'ACCEPT'

Ahora la configuración inicial de la red está completa.

Servicio

A continuación, nos aseguramos de que el servicio podman se inicie al arrancar. Esto es opcional, pero si desea seguir esta guía, le resultará útil más adelante. El servicio Podman no crea/inicia/etc. ningún contenedor, solo inicia el servicio en segundo plano y crea un socket Unix utilizado para la comunicación. Este socket está ubicado en /var/run/podman/podman.sock; este socket acepta comunicación similar/compatible como Docker. Un paquete llamado podman-docker-compatibility generalmente solo contiene un enlace a este socket a la ruta estándar del socket de Docker y un script contenedor de Docker que reenvía su línea de comando al comando Podman. En realidad, el servicio se inicia cuando usa podman, pero queremos asegurarnos de que aparezca como instancia de openwrt y, para una configuración más avanzada, esto es útil.

/etc/init.d/podman enable

Después de reiniciar, puede comenzar a usar la configuración de podman. La guía continúa, creamos un servidor web y un proxy caddy como proyectos de ejemplo y manejamos el reenvío del tráfico hacia ellos. Almacenaremos los datos de nuestro contenedor en /srv.

Almacenamiento del contenedor (opcional)

Tengo mucho espacio en disco disponible, así que configuro mi Graphroot en el disco duro, en lugar de almacenar datos del contenedor en la RAM, como /tmp o /var, que son los valores predeterminados. Primero haga un camino adecuado:

mkdir -p /srv/.podman/storage

Y edite su /etc/containers/storage.conf:

graphroot = "/srv/.podman/storage"

el valor predeterminado era: “/var/lib/containers/storage

Sin configurar Graphroot, su configuración también funciona, es solo que tengo mucho espacio en disco en mis configuraciones, prefiero almacenarlas en el disco duro en lugar de en la memoria, como /tmp o /var.

Almacenamiento local de imagen (opcional)

Mi configuración compila mis contenedores en cada arranque y quiero acelerar ese proceso, por lo que quiero que las imágenes para el proxy Caddy y el servidor web nginx se almacenen en un almacenamiento permanente. Pero tenga cuidado, cada vez que desee realizar cambios en estas imágenes, deberá reiniciar este proceso completamente desde el principio, primero restableciendo la configuración additionalimagestores a su valor predeterminado, eliminar archivos de imágenes físicas y, por supuesto, antes de todo esto, debe eliminar los contenedores que usan estas imágenes, junto con las imágenes del propio sistema de Podman y, finalmente, extraiga nuevas imágenes y restaure la configuración additionalimagestores en la ruta de su imagen. Además, como el servicio de Podman carece de la función de parada, también son necesarios algunos reinicios. ¿Complicado? Sí, lo es, pero al final podría dar sus frutos, ya que no es necesario extraer las imágenes después de cada reinicio. Para empezar, reinicie su computadora y asegúrese de que no se creen, inicien o incluso existan contenedores, esto ayudará a evitar problemas.

Crear directorio /srv/.podman/images:

mkdir -p /srv/.podman/images

Luego revise su /etc/containers/storage.conf:

additionalimagestores = []

Si tiene que cambiar esta línea, debe reiniciar el servicio Podman para que esto surta efecto. Debería encontrarse la siguiente línea, es la configuración predeterminada. Con esta línea, no tenemos imágenes almacenadas localmente en almacén permanente. A continuación emita los siguientes comandos:

podman --root /srv/.podman/images images
podman --root /srv/.podman/images pull docker.io/me/my_caddy_image:latest
podman --root /srv/.podman/images pull docker.io/me/my_nginx_image:latest

reemplace las URL de imágenes con las suyas propias, el primer comando configura las imágenes de ruta como almacén de imágenes local y los siguientes comandos llevan sus imágenes al almacén de imágenes local. También puede extraer su imagen de pausa, por ejemplo: k8s.gcr.io/pause:3.5

Después de esto, realice cambios en su /etc/containers/storage.conf:

additionalimagestores = [
        "/srv/.podman/images"
]

y reiniciar. Ahora, al crear contenedores que utilizan imágenes almacenadas localmente, no es necesario extraerlas de Internet, están disponibles al instante. Si utiliza imágenes que no están almacenadas localmente, funcionan bien; simplemente son extraídos de internet. Esta es una operación única; Los cambios, eliminaciones y adiciones de imágenes almacenadas localmente no se pueden modificar muy fácilmente. Para reiniciar el proceso; elimine contenedores usando imágenes almacenadas localmente, elimine estas imágenes de podman (podman image rm <imageid>), cambie sus additionalimagestores nuevamente a [], deshabilite el servicio podman y asegúrese de que el servicio podman no comience con la creación /inicio de contenedores durante el arranque. Luego elimine todos los archivos, de /srv/.podman/images:

rm -rf /srv/.podman/images

reinicie nuevamente y comience esto nuevamente desde el inicio de esta sección de la guía.

Pod Empezaremos creando un pod. El pod puede contener varios contenedores y comparten algunos atributos, como la dirección IP. Mientras intentamos crear una configuración de servidor web, queremos que la dirección IP sea siempre la misma para este pod. He creado un script /srv/create.sh para construir este pod:

#!/bin/sh
 
podman pod create \
		--replace \
		--name servers \
		--hostname srv \
		--ip 10.129.0.2
 
podman pod start servers

Esto crea, o reemplaza, si existe, servidores con nombres de pod, le asigna un nombre de host srv (no importante) y una dirección IP estática 10.129.0.2.

Contenedores Todas las configuraciones y los datos exportados estáticamente también se encuentran en /srv. En /srv/caddy tengo todo lo necesario para construir mi contenedor caddy, como las configuraciones y cualquier contenedor caddy que necesite. También tengo un script de compilación allí, /srv/caddy/create.sh:

#!/bin/sh
 
podman create \
	--name caddy \
	--pod servers \
	--replace \
	--systemd false \
	--label app=caddy \
	--volume /srv/caddy/conf/:/etc/caddy/:Z,rw \
	--volume /srv/caddy/htdocs/:/var/htdocs/:z,rw \
	--volume /srv/caddy/logs/:/var/log/:z,rw \
	--volume /dev/log:/dev/log:Z,rw \
	--mount="type=bind,src=/etc/acme/domain.tld_ecc/domain.tld.cer,dst=/etc/caddy/ssl/server.pem,ro=true,idmap=uids=0-82-1;gids=0-82-1" \
	--mount="type=bind,src=/etc/acme/domain.tld_ecc/domain.tld.key,dst=/etc/caddy/ssl/server.key,ro=true,idmap=uids=0-82-1;gids=0-82-1" \
	docker.io/me/my_caddy_image:latest
 
podman start caddy

En esta guía no reviso la configuración de Caddy, la busco en los documentos de Caddy. Mi caddy está configurado para ejecutarse como usuario www:www-data, que en esa configuración son uid 82 y gid 82, acme se usa para obtener certificados, pero el usuario www(82) no puede leer archivos de propiedad raíz, por lo que usamos idmapping para asignar estos 2 archivos para el usuario www:www-data. Hay varias formas de hacer esto, este es solo un enfoque. También puede configurar un sistema que modifique esos archivos para que estén disponibles para que todos los lean, o al menos para el usuario y/o el grupo 82. O cópielos localmente y cópielos estáticamente en esa ubicación.

Y tengo un script similar para nginx:

#!/bin/sh
 
podman create \
	--name nginx \
	--pod servers \
	--replace \
	--systemd false \
	--label app=nginx \
	--volume /srv/nginx/conf/:/etc/nginx/:Z,rw \
	--volume /srv/nginx/logs/:/var/log/nginx/:Z,rw \
	--volume /srv/nginx/htdocs/:/var/htdocs/:z,rw \
	--volume /dev/log:/dev/log:Z,rw \
	docker.io/me/my_nginx_image:latest
 
podman start nginx

Ahora, después de haber configurado correctamente su caddy y nginx, deberíamos tener un servidor funcionando correctamente. Necesitamos configurar redirecciones desde wan.

Exponer a wan

Ahora que tenemos caddy sirviendo en 10.129.0.2, puertos 80 y 443, editamos /etc/config/firewall nuevamente:

config redirect
	option name 'Allow-HTTP'
	option src 'wan'
	option dest 'podman'
	option src_dport '80'
	option dest_ip '10.129.0.2'
	option dest_port '80'
	option proto 'tcp'
	option reflection '0'
	option target 'DNAT'
	option enabled '1'

config redirect
	option name 'Allow-HTTPS'
	option src 'wan'
	option dest 'podman'
	option src_dport '443'
	option dest_ip '10.129.0.2'
	option dest_port '443'
	option proto 'tcp'
	option reflection '0'
	option target 'DNAT'
	option enabled '1'

Automatización

Finalmente, queremos que nuestro pod y contenedores se construyan y se inicien durante el arranque, también tenemos acme manejando nuestros certificados, por lo que queremos reiniciar caddy cuando se renuevan los certificados.

Agregué el directorio /srv/scripts y agregué allí el archivo restart_caddy.sh:

#!/bin/sh
 
/etc/init.d/podman enabled || exit
logger -t acme -p daemon.info "SSL certificates renewed, restarting container servers:caddy"
 
podman stop caddy
sleep 1
podman start caddy

Y luego el resto lo maneja /etc/rc.local:

# 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 "/etc/init.d/acme" ] || exit
	/etc/init.d/acme enabled || exit
 
	while [ "$counter" -gt 0 ]; do
		[ "$(service podman status)" = "running" ] && {
			running=1
			counter=0
		} || {
			sleep 1
			counter=$(($counter-1))
		}
	done
 
	[ "$running" -eq 1 ] && {
		ubus call service set '{ "name": "podman", "triggers": [[ "acme.renew", [[ "run_script", "/srv/scripts/restart_caddy.sh" ]], 2000 ]], "data": {}}'
		logger -t podman -p daemon.info "podman: added service trigger for acme.renew event to restart servers:caddy"
	}
}
 
start_podman_services() {
 
	/etc/init.d/podman enabled && {
		[ -f /tmp/.podman_created ] || {
 
			touch /tmp/.podman_created
 
			sleep 1
			/srv/create.sh
			sleep 2
			/srv/caddy/create.sh
			sleep 2
			/srv/nginx/create.sh
 
			add_podman_trigger &
		}
	}
}

Es por eso que iniciar el servicio podman con /etc/init.d/podman resulta útil; podemos ignorar todos los contenedores relacionados durante el arranque, simplemente deshabilitando el servicio, ya que no se inicia nada relacionado con podman si el servicio está deshabilitado. Esto construye nuestro pod y ambos contenedores y luego agrega un activador para que el servicio podman reinicie caddy cuando se renuevan los certificados SSL. Hay una rutina que verifica si el servicio podman se ha iniciado, porque el disparador debe agregarse DESPUÉS de que se haya iniciado el servicio podman.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2023/12/03 16:01
  • by brodrigueznu