After highlighting how easy it is to run docker swarm in Photon OS, I had a follow on question on how easy it would be to test the functionality. Just to recap, the only additional step you need to get Docker Swarm running on Photon OS was to open port 2377 on the master node. After that, you simply initialize the master, and all the other nodes/VMs are added as swarm workers. You might be wondering if you need to do a bunch of other stuff in iptables for docker, but the answer is no (for this relatively simple test anyway). Once you enable docker on Photon OS, a whole bunch of docker chain rules are added automatically. So let’s look at the following example of a single master with two worker nodes (3 different Photon OS VMs). What I plan to do is create a web server service using Nginx, and scale it out from 1 to 2 to 3 replicas across my swarm cluster. Let’s kick it off.

Let’s start with a look at the iptables on one of my Photon OS VMs that will become a Docker Swarm worker. This is before I enable and start docker. You can see the difference for yourself.

root@photon-worker2 [ ~ ]# iptables --list Chain INPUT (policy DROP) target prot opt source destination ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT tcp -- anywhere anywhere tcp dpt:ssh Chain FORWARD (policy DROP) target prot opt source destination Chain OUTPUT (policy DROP) target prot opt source destination ACCEPT all -- anywhere anywhere root@photon-worker2 [ ~ ]# systemctl enable docker Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /lib/systemd/system/docker.service. root@photon-worker2 [ ~ ]# systemctl start docker root@photon-worker2 [ ~ ]#iptables --list Chain INPUT (policy DROP) target prot opt source destination ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT tcp -- anywhere anywhere tcp dpt:ssh Chain FORWARD (policy DROP) target prot opt source destination DOCKER-USER all -- anywhere anywhere DOCKER-ISOLATION all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED DOCKER all -- anywhere anywhere ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere Chain OUTPUT (policy DROP) target prot opt source destination ACCEPT all -- anywhere anywhere Chain DOCKER (1 references) target prot opt source destination Chain DOCKER-ISOLATION (1 references) target prot opt source destination RETURN all -- anywhere anywhere Chain DOCKER-USER (1 references) target prot opt source destination RETURN all -- anywhere anywhere root@photon-worker2 [ ~ ]#

OK – let’s flip over to the master node/VM, and open that port I mentioned and initialize the Docker Swarm.

root@photon-master [ ~ ]# docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 4 Server Version: 17.06.0-ce Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog Swarm: inactive Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: cfb82a876ecc11b5ca0977d1733adbe58599088a runc version: 2d41c047c83e09a6d61d464906feb2a2f3c52aa4 init version: 949e6fa Security Options: seccomp Profile: default Kernel Version: 4.9.47-2.ph2-esx Operating System: VMware Photon OS/Linux OSType: linux Architecture: x86_64 CPUs: 1 Total Memory: 1.958GiB Name: photon-master ID: IHWT:3LUB:TTMT:GZF2:YHI3:6RAE:AN7S:5SDY:COQA:2UNZ:4GAH:5AXW Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): false No Proxy: 10.27.51.47 Registry: https://index.docker.io/v1/ Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false root@photon-master [ ~ ]#iptables -A INPUT -p tcp --dport 2377 -j ACCEPT root@photon-master [ ~ ]#iptables --list Chain INPUT (policy DROP) target prot opt source destination ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT tcp -- anywhere anywhere tcp dpt:ssh ACCEPT tcp -- anywhere anywhere tcp dpt:2377 Chain FORWARD (policy DROP) target prot opt source destination DOCKER-USER all -- anywhere anywhere DOCKER-ISOLATION all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED DOCKER all -- anywhere anywhere ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED DOCKER all -- anywhere anywhere ACCEPT all -- anywhere anywhere DROP all -- anywhere anywhere Chain OUTPUT (policy DROP) target prot opt source destination ACCEPT all -- anywhere anywhere Chain DOCKER (2 references) target prot opt source destination Chain DOCKER-ISOLATION (1 references) target prot opt source destination DROP all -- anywhere anywhere DROP all -- anywhere anywhere RETURN all -- anywhere anywhere Chain DOCKER-USER (1 references) target prot opt source destination RETURN all -- anywhere anywhere root@photon-master [ ~ ]# root@photon-master [ ~ ]# docker swarm init Swarm initialized: current node (jxk3918ei5sj9h0dso0j4jl34) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-5r9kckkp9kcxmwcwyvrtczmk2e3bakp3izounhk19icc3gkxf5-cj26dynkq3z20wfgm9hyci6ed 10.27.51.47:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. root@photon-master [ ~ ]#

Now you might like to save this permanently. This can be done by using the iptables-save command, and storing the output in ip4save, which is located in /etc/systemd/scripts . When iptables restarts, it will read the saved iptables rules in from this file.

Let’s now join both of our workers to the swarm cluster.

root@photon-worker[ ~ ]# docker swarm join --token SWMTKN-1-5r9kckkp9kcxmwcwyvrtczmk2e3bakp3izounhk19icc3gkxf5-cj26dynkq3z20wfgm9hyci6ed 10.27.51.47:2377 This node joined a swarm as a worker. root@photon-worker[ ~ ]# docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 17 Server Version: 17.06.0-ce Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog Swarm: active NodeID: u3rcmqv678hbmrz9vp5uycs1v Is Manager: false Node Address: 10.27.51.17 Manager Addresses: 10.27.51.47:2377 Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: cfb82a876ecc11b5ca0977d1733adbe58599088a runc version: 2d41c047c83e09a6d61d464906feb2a2f3c52aa4 init version: 949e6fa Security Options: seccomp Profile: default Kernel Version: 4.9.60-1.ph2-esx Operating System: VMware Photon OS/Linux OSType: linux Architecture: x86_64 CPUs: 4 Total Memory: 7.792GiB Name: photon-worker ID: R7DL:MSZ4:MCAE:SKFS:2HN3:ZZOV:2TJC:T757:H5DM:DRWV:QC6P:YE2R Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): false No Proxy: 10.27.51.47 Registry: https://index.docker.io/v1/ Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false root@photon-worker [ ~ ]# root@photon-worker2 [ ~ ]# docker swarm join --token SWMTKN-1-5r9kckkp9kcxmwcwyvrtczmk2e3bakp3izounhk19icc3gkxf5-cj26dynkq3z20wfgm9hyci6ed 10.27.51.47:2377 This node joined a swarm as a worker. root@photon-worker2[ ~ ]# docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 17.06.0-ce Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog Swarm: active NodeID: pg8siha3bpwwxcjn395p2fshi Is Manager: false Node Address: 10.27.51.145 Manager Addresses: 10.27.51.47:2377 Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: cfb82a876ecc11b5ca0977d1733adbe58599088a runc version: 2d41c047c83e09a6d61d464906feb2a2f3c52aa4 init version: 949e6fa Security Options: seccomp Profile: default Kernel Version: 4.9.47-2.ph2-esx Operating System: VMware Photon OS/Linux OSType: linux Architecture: x86_64 CPUs: 1 Total Memory: 1.958GiB Name: photon-worker2 ID: OP7U:3IGC:FA44:H42Q:ZM3J:7PA6:26FX:7OPK:S6SQ:34AI:DF7F:XR3Q Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): false Registry: https://index.docker.io/v1/ Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false root@photon-worker2 [ ~ ]#

Looks like both nodes are active in the cluster. Let’s check it from the master. We should now see 3 nodes in the cluster.

root@photon-master [ ~ ]# docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 4 Server Version: 17.06.0-ce Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog Swarm: active NodeID: jxk3918ei5sj9h0dso0j4jl34 Is Manager: true ClusterID: w978k88h8fhk149i8w9lyvr1w Managers: 1 Nodes: 3 Orchestration: Task History Retention Limit: 5 Raft: Snapshot Interval: 10000 Number of Old Snapshots to Retain: 0 Heartbeat Tick: 1 Election Tick: 3 Dispatcher: Heartbeat Period: 5 seconds CA Configuration: Expiry Duration: 3 months Force Rotate: 0 Root Rotation In Progress: false Node Address: 10.27.51.47 Manager Addresses: 10.27.51.47:2377 Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: cfb82a876ecc11b5ca0977d1733adbe58599088a runc version: 2d41c047c83e09a6d61d464906feb2a2f3c52aa4 init version: 949e6fa Security Options: seccomp Profile: default Kernel Version: 4.9.47-2.ph2-esx Operating System: VMware Photon OS/Linux OSType: linux Architecture: x86_64 CPUs: 1 Total Memory: 1.958GiB Name: photon-master ID: IHWT:3LUB:TTMT:GZF2:YHI3:6RAE:AN7S:5SDY:COQA:2UNZ:4GAH:5AXW Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): false No Proxy: 10.27.51.47 Registry: https://index.docker.io/v1/ Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false root@photon-master [ ~ ]#

Everything looks good. Let’s now create our service to run in our Swarm. Like I said, I’m choosing Nginx as my web server, and I am mapping container port 80 to port 8080 on my VM/node. Initially, I will start with just a single replica, and I will give the service a name “web”:

root@photon-master [ ~ ]# docker service create --replicas 1 -p 8080:80 --name web nginx omv53xwfjzl1oicbwmylrto7y Since --detach=false was not specified, tasks will be created in the background. In a future release, --detach=false will become the default.

And lets see where the single replica of the container is running:

root@photon-master [ ~ ]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS omv53xwfjzl1 web replicated 1/1 nginx:latest *:8080->80/tcp root@photon-master[ ~ ]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES root@photon-master [ ~ ]# root@photon-worker2[ ~ ]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES root@photon-worker2 [ ~ ]# root@photon-worker[ ~ ]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 94c325a77e0c nginx:latest "nginx -g 'daemon ..." 18 seconds ago Up 18 seconds 80/tcp web.1.mtk6rsovie5mi5w0m46y00txs

Looks like it is running on one of the workers. Let’s see if we can access the welcome page from that worker.

root@photon-worker [ ~ ]# curl 127.0.0.1:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to http://nginx.org/ "> nginx.org .

Commercial support is available at http://nginx.com/ "> nginx.com . <p><em>Thank you for using nginx.</em></p> </body> </html> root@photon-worker [ ~ ]#

OK – that was successful. Let’s scale it out with another replica.

root@photon-master[ ~ ]# docker service scale web=2 web scaled to 2 root@photon-master[ ~ ]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS omv53xwfjzl1 web replicated 2/2 nginx:latest *:8080->80/tcp

This one seems to have started on the master. Let’s see if we can access the web server from that node.

root@photon-master [ ~ ]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 806b6eee2e89 nginx:latest "nginx -g 'daemon ..." 6 seconds ago Up 5 seconds 80/tcp web.2.seg6tb6fayvv0q4sdgnvqmz9v root@photon-master [ ~ ]# curl 127.0.0.1:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to http://nginx.org/ "> nginx.org .

Commercial support is available at http://nginx.com/ "> nginx.com . <p><em>Thank you for using nginx.</em></p> </body> </html>

Looks good. Let’s scale the service out one more time, so we have 3 replicas. And as before, lets see where it ends up running, and if it can be accessed from the node.

root@photon-master[ ~ ]# docker service scale web=3 web scaled to 3 root@photon-master [ ~ ]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS omv53xwfjzl1 web replicated 3/3 nginx:latest *:8080->80/tcp root@photon-master [ ~ ]# root@photon-worker2[ ~ ]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8d9df2a11660 nginx:latest "nginx -g 'daemon ..." 52 seconds ago Up 52 seconds 80/tcp web.3.ykq6lrfus4mgwv744a5uwq5pn root@photon-worker2 [ ~ ]# curl 127.0.0.1:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to http://nginx.org/ "> nginx.org .

Commercial support is available at http://nginx.com/ "> nginx.com . <p><em>Thank you for using nginx.</em></p> </body> </html>

So this time, it was started on the final worker node. Now we have 3 replicas of the service running.

Finally, lets examine the service in more detail from the master: