2

Docker remote APIs. More than meets the eye

illus5-colorful-square

Docker remote APIs have come leaps and bounds in the last few releases. If the aim is to never login to a server, automation and repeatability a must. Docker sometimes seems like a strange choice as its so heavily command line based. There is more to Docker than just that with the remote api, we can do things like list and get container info. That is really useful stuff but for this blog we are going to do something a little more flashy. We are going to automate a swarm cluster and deploy a service to the cluster. This service will contain 4 replicas of the test web app. To read about the full extent of the Docker remote api, see the following docs

To build this we are going to need two boxes with Docker 1.12 installed and make sure we have the daemon configured to the following DOCKER_OPTS=" -H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock
--ip-forward=true --iptables=true --ip-masq=true

(Disclaimer !!! Don’t set your daemon to listen to 0.0.0.0 in a production like environment. It’s not secure. Read the offical security advise here).

I am using Ubuntu so the daemon configuration is set in/etc/default/docker If you are using any other OS please see the official Docker docs Don’t forget to restart the daemon after you make the change. My hosts will be docker-01 with the ip 172.17.10.101 and docker-02 with the ip of 172.17.10.102

As we are going to be passing JSON payloads to the Docker remote api, a great tool to use is Postman. Thats what we will be using and you will see the benefits as we go along.

The fist thing that we are going to do is make our first node a swarm manager, we will do this by sending a JSON payload to our daemon at http://172.17.10.101:4243/v1.24/swarm/init. You can tell from the URL the function we are going to carry out. So in Postman we will create a call with the POST type, no auth, the headers are application/json and the body of

{
"ListenAddr": "0.0.0.0:2377",
"AdvertiseAddr": "172.17.10.101:2377",
"ForceNewCluster": false,
"Spec": {
"Orchestration": {},
"Raft": {},
"Dispatcher": {},
"CAConfig": {}
}
}

In Postman it will look like this
postman-init

Then we can hit the send button, and we will get a response that looks like "awl3c1oh5n1cu36whsuklp4u5"

Now we have our Swarm manager, lets inspect our cluster. This time we will use the GET method to the following URL http://172.17.10.101:4243/v1.24/swarm If your cluster is up and running you will get the following response

{
"ID": "3mp3v5hdcx9lwuemovj578q8s",
"Version": {
"Index": 11
},
"CreatedAt": "2016-08-30T00:52:38.812390319Z",
"UpdatedAt": "2016-08-30T00:52:38.862244559Z",
"Spec": {
"Name": "default",
"Orchestration": {
"TaskHistoryRetentionLimit": 10
},
"Raft": {
"SnapshotInterval": 10000,
"LogEntriesForSlowFollowers": 500,
"HeartbeatTick": 1,
"ElectionTick": 3
},
"Dispatcher": {
"HeartbeatPeriod": 5000000000
},
"CAConfig": {
"NodeCertExpiry": 7776000000000000
},
"TaskDefaults": {}
},
"JoinTokens": {
"Worker": "SWMTKN-1-1122j7c3tpt3rjkye7im1srysjcx2s1xjt2rcmz9emmhcf8c30-688h32jf1wy16tguuw78m4h55",
"Manager": "SWMTKN-1-1122j7c3tpt3rjkye7im1srysjcx2s1xjt2rcmz9emmhcf8c30-b42a5lexfpem0o8qgm2uevdwp"
}
}

The important part of the last call was to get our join tokens. Now we have them we can join our second node to the cluster. In Postman we will create a new call that uses the POST type gain to http://172.17.10.101:4243/v1.24/swarm/join, no auth, the headers are application/json and the body of

{
"ListenAddr": "0.0.0.0:2377",
"AdvertiseAddr": "172.17.10.102:2377",
"RemoteAddrs": ["172.17.10.101:2377"],
"JoinToken": "SWMTKN-1-1122j7c3tpt3rjkye7im1srysjcx2s1xjt2rcmz9emmhcf8c30-688h32jf1wy16tguuw78m4h55"
}

In Postman it will look like
Screen Shot 2016-08-30 at 11.03.34 AM
You will notice I copied the JoinToken from the GET we did earlier.

So now we have our Swarm cluster up, to deploy a service is just another call to another end point. This time the endpoint will be http://172.17.10.101:4243/v1.24/services/create?name=web-app You will notice we are also passing the name of the service in the URL web-app Again we will use the POST method, no auth, the headers are application/json and the body of

{
"Name": "web-app",
"TaskTemplate": {
"ContainerSpec": {
"Image": "scottyc/webapp",
"Mounts": [],
"User": "33"
},
"LogDriver": {
"Name": "json-file",
"Options": {
"max-file": "3",
"max-size": "10M"
}
},
"Placement": {},
"Resources": {
"Limits": {
"MemoryBytes": 104857600
},
"Reservations": {
}
},
"RestartPolicy": {
"Condition": "on-failure",
"Delay": 10000000000,
"MaxAttempts": 10
}
},
"Mode": {
"Replicated": {
"Replicas": 4
}
},
"UpdateConfig": {
"Delay": 30000000000,
"Parallelism": 2,
"FailureAction": "pause"
},
"EndpointSpec": {
"Ports": [
{
"Protocol": "tcp",
"PublishedPort": 80,
"TargetPort": 3000
}
]
},
"Labels": {
"web": "app"
}
}

In Postman it will look like
Screen Shot 2016-08-30 at 11.09.47 AM
We will get a response like {"ID": "7djapp2ev06grh2pz72ukql47"} If we logon to docker-01 and issue the command docker service ps web-app we will get the following

ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
adx7cex8y6fjhg8qs40gtrylv web-app.1 scottyc/webapp docker-01 Running Running 29 seconds ago
2hasp5a46rifypwx9n7hy2bzb web-app.2 scottyc/webapp docker-01 Running Running 29 seconds ago
4cezab5i4inw3j4f3ve3g2a7l web-app.3 scottyc/webapp docker-02 Running Running 13 seconds ago
c3ii478u1xeqyhqxiv00v0rm0 web-app.4 scottyc/webapp docker-02 Running Running 13 seconds ago

If we open our browser and go to either http://172.17.10.101 or http://172.17.10.102 you will get the web app. Lastly don’t forget to save all the calls that you have made in Postman. One of the really cool features of Postman is the ability to share your work, so it can be reusable or improved by a team mate.

If you wanted to take this to the next level you could port your Postman payloads (the POST and GET ) to a Jenkins job to orchestrate the full lifecycle of an instance of the engine, the swarm cluster and the service itself. The sky would be the limit to what you could accomplish.

scottyc

Linux geek, Docker Captain and Retro Gamer

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

1 + one =