"systemd" 대상에 대한 단위 목록을 동적으로 생성하는 방법은 무엇입니까?

"systemd" 대상에 대한 단위 목록을 동적으로 생성하는 방법은 무엇입니까?

초보 systemd사용자입니다. 이 질문이 "너무 기본적"이어서 StackOverflow 생태계의 다른 곳에서 질문하는 것이 더 나을 것 같다면 사과드립니다...

나는 init약 12년 전에 작성한 일부 고대 서비스 파일을 변환하고 있습니다 systemd. 그 중 일부는 일련의 서비스를 구성하는 방법을 알아내기 위해 꽤 괴상한 방법을 사용했습니다.경험이 풍부한시스템 프로그래머인 저는 개념 증명으로 작업을 수행하기 위한 빠르고 더러운 해킹을 좋아합니다. 이러한 해킹은 나중에 언제든지 개선될 수 있습니다.

즉, 제가 달성하려는 것은 다음과 같습니다. 저는 동일한 바이너리의 여러 인스턴스를 실행하지만 다른 매개변수를 사용하는 온라인 가상 세계 플랫폼(아니요, Minecraft가 아닙니다...)을 호스팅하고 있습니다(샤딩하고 싶다고 생각하세요). . 미리 시작할 인스턴스 수(예: 시작 시)를 알고 있거나 최소한 어떤 매개변수 세트를 사용할지 알고 있거나 해당 정보의 실제 목록(아마도 미리 생성된)이 있는 경우에도 이 작업을 쉽게 수행할 수 있습니다. .

실제로 다음과 같은 일이 발생합니다. 특별한 "구성" 디렉터리가 있습니다(최소한미리 알아두세요! ) 여러 하위 디렉터리가 있습니다(번호는 다음과 같습니다).아니요미리 알아두세요! ), 각각은 특정 인스턴스의 이름을 따서 명명되었습니다. 이름중요한 점은(즉, 연속된 이름을 사용하면 작업을 더 쉽게 만들 수 없다는 것입니다. 이렇게 하면 몇 가지 트릭을 적용하기가 더 쉬워집니다. 심지어 하위 디렉터리가 몇 개 있는지 미리 알고 있다고 가정하더라도 마찬가지입니다. 그렇지 않습니다!). 하위 디렉토리 이름이 도시 이름(예: NewYork, Chicago, ) 이라고 가정합니다 LA.KansasCity

간단히 말해서 전체 디렉토리 트리를 다음과 같이 생각할 수 있습니다.

game-root-folder 
|
\___ bin
|     \___________ myapp (the actual game engine for each instance)
|     \___________ myapp-single-instance.sh (explained below)
|
\___ config-folder
       \__________ NewYork
       |              \______ config.ini
       |          
       \__________ Chicago
       |              \______ config.ini
       |          
       \__________ LA
       |              \______ config.ini
       |          
       \__________ KansasCity
       :              \______ config.ini

(죄송합니다. 저는 ASCII 차트를 잘 다루지 못합니다...)

각 좌표는 config.ini해당 도시의 GPS 좌표 집합이라고 가정합니다(실제로는 그렇지 않습니다).많은그보다 더 복잡하지만 중요한 것은 이 파일에 사람이 작성한 것이 아니라 "관리 응용 프로그램"에서 자동으로 생성된 항목이 충분하다는 것입니다.어려운환경 변수의 모든 항목을 인코딩합니다. 즉, 고유하고 도시마다 다르며 해당 도시의 출시 구성에 하드코딩되는 항목입니다.

실제로 실제 레이아웃은 훨씬 더 복잡하지만 이 정도면 내 문제를 설명하기에 충분할 것입니다.

언제아니요셸에서 모든 것을 수동으로 시작하는 대신 systemd작업은 간단합니다. 각 인스턴스를 시작하는 명령은 다음과 같습니다.

# Call this script with `myapp-single-instance.sh start <city name>`
case "$1" in
    start)
        cd /full/path/to/game-root-folder/bin
        /usr/bin/screen -S $2 -d -m -l myapp \
          --config=/path/to/game-root-folder/config-folder/$2/config.ini
        ;;
    stop)
        # discussed below
        ;;
    *)
        # show usage
        exit 1
        ;;
esac
        

구성 이 아닌 systemd파일에는 해당 구성 디렉터리의 내용을 나열하고 하위 디렉터리의 이름을 추출하여 각 도시 이름에 대한 시작/중지 스크립트를 실행하는 추가 스크립트가 있습니다. 예를 들면 다음과 같습니다.

cd /full/path/to/game-root-folder/bin
for CITY_NAME in `ls /path/to/game-root-folder/config-folder`
do
    myapp-single-instance.sh start $CITY_NAME
done

참고로,멈추다이러한 경우 사람들은 다음과 같이 할 수 있습니다.

/usr/bin/screen -S $CITY_NAME -X eval 'stuff "quit"\015'

(인스턴스를 종료하기 위한 콘솔 명령이라고 가정 quit) 또는 어떤 이유로 위의 방법이 작동하지 않는 경우(인스턴스가 명령을 받아들이지 않고 무한 루프에 있음):

/usr/bin/screen -X -S $CITY_NAME kill

이 경우에는 kill그 자체에 대한 명령이며 screen, 그 자체와 그 안에 있는 모든 것을 우아하게 종료합니다. SIGKILL물론 마지막 옵션은 하나를 보내는 것입니다(현재 기존 스크립트를 통해서도 수행됨).

보시다시피 누군가 추가하고 싶다면새로운예를 들어, 새로운 도시의 경우 필요한 것은 새 하위 디렉터리를 만들고 Boston한 줄의 시작/중지 스크립트를 사용하여 시작하는 것뿐입니다. 필요한 경우 개별 인스턴스를 수동으로 중지하거나 시작할 수 있습니다.새로운어쨌든 인스턴스에는 수동 개입(제자리에 배치)이 필요할 수 있으므로 config.ini인스턴스를 설정하려면 추가 명령이 필요합니다.새로운인스턴스 - 각 인스턴스가 본질적으로 다른 인스턴스와 독립적이고 직접 상호 작용하지 않는 한 인스턴스에 신호를 보낼 필요가 없습니다.기존의추가하는 경우의 예새로운하나(또는 기존 항목 삭제). 다시 말하지만 실제로는 뒤에서 일부 관리 작업을 수행하는 서버를 모니터링하는 "마스터" 인스턴스가 있지만 이 질문의 목적을 위해 지금은 그 존재를 무시하겠습니다.

위의 예는 init시나리오에 적용하기 간단합니다.

이제 다음에는 무엇을 해야 할까요 systemd? 음, 2계층 접근 방식이 사용하기에 적합한 것 같습니다.systemd 표적, 하나에서 여러 인스턴스 시작주형. 이건 나의 순진한 시도였고,느슨하게 이 답변을 기반으로(포인트 3), 다른 곳에도 적용됩니다:

; [email protected]

[Unit]
Description=Game city name %I
After=syslog.target
After=network.target
Requires=mariadb.service mysqld.service
PartOf=myapp.target

[Service]
Type=forking
User=myappuser
Group=myappgroup
WorkingDirectory=/full/path/to/game-root-folder/bin
ExecStart=myapp-single-instance.sh start "%I"
ExecStop=myapp-single-instance.sh stop "%I"
ExecStop=/bin/sleep 5
KillSignal=SIGCONT
Restart=always
RestartSec=30s
Environment=USER=myappuser HOME=/full/path/to/game-root-folder/bin
RemainAfterExit=false
SuccessExitStatus=1

[Install]
WantedBy=multi-user.target

지금까지는 훌륭했습니다. 기본적으로 기존 스크립트를 systemd구성으로 래핑하는 것뿐입니다.

하지만 이제 목표를 어떻게 해야 할까요?

; myapp.target
[Unit]
Description=Launch all game instances
[email protected] [email protected] [email protected]

[Install]
WantedBy=multi-user.target

이런, 시카고를 깜빡했어요! 보세요, 구성을 수동으로 편집하면 이런 일이 발생합니다...

기본적으로 나는동적이를 수행하는 방법은 제가 놀라운 명령을 사용한 `ls /path/to/game-root-folder/config-folder`것과 같습니다 . 매우 간단하지만 매우 강력합니다! 그러나 주제에 대해 내가 읽은 바에 따르면 쉘 명령의 출력을 해당 줄로 파이프하는 것은 불가능합니다. Wants=...실제로 유일한 줄은 다음과 같습니다.할 수 있는쉘 실행 명령은 다음과 같습니다 Exec...=(명백한 이유에서). 나에게 정말 불운이 있습니다!

다른 사람들도 비슷한 문제를 겪었습니다. 해결책은 실행할 인스턴스를 선택하기 위한 논리를 환경 변수로 푸시하는 것입니다. 그러면 환경 변수가 "마사지"되어 실행 파일 자체로 전송될 수 있습니다. 내 경우에는 이러한 솔루션을 구현하는 방법이 약간 혼란스럽기는 하지만 내 요구 사항에 맞지 않는 것 같습니다. 사실, 이러한 "명백한" 사용 사례가 대상에서 서비스 단위를 동적으로 호출하는 것이 이상하다는 것을 알았습니다. 여기서 "동적"은 "크기를 알 수 없는 단일 실행 파일에 대한 서비스 단위 세트"를 의미합니다. 각 요소는 서로 다른 매개변수 범위를 가질 수 있으며 사전에 알려지지 않았습니다.') - 이것은 "사소한" 시나리오가 아니며 systemd이 문제를 해결하는 방법을 완전히 이해하지 못할 수도 있습니다.

systemd솔루션이 다른 파일을 포함하고( 즉, 파일을 그 안에 넣음 /etc/systemd/myapp.service.d/) 이러한 추가 구성 검색을 시작 하거나 systemd스크립트에서 호출할 수 있는 모든 인스턴스를 나열하는 두 가지 예를 보았습니다. .target파일에 저장하거나 환경 변수에 저장하기도 합니다. 누군가가 추가할 때마다추가의예를 들어, 백그라운드 타이머 프로세스는 각 인스턴스의 하위 디렉터리를 확인하고 수집된 데이터의 각 비트가 포함된 환경 변수를 반환하거나 이를 특수 파일에 씁니다(파일이 잘 알려진 위치에서 공유된다고 가정합니다). 부팅 시 각 인스턴스를 시작하는 방법을 알아보려면 이를 읽어보세요. 이러한 솔루션은 사용 가능한 파일을 확인하고 다시 로드를 통해 대상 장치를 호출하기 전에 해당 파일을 플러시하는 백그라운드 데몬을 실행하는 것을 의미합니다. 그러나 이러한 솔루션은 Wants=..또는 행을 변경하려고 할 때 Require=..작동하지 않는 것 같습니다 . Exec...=이 방법으로 해결할 수 있는 것은 대부분 매개변수입니다(일부 충돌하는 지침을 보았지만 일부 systemd코드 버전은 "동적"을 사용한다고 합니다). 필드를 채우는 방법...).

또한 인스턴스를 시작/중지/다시 로드하는 기능을 유지하고 싶습니다.개별적으로, 부팅 시 모두 시작되도록 할 수도 있습니다(또는 강제로 다시 시작하기 전에 정상적으로 중지하는 등).

위의 모든 사항을 고려할 때 어떤 솔루션을 권장하시겠습니까?

미리 감사드리며 이 커뮤니티에서 계속 훌륭한 활동을 펼치시기 바랍니다!

건배,

  • 그웬

답변1

만약에부분서비스의 일부입니다[단위]그러면 대상은 필요하지 않습니다.~ 고 싶어요; systemd는 대상에서 어떤 템플릿이 활성화되어 있는지 자동으로 파악합니다.

systemctl enable myapp@Chicago(등)은 설명에 명시된 내용과 함께 사용해야 합니다.

대상에서 Wants= 제거그러면... 무슨 일이 systemctl enable myapp일어나나요 systemctl start myapp?

관련 정보