동작하는 명령줄을 ExecStart에 복사하고 다시 불러오면, 인수가 파일 이름으로 취급되면서 유닛이 실패하거나 조용히 종료됩니다. systemd에 잘못된 것은 없습니다. ExecStart는 인수 목록이지 셸이 아니므로 파이프, 리디렉션, 조건문은 터미널을 떠나는 순간 의미를 잃습니다. 셸이 어디에 속하는지 정한 뒤, 무엇을 실행해야 하는지 정확히 밝히는 유닛을 작성하세요.

ExecStart의 파이프라인이 아무 일도 하지 않는 이유

터미널에서 명령을 입력하면 셸이 먼저 그것을 해석합니다. 파이프라인을 만들고, 리디렉션용 파일을 열고, 변수를 확장한 다음에야 프로그램이 시작됩니다. systemd는 그중 아무것도 하지 않습니다. 자체 규칙으로 ExecStart를 나누고, 그 결과 프로그램을 해당 인수로 실행할 뿐입니다. 그래서 > 는 문자 그대로의 인수로 도착하고, | 는 두 프로세스를 연결하는 대신 프로그램에 넘겨집니다. 유닛이 잘못 동작하는 것이 아니라, 문법이 다른 뜻이었던 것입니다.

이는 부주의가 아니라 의도된 설계입니다. 인수 처리가 예측 가능해야 유닛을 감사할 수 있기 때문입니다. 셸이 있다고 가정하는 사람만 놀랄 뿐입니다.

무엇이 실행되는지 정확히 밝히는 유닛 쓰기

저널에서 목적이 보이는 작은 유닛부터 시작하면 파싱 문제와 터널 문제를 분리할 수 있습니다. 이것은 바이너리 버전만 보고합니다.

[Unit]
Description=Report the installed OpenVPN version

[Service]
Type=oneshot
ExecStart=/usr/sbin/openvpn --version
StandardOutput=journal
StandardError=journal

경로는 자리 표시자이므로 실제로 설치된 바이너리로 바꾸세요. Type=oneshot 는 끝까지 실행되는 명령에 맞고, 저널 설정은 그 출력을 터미널 없이 읽을 수 있는 곳에 둡니다. 설치 섹션은 의도적으로 없으므로 유닛은 부팅 시 활성화되는 대신 손으로 호출합니다. 시작하기 전에 파일을 검증하세요. 지시문 이름의 오타는 경로의 오타와 다르게 실패합니다.

systemd-analyze verify /etc/systemd/system/openvpn-version.service
systemctl start openvpn-version.service

셸 로직은 스크립트에 두기

헬퍼에 조건부 로직이 정말 필요하다면 그 로직을 스크립트에 넣고 명시적으로 호출하세요. 스크립트에는 인터프리터 줄, 실행 권한, 서비스가 보고할 수 있는 종료 코드가 필요합니다. /bin/sh -c 를 따옴표로 묶은 명령과 함께 실행하는 것은 실제로 동작하지만, 인용 규칙을 유닛 안에 숨기고 검토를 어렵게 만듭니다. 이름 있는 스크립트는 손으로 시험하고 나중에 유닛을 고치지 않고 교체할 수 있습니다. 대화형 사용자의 셸 프로필에 의존하지 마세요. 서비스는 자체 환경으로 시작합니다.

상대 경로가 중요할 때 WorkingDirectory 를 설정하고, 환경은 터미널에서 내보내는 대신 유닛이 지원하는 지시문으로 전달하세요. 비밀은 보호된 파일이나 자격 증명에 속하며, 저널이 기록할 수 있는 인수 목록에 속하지 않습니다.

실제로 존재하는 유닛 확인하기

drop-in 파일은 편집했다고 기억하는 파일을 건드리지 않고 동작을 바꿉니다. 관리자가 무엇을 불러왔고 마지막 실행이 무엇을 보고했는지 읽으세요.

systemctl cat vpn-helper.service
journalctl -u vpn-helper.service -n 20 --no-pager

유닛 덤프에는 drop-in 파일이 로드 순서대로 포함되며, 예상치 못한 ExecStart는 대개 거기서 나옵니다. 변경한 뒤에는 관리자를 다시 불러오고 새 정의가 systemctl cat 가 출력하는 것인지 확인하세요. 저널은 정확한 명령줄과 종료 상태를 보여 줍니다, 이는 인수 문제를 바이너리 누락이나 권한 오류와 분리합니다. 그다음 헬퍼가 기존 VPN 수명 주기에 속하는지, 수동 진단으로 남아야 하는지 결정하세요. 두 번째 백그라운드 터널 프로세스는 경쟁하는 소유자를 만들기 때문입니다.

요청에 따라 올바른 출력을 내는 유닛은 좋은 첫 이정표입니다. 아직 터널이 아니며, 터널인 척해서도 안 됩니다.

관련 글