mirror of
https://github.com/containers/podman.git
synced 2025-05-19 16:18:51 +08:00
Merge pull request #14909 from eriksjolund/add_socket_activation_tutorial
[CI:DOCS] Add socket_activation.md
This commit is contained in:
@ -31,3 +31,7 @@ Learn how to set up and use image signing with Podman.
|
|||||||
**[Basic Networking](basic_networking.md)**
|
**[Basic Networking](basic_networking.md)**
|
||||||
|
|
||||||
A basic guide to common network setups with Podman
|
A basic guide to common network setups with Podman
|
||||||
|
|
||||||
|
**[Socket activation](socket_activation.md)**
|
||||||
|
|
||||||
|
Learn how to run containers that support socket activation.
|
||||||
|
212
docs/tutorials/socket_activation.md
Normal file
212
docs/tutorials/socket_activation.md
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
## Podman socket activation
|
||||||
|
|
||||||
|
Socket activation conceptually works by having systemd create a socket (e.g. TCP, UDP or Unix
|
||||||
|
socket). As soon as a client connects to the socket, systemd will start the systemd service that is
|
||||||
|
configured for the socket. The newly started program inherits the file descriptor of the socket
|
||||||
|
and can then accept the incoming connection (in other words run the system call `accept()`).
|
||||||
|
This description corresponds to the default systemd socket configuration
|
||||||
|
[`Accept=no`](https://www.freedesktop.org/software/systemd/man/systemd.socket.html#Accept=)
|
||||||
|
that lets the service accept the socket.
|
||||||
|
|
||||||
|
Podman supports two forms of socket activation:
|
||||||
|
|
||||||
|
* Socket activation of the API service
|
||||||
|
* Socket activation of containers
|
||||||
|
|
||||||
|
### Socket activation of the API service
|
||||||
|
|
||||||
|
The architecture looks like this
|
||||||
|
|
||||||
|
``` mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> systemd: client connects
|
||||||
|
systemd --> podman: socket inherited via fork/exec
|
||||||
|
```
|
||||||
|
|
||||||
|
The file _/usr/lib/systemd/user/podman.socket_ on a Fedora system defines the Podman API socket for
|
||||||
|
rootless users:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ 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
|
||||||
|
```
|
||||||
|
|
||||||
|
The socket is configured to be a Unix socket and can be started like this
|
||||||
|
|
||||||
|
```
|
||||||
|
$ systemctl --user start podman.socket
|
||||||
|
$ ls $XDG_RUNTIME_DIR/podman/podman.sock
|
||||||
|
/run/user/1000/podman/podman.sock
|
||||||
|
$
|
||||||
|
```
|
||||||
|
The socket can later be used by for instance __docker-compose__ that needs a Docker-compatible API
|
||||||
|
|
||||||
|
```
|
||||||
|
$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
|
||||||
|
$ docker-compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Socket activation of containers
|
||||||
|
|
||||||
|
Since version 3.4.0 Podman supports socket activation of containers, i.e., passing
|
||||||
|
a socket-activated socket to the container. Thanks to the fork/exec model of Podman, the socket will be first
|
||||||
|
inherited by conmon and then by the OCI runtime and finally by the container
|
||||||
|
as can be seen in the following diagram:
|
||||||
|
|
||||||
|
|
||||||
|
``` mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> systemd: client connects
|
||||||
|
systemd --> podman: socket inherited via fork/exec
|
||||||
|
state "OCI runtime" as s2
|
||||||
|
podman --> conmon: socket inherited via double fork/exec
|
||||||
|
conmon --> s2: socket inherited via fork/exec
|
||||||
|
s2 --> container: socket inherited via exec
|
||||||
|
```
|
||||||
|
|
||||||
|
This type of socket activation can be used in systemd services that are generated with the command
|
||||||
|
[`podman generate systemd`](https://docs.podman.io/en/latest/markdown/podman-generate-systemd.1.html).
|
||||||
|
The container must also support socket activation. Not all software daemons support socket activation
|
||||||
|
but it's getting more popular. For instance Apache HTTP server, MariaDB, DBUS, PipeWire, Gunicorn, CUPS
|
||||||
|
all have socket activation support.
|
||||||
|
|
||||||
|
#### Example: socket-activated echo server container in a systemd service
|
||||||
|
|
||||||
|
Let's try out [socket-activate-echo](https://github.com/eriksjolund/socket-activate-echo/pkgs/container/socket-activate-echo), a simple echo server container that supports socket activation.
|
||||||
|
|
||||||
|
Create the container
|
||||||
|
|
||||||
|
```
|
||||||
|
$ podman create --rm --name echo --network none ghcr.io/eriksjolund/socket-activate-echo
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate the systemd service unit
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir -p ~/.config/systemd/user
|
||||||
|
$ podman generate systemd --name --new echo > ~/.config/systemd/user/echo.service
|
||||||
|
```
|
||||||
|
|
||||||
|
A socket activated service also requires a systemd socket unit.
|
||||||
|
Create the file _~/.config/systemd/user/echo.socket_ that defines the
|
||||||
|
sockets that the container should use
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=echo server
|
||||||
|
|
||||||
|
[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=default.target
|
||||||
|
```
|
||||||
|
|
||||||
|
`%h` is a systemd specifier that expands to the user's home directory.
|
||||||
|
|
||||||
|
After editing the unit files, systemd needs to reload it's configuration
|
||||||
|
|
||||||
|
```
|
||||||
|
$ systemctl --user daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the socket unit
|
||||||
|
|
||||||
|
```
|
||||||
|
$ systemctl --user start echo.socket
|
||||||
|
```
|
||||||
|
|
||||||
|
Test the echo server with the program __socat__
|
||||||
|
|
||||||
|
```
|
||||||
|
$ echo hello | socat - tcp4:127.0.0.1:3000
|
||||||
|
hello
|
||||||
|
$ echo hello | socat - tcp6:[::1]:3000
|
||||||
|
hello
|
||||||
|
$ echo hello | socat - udp4:127.0.0.1:3000
|
||||||
|
hello
|
||||||
|
$ echo hello | socat - udp6:[::1]:3000
|
||||||
|
hello
|
||||||
|
$ echo hello | socat - unix:$HOME/echo_stream_sock
|
||||||
|
hello
|
||||||
|
$ echo hello | socat - VSOCK-CONNECT:1:3000
|
||||||
|
hello
|
||||||
|
```
|
||||||
|
|
||||||
|
The echo server works as expected. It replies _"hello"_ after receiving the text _"hello"_.
|
||||||
|
|
||||||
|
### Socket activate an Apache HTTP server with systemd-socket-activate
|
||||||
|
|
||||||
|
Instead of setting up a systemd service to test out socket activation, an alternative is to use the command-line
|
||||||
|
tool [__systemd-socket-activate__](https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html#).
|
||||||
|
|
||||||
|
Let's build a container image for the Apache HTTP server that is configured to support socket activation on port 8080.
|
||||||
|
|
||||||
|
Create a new directory _ctr_ and a file _ctr/Containerfile_ with this contents
|
||||||
|
|
||||||
|
```
|
||||||
|
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"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the container image
|
||||||
|
|
||||||
|
```
|
||||||
|
$ podman build -t socket-activate-httpd ctr
|
||||||
|
```
|
||||||
|
|
||||||
|
In one shell, start __systemd-socket-activate__.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ systemd-socket-activate -l 8080 podman run --rm --network=none localhost/socket-activate-httpd
|
||||||
|
```
|
||||||
|
|
||||||
|
The TCP port number 8080 is given as an option to __systemd-socket-activate__. The __--publish__ (__-p__)
|
||||||
|
option for `podman run` is not used.
|
||||||
|
|
||||||
|
In another shell, fetch a web page from _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>
|
||||||
|
$
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disabling the network with _--network=none_
|
||||||
|
|
||||||
|
If the container only needs to communicate over the socket-activated socket, it's possible to disable
|
||||||
|
the network by passing __--network=none__ to `podman run`. This improves security because the
|
||||||
|
container then runs with less privileges.
|
||||||
|
|
||||||
|
### Native network performance over the socket-activated socket
|
||||||
|
|
||||||
|
When using rootless Podman, network traffic is normally passed through slirp4netns. This comes with
|
||||||
|
a performance penalty. Fortunately, communication over the socket-activated socket does not pass through
|
||||||
|
slirp4netns so it has the same performance characteristics as the normal network on the host.
|
||||||
|
Note, there is a delay when the first connection is made because the container needs to
|
||||||
|
start up. To minimize this delay, consider passing __--pull=never__ to `podman run` and instead
|
||||||
|
pull the container image beforehand.
|
Reference in New Issue
Block a user