How to decide what dependencies a systemd service unit definition should have

https://access.redhat.com/solutions/3116611

 SOLUTION 已验证 - 已更新 2017年八月2日11:42 - 

English 

环境

  • Red Hat Enterprise Linux 7
  • systemd

问题

  • Deciding what dependencies systemd service unit definition should have can be a reasonably complex issue and it can be difficult to decide what they should be. This solution article tries to explain some of the issues.

决议

Background:

Unlike RHEL 6 where startup and shutdown scripts were executed sequentially according to start/stop symlinks on RHEL 7, many services can start in parallel according to dependencies they have explicitly defined.

That raises the important idea that with accurate dependency information defined in one of:

Raw

After=
Before=
WantedBy=

Your services may not start in the way that you expect (the use of other dependency keywords such as Requisite= BindsTo= Conflicts= PartOf= Wants= Requires= etc are not covered here please read the system.unit manual page or 9.6. Creating and Modifying systemd Unit Files in System Administration guide for additional information).

Detailed information:

Raw

After=/Before=

Definitions that are defined After= has the inverse meaning of Before= when giving a unit as After= there is an automatic assumption of equalence for the other unit. That is if a.service contains After=b.service the effect should be the same as if b.service defined Before=a.service.

That means that a.service cannot start until b.service has finished. These dependencies are applied in an inverse way during shutdown. That is a.service must be stopped (if it is running) before b.service can be stopped.

If both a.service and b.service instead both defined just Before=multi-user.target, they would both start and must complete before multi-user.target could be reached - however they may be started concurrently because they would no longer have any explicit dependency on each other.

It is important to understand that After=/Before= make no distinction between a service failing or starting successfully. The only meaning After=/Before= convey is about the order for stopping or starting a service. A discussion of other configuration that would prevent certain units from starting if dependencies fail is outside the scope of this discussion (some of the keywords mentioned above help with starting services only if something else started successfully).

Raw

WantedBy=

WantedBy= may be used more than once, or a space-separated list of unit names may be given. A symbolic link is created in the .wants/ directory of each of the listed units when this unit is installed by systemctl enable command. This has the effect that a dependency of type Wants= is added from the listed unit to the current unit. The primary result is that the current unit will be started when the listed unit is started.

Unless you have very specific requirements to start a service very early in the boot you should always put the following in the [Install] section of the service definition:

Raw

WantedBy=multi-user.target

For example, if a unit file of a.service has that option and a.service is enabled, multi-user.target will gain dependencies Wants=a.serviceand After=a.serivce. It means that option, WantedBy=multi-user.target, will ensure that a unit file which contains that opiton gets started (if not disabled or masked) before multi-user.target is reached.

Specific recommendations:

  1. Keep your ordering as simple as possible. If you have 3 services and they need to be started sequentially then create them as follows:

    Raw

    a.service (Before=b.service)
    b.service (After=a.service and Before=c.service)
    c.service (After=b.service)
    

    Adding Before=/After= as appropriate keeps the service unit definitions self contained and looking at one you know what its order will be. For example, in b.service you know it would be delayed from starting until after a.service and c.service will be delayed from starting until b.service has started. Again After= (during startup) provide ordering only they do not have any effect on if something will start if something it needs to have started does not do so.
    Let's use following examples to understand the dependency. These service unit files exist under /etc/systemd/system directory.

    • a.service

      Raw

      [Unit]
      Description=Test "a" Service
      Before=b.service
      
      [Service]
      ExecStart=/bin/true
      Type=oneshot
      
      [Install]
      WantedBy=multi-user.target
      
    • b.service

      Raw

      [Unit]
      Description=Test "b" Service
      After=a.service
      Before=c.service
      
      [Service]
      ExecStart=/bin/true
      Type=oneshot
      
      [Install]
      WantedBy=multi-user.target
      
    • c.service

      Raw

      [Unit]
      Description=Test "c" Service
      After=b.service
      
      [Service]
      ExecStart=/bin/true
      Type=oneshot
      
      [Install]
      WantedBy=multi-user.target
      

    Obviously these services do nothing and are simply used to illustrate the following commands. Now we can see what needs to be started before b.service can start and what gets started after:

    Raw

    # systemctl list-dependencies --after b.service
    b.service
    ├─a.service
    ├─system.slice
    ├─systemd-journald.socket
    └─basic.target
      ├─rhel-import-state.service
      ├─systemd-ask-password-plymouth.path
      ├─paths.target
      │ ├─brandbot.path
      │ ├─systemd-ask-password-console.path
      │ └─systemd-ask-password-wall.path
      ├─slices.target
    

    The --after option to systemctl list-dependencies shows what is listed as a dependency on the service given to the command. That is what has an Before= dependency on b.service - it does not show what will be started after b.service. We do expect to see a.service listed because it has an explicit Before= on b.service. The other dependencies are automatic ones.

    Conversely with the --before option we can see what has a After= dependency on b.service.

    Raw

    # systemctl list-dependencies --before b.service 
    b.service
     ├─c.service
     ├─multi-user.target
     │ ├─systemd-readahead-done.service
     │ ├─systemd-readahead-done.timer
     │ ├─systemd-update-utmp-runlevel.service
     │ └─graphical.target
     │   └─systemd-update-utmp-runlevel.service
     └─shutdown.target
       ├─systemd-reboot.service
       └─final.target
         └─systemd-reboot.service
    

    This command can help you understand the ordering of services in systemd. If a service did not have a dependency on another service it would be started in parallel at before the appropriate target was reached as defined by either, Before= After= or WantedBy=. Note that the shutdown.target is added automatically as the service listed must be stopped before shutdown.target can be reached.

  2. Using After=network-online.target

    How do I ensure that my service won't start until after networking is up and available? You need an After=network-online.target - See Running Services After the Network is up for additional information. In a lot of cases this should not be required unless your startup script requires active networking to be available (waiting on network-online.target may cause the boot to take longer). The URL provide gives information on how to create network services that can dynamically adapt to networking changes and do not require a specific interface to be up or a specific IP address to be available to use (at the bottom).

    Note that during shutdown there is no network-online.target. To be called for the shutdown before networking is torn down during a shutdown you must also add network.target to the After= definition. That assumes that the service needs networking to be up in order for it to be able to shutdown.

Specific issues created by ordering choices:

  1. Using After=something.target (where something would be replaced by a valid target name). An example some commands may not function reliably until after sysinit.target has been reached. An example is the rpm command which may work reliably until after sysinit.targethas been reached (see The rpm command may core dump with SIGBUS during and after system startup. for more information). In man 7 bootup, the overview of bootup sequence in systemd is described as below (Please be noticed that the quote loses formatting information in the man page. You should refer the original one on the machine with the man command also). For more details of each special target in the overview below, you should refer to man 7 systemd.special.

    Raw

    SYSTEM MANAGER BOOTUP
           At boot, the system manager on the OS image is responsible for initializing the required file systems, services and drivers that are necessary for operation of the system. On systemd(1) systems, this process is split up in various discrete steps which are
           exposed as target units. (See systemd.target(5) for detailed information about target units.) The boot-up process is highly parallelized so that the order in which specific target units are reached is not deterministic, but still adheres to a limited amount
           of ordering structure.
    
           When systemd starts up the system, it will activate all units that are dependencies of default.target (as well as recursively all dependencies of these dependencies). Usually, default.target is simply an alias of graphical.target or multi-user.target,
           depending on whether the system is configured for a graphical UI or only for a text console. To enforce minimal ordering between the units pulled in, a number of well-known target units are available, as listed on systemd.special(7).
    
           The following chart is a structural overview of these well-known units and their position in the boot-up logic. The arrows describe which units are pulled in and ordered before which other units. Units near the top are started before units nearer to the
           bottom of the chart.
    
               local-fs-pre.target
                        |
                        v
               (various mounts and   (various swap   (various cryptsetup
                fsck services...)     devices...)        devices...)       (various low-level   (various low-level
                        |                  |                  |             services: udevd,     API VFS mounts:
                        v                  v                  v             tmpfiles, random     mqueue, configfs,
                 local-fs.target      swap.target     cryptsetup.target    seed, sysctl, ...)      debugfs, ...)
                        |                  |                  |                    |                    |
                        \__________________|_________________ | ___________________|____________________/
                                                             \|/
                                                              v
                                                       sysinit.target
                                                              |
                         ____________________________________/|\________________________________________
                        /                  |                  |                    |                    \
                        |                  |                  |                    |                    |
                        v                  v                  |                    v                    v
                    (various           (various               |                (various          rescue.service
                   timers...)          paths...)              |               sockets...)               |
                        |                  |                  |                    |                    v
                        v                  v                  |                    v              rescue.target
                  timers.target      paths.target             |             sockets.target
                        |                  |                  |                    |
                        v                  \_________________ | ___________________/
                                                             \|/
                                                              v
                                                        basic.target
                                                              |
                         ____________________________________/|                                 emergency.service
                        /                  |                  |                                         |
                        |                  |                  |                                         v
                        v                  v                  v                                 emergency.target
                    display-        (various system    (various system
                manager.service         services           services)
                        |             required for            |
                        |            graphical UIs)           v
                        |                  |           multi-user.target
                        |                  |                  |
                        \_________________ | _________________/
                                          \|/
                                           v
                                 graphical.target
    
           Target units that are commonly used as boot targets are emphasized. These units are good choices as goal targets, for example by passing them to the systemd.unit= kernel command line option (see systemd(1)) or by symlinking default.target to them.
    
           timers.target is pulled-in by basic.target asynchronously. This allows timers units to depend on services which become only available later in boot.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值