Blog about things

blogging experiment about hacking

Kicking the Tires of coreOS

I’m going to try out CoreOS to create a cluster of machines to host an app consisting of docker containers.

CoreOS is a linux distribution designed to run clusters of containers efficiently and securely. Our application components run in Docker containers, organized as services. The distribution also includes etcd, a key/value store for distributed configuration management and service discovery; and fleet to manage services across clusters. The machines can be automatically updated with any security fixes and patches.

First, let’s create a CloudFormation template we will use to create our stack. I got a minimal template for CoreOS here, adding parameters for VPC subnets and related availability zones. I also updated the AMI for us-east-1, where I’m deploying this cluster, to the most recent stable version of CoreOS.

coreos-cloudformation-template.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "CoreOS on EC2: http://coreos.com/docs/running-coreos/cloud-providers/ec2/",
  "Mappings" : {
      "RegionMap" : {
          "ap-northeast-1" : { "AMI" : "ami-ab9fbeaa" },
          "sa-east-1" :      { "AMI" : "ami-3169c22c" },
          "ap-southeast-2" : { "AMI" : "ami-a31f7f99" },
          "ap-southeast-1" : { "AMI" : "ami-3e54716c" },
          "us-east-1" :      { "AMI" : "ami-b83295d0" },
          "us-west-2" :      { "AMI" : "ami-d32462e3" },
          "us-west-1" :      { "AMI" : "ami-9b5d53de" },
          "eu-west-1" :      { "AMI" : "ami-cec912b9" }
      }
  },
  "Parameters": {
    "InstanceType" : {
      "Description" : "EC2 HVM instance type (m3.medium, etc).",
      "Type" : "String",
      "Default" : "m3.medium",
      "AllowedValues" : [ "m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge", "c3.large","c3.xlarge", "c3.2xlarge", "c3.4xlarge","c3.8xlarge", "cc2.8xlarge","cr1.8xlarge","hi1.4xlarge", "hs1.8xlarge", "i2.xlarge", "i2.2xlarge", "i2.4xlarge", "i2.8xlarge", "r3.large", "r3.xlarge", "r3.2xlarge","r3.4xlarge", "r3.8xlarge", "t2.micro", "t2.small", "t2.medium" ],
      "ConstraintDescription" : "Must be a valid EC2 HVM instance type."
    },
    "VPCId": {
      "Description": "ID of VPC hosting cluster",
      "Type": "AWS::EC2::VPC::Id"
    },
    "SubnetIdList": {
      "Description": "Comma-separated list of VPC Subnet IDs to place instances",
      "Type": "List<AWS::EC2::Subnet::Id>"
    },
    "ClusterSize": {
      "Default": "3",
      "MinValue": "3",
      "MaxValue": "12",
      "Description": "Number of nodes in cluster (3-12).",
      "Type": "Number"
    },
    "DiscoveryURL": {
      "Description": "An unique etcd cluster discovery URL. Grab a new token from https://discovery.etcd.io/new",
      "Type": "String"
    },
    "AdvertisedIPAddress": {
      "Description": "Use 'private' if your etcd cluster is within one region or 'public' if it spans regions or cloud providers.",
      "Default": "private",
      "AllowedValues": ["private", "public"],
      "Type": "String"
    },
    "AllowSSHFrom": {
      "Description": "The net block (CIDR) that SSH is available to.",
      "Default": "0.0.0.0/0",
      "Type": "String"
    },
    "KeyPair" : {
      "Description" : "The name of an EC2 Key Pair to allow SSH access to the instance.",
      "Type" : "String"
    }
  },
  "Resources": {
    "CoreOSSecurityGroup": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "CoreOS SecurityGroup",
        "SecurityGroupIngress": [
          {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": {"Ref": "AllowSSHFrom"}}
        ]
      }
    },
    "Ingress4001": {
      "Type": "AWS::EC2::SecurityGroupIngress",
      "Properties": {
        "GroupName": {"Ref": "CoreOSSecurityGroup"}, "IpProtocol": "tcp", "FromPort": "4001", "ToPort": "4001", "SourceSecurityGroupId": {
          "Fn::GetAtt" : [ "CoreOSSecurityGroup", "GroupId" ]
        }
      }
    },
    "Ingress7001": {
      "Type": "AWS::EC2::SecurityGroupIngress",
      "Properties": {
        "GroupName": {"Ref": "CoreOSSecurityGroup"}, "IpProtocol": "tcp", "FromPort": "7001", "ToPort": "7001", "SourceSecurityGroupId": {
          "Fn::GetAtt" : [ "CoreOSSecurityGroup", "GroupId" ]
        }
      }
    },
    "CoreOSServerAutoScale": {
      "Type": "AWS::AutoScaling::AutoScalingGroup",
      "Properties": {
        "AvailabilityZones": {"Ref": "AvailabilityZones"},
        "VPCZoneIdentifier": {"Ref": "SubnetIdList"},
        "LaunchConfigurationName": {"Ref": "CoreOSServerLaunchConfig"},
        "MinSize": "3",
        "MaxSize": "12",
        "DesiredCapacity": {"Ref": "ClusterSize"},
        "Tags": [
            {"Key": "Name", "Value": { "Ref" : "AWS::StackName" }, "PropagateAtLaunch": true}
        ]
      }
    },
    "CoreOSServerLaunchConfig": {
      "Type": "AWS::AutoScaling::LaunchConfiguration",
      "Properties": {
        "ImageId" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "AMI" ]},
        "InstanceType": {"Ref": "InstanceType"},
        "KeyName": {"Ref": "KeyPair"},
        "SecurityGroups": [{"Ref": "CoreOSSecurityGroup"}],
        "UserData" : { "Fn::Base64":
          { "Fn::Join": [ "", [
            "#cloud-config\n\n",
            "coreos:\n",
            "  etcd:\n",
            "    discovery: ", { "Ref": "DiscoveryURL" }, "\n",
            "    addr: $", { "Ref": "AdvertisedIPAddress" }, "_ipv4:4001\n",
            "    peer-addr: $", { "Ref": "AdvertisedIPAddress" }, "_ipv4:7001\n",
            "  units:\n",
            "    - name: etcd.service\n",
            "      command: start\n",
            "    - name: fleet.service\n",
            "      command: start\n"
            ] ]
          }
        }
      }
    }
  }
}

The most interesting part is probably the UserData value for the CoreOSServerLaunchConfig resource, which configures the etcd service and ensures the etcd and fleet services are started at boot time. We’ll get back to those later.

aws cli cloudformation create-stack can accept JSON-formatted command specification. It has a template generator command switch –generate-cli-skeleton, which will generate an empty json file template with parameter placeholders. It contains some mutually exclusive keys so you’ll need to remove some keys.

I ran the command, saved the output as a file, and modified it to my needs. I populated the DiscveryURL parameter with a new unique key from from https://discovery.etcd.io/new.

aws-cfn-cli-skeleton.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
    "StackName": "coreos-test",
    "Parameters": [
        {
            "ParameterKey": "InstanceType",
            "ParameterValue": "t2.small"
        },
        {
            "ParameterKey": "AvailabilityZones",
            "ParameterValue": "us-east-1e"
        },
        {
            "ParameterKey": "SubnetIdList",
            "ParameterValue": "subnet-979a58bc"
        },
        {
            "ParameterKey": "ClusterSize",
            "ParameterValue": "3"
        },
        {
            "ParameterKey": "DiscoveryURL",
            "ParameterValue": "https://discovery.etcd.io/87a8b544cf5hhf7436d601273c1d22d8"
        },
        {
            "ParameterKey": "KeyPair",
            "ParameterValue": "mfrasier_key"
        },
        {
            "ParameterKey": "AdvertisedIPAddress",
            "ParameterValue": "private"
        },
        {
            "ParameterKey": "AllowSSHFrom",
            "ParameterValue": "0.0.0.0/0"
        }
    ],
    "DisableRollback": true,
    "TimeoutInMinutes": 5,
    "Tags": [
        {
            "Key": "Environment",
            "Value": "test"
        }
    ]
}

create AWS cloudformation stack

Create a new stack consisting of three coreOS instances using a command like the following:

1
2
3
 aws cloudformation create-stack \
   --cli-input-json file://aws-cfn-cli-skeleton.json \
   --template-body file://coreos-cloudformation-template.json

We can use the usual AWS tools to verify our new stack and instances are running.
We can also verify our cluster members are registered with the free cluster registery we’re using here. It will return the cluster member nodes that have registered.

For example: curl https://discovery.etcd.io/87a8b544cf5hhf7436d601273c1d22d8 might return something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
  "action": "get",
  "node": {
    "key": "\/_etcd\/registry\/f38f46b1a47b6cb10e1ceec59c7a3b74",
    "dir": true,
    "nodes": [
      {
        "key": "\/_etcd\/registry\/f38f46b1a47b6cb10e1ceec59c7a3b74\/de37e1c31c034971a3c7d08dc1067c2e",
        "value": "http:\/\/10.0.1.114:7001",
        "expiration": "2015-01-14T18:56:05.739262331Z",
        "ttl": 604160,
        "modifiedIndex": 272416204,
        "createdIndex": 272416204
      },
      {
        "key": "\/_etcd\/registry\/f38f46b1a47b6cb10e1ceec59c7a3b74\/c5c43f7f35a44eb2a67144bbca5f1477",
        "value": "http:\/\/10.0.1.113:7001",
        "expiration": "2015-01-14T18:57:54.178397919Z",
        "ttl": 604269,
        "modifiedIndex": 272416335,
        "createdIndex": 272416335
      },
      {
        "key": "\/_etcd\/registry\/f38f46b1a47b6cb10e1ceec59c7a3b74\/dfdaa3cf76504463bbc8e903905a227e",
        "value": "http:\/\/10.0.1.112:7001",
        "expiration": "2015-01-14T18:57:56.195430344Z",
        "ttl": 604271,
        "modifiedIndex": 272416369,
        "createdIndex": 272416369
      }
    ],
    "modifiedIndex": 272410125,
    "createdIndex": 272410125
  }
}

Sweet. I presume we may prefer our own cluster discovery service at some point. It’s just an etcd service.

interact with stack instances

start ssh-agent and add private key

This uses ssh-agent to provide your key when necessary. The ssh -A comand forwards the agent to the hosts so you can ssh between hosts (fleetctl needs it). Use the key you specified when creating your instances.

1
2
3
4
$ eval $(ssh-agent)
Agent pid 8456
$ ssh-add my_key.pem 
Identity added: my_key.pem (my_key.pem)

login to instances, at least two, in separate terminal windows

1
2
3
ssh -A core@<public-ip-address-1>
ssh -A core@<public-ip-address-2>
ssh -A core@<public-ip-address-3>

The username is core. You obviously need to allow SSH traffic into your new instances from you management workstation using the AWS security group rules.

check status of etcd service

Be sure to allow the instances to chat with each other on TCP ports 4001 and 7001. At the start of the output below I hadn’t yet edited my security group to allow that communication. Once I made the change, one host became the leader and they added peers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
systemctl -l status etcd.service
* etcd.service - etcd
   Loaded: loaded (/usr/lib64/systemd/system/etcd.service; static)
  Drop-In: /run/systemd/system/etcd.service.d
           └─10-oem.conf, 20-cloudinit.conf
   Active: active (running) since Tue 2015-01-06 21:36:55 UTC; 45s ago
 Main PID: 781 (etcd)
   CGroup: /system.slice/etcd.service
           └─781 /usr/bin/etcd

Jan 06 21:37:07 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:07.550 INFO      | 33edd7a8e0a844648df3f5dd2a4b6fbd attempted to join via 10.0.1.251:7001 failed: fail checking join version: Client Internal Error (Get http://10.0.1.251:7001/version: dial tcp 10.0.1.251:7001: i/o timeout)
Jan 06 21:37:07 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:07.550 INFO      | 33edd7a8e0a844648df3f5dd2a4b6fbd is unable to join the cluster using any of the peers [10.0.1.249:7001 10.0.1.251:7001] at 1th time. Retrying in 2.8 seconds
Jan 06 21:37:09 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:09.553 INFO      | Send Join Request to http://10.0.1.249:7001/join
Jan 06 21:37:09 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:09.564 INFO      | 33edd7a8e0a844648df3f5dd2a4b6fbd joined the cluster via peer 10.0.1.249:7001
Jan 06 21:37:09 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:09.601 INFO      | etcd server [name 33edd7a8e0a844648df3f5dd2a4b6fbd, listen on :4001, advertised url http://10.0.1.250:4001]
Jan 06 21:37:09 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:09.602 INFO      | peer server [name 33edd7a8e0a844648df3f5dd2a4b6fbd, listen on :7001, advertised url http://10.0.1.250:7001]
Jan 06 21:37:09 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:09.603 INFO      | 33edd7a8e0a844648df3f5dd2a4b6fbd starting in peer mode
Jan 06 21:37:09 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:09.603 INFO      | 33edd7a8e0a844648df3f5dd2a4b6fbd: state changed from 'initialized' to 'follower'.
Jan 06 21:37:09 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:09.669 INFO      | 33edd7a8e0a844648df3f5dd2a4b6fbd: peer added: '87b320be82a54d34851b253f61db489c'
Jan 06 21:37:12 ip-10-0-1-250.ec2.internal etcd[781]: [etcd] Jan  6 21:37:12.218 INFO      | 33edd7a8e0a844648df3f5dd2a4b6fbd: peer added: '50aed17347a1456e85279f8dda41e663'

test etcd distributed key service

see https://coreos.com/docs/quickstart/ as these steps are basically copied from that.

Set a key on one host

1
2
core@ip-10-0-1-249 ~ $ curl -L http://localhost:4001/v1/keys/message -d value="hello coreOS"
{"action":"set","key":"/message","value":"hello coreOS","newKey":true,"index":336}

Query the key from another host

1
2
core@ip-10-0-1-250 ~ $ curl -L http://localhost:4001/v1/keys/message
{"action":"get","key":"/message","value":"hello coreOS","index":349}

list machines and service units controlled by fleet

1
2
3
4
5
6
7
8
9
10
11
core@ip-10-0-1-249 ~ $ fleetctl list-machines
MACHINE         IP              METADATA
33edd7a8...     10.0.1.250      -
50aed173...     10.0.1.251      -
87b320be...     10.0.1.249      -

core@ip-10-0-1-249 ~ $ fleetctl list-units
UNIT    MACHINE ACTIVE  SUB

core@ip-10-0-1-249 ~ $ fleetctl list-unit-files
UNIT    HASH    DSTATE  STATE   TARGET

Good, we have some machines but no fleet controlled units yet. Let’s create a service.

Docker container services

Create a docker container nano-service

Create file ~/hello.service

hello.service
1
2
3
4
5
6
7
8
9
10
11
[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

load and start the unit

1
2
3
4
5
core@ip-10-0-1-249 ~ $ fleetctl load hello.service
Unit hello.service loaded on 33edd7a8.../10.0.1.250

core@ip-10-0-1-249 ~ $ fleetctl start hello.service
Unit hello.service launched on 33edd7a8.../10.0.1.250

You can see from the output the service was loaded and installed on a host other than the one we’re logged into. View cluster services with fleetctl stats <name.service>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
core@ip-10-0-1-249 ~ $ fleetctl list-units
UNIT            MACHINE                 ACTIVE  SUB
hello.service   33edd7a8.../10.0.1.250  active  running

core@ip-10-0-1-249 ~ $ fleetctl status hello.service
● hello.service - My Service
   Loaded: loaded (/run/fleet/units/hello.service; linked-runtime)
   Active: active (running) since Tue 2015-01-06 22:19:17 UTC; 1min 35s ago
  Process: 3872 ExecStartPre=/usr/bin/docker pull busybox (code=exited, status=0/SUCCESS)
  Process: 3860 ExecStartPre=/usr/bin/docker rm hello (code=exited, status=1/FAILURE)
  Process: 3796 ExecStartPre=/usr/bin/docker kill hello (code=exited, status=1/FAILURE)
 Main PID: 3917 (docker)
   CGroup: /system.slice/hello.service
           └─3917 /usr/bin/docker run --name hello busybox /bin/sh -c while true; do echo Hello World; sleep 1; done

Jan 06 22:20:43 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:44 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:45 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:46 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:47 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:48 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:49 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:50 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:51 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:20:52 ip-10-0-1-250.ec2.internal docker[3917]: Hello World

Notice that two of the ExecStatPre tasks exited with failure. Oh my, what has happened? Those are the docker rm and kill commands which failed because the container named hello hadn’t been created yet. Those commands are executed to ensure we create a new container upon service start and it’s ok if they fail with nothing to do.

Some container log lines are displayed also.

inspect docker images and containers

We can look on the host where our service was installed to verify it has docker image(s) and running container named hello.

1
2
3
4
5
6
7
8
9
10
11
12
13
core@ip-10-0-1-249 ~ $ ssh  ip-10-0-1-250 docker images
The authenticity of host 'ip-10-0-1-250 (10.0.1.250)' can't be established.
ED25519 key fingerprint is 8c:2d:b0:8d:08:b5:b2:6b:54:bb:8a:49:81:fd:a1:70.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ip-10-0-1-250,10.0.1.250' (ED25519) to the list of known hosts.

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
busybox             buildroot-2014.02   4986bf8c1536        6 days ago          2.433 MB
busybox             latest              4986bf8c1536        6 days ago          2.433 MB

core@ip-10-0-1-249 ~ $ ssh  ip-10-0-1-250 docker ps    
CONTAINER ID        IMAGE                       COMMAND                CREATED             STATUS              PORTS               NAMES
8c96226a4c0e        busybox:buildroot-2014.02   "/bin/sh -c 'while t   8 minutes ago       Up 8 minutes                            hello 

Or, looks like we can use fleetclt to ssh to other machines. We can use the machine id reported by fleetctl list-machines or list-units.

1
2
3
4
5
6
7
core@ip-10-0-1-249 ~ $ fleetctl list-units
UNIT            MACHINE                 ACTIVE  SUB
hello.service   33edd7a8.../10.0.1.250  active  running

core@ip-10-0-1-249 ~ $ fleetctl ssh 33edd7a8 docker ps
CONTAINER ID        IMAGE                       COMMAND                CREATED             STATUS              PORTS               NAMES
8c96226a4c0e        busybox:buildroot-2014.02   "/bin/sh -c 'while t   11 minutes ago      Up 11 minutes                           hello

super cool!

stop hello.service

Stop the service.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
core@ip-10-0-1-249 ~ $ fleetctl stop hello.service
Unit hello.service loaded on 33edd7a8.../10.0.1.250

core@ip-10-0-1-249 ~ $ fleetctl status hello.service
● hello.service - My Service
   Loaded: loaded (/run/fleet/units/hello.service; linked-runtime)
   Active: failed (Result: exit-code) since Tue 2015-01-06 22:42:36 UTC; 2s ago
  Process: 5601 ExecStop=/usr/bin/docker stop hello (code=exited, status=0/SUCCESS)
  Process: 3917 ExecStart=/usr/bin/docker run --name hello busybox /bin/sh -c while true; do echo Hello World; sleep 1; done (code=exited, status=255)
  Process: 3872 ExecStartPre=/usr/bin/docker pull busybox (code=exited, status=0/SUCCESS)
  Process: 3860 ExecStartPre=/usr/bin/docker rm hello (code=exited, status=1/FAILURE)
  Process: 3796 ExecStartPre=/usr/bin/docker kill hello (code=exited, status=1/FAILURE)
 Main PID: 3917 (code=exited, status=255)

Jan 06 22:42:30 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:42:31 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:42:32 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:42:33 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:42:34 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:42:35 ip-10-0-1-250.ec2.internal docker[3917]: Hello World
Jan 06 22:42:36 ip-10-0-1-250.ec2.internal docker[5601]: hello
Jan 06 22:42:36 ip-10-0-1-250.ec2.internal systemd[1]: hello.service: main process exited, code=exited, status=255/n/a
Jan 06 22:42:36 ip-10-0-1-250.ec2.internal systemd[1]: Stopped My Service.
Jan 06 22:42:36 ip-10-0-1-250.ec2.internal systemd[1]: Unit hello.service entered failed state.

restart hello.service

The hello.service unit is still loaded; we’ll restart it. This time the ExecStartPre tasks removing the previous container should succeed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
core@ip-10-0-1-249 ~ $ fleetctl start hello.service
Unit hello.service launched on 33edd7a8.../10.0.1.250
core@ip-10-0-1-249 ~ $ 
core@ip-10-0-1-249 ~ $ fleetctl status hello.service
● hello.service - My Service
   Loaded: loaded (/run/fleet/units/hello.service; linked-runtime)
   Active: active (running) since Tue 2015-01-06 22:43:47 UTC; 2s ago
  Process: 5601 ExecStop=/usr/bin/docker stop hello (code=exited, status=0/SUCCESS)
  Process: 5659 ExecStartPre=/usr/bin/docker pull busybox (code=exited, status=0/SUCCESS)
  Process: 5649 ExecStartPre=/usr/bin/docker rm hello (code=exited, status=0/SUCCESS)
  Process: 5641 ExecStartPre=/usr/bin/docker kill hello (code=exited, status=0/SUCCESS)
 Main PID: 5670 (docker)
   CGroup: /system.slice/hello.service
           └─5670 /usr/bin/docker run --name hello busybox /bin/sh -c while true; do echo Hello World; sleep 1; done

Jan 06 22:43:45 ip-10-0-1-250.ec2.internal systemd[1]: Starting My Service...
Jan 06 22:43:46 ip-10-0-1-250.ec2.internal docker[5641]: hello
Jan 06 22:43:46 ip-10-0-1-250.ec2.internal docker[5649]: hello
Jan 06 22:43:46 ip-10-0-1-250.ec2.internal docker[5659]: Pulling repository busybox
Jan 06 22:43:47 ip-10-0-1-250.ec2.internal docker[5659]: Status: Image is up to date for busybox:latest
Jan 06 22:43:47 ip-10-0-1-250.ec2.internal systemd[1]: Started My Service.
Jan 06 22:43:47 ip-10-0-1-250.ec2.internal docker[5670]: Hello World
Jan 06 22:43:48 ip-10-0-1-250.ec2.internal docker[5670]: Hello World
Jan 06 22:43:49 ip-10-0-1-250.ec2.internal docker[5670]: Hello World

That’s the simple quickstart tour of coreOS, etcd, and mantaing services with fleet on an Amazon AWS cluster. I think the next step is to define some more useful, multi-tiered, services. We’ll need to define the relationships betwen the services, configurations, etc. Let’s use etcd!

a bonus service

Here’s the beginning of a private docker registry service.

registry.service
1
2
3
4
5
6
7
8
9
10
11
12
core@ip-10-0-1-249 ~ $ cat registry.service
[Unit]
Description=My Registry Service
After=docker.service

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