루트 없는 컨테이너를 실행하기 위해 Podman을 시스템 서비스로 사용하려고 합니다. 또한 루트가 아닌 권한으로 서비스 자체를 실행하고 싶습니다.
a) 시스템 서비스이지만 User=
서비스 사용자로 설정되거나 b) 장기 실행 서비스를 허용하기 위해 이전에 실행되었던
해당 서비스 사용자에 대한 사용자 서비스( )로 설정됩니다 .systemd --user
loginctl enable-linger <username>
아이디어는 프로덕션 환경에서 소규모 데몬 및 루트 없는 컨테이너 배포에 대한 향후 접근 방식을 표준화하는 것이지만 모든 함정을 알지 못하기 때문에 이러한 접근 방식 중 어떤 것을 선택해야 할지 완전히 확신할 수 없습니다. 아래에는 배경에 대한 논의가 있으며 실제 문제는 굵은 글씨로 표시되어 있습니다.
초기 실험으로 maptiler/tileserver-gl
로컬 파일 시스템 소스에서 지도 타일을 제공하는 이미지를 실행하고 있습니다.
User=
내가 주목한 한 가지는 옵션 a를 사용할 때 유닛 파일 내에 다음 과 같이 지정된 사용자에 대한 정보에 액세스할 수 있는 내장 메서드가 없는 것 같다는 것입니다.systemd.unit(5)의 "지정자"; 예를 들어 %t
점 /run
및 %h
점 /root
). 이것은 원래 컨테이너 ID가 포함된 파일을 생성(및 삭제)하기 위해 in을 사용하여 시작 및 중지할 때마다 컨테이너를 편리하게 생성 및 삭제하는 를 사용하여 단위 tileserver-gl
파일을 생성했기 때문에 관련이 있습니다. 물론, 우리 서비스 사용자에게는 쓰기 권한이 없습니다 . 간단히 하드코딩할 수 있기 때문에( 또는 컨테이너를 실행하는 비재생성 방법을 사용할 수 있으므로) 이는 문제가 되지 않지만 "활성"의 손실은 부끄러운 일입니다.podman generate systemd --new
--new
--cidfile=%t/%n.ctr-id
ExecStart=
ExecStartPre=
/run
/run/user/<uuid>
%t
User=
시스템 서비스 파일에서 사용자의 UID를 가져와서 ExecStart=
내가 놓친 명령 에 삽입하는 방법이 있습니까 ?
내가 올바르게 이해했다면 다른 해결 방법을 취하지 않는 한 두 옵션 모두 1024 미만의 TCP 및 UDP 포트에 바인딩할 수 없는 것 같습니다(이것우리의 경우에는 좋아 보입니다). 처음에는 서비스가 권한을 포기하기 전에 바인딩을 수행할 수 있다고 생각했는데 User=
, 그렇지 않은 것 같습니다. 일부 연구에 따르면 가장 "시스템 네이티브" 방식은 이를 달성하기 위해 소켓 장치를 사용하는 것이지만 이를 적극적으로 지원하려면 실행 중인 서비스가 필요한 것 같습니다.두 옵션 모두 소켓 장치 없이 하위 1024 포트에 "기본적으로" 바인딩할 수 없으며 이러한 포트를 사용하는 경우에도 일부 경우에만 해당됩니까?
궁극적으로 저는 여기서 모범 사례를 찾고 있습니다. 읽는 출처에 따라 --user
로그인 세션 중 임시 서비스를 강조하는 경우도 있지만 OTOH의 존재는 enable-linger
장기 실행 세션에 더 폭넓게 적용할 수 있음을 나타내는 것 같습니다.이 두 가지 옵션의 중요한 장점과 단점은 무엇이라고 생각하시나요? 이 "서비스 사용자" 사례에는 어떤 옵션이 더 좋다고 생각하며 그 이유는 무엇입니까?
답변1
User=
systemd 지시문을 사용하여 systemd 시스템 서비스에서 루트 없는 Podman을 실행하는 것은 현재 지원되지 않습니다.
바라보다기능 요구 사항Podman GitHub 프로젝트에서.
2023년 11월 21일에 업데이트되었습니다.
이 기능은 아직 공식적으로 지원되지 않는 것 같지만 , 및 다음을 사용하여 systemd User=test
시스템 Type=notify
서비스를 만들었습니다 Delegate=yes
.
[Unit]
Wants=network-online.target
After=network-online.target
[email protected]
[email protected]
RequiresMountsFor=/run/user/1000/containers
[Service]
User=test
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/usr/bin/podman rm -f -i --cidfile=/run/user/1000/%N.cid
ExecStopPost=-/usr/bin/podman rm -f -i --cidfile=/run/user/1000/%N.cid
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/usr/bin/podman run \
--cidfile=/run/user/1000/%N.cid \
--cgroups=split \
--rm \
--env "NGINX=3;" \
-d \
--replace \
--name systemd-%N \
--sdnotify=conmon \
docker.io/library/nginx
나는 Quadlet에서 생성된 서비스의 모습에 최대한 가깝게 만들려고 노력했습니다.
웹 서버를 테스트합니다. 작동하는 것 같습니다.
$ curl localhost:80 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
이 예에서는 특권 포트에서 수신 대기하기 위해 소켓 장치도 사용했습니다. 자세히보다 https://unix.stackexchange.com/a/762009/9360
방금 이것을 시도했기 때문에 이 솔루션이 얼마나 잘 작동하는지 파악해야 합니다.
인용하다:
예제 3과 4는 다음과 같습니다. https://github.com/eriksjolund/podman-nginx-socket-activation/
답변2
다음은 SELinux가 활성화된 RHEL 8.6 및 9.1, podman 4.0.2에서 테스트되었습니다. 서비스를 시작하기 위해 사용자 로그인이 필요하지 않습니다.
Linux 사용자 svcacct의 사용자 ID는 1000입니다. 쉘 스크립트 launchContainers.sh
에는 shutdownContainers.sh
Pod를 시작하고 중지하기 위한 해당 Podman 명령이 포함되어 있습니다. 이 스크립트는 systemd와 독립적으로 사용하고 테스트할 수 있습니다. loginctl enable-linger svcacct
sudo 사용자로 한 번 실행하세요. /etc/systemd/system/의 systemd 단위는 다음과 같습니다:
[Unit]
Description=Demo Containers
[email protected]
[email protected]
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/local/demo/launchContainers.sh
ExecStop=/usr/local/demo/shutdownContainers.sh
User=svcacct
Group=svcacct
[Install]
WantedBy=multi-user.target
답변3
저도 동일한 사용 사례를 갖고 있는데, 솔직히 아직 더 나은 지원이 없다는 사실이 놀랍습니다. 이는 제한된 권한 서비스 계정으로 서비스를 실행하는 데 대한 오랜 모범 사례와 Podman 모범 사례를 위한 Podman 모범 사례의 완벽한 교차점인 것 같습니다. 아마도 루트가 없을 것입니다.
현재 컨테이너로 제공되는 애플리케이션/서비스 수를 고려하면 서비스 사용자로서 서비스 컨테이너를 실행하는 것이 당연한 모범 사례처럼 보입니다.
내 "솔루션"은 시스템 서비스를 사용하는 것이었고 runuser
지금까지 가장 스크립트 가능한 솔루션인 것으로 나타났습니다. 여전히 사용자로서 컨테이너 이미지를 생성해야 하지만 다시 말하지만 이 방법은 작동합니다 runuser
. 스크립팅을 조사하는 데 많은 시간을 소비하지 않았지만 machinectl
연구에서 알 수 있는 한 machinectl
등을 기반으로 한 대안은 모두 실행이 필요합니다. 스크립팅 기능을 중단하는 서비스 사용자 대화형 셸로 사용됩니다.
그래서 기본 아이디어는
/usr/bin/sudo /usr/sbin/runuser -u serviceUser myCntnrBuildScript containerName
# myCntnrBuildScript does
# _mnt=$(${BUILDAH} mount $_containerImage)
# populate ${_mnt}, e.g., via cp, etc.
# ${BUILDAH} commit $_containerImage $_containerName
# etc., where the various variables are managed in my script
...
# then, in the systemd file,
ExecStart=/usr/sbin/runuser -u serviceUser -- /usr/bin/podman run --cidfile=%t/user/$(id -u serviceUser)/__cidfile__ ...
...
ExecStop=/usr/sbin/runuser -u cds-ipa -- /usr/bin/podman stop --ignore --cidfile=%t/user/$(id -u serviceUser)/__cidfile__
# in practice, `$(id -u serviceUser)` does not appear in the systemd file,
# which is generated programmatically: `$(id -u serviceUser)` appears in
# the script that generates the systemd file.
솔직히 말해서 제가 이 길을 처음 시작했을 때 사용하는 것은 --cidfile=%t/user/$(id -u serviceUser)/__cidfile__
실제로 이 목표를 달성하기 위한 "인정된 지혜" 접근 방식이었습니다. 이제 이러한 부분이 어떻게 더 잘 작동하는지 이해했으므로 %t/user/$(id -u serviceUser)
이를 내 사용 사례에 더 적합한 것으로 교체할 생각입니다. 이 목적을 위해 ExecStartPre
명령문을 사용하여 serviceUser 읽을 수 있는 디렉터리를 생성하고 명령문을 사용하여 정리할 수 있기 때문입니다. up 필요하다면 방법이 통하므로 깨지지 않는다면...tmpfs
ExecStopPost
%t/user/