We hope you find this tutorial helpful. In addition to guides like this one, we provide simple cloud infrastructure for developers. Learn more →

How To Use Fleet and Fleetctl to Manage your CoreOS Cluster

PostedSeptember 12, 2014 50.3k views Clustering Scaling Networking System Tools CoreOS

Status: Deprecated

Reason: On December 22, 2016, CoreOS announced that it will no longer maintain fleet; It will receive security updates and bug fixes until February of 2017, when it will be removed from CoreOS. The project recommends using Kubernetes for all clustering needs.

See Instead: For guidance using Kubernetes on CoreOS without fleet, see the Kubernetes on CoreOS Documentation.

Introduction

CoreOS provides an excellent environment for managing Docker containers across multi-server environments. One of the most essential components for making this cluster management simple is a service called fleet.

Fleet allows users to manage Docker containers as services for their entire cluster. It works by acting as an interface and a level of abstraction over each cluster member's systemd init system. Users can set constraints that affect the conditions under which a service runs. This lets administrators define what they would like their infrastructure to look like by telling certain applications to run on the same or separate hosts based on the supplied criteria.

In this guide, we will explore fleet and the fleetctl utility that allows you to control the daemon.

Prerequisites

To follow along with this guide, you should have a CoreOS cluster available.

The cluster we are using in this guide can be created by following our guide on how to create a CoreOS cluster on DigitalOcean. We will assume that you have the cluster configuration described in that guide.

The cluster that has been configured has three nodes. They are configured to communicate between each other using the private networking interface. The public interface is available on each of these nodes for running public services. The node names are:

  • coreos-1
  • coreos-2
  • coreos-3

When you have your cluster ready, continue on to learn more about fleet.

Working with Service Unit Files

Before we get into the fleetctl tool, we should talk a bit about service unit files.

Unit files are used by the systemd init system to describe each available service, define the commands needed to manage it, and set dependency information to ensure the system is in a workable state when each service is started. The fleet daemon is built on top of systemd in order to manage services on a cluster-wide level. Because of this, it uses slightly modified versions of standard systemd unit files.

To get an in-depth look at fleet unit files, follow our deep dive on the subject. For this guide, we will just be giving a rough overview of the format of these files. We will also provide an example unit file that you can use to learn about fleetctl.

Fleet Unit File Sections

The basic sections that most unit files will have are:

  • Unit: This section is used to provide generic information about the unit that is not dependent on the "type" of unit. This will include metadata information and dependency information. This is mainly used in fleet to provide descriptions and specify this units place in connection to other service units.
  • Unit Type Section: The fleet daemon can take units of different types, including:
    • Service
    • Socket
    • Device
    • Mount
    • Automount
    • Timer
    • Path

If the type has specific options, then a section of the associated type is allowed. The Service section type is by far the most common. This section is used to define type-specific attributes. For Service units, this usually involves defining the start and stop commands, as well as the pre and post start or stop commands that might perform associated actions.

  • X-Fleet: This section is used to provide fleet-specific configuration options. This mainly means that you can specify that a service must or must not be scheduled in a certain way based on criteria like machine ID, currently running services, metadata information, etc.

The general format of a unit file will be:

[Unit]
Generic_option_1
Generic_option_2

[Service]
Service_specific_option_1
Service_specific_option_2

[X-Fleet]
Fleet_option_1
Fleet_option_2

Sample Service Unit File

To get started with this tutorial, we will give you a unit file to use. This is taken from the CoreOS quickstart page as an example. On one of your CoreOS machines, type:

vim hello.service

Inside, type out our example service file:

[Unit]
Description=My Service
After=docker.service

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill hello
ExecStartPre=-/usr/bin/docker rm hello
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --name hello busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop=/usr/bin/docker stop hello

Let's quickly go over what this does.

In the [Unit] section, a description is set and we tell systemd that this service can only be run after the docker.service unit has started. This is because our unit relies on Docker to function.

In the [Service] section, we disable the starting timeout and then we set up some actions to run prior to starting the service. The ExecStartPre is executed before the main ExecStart action. If these are called with =- it means that the action can fail and not affect the service's completion. This is necessary since our pre-start actions basically tear down any previously running service that may have been running. This will fail if none can be found and we do not want that to stop our service from starting since this is just a cleanup procedure.

The last pre-start action pulls down the basic busybox image which will be used to run our commands. Since this is necessary, we don't use the =- syntax. We then start a container with this image with an infinite loop that prints out "Hello World" once a second. The stop action simply stops this container.

This will be just enough to get you up and running. If you want more information about how to develop fleet files, check out our guide on fleet unit files.

Basic Machine Management Commands

The first thing we will do is introduce you to the fleetctl utility. As a cluster administrator, this tool will be your main interface to manage your fleet of machines. Much of the syntax has been carried over from systemctl, systemd's management tool.

To start off, we can get a list of all of the cluster members by typing:

fleetctl list-machines
MACHINE     IP      METADATA
14ffe4c3... 10.132.249.212  -
1af37f7c... 10.132.249.206  -
9e389e93... 10.132.248.177  -

As you can see, each of your machines is listed here as being available. When each member bootstraps itself using the cloud-config file, it generates a unique machine ID, which is used to identify each node. This is written to a file at /etc/machine-id.

By default, fleet will use the machine's public IPv4 address for communication with other members. However, in our cloud-config file, we told fleet to use our private interfaces for communication. These are the IP addresses shown in the above output.

The "METADATA" column is currently blank in the above example. However, we could have added arbitrary key-value pairs under the metadata attribute for fleet in the cloud-config. This might look like this:

#cloud-config
. . .
coreos:
  fleet:
    public-ip: $private_ipv4
    metadata: region=europe,public_ip=$public_ipv4

If you set this in your cloud-config to bootstrap all of your machines, your output would look more like this:

MACHINE     IP      METADATA
14ffe4c3... 10.132.249.212  public_ip=104.131.36.200,region=europe
1af37f7c... 10.132.249.206  public_ip=104.131.15.192,region=europe
9e389e93... 10.132.248.177  public_ip=104.131.15.192,region=europe

This extra data is useful for quickly getting information about a node from a management perspective, but it can also be used in service definitions to target specific hosts.

To connect to a specific machine within the cluster, you can use the fleetctl ssh command. This will allow you to identify a machine to connect to based on its machine ID or machine associated with a supplied unit name.

For instance, if you have a unit running called nginx.service, you can connect to whatever host is running that service by typing:

fleetctl ssh nginx

You will be dropped into a shell session on the associated host. You can also run a single command on a remote host, just as you can when running with the normal ssh executable. For instance, to get the values of the COREOS_PRIVATE_IPV4 and COREOS_PUBLIC_IPV4 variables that CoreOS sets in a file called /etc/environment (based on the parameters of the cloud-config and the networking interfaces available), you can type:

fleetctl ssh nginx cat /etc/environment
COREOS_PRIVATE_IPV4=10.132.249.212
COREOS_PUBLIC_IPV4=104.131.29.80

Service Management

Most of the other commands available through fleetctl are based around service management.

Starting a Service

Starting a service involves quite a few steps. The service file must be uploaded into fleet so that it is aware of the unit. It must then be scheduled onto a specific machine out of the cluster. It can then be started. There are commands for each of these with fleetctl, with the commands responsible for the latter stages also performing the former if necessary.

You can use the submit command to submit your unit file into fleet. This will simply cause fleet to read the file contents into memory, making it available for further actions.

fleetctl submit hello.service

Your hello.service file is now known to fleet. To see the unit files that have been submitted, you can type:

fleetctl list-unit-files
UNIT        HASH    DSTATE      STATE       TMACHINE
hello.service   0d1c468 inactive    inactive    -

As you can see, the unit file is present, but has not been scheduled on any hosts or started.

To see the contents of a unit file that fleet knows about, you can type:

fleetctl cat hello.service
[Unit]
Description=My Service
After=docker.service

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill hello
ExecStartPre=-/usr/bin/docker rm hello
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --name hello busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop=/usr/bin/docker stop hello

This will allow you to see the current file that fleet knows about.

Note: The submit command is idempotent, meaning that fleet will not update the in-memory unit file if you re-submit it. If you need to update your unit file, you must remove it completely and then re-submit it. We will cover how to do this later.

Once your unit is submitted, the next step is to schedule it on a machine. Scheduling the unit involves the fleet engine looking at the unit to decide on the best machine in the cluster to pass the unit to. This will be predicated on the conditions within the [X-Fleet] section of the unit, as well as the current work volume of each machine in the cluster. When the unit has been scheduled, it has been passed to the specific machine and loaded into the local systemd instance.

Use the load command to load and schedule the unit:

fleetctl load hello.service
Unit hello.service loaded on 14ffe4c3.../10.132.249.212

If you had not manually loaded your unit earlier, it will be loaded automatically as part of this process by searching for the appropriate filename in the current directory.

Now, if we check our unit files, we can see that it has been loaded. We can even see which machine it is scheduled on:

fleetctl list-unit-files
UNIT        HASH    DSTATE  STATE   TMACHINE
hello.service   0d1c468 loaded  loaded  14ffe4c3.../10.132.249.212

This is also our first opportunity to check out the list-units command. This command is used to show any running or scheduled units and their statuses:

fleetctl list-units
UNIT        MACHINE             ACTIVE      SUB
hello.service   14ffe4c3.../10.132.249.212  inactive    dead

To actually start a unit, you can use the start command. This will start the unit on the machine in which it has been loaded by executing the start commands defined in the unit file:

fleetctl start hello.service
Unit hello.service launched on 14ffe4c3.../10.132.249.212

Once again, we should check the list-unit-files:

fleetctl list-unit-files
UNIT        HASH    DSTATE      STATE       TMACHINE
hello.service   0d1c468 launched    launched    14ffe4c3.../10.132.249.212

In the above output, we can see that the service has been launched. The DSTATE column indicates the "desired state" and the STATE indicates the actual state. If these two match, this usually means that the action was successful.

We should also look at list-units again:

fleetctl list-units
UNIT        MACHINE             ACTIVE  SUB
hello.service   14ffe4c3.../10.132.249.212  active  running

This gives us information about the systemd state. It is collected directly from the local daemon, so this is a better picture of how the local system sees the service state. The ACTIVE column is a generalized state of the unit, while SUB is a more low-level description.

Stopping a Service

Each of the commands above have a companion command that reverses the state.

For instance, to stop a service from running, use the stop command. This will cause the local machine's systemd instance to execute the stopping commands defined in the unit:

fleetctl stop hello.service
Unit hello.service loaded on 14ffe4c3.../10.132.249.212

As you can see, the service has reverted back to the loaded state. This means that it is still loaded in the machine's systemd, but it is not currently running. We can confirm that here:

fleetctl list-unit-files
UNIT        HASH    DSTATE  STATE   TMACHINE
hello.service   0d1c468 loaded  loaded  14ffe4c3.../10.132.249.212

To remove the unit from that machine's systemd, but keep it available in fleet, you can unload the unit. If the unit is currently active, it will be stopped prior to being unloaded:

fleetctl unload hello.service

If we check the state, we can see that it is now marked as inactive. It also does not have a target machine listed:

fleetctl list-unit-files
UNIT        HASH    DSTATE      STATE       TMACHINE
hello.service   0d1c468 inactive    inactive    -

If we want to remove the unit from fleet entirely, we can use the destroy command. This will stop and unload the unit if necessary, and then remove the unit from fleet:

fleetctl destroy hello.service

If you modify a unit file, you must destroy the current unit in fleet before submitting/starting it again.

Getting Unit Statuses

You have already seen some of the methods of getting information about the status of units.

For instance, we have covered how list-units will list all of the units that have currently been scheduled on a machine:

fleetctl list-units
UNIT        MACHINE             ACTIVE  SUB
hello.service   14ffe4c3.../10.132.249.212  active  running

The list-unit-files provides a list of all units that fleet knows about. It also gives information about the desired and actual state:

fleetctl list-unit-files
UNIT        HASH    DSTATE      STATE       TMACHINE
hello.service   0d1c468 launched    launched    14ffe4c3.../10.132.249.212

For more specific information about a unit that has been started, there are a few other commands. The status command passes back the systemctl status result for the service on the host that is running the unit:

fleetctl status hello.service
● hello.service - My Service
   Loaded: loaded (/run/fleet/units/hello.service; linked-runtime)
   Active: active (running) since Mon 2014-09-08 21:51:22 UTC; 3min 57s ago
  Process: 7630 ExecStartPre=/usr/bin/docker pull busybox (code=exited, status=0/SUCCESS)
  Process: 7618 ExecStartPre=/usr/bin/docker rm hello (code=exited, status=0/SUCCESS)
  Process: 7609 ExecStartPre=/usr/bin/docker kill hello (code=exited, status=0/SUCCESS)
 Main PID: 7638 (docker)
   CGroup: /system.slice/hello.service
           └─7638 /usr/bin/docker run --name hello busybox /bin/sh -c while true; do echo Hello World; sleep 1; done

Sep 08 21:55:11 coreos-3 docker[7638]: Hello World
Sep 08 21:55:12 coreos-3 docker[7638]: Hello World
Sep 08 21:55:13 coreos-3 docker[7638]: Hello World
Sep 08 21:55:14 coreos-3 docker[7638]: Hello World
Sep 08 21:55:15 coreos-3 docker[7638]: Hello World
Sep 08 21:55:16 coreos-3 docker[7638]: Hello World
Sep 08 21:55:17 coreos-3 docker[7638]: Hello World
Sep 08 21:55:18 coreos-3 docker[7638]: Hello World
Sep 08 21:55:19 coreos-3 docker[7638]: Hello World
Sep 08 21:55:20 coreos-3 docker[7638]: Hello World

As you can see, we finally get to verify that the output of our unit is being produced.

Similarly, if you wish to see the journal entry for the service that is available on the associated machine, you can use the journal command:

fleetctl journal hello.service
-- Logs begin at Mon 2014-09-08 14:22:14 UTC, end at Mon 2014-09-08 21:55:47 UTC. --
Sep 08 21:55:38 coreos-3 docker[7638]: Hello World
Sep 08 21:55:39 coreos-3 docker[7638]: Hello World
Sep 08 21:55:40 coreos-3 docker[7638]: Hello World
Sep 08 21:55:41 coreos-3 docker[7638]: Hello World
Sep 08 21:55:42 coreos-3 docker[7638]: Hello World
Sep 08 21:55:43 coreos-3 docker[7638]: Hello World
Sep 08 21:55:44 coreos-3 docker[7638]: Hello World
Sep 08 21:55:45 coreos-3 docker[7638]: Hello World
Sep 08 21:55:46 coreos-3 docker[7638]: Hello World
Sep 08 21:55:47 coreos-3 docker[7638]: Hello World

By default, this will show the last 10 lines. You can adjust this by adding a --lines parameter, like this:

fleetctl journal --lines 20 hello.service

You can also use the -f parameter, which stands for "follow". This behaves in a similar way to tail -f in that it will continue to pass back the latest log entries:

fleetctl journal -f hello.service

Conclusion

By learning how to use fleet and fleetctl effectively, you can easily control your CoreOS cluster. Your services and containers can be moved around to different machines without much of a problem.

In a later guide, we'll discuss more in-depth how to create fleet unit files. This will allow you to create flexible and powerful services that takes advantage of the CoreOS architecture.

7 Comments

Creative Commons License