I am running alpine linux in a Balena container composition. This is not your typical docker usecase. Instead, Balena deploys docker container compositions to embedded software as an easy way to deploy complex applications on small, idiosyncratic devices.
In that context, there is a lot of freedom. Docker is used more as an application module, and the balena OS is designed to handle this.
So, an atypical use case for docker containers is to run systemctl/systemd services in a container; but this represents a very typical use case for a Balena deployment when porting a normal, PC-centric application to the Balena world.
For the purposes of this question, I am unable to change code, and running services is an absolute requirement. Is is exactly what needs to be done, and the question is: what is the best way to do it on alpine? (I've already got it working on debian w/ systemctl
)
I can get services running over openrc
, alpine's analog to systemctl|systemd
in debian. However, in order to do it, I am enabling softlevel
mode in the docker container:
touch /run/openrc/softlevel
Rather than "booting" from openrc
. However, openrc
itself comes with a bright yellow warning line obtained from within the running docker container:
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/blkio/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/cpu/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/cpuacct/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/cpuset/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/devices/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/freezer/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/hugetlb/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/memory/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/misc/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/net_cls/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/net_prio/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/perf_event/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/pids/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/rdma/tasks: Read-only file system
/lib/rc/sh/openrc-run.sh: line 108: can't create /sys/fs/cgroup/systemd/tasks: Read-only file system
* You are attempting to run an openrc service on a
* system which openrc did not boot.
* You may be inside a chroot or you may have used
* another initialization system to boot this system.
* In this situation, you will get unpredictable results!
* If you really want to do this, issue the following command:
* touch /run/openrc/softlevel
Is this fine, or is there a better approach to getting openrc
running in docker? Does it even matter? Or will it just work?
Ideally, I'll keep the CMD|ENTRYPOINT
arguments to the docker container clean so it is as usable as possible, with openrc
working as is, if possible.
ENTRYPOINT specifies the command to run when the container starts. If the Dockerfile has both ENTRYPOINT and CMD instructions, the CMD arguments are passed as arguments to the ENTRYPOINT command. You can override the ENTRYPOINT command by specifying a new command when you run the container.
This explains that, even if I get the docker container set up correctly, it will not behave as intended if the CMD/ENTRYPOINT docker commands contain some openrc
startup invective. That means downstream usage of this container with ENTRYPOINT overloads in scripts, or by myself some time from now will break unless I know to include the correct openrc
abra-cadabra.
Instead, maybe softlevel
mode is exactly what I want to enable if I am running in the balenaOS
with openrc
, and ENTRYPOINT will often be overridden.
But I have no reason other than testing and seeing if it works to believe that is the case.
What would be the best way to run services with openrc
in a docker container given the context?
A container doesn't really "boot". You simply choose what process starts when the container starts -- maybe that's a process manager, like openrc, but more generally it's a specific service.
If we're designing a container to work with openrc, we could start with something like this:
FROM docker.io/alpine:latest
RUN apk add openrc mdevd-openrc
RUN sed -i '/getty/d' /etc/inittab
CMD ["/sbin/init"]
A container that runs a couple of services under the control of openrc might look like this:
FROM openrc-base
RUN apk add openssh-server openssh-server-common-openrc darkhttpd darkhttpd-openrc
RUN rc-update add sshd default && rc-update add darkhttpd default
RUN mkdir -p -m 700 /root/.ssh
COPY id_rsa.pub /root/.ssh/authorized_keys
RUN chmod 600 /root/.ssh/authorized_keys
If we build an image from that Dockerfile and run it...
docker run --rm -p 2200:22 -p 8080:80 openrc-multiservice
...we'll see output like this:
* /proc is already mounted
* /run/openrc: creating directory
* /run/lock: correcting mode
* /run/lock: correcting owner
OpenRC 0.52.1 is starting up Linux 6.7.10-200.fc39.x86_64 (x86_64)
* Caching service dependencies ... [ ok ]
* /var/log/darkhttpd: correcting owner
* Starting darkhttpd web server ... [ ok ]
ssh-keygen: generating new host keys: RSA ECDSA ED25519
* Starting sshd ... [ ok ]
No errors, I didn't have to enable "softlevel" mode, and our services start up as expected. But maybe someone only wants to run a single service without using openrc! In that case, they can override CMD
on the command line:
docker run --rm -p 8080:80 openrc-multiservice darkhttpd /etc --port 80
That works, too.
In general, images based on our openrc image should be designed with the fact that openrc is an option in mind (they should install rc files for any provided services, etc), but they can have any ENTRYPOINT
script they want as long as it respects CMD
and ultimately starts /sbin/init
.
At the same time, they can support single-service instances as well.