Find the official installation docs for v2.23 at teamhephy.com.
Feb 12, 2016 UPDATE: Jan 2, 2023
This post is available in: Chinese`
This is the third and final post in a series looking at CoreOS.
In my last post, we looked at the cloud-config file, running etcd in proxy mode, and some common etcd cluster setups.
In this post, we take a closer look at systemd, unit files, Fleet, and fleetctl.
systemd is an init system used by CoreOS that provides many powerful features for starting, stopping, monitoring, and restarting process. On CoreOS, systemd is used to manage the lifecycle of your Docker containers and also for different system bootstrap tasks.
Learning systemd would need a series of blog posts in itself. Here we only cover systemd to the extent that we need to run systemd units for Docker containers on CoreOS.
For more information about systemd, see the documentation.
systemd records initialization instructions for each daemon in a configuration file called a unit.
This system replaces the traditional System V per-daemon init scripts.
There are many different unit file types, but we’ll only cover the services type used to run Docker containers. We call these service units.
Here’s a list of the most useful features, presented in the order they’ll occur in the lifecycle of a service unit:
ExecStartPre: commands that run before
ExecStart: main commands to run for this unit
ExecStartPost: commands that run after all
ExecStartcommands have completed
ExecReload: commands that run when this unit is reloaded via
systemctl reload foo.service
ExecStop: commands that run when this unit is considered failed or if it is stopped via
systemctl stop foo.service
ExecStopPost: commands that run after
RestartSec: the amount of time to sleep before restarting a service (useful to prevent your failed service from attempting to restart itself every 100ms)
Let’s create a simple unit file called
[Unit] Description=HelloWorldApp After=docker.service Requires=docker.service [Service] TimeoutStartSec=0 ExecStartPre=-/usr/bin/docker rm busybox1 ExecStartPre=/usr/bin/docker pull busybox ExecStart=/usr/bin/docker run --rm --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done" ExecStop=/usr/bin/docker stop busybox1 [Install] WantedBy=multi-user.target
Let’s review the unit file we just created:
Descriptionshows up in the systemd log (a unit’s log can be checked with journalctl or systemdctl).
Requires=docker.servicemeans this unit only starts after
docker.serviceis active. You can define as many of these as you want. You can delimit multiple
Afterrequirements using space as a separator.
=-is systemd syntax to ignore errors for this command. In our case, Docker will return a non-zero exit code if we try to stop a container that doesn’t exist, so this is not an error for us.
ExecStartPre=-/usr/bin/docker rm busybox1will remove the Docker image
busybox1if such image exists. If there’s no Docker image to remove, the script will just move on.
ExecStartPre=/usr/bin/docker pull busyboxwill pull the
busyboximage from the Docker registry.
ExecStart=allows you to specify any command that you’d like to run when this unit is started. Do not run Docker containers with
-dbecause this will prevent the container from starting as a child of this process. systemd will think the process has exited and the unit will be stopped.
ExecStop=/usr/bin/docker stop busybox1will stop the Docker container
WantedBy=multi-user.target” tells systemd to pull in the unit when starting
Now to start a new unit, we need to tell systemd to create the symlink and then start the file:
$ sudo systemctl enable /etc/systemd/system/hello.service $ sudo systemctl start hello.service
To verify the unit started, check the list of containers running with:
Then, read the unit’s output with journalctl, like so:
$ journalctl -f -u hello.service -- Logs begin at Fri 2014-02-07 00:05:55 UTC. -- Feb 11 17:46:26 localhost docker: Hello World Feb 11 17:46:27 localhost docker: Hello World Feb 11 17:46:28 localhost docker: Hello World [...]
-u means unit, and
-f means follow. Press CTRL-c to exit.
The systemd service units can only run and be controlled on a single machine. They are best used for simple tasks, like downloading files on reboot, and so on.
For more information about systemd units, see the documentation.
Fleet runs on top of systemd and controls systemd at the cluster level, creating a distributed systemd:
To run your services in the cluster, you must submit regular systemd units combined with fleet-specific properties.
fleetctl is a command line tool allowing you to control fleet units on local and remote CoreOS Clusters.
The fleetctl commands are very similar to systemctl commands, and we do not have to use sudo with fleetctl.
fleetctl commands can be run directly on a cluster machine.
Or, if you prefer to execute fleetctl from an external host (i.e. your workstation), a system environment variable can be used to tunnel communication with your fleet cluster over SSH, like so:
$ export FLEETCTL_TUNNEL=remote_machines_ip
Let’s look at some of the tasks you can perform, listed with the required commands.
Loading and starting the unit:
$ fleetctl start hello_world.service
Checking the status of the unit:
$ fleetctl status hello_world.service
Stopping the service:
$ fleetctl stop hello_world.service
Viewing the content of service file:
$ fleetctl cat hello_world.service
If you want to upload the unit file without starting it:
$ fleetctl submit hello_world.service
Check the hello_world.service logs:
$ fleetctl journal hello_world.service
You can even ssh to the host that runs the unit:
$ fleetctl ssh hello_world.service
Listing all running fleet units:
$ fleetctl list-units
Listing fleet cluster machines:
$ fleetctl list-machines
Unit files are the primary interaction with fleet.
As with systemd units, fleet units define what you want to do and how fleet should provision them. Fleet schedules a valid unit file to one or more machines in a cluster, taking in mind fleet’s special properties from the
[X-Fleet] section, which replace the systemd unit’s
The rest of systemd’s sections remain the same in fleet units.
Let’s review fleet specific options:
MachineID: Unit will be scheduled to the machine identified by a given string
MachineOf: Limit eligible machines to the one that hosts a specific unit
MachineMetadata: Limit eligible machines to those with this specific metadata
Conflicts: Prevent a unit from being collocated with other units using glob-matching on the other unit names
Global: Schedule this unit on all machines in the cluster
All of these go in the
[X-Fleet] section of the unit file.
A unit is considered invalid if options other than
MachineMetadata are provided alongside
Global=true. Global units are very useful if you want to schedule the same unit on all machines or on some machines which
MachineMetadata matches some value.
Let’s convert the systemd unit file
hello_world.service we used above to a fleet unit:
[Unit] Description=HelloWorldApp After=docker.service Requires=docker.service [Service] TimeoutStartSec=0 ExecStartPre=-/usr/bin/docker rm busybox1 ExecStartPre=/usr/bin/docker pull busybox ExecStart=/usr/bin/docker run --rm --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done" ExecStop=/usr/bin/docker stop busybox1 [X-Fleet] MachineMetadata=group=hw_webservers Conflicts=hello_world@*
hello_world@* do in this example?
Well, let’s say we have two identical
We can start them on the cluster like so:
$ fleetctl start hello_world@*
These two fleet units will be deployed to servers with
MachineMetadata=group=hw_webservers metadada set, and
Conflicts=hello_world@* will prevent a unit from being collocated with other units using glob-matching on the other unit names.
Let’s look at a few more examples with other fleet options for
This schedules this unit on all machines in the cluster:
This schedules the unit on the machine that has the
This schedules the unit to the machine with the ID 562999f8:
You can check machine ID with:
$ fleetctl list-machines
For more information about fleet units, see the documentation.
Running a single container is very easy.
All you need to do is provide a regular unit file without an
[Install] section, like so:
[Unit] Description=hello_docker After=docker.service Requires=docker.service [Service] TimeoutStartSec=0 ExecStartPre=-/usr/bin/docker rm busybox1 ExecStartPre=/usr/bin/docker pull busybox ExecStart=/usr/bin/docker run --rm --name busybox1 busybox /bin/sh -c "while true; do echo Hello Docker; sleep 1; done" ExecStop=/usr/bin/docker stop busybox1
Note: If you’ve been running Docker commands manually, make sure you don’t copy a Docker run command that starts a container in detached mode (indicated by the
-d switch). This will cause the unit to run for just a few seconds and then exit.
In this post, we looked at:
Don’t miss the previous two parts of this miniseries. In part one, we looked the basics of CoreOS, atomic upgrades and release channels, and cluster discovery. And in part two, we looked at cloud-config and etcd.
In my next miniseries, I’ll be looking at Docker.