User= 또는 --user를 사용하여 루팅되지 않은 컨테이너를 시스템 서비스로 실행하는 모범 사례?

User= 또는 --user를 사용하여 루팅되지 않은 컨테이너를 시스템 서비스로 실행하는 모범 사례?

루트 없는 컨테이너를 실행하기 위해 Podman을 시스템 서비스로 사용하려고 합니다. 또한 루트가 아닌 권한으로 서비스 자체를 실행하고 싶습니다.

a) 시스템 서비스이지만 User=서비스 사용자로 설정되거나 b) 장기 실행 서비스를 허용하기 위해 이전에 실행되었던
해당 서비스 사용자에 대한 사용자 서비스( )로 설정됩니다 .systemd --userloginctl 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-idExecStart=ExecStartPre=/run/run/user/<uuid>%tUser=시스템 서비스 파일에서 사용자의 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.shPod를 시작하고 중지하기 위한 해당 Podman 명령이 포함되어 있습니다. 이 스크립트는 systemd와 독립적으로 사용하고 테스트할 수 있습니다. loginctl enable-linger svcacctsudo 사용자로 한 번 실행하세요. /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 필요하다면 방법이 통하므로 깨지지 않는다면...tmpfsExecStopPost%t/user/

관련 정보