systemd: inicialização de serialização

3

A inicialização no meu Ubuntu 17.04 falha e eu quero depurá-lo.

A inicialização falha aleatoriamente e acredito que seja devido a uma condição de corrida.

Posso pedir ao systemd para não paralelizar nenhuma tarefa, para que eu possa ver se isso faz com que a inicialização falhe de maneira previsível?

    
por Ole Tange 30.04.2017 / 11:04

1 resposta

1

Uma solução alternativa: executar serviços manualmente

Eu tive um travamento durante a inicialização . No meu caso, a falha ocorreu depois de atingir a meta basic.target , mas antes de multi-user.target , então eu queria descobrir qual dos serviços iniciados por multi-user.target estava causando a falha.

Primeiro eu organizei para inicializar basic.target mais um shell de root. Você pode fazer isso permanentemente (desde que você consiga inicializar!) Com

systemctl set-default basic.target
systemctl enable debug-shell

O serviço debug-shell executa um shell raiz no tty 9.

Você pode obter o mesmo efeito adicionando os parâmetros systemd.unit=basic.target systemd.debug-shell à linha de comando do kernel. Por exemplo, no Grub, edite a linha de comando para algo como

linux /vmlinuz-4.13.0-38-generic root=/dev/mapper/crypt-root ro systemd.unit=basic.target systemd.debug-shell

A partir deste shell, executei o seguinte script para iniciar os serviços, um por um. Observe que isso é em grande parte não testado (eu corri uma vez e ele caiu como esperado no serviço problemático).

#!/bin/sh
wants=$(systemctl show -p Wants multi-user.target | sed 's/^Wants=//' | tr ' ' '\n' | sort)
log=/var/tmp/multi-user-steps-$(date +%Y%m%d-%H%M%S)

log () {
  echo "$* ..." | tee -a "$log"
  sync
  "$@"
  ret=$?
  echo "$* -> $ret" | tee -a "$log"
  sync
  return $ret
}

# systemd services
for service in $wants; do
  log systemctl start $service
  sleep 2
done

# upstart services
for conf in /etc/init/*.conf; do
  service=${conf##*/}; service=${service%.conf}
  log service ${service} start
  sleep 2
done

# sysvinit services
for service in /etc/rc3.d/S*; do
  log ${service} start
  sleep 2
done

Adicionar dependências extras

O script abaixo declara dependências “Antes” das unidades do systemd que são dependências diretas de um determinado destino, a fim de forçá-las a executar em uma ordem específica. Você pode querer executá-lo em multi-user.target ou basic.target .

Note que este script não funciona em geral porque não leva em conta as dependências existentes: ele pode causar um loop de dependência. Um script adequado deve coletar dependências existentes e fazer um tipo topológico. Eu resolvi meu problema, então não pretendo mais trabalhar nele; Estou postando no caso de alguém querer adaptá-lo às suas necessidades.

Note também que isso não afeta os serviços do Upstart e SysVinit.

Faça um backup de /etc antes de executar isso! (Eu recomendo strongmente usar o etckeeper .)

#!/bin/sh
set -e

if [ $# -eq 0 ] || [ "$1" = "--help" ]; then
  cat <<EOF
Usage: $0 TARGET
Linearize the dependencies of a systemd target so it starts deterministically.
This scripts adds systemd unit files called linearize-for*.conf containing
extra Before= dependencies for each dependency of TARGET.
EOF
fi

service_dir=/etc/systemd/system
target=$1

wants=$(systemctl show -p Wants "$target" | sed 's/[^= ]*=//' |
                                            tr ' ' '\n' | sort)
previous=
for want in $wants; do
  [ -d "$service_dir/$want.d" ] || mkdir "$service_dir/$want.d"
  cat <<EOF >"$service_dir/$want.d/linearize-for-${target%.*}.conf"
[Unit]
Before=$previous
EOF
  previous=$want
done
    
por 09.04.2018 / 01:32