Tenho que unidades Systemd de um serviço e um soquete. O comportamento desejado é que quando eu iniciar o soquete o serviço será iniciado primeiro seguido pelo soquete e quando qualquer um for desativado ambos devem ser desativados. O serviço se vincula ao soquete antes que a unidade de soquete seja iniciada, e é por isso que o tenho como um serviço de notificação para que ele não relate como iniciado até que tenha o vínculo no soquete.
Aqui estão os arquivos das duas Unidades.
#rustyvxcan.service
[Unit]
Description=Docker VXCAN plugin Service
#PartOf=rustyvxcan.socket
Before=docker.service
After=network.target
[Service]
Type=notify
ExecStartPre=/usr/bin/mkdir -p /run/docker/plugins
ExecStart=/home/braedon/.cargo/bin/rustycan4docker
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
#rustyvxcan.socket
[Unit]
Description=A network plugin for vxcan
Before=docker.service
AssertPathExists=/run/docker/plugins
Requires=rustyvxcan.service
After=rustyvxcan.service
[Socket]
ListenStream=/run/docker/plugins/rustyvxcan.sock
RemoveOnStop=True
[Install]
WantedBy=sockets.target
A mensagem de erro é a seguinte.
Sep 19 16:50:38 localhost systemd[1]: rustyvxcan.service: Found ordering cycle on rustyvxcan.socket/start
Sep 19 16:50:38 localhost systemd[1]: rustyvxcan.service: Found dependency on rustyvxcan.service/start
Sep 19 16:50:38 localhost systemd[1]: rustyvxcan.service: Unable to break cycle starting with rustyvxcan.service/start
Sep 19 16:55:09 localhost systemd[1]: rustyvxcan.socket: Found ordering cycle on rustyvxcan.service/start
Sep 19 16:55:09 localhost systemd[1]: rustyvxcan.socket: Found dependency on rustyvxcan.socket/start
Sep 19 16:55:09 localhost systemd[1]: rustyvxcan.socket: Unable to break cycle starting with rustyvxcan.socket/start
Pelos recursos, parece que significa que o ciclo está acontecendo apenas com esses dois arquivos e eu posso limpar o erro removendo a linha After do arquivo .socket, mas então eles inicializam simultaneamente e o executável para o serviço falha. Eu provavelmente poderia resolver isso simplesmente adicionando um sleep ao socket, mas não vejo informações de dependência suficientes aqui para causar um ciclo.
Eu tentei limpar /{lib,etc}/systemd/system do serviço e do arquivo de socket e reinstalar os que foram colados aqui só para ter certeza de que versões mais antigas não estavam em execução e, claro, executar systemctl daemon-reload regularmente enquanto o modificava. Mas não consigo entender por que especificar depois quebra o gráfico de dependências mesmo depois de remover as dependências do arquivo de serviço.
Onde está acontecendo o loop que estou perdendo?
Isso parece que você está tentando ligar
bind()
para si mesmo. O socket systemd realmente fará isso para você. É por isso que seu socket está falhando em vincular quando você força o serviço a iniciar primeiro.No caso de
ListenStream=
(uma conexão TCP ), o systemd chamarásocket()
,bind()
, elisten()
para você.Se você tiver
Accept=no
(padrão), quando os dados forem recebidos no socket,systemd
iniciará seu processo e passará o FD daquele socket para você na variável de ambiente$LISTEN_FDS
(veja sd_listen_fds(3) para detalhes). Este FD é o descritor de arquivo do socket de escuta . Você precisa chamaraccept()
para gerar um socket de conexão em cada conexão de entrada.Com
Accept=yes
, seu serviço deve ser uma instância (por exemplo,foo.socket
e[email protected]
). Nesse caso, o systemd chamaaccept()
você e inicia seuExecStart=
uma vez por conexão, passando o FD do soquete de conexão . Você poderead()
,select()
,poll()
de, ouwrite()
para esse FD imediatamente.Eu costumo usar
Accept=yes
,StandardInput=socket
eStandardOutput=socket
. Então não preciso me preocupar com$LISTEN_FDS
, TCP vs UDP. Eu apenas leio do stdin e escrevo no stdout. Nesse caso, qualquer coisa que eu queira registrar é escrita parastderr
o que vai para o diário.Se você quiser chamar
socket()
,bind()
e vocêlisten()
mesmoaccept()
, não há necessidade de uma unidade systemd.socket.Em termos de uso...
Geralmente só faz sentido habilitar/iniciar o socket. O socket cuidará de iniciar o serviço somente quando os dados forem recebidos. Sua briga com
After=
,Requires=
,Assert*=
, eExecStartPre=mkdir...
na verdade só torna sua vida mais difícil porque você está indo contra o uso pretendido. Eu faria isso:Em vez disso,
ExecStartPre=mkdir
eu usaria deploy umatmpfiles.d
configuração para garantir que o systemd gere esse caminho.systemd-tmpfiles-setup.service
usará este arquivo para garantir que o diretório seja criado quando você inicializar. Se você quiser que o arquivo seja criado agora (talvez na instalação), entãosystemd-tmpfiles --create
fará isso para você.