[CICD Study 1기 by Gasida] - Podman 소켓 활성화
소켓 활성화란?
systemd
가 소켓을 미리 만들어두고, 클라이언트가 연결하면 그때 서비스를 자동으로 시작하는 방식
동작 원리
- systemd가 소켓(TCP, UDP, Unix 소켓 등)을 생성하고 대기
- 클라이언트가 소켓에 연결 시도
- systemd가 해당 서비스를 자동으로 시작
- 서비스가 소켓의 파일 디스크립터를 상속받아 연결 수락
Podman의 2가지 소켓 활성화 방식
- API 서비스 소켓 활성화
- 컨테이너 소켓 활성화
1. API 서비스 소켓 활성화
Podman API를 소켓을 통해 자동으로 시작 (설정 파일 : /usr/lib/systemd/user/podman.socket
)
1
2
3
4
5
# 소켓 시작
systemctl --user start podman.socket
# 소켓 파일 확인
ls $XDG_RUNTIME_DIR/podman/podman.sock
1
2
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
docker-compose up
docker-compose또는 다른 클라이언트가 UNIX소켓에 연결하면 podman.service ($XDG_RUNTIME_DIR/podman/podman.sock)가 시작된다. (/usr/lib/systemd/user/podman.service
파일에서 정의)
요구사항
- Podman 4.4.0 이상 권장
- cgroup v2 필요 (quadlet 사용 시)
- 컨테이너 내부 애플리케이션이 소켓 활성화 지원 필요
- 지원 소프트웨어: Apache HTTP, MariaDB, DBUS, PipeWire, Gunicorn, CUPS 등
2. 컨테이너 소켓 활성화
Podman 3.4.0 버전부터 컨테이너의 소켓 활성화, 즉 소켓 활성화된 소켓을 컨테이너에 전달하는 기능을 지원한다. Podman의 fork/exec 모델 덕분에 소켓은 먼저 common에 상속되고 그 다음 OCI 런타임에 상속되며 마지막으로 컨테이너로 상속된다.
예) systemd 서비스의 소켓활성화 에코서버 컨테이너
(systemd 사용자 서비스에서 소켓 활성화 에코 서버인 socket-activate-echo를 실행하는 방법을 보여준다)
- 아래 명령어의 효과는 다음과 같다.
- 재부팅 후 자동으로 시작
- 로그아웃 후 유닛은 자동으로 중지되지 않음
1
loginctl enable-linger $USER
- 디렉토리 생성
1 2
mkdir -p ~/.config/systemd/user mkdir -p ~/.config/containers/systemd
~/.config/containers/systemd/echo.container
에 아래와 같은 내용으로 파일 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=Example echo service
Requires=echo.socket
After=echo.socket
[Container]
Image=ghcr.io/eriksjolund/socket-activate-echo
### 컨테이너의 네트워크 연결을 제거하여 보안강화. 활성화된 소켓에는 영향을 끼치지않는다.
Network=none
## 선택사항 : 아래 두줄을 삭제하면 재부팅후 echo.service가 자동으로 시작되지않는다. 대신 첫번째 클라이언트가 소켓에 연결될때
## echo.service가 시작된다.
[Install]
WantedBy=default.target
- 소켓 활성화 서비스에는
systemd
소켓 유닛도 필요하다. 컨테이너가 사용할 소켓을 정의하는~/.config/systemd/user/echo.socket
파일을 생성한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[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
지정자이다.
1
2
# 파일을 편집한 후 재로드
systemctl --user daemon-reload
daemon-reload` 명령을 실행하면 systemd는:
- 모든 unit generator를 실행
- Unit generator = systemd 유닛 파일을 자동 생성하는 프로그램
- Podman의 generator:
/usr/lib/systemd/system-generators/podman-system-generator
- Generator가 특정 디렉토리를 스캔
~/.config/containers/systemd/
디렉토리를 확인*.container
,*.volume
,*.network
,*.kube
파일을 찾음
- 실제 systemd 유닛 파일 자동 생성
echo.container
발견- →
echo.service
자동 생성 (실제 systemd 서비스 파일)
1
2
3
4
5
# Optional: 생성된 echo.service를 보고 podman run으로 실행될 명령을 확인
systemctl --user cat echo.service
# 재부팅 후에 echo.socket이 자동으로 systemd에 구성된다.
systemctl --user enable echo.socket
1
2
3
4
5
# 사전에 이미지를 풀링
podman pull ghcr.io/eriksjolund/socket-activate-echo
# 소켓 유닛 시작
systemctl --user start echo.socket
소캣 프로그램으로 echo서버 테스트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# TCP IPv4
echo hello | socat -t 30 - tcp4:127.0.0.1:3000
# 출력: hello
# TCP IPv6
echo hello | socat -t 30 - tcp6:[::1]:3000
# 출력: hello
# UDP IPv4
echo hello | socat -t 30 - udp4:127.0.0.1:3000
# 출력: hello
# UDP IPv6
echo hello | socat -t 30 - udp6:[::1]:3000
# 출력: hello
# Unix 소켓
echo hello | socat -t 30 - unix:$HOME/echo_stream_sock
# 출력: hello
# VSOCK
echo hello | socat -t 30 - VSOCK-CONNECT:1:3000
# 출력: hello
예) Apache HTTP 서버(systemd-socket-activate사용)
소켓 활성화를 테스트하기 위해 systemd 서비스를 설정하는 대신, 명렬줄 도구 systemd-socket-activate를 사용하는 것이 대안이다. 포트 8080에서 소켓 활성화를 지원하도록 구성된 Apache HTTP 서버용 컨테이너 이미지를 빌드해본다.
- 테스트를 위해 Podman 머신 접속 하기
1
2
3
4
5
6
7
8
# Podman 머신 목록 확인
podman machine list
# Podman 머신 시작
Podman machine start
# SSH 접속
podman machine ssh
- 아래의 내용을 포함하는
Containerfile
을 생성한다. (Apache HTTP 서버용 컨테이너 이미지)
1
2
3
4
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"]
- 아래 명령어로 컨테이너 이미지를 빌드한다.
1
podman build -t socket-activate-httpd .
- 호스트에서 systemd-socket-activate를 시작한다.
1
systemd-socket-activate -l 8080 podman run --rm --network=none localhost/socket-activate-httpd
- 또 다른 쉘에서 localhost:8080으로 curl요청을 날려본다.
1
curl -s localhost:8080 | head -6
- 결과 확인
주요 장점
–network=none으로 네트워크 비활성화
컨테이너가 소켓이 활성화된 소켓을 통해서만 통신해야 하는 경우, –network=none 옵션을 전달하여 네트워크를 비활성화할 수 있다. 따라서 더 적은 권한의 컨테이너로 실행되므로 보안이 향상된다.
소켓 활성화를 통한 기본 네트워크 성능
Rootless Podman을 사용할 경우 네트워크 트래픽은 일반적으로 slirp4netns를 통해 전달된다. 이로 인해 성능저하가 발생하는데 다행히 소켓 활성화을 통한 통신은 slirp4netns를 거치지 않으므로 호스트의 일반 네트워크와 동일한 성능 특성을 보인다.
소켓 활성화 서비스 시작
컨테이너를 시작해야 하므로 첫 번째 연결이 이루어질때 지연이 발생한다. 이 지연을 최소화하려면 –pull=never 옵션을 설정하여 컨테이너 이미지를 미리 가져오는 것이 좋다. 첫번째 클라이언트가 연결하여 서비스 시작을 기다리는 대신 서비스를 명시적으로 시작할 수 도 있다.
소켓 활성화 서비스 중지
일부 서비스는 일정시간 동안 활동이 없으면 종료되는 명령을 실행한다. 서비스 재시작 구성에따라 서비스가 중지될 수 있으며 다음 클라이언트가 소켓에 연결되면 서비스가 다시 시작된다. (예를들어 일정시간동안 활동이 없으면 종료되는 podman.service가 있다. )