套接字激活的概念机制是通过systemd创建套接字(例如TCP、UDP或Unix套接字)来实现的。当客户端连接到套接字时,systemd会立即启动为该套接字配置的systemd服务。新启动的程序会继承套接字的文件描述符,从而能够接受传入连接(即执行系统调用accept())。此描述对应于systemd套接字配置的默认值Accept=no,该设置允许服务接受套接字连接。
Podman支持两种形式的套接字激活:
API服务的套接字激活
容器的套接字激活
架构如下所示:

在 Fedora 系统中,文件 /usr/lib/systemd/user/podman.socket 定义了适用于无特权用户的 Podman API 套接字:
$ cat /usr/lib/systemd/user/podman.socket [Unit] Description=Podman API Socket Documentation=man:podman-system-service(1) [Socket] ListenStream=%t/podman/podman.sock SocketMode=0660 [Install] WantedBy=sockets.target
该套接字配置为 Unix 套接字,可通过以下方式启动:
$ systemctl --user start podman.socket $ ls $XDG_RUNTIME_DIR/podman/podman.sock /run/user/1000/podman/podman.sock $
该接口后续可供需要Docker兼容API的工具(例如 docker-compose)使用:
$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock $ docker-compose up
当 docker-compose 或任何其他客户端连接到 UNIX 套接字 $XDG_RUNTIME_DIR/podman/podman.sock 时,podman.service 服务将被启动。请参阅文件 /usr/lib/systemd/user/podman.service 中的服务定义。
自版本 3.4.0 起,Podman 支持容器的套接字激活,即向容器传递一个套接字激活的套接字。得益于 Podman 的 fork/exec 模型,该套接字将首先被 conmon 继承,随后由 OCI 运行时继承,最终由容器继承,如下图所示:

此类套接字激活机制可用于由容器单元文件生成的 systemd 服务(参见 podman-systemd.unit(5))(注:quadlet 需使用 cgroup v2)或通过命令podman generate systemd 创建的服务。容器本身也必须支持套接字激活。并非所有软件守护进程都支持套接字激活,但该技术正日益普及。例如 Apache HTTP 服务器、MariaDB、DBUS、PipeWire、Gunicorn、CUPS 均支持套接字激活。
此示例演示如何在 systemd 用户服务中运行套接字激活的回显服务器 socket-activate-echo。需使用 Podman 4.4.0 及以上版本。
为普通用户启用残留进程:
$ loginctl enable-linger $USER
该命令对您启用的 systemd 用户单元具有以下效果:
重启后单元会自动启动
注销后单元不会自动停止
创建目录:
$ mkdir -p ~/.config/systemd/user $ mkdir -p ~/.config/containers/systemd
创建文件 ~/.config/containers/systemd/echo.container,内容如下:
[Unit] Description=Example echo service Requires=echo.socket After=echo.socket [Container] Image=ghcr.io/eriksjolund/socket-activate-echo Network=none [Install] WantedBy=default.target
该文件遵循 podman-systemd.unit 中描述的语法。
[Install] 部分为可选项。若删除最后两行,重启后 echo.service将不会自动启动。取而代之的是,当首个客户端连接套接字时 echo.service 才会启动。
Network=none 配置行可选。该设置通过禁用容器网络连接增强安全性。但容器仍可向互联网提供服务,因为 Network=none 对已激活的套接字无效。
套接字激活服务还需配置 systemd 套接字单元。请创建文件 ~/.config/systemd/user/echo.socket,用于定义容器应使用的套接字。
[Unit] Description=Example echo socket [Socket] ListenStream=127.0.0.1:3000 ListenDatagram=127.0.0.1:3000 ListenStream=[::1]:3000 ListenDatagram=[::1]:3000 ListenStream=%h/echo_stream_sock # VMADDR_CID_ANY (-1U) = 2^32 -1 = 4294967295 # See "man vsock" ListenStream=vsock:4294967295:3000 [Install] WantedBy=sockets.target
%h 是 systemd 的指定符,展开后表示用户的 home 目录。
编辑完单元文件后,systemd 需要重新加载其配置。
$ systemctl --user daemon-reload
在重新加载配置时,systemd 会通过执行单元生成器 /usr/lib/systemd/system-generators/podman-system-generator,从文件 ~/.config/containers/systemd/echo.container 生成单元 echo.service。
可选操作:查看生成的 echo.service 文件,可了解将要执行的 podman run 命令。
$ systemctl --user cat echo.service
配置 systemd,使其在重启后自动启动 echo.socket。
$ systemctl --user enable echo.socket
提前拉取容器镜像:
$ podman pull ghcr.io/eriksjolund/socket-activate-echo
启动套接字单元:
$ systemctl --user start echo.socket
使用程序socat测试回显服务器:
$ echo hello | socat -t 30 - tcp4:127.0.0.1:3000 hello $ echo hello | socat -t 30 - tcp6:[::1]:3000 hello $ echo hello | socat -t 30 - udp4:127.0.0.1:3000 hello $ echo hello | socat -t 30 - udp6:[::1]:3000 hello $ echo hello | socat -t 30 - unix:$HOME/echo_stream_sock hello $ echo hello | socat -t 30 - VSOCK-CONNECT:1:3000 hello
选项 -t 30 将 socat 的超时时间配置为 30 秒,该超时时间适用于 socat 从套接字读取数据并等待文件结束标志 (EOF) 的场景。由于容器镜像已完成拉取,如此长的超时时间实则并无必要。
回显服务器运行正常。当接收到文本 “hello” 后,它会返回 “hello” 作为响应。
要测试套接字激活功能,除了创建 systemd 服务外,还可以使用命令行工具 systemd-socket-activate。
现在为 Apache HTTP 服务器构建一个容器镜像,配置其在8080端口支持套接字激活。
创建新目录 ctr,并在 ctr/ 目录下创建文件 Containerfile,内容如下:
FROM docker.io/library/fedora RUN dnf -y update && dnf install -y httpd && dnf clean all RUN sed -i "s/Listen 80/Listen 127.0.0.1:8080/g" /etc/httpd/conf/httpd.conf CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
构建容器镜像:
$ podman build -t socket-activate-httpd ctr
在一个 shell 中,启动 systemd-socket-activate。
$ systemd-socket-activate -l 8080 podman run --rm --network=none localhost/socket-activate-httpd
TCP 端口号 8080 作为选项传递给 systemd-socket-activate。podman run 命令的 --publish (-p) 选项未被使用。
在另一个终端中,从 localhost:8080 获取网页:
$ curl -s localhost:8080 | head -6 <!doctype html> <html> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1'> <title>Test Page for the HTTP Server on Fedora</title> $
如果容器仅需通过套接字激活的套接字进行通信,可通过向 podman run 传递 --network=none 参数禁用网络功能。此举能提升安全性,因为容器将以更低的权限运行。
使用无 root 权限 Podman 时,网络流量通常通过 slirp4netns 传输。这会带来性能损失。幸运的是,通过套接字激活套接字进行的通信无需经过slirp4netns,因此其性能表现与主机上的常规网络相同。
首次建立连接时会出现延迟,因为容器需要启动。为最大限度减少此延迟,建议在调用 podman run 时添加 --pull=never 参数,并提前拉取容器镜像。此外,服务无需等待首个客户端连接触发启动,也可通过系统命令显式启动(systemctl --user start echo.service)。
某些服务会运行一条命令(由systemd指令ExecStart配置),该命令在闲置一段时间后退出。根据服务的重启配置(systemd指令Restart),此时服务可能被停止。例如podman.service在闲置一段时间后会停止运行。当下次客户端连接到套接字时,该服务将重新启动。