My full name is Mark Allen Miller. You can find my profile on LinkedIn under my full name https://www.linkedin.com/in/markallenmiller/. I went to college with two other Mark Millers. One of them also had the same middle initial as me so my name is not the most unique name in the world. My dad’s name is Siegfried Miller. At the age of 18, because he could “change the world”, he changed his last name from Mueller to Miller and yep, he doesn’t have a middle name. My grandfather’s name is Karl Mueller. His Austrian surname, prior to immigrating to the US in 1950, was Müller with an umlaut which is a mark ( ¨ ) used over a vowel to indicate a different vowel quality. Interesting trivia you might say, but what does this have to do with Docker?
Well, Docker originally had the name dotCloud. According to wikipedia “Docker represents an evolution of dotCloud’s proprietary technology, which is itself built on earlier open-source projects such as Cloudlets.” I had never even heard of Cloudlets until I wrote this blog.
Docker containers have names also. These names give us humans something a little more interesting to work with instead of the typical container id such as 648f7f486b24
. The name of a container can be used to identify a running instance of an image, but it can also be used in most commands in place of the container id.
Container Names
To start a container with a specific name you can use the --name
parameter to the docker run command which will assign a name to the container. You can then stop the container by its name. Let’s use tommy
for our example. Once a container has been named, whether it is running or not, you cannot start another container with the same name. You will have to remove the original tommy
container first before launching it again. When a container is not running you can start it up again using its name. In the following examples I will be utilizing a current version of the Apache Tomcat image running in JRE 11.
$ docker run -d --name tommy tomcat:9-jre11
01ade0d133f70b2458087097409d8423387ea300b3316f69ad809ff6a997be7f
$ docker stop Tommy
tommy
$ docker run -d --name tommy tomcat:9-jre11
docker: Error response from daemon: Conflict. The container name "/tommy" is already in use by container "52650ae6e461a55871654792c480f71df0731f84008b38b86b89a9cd01e0e832". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
$ docker start tommy
tommy
Well, it is running but I cannot do anything useful to it since I forgot to map the ports to my local host. So let’s remove the running container and start it up again. Finally, let’s prove its actually serving up static content.
$ docker rm -f tommy
tommy
$ docker run -d --name tommy --publish 8080:8080 tomcat:9-jre11
37d67ee4ba4cf5d59de25a27b915ce138289f40665aa1455886c307e249876fb
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f5ab0e5dc91f tomcat:9-jre11 "catalina.sh run" 4 seconds ago Up 3 seconds 0.0.0.0:8080->8080/tcp tommy
$ curl http://localhost:8080
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Apache Tomcat/9.0.19</title>
...
Container Hostname
Another type of name that is important to many applications is the hostname. The docker run command supports a --hostname
parameter. The --hostname
flag only changes the hostname inside your container. This may be needed if your application expects a specific value for the hostname. It does not change DNS outside of docker, nor does it change the networking isolation, so it will not allow others to connect to the container with that name. This means that the users outside of the container will not be able to resolve to the hostname just by setting that value via the --hostname
on the container.
The default hostname
of the container is its container id. But we want to change that to something more meaningful such as tomhost
.
$ docker exec -it tommy hostname
f5ab0e5dc91f
$ docker rm -f tommy
tommy
$ docker run -d --name tommy --hostname tomhost --publish 8080:8080 tomcat:9-jre11
57913b4ca9e22e12ecc3125db7436c4a99515fe66c47e25e5f8a5f17751b2207
$ docker exec -it tommy hostname
tomhost
Container Network Name
You can use the container name (or the container’s id) to connect from one container to another container using docker’s embedded DNS as long as both containers share the same network and that network is not the default bridge network.
Let’s create a network tomnet
and attach it to my already running tomcat container. Then let’s launch another container and curl back to the tomcat server using its name tommy
.
$ docker network create tomnet
dab5175522b28b94baa6949ab1c9545be042f62a6734f5062817b643865d07c4
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
b27b0c0a82d6 bridge bridge local
39d4efbc37bc docker_gwbridge bridge local
0990ed079c58 host host local
v6s1e7qyo419 ingress overlay swarm
5456492e4c0f none null local
dab5175522b2 tomnet bridge local
$ docker network connect tomnet tommy
$ docker run -it --network tomnet nicolaka/netshoot curl http://tommy:8080
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Apache Tomcat/9.0.19</title>
...
Docker Service Names
The docker run command simply starts a single container. A docker service create command works with the Swarm orchestrator to provision a service. A service is a declaration of the state of your particular application. Services are really just “containers in production.” A service references a single image, but it contains the declared state of the way that image runs. That declarative model of a service defines things like what ports it should use, how many container instances should run, where the services should run, any resource reservations and limitations, and so on. And, of course, services have names.
If I create a service without a name, one will be assigned by docker. In the example below you can see the service name of blissful_meninsky
.
$ docker service create redis:3.0.6
5qxktvdvweir2ncb3sllpl9h7
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
5qxktvdvweir blissful_meninsky replicated 1/1 redis:3.0.6
By specifying the --name
parameter I can name my service. Here I am creating a service with 3 container instances. You can see the service named myredis
.
$ docker service create --name myredis --replicas 3 redis:3.0.6
p34u8bzc0l85eg9mvxjsd20ty
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
5qxktvdvweir blissful_meninsky replicated 1/1 redis:3.0.6
p34u8bzc0l85 myredis replicated 3/3 redis:3.0.6
A service is composed of tasks. Each task represents the state that a container should run under. I can show the tasks of a service with the following.
$ docker service ps myredis
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
k58qnaq64ch8 myredis.1 redis:3.0.6 linuxkit-025000000001 Running Running 30 seconds ago
shsdutogi38q myredis.2 redis:3.0.6 linuxkit-025000000001 Running Running 31 seconds ago
iw5zmiteavc6 myredis.3 redis:3.0.6 linuxkit-025000000001 Running Running 30 seconds ago
Notice that the tasks have names too. Under the NAME column you can see the three instance names in the form of <service-name>.<slot-number>.
The containers can be viewed with the docker ps command. Notice how the containers have names in the form of <service-name>.<slot-number>.<task-id>.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cf67de83161a redis:3.0.6 "/entrypoint.sh redi…" 5 minutes ago Up 5 minutes 6379/tcp myredis.1.k58qnaq64ch8bey5epoubxcls
7da623fb36f5 redis:3.0.6 "/entrypoint.sh redi…" 5 minutes ago Up 5 minutes 6379/tcp myredis.3.iw5zmiteavc67t8fr9ag4je12
8da70efc4a37 redis:3.0.6 "/entrypoint.sh redi…" 5 minutes ago Up 5 minutes 6379/tcp myredis.2.shsdutogi38qh1cbghlqpyudp
8f0e46e42f47 redis:3.0.6 "/entrypoint.sh redi…" 9 minutes ago Up 9 minutes 6379/tcp blissful_meninsky.1.hxqqsmi4qcuvq30xuqhmzslg0
Within a service you can communicate to another service using its (you guessed it) service name. First, we will create new network. Second, update the myredis service to use that network. Third, we will exec
into a container under the myclient
service and test that we can access the redis service by the name myredis
.
$ docker network create --driver overlay redisnet
krz71c4bvcjwn5wd14yaf7p6g
$ docker service update --network-add redisnet myredis
myredis
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
$ docker service create --name myclient --network redisnet nicolaka/netshoot tail -f /dev/null
ew4tcu3j9wb1opb8y1islvbq3
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a9540e066eb3 nicolaka/netshoot:latest "tail -f /dev/null" 5 minutes ago Up 5 minutes myclient.1.la5gq78ljem9yuhqpad19kn7x
8abc7c5309ad redis:3.0.6 "/entrypoint.sh redi…" 9 minutes ago Up 9 minutes 6379/tcp myredis.2.wu8v29hiww95sua71ynnrloh4
108222757e5a redis:3.0.6 "/entrypoint.sh redi…" 9 minutes ago Up 9 minutes 6379/tcp myredis.1.h41a2fvznncfjf27ncxompziu
da600a22e9ea redis:3.0.6 "/entrypoint.sh redi…" 10 minutes ago Up 9 minutes 6379/tcp myredis.3.6rcpzblkc353scjpqo1ejwk21
8f0e46e42f47 redis:3.0.6 "/entrypoint.sh redi…" 30 minutes ago Up 30 minutes 6379/tcp blissful_meninsky.1.hxqqsmi4qcuvq30xuqhmzslg0
$ docker exec -it a9540e066eb3 sh
/ # echo 'PING' | nc myredis 6379
+PONG
^C
Great! We sent PING and receive a response of PONG from the myredis
service.
If we stay logged into the myclient
container, we can use the dig
command to lookup some interesting things about the service.
/ # dig myredis
; <<>> DiG 9.12.3-P1 <<>> myredis
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59798
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;myredis. IN A
;; ANSWER SECTION:
myredis. 600 IN A 10.0.0.2
;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Wed Apr 24 03:34:44 UTC 2019
;; MSG SIZE rcvd: 48
/ # dig tasks.myredis
; <<>> DiG 9.12.3-P1 <<>> tasks.myredis
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19633
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;tasks.myredis. IN A
;; ANSWER SECTION:
tasks.myredis. 600 IN A 10.0.0.6
tasks.myredis. 600 IN A 10.0.0.5
tasks.myredis. 600 IN A 10.0.0.3
;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Wed Apr 24 03:34:53 UTC 2019
;; MSG SIZE rcvd: 118
The first dig command results in the virtual IP of the myredis
service which is 10.0.0.2. That IP address is on the redisnet
network we created earlier.
The second dig command against the name tasks.myredis
results in three IP addresses which represent the IP address of each unique container that sits behind the VIP.
So, the interesting thing here is that docker maintains the VIP and round-robins requests amongst the live containers. So, if one container goes down then the other two containers are routed any inbound requests.
Docker Stack Names
A stack is a group of interrelated services that share common networks, volumes, configurations, and secrets. A single stack is capable of defining and coordinating the functionality of an entire application of numerous micro-services. Although, multiple stack deployments may suite a complex micro-services application.
And a docker stack has a name. The name is mandatory for the stack deployment. The services get deployed with the stack name prepended to the service name.
The following stack file named docker-stack.yml
will mimic the previous service and network deployments.
$ cat redis-stack.yml
version: "3.7"
services:
myredis:
image: redis
networks:
rednet:
deploy:
replicas: 3
myclient:
image: nicolaka/netshoot
command: tail -f /dev/null
networks:
rednet:
networks:
rednet:
Now to deploy the stack.
$ docker stack deploy -c redis-stack.yml marks
Creating network marks_rednet
Creating service marks_myredis
Creating service marks_myclient
$ docker stack ls
NAME SERVICES ORCHESTRATOR
marks 2 Swarm
$ docker stack ps marks
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xebv9ws2kbo5 marks_myclient.1 nicolaka/netshoot:latest linuxkit-025000000001 Running Running about a minute ago
epldn8afn1iq marks_myredis.1 redis:latest linuxkit-025000000001 Running Running about a minute ago
jlzpn5f34g4d marks_myredis.2 redis:latest linuxkit-025000000001 Running Running about a minute ago
is7gaa8ra6rm marks_myredis.3 redis:latest linuxkit-025000000001 Running Running about a minute ago
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
v5njxpd1iq9q marks_myclient replicated 1/1 nicolaka/netshoot:latest
yfoe0o30ogzt marks_myredis replicated 3/3 redis:latest
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bb2cf90a6379 redis:latest "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp marks_myredis.2.jlzpn5f34g4dci4239emypnlt
d64d7af977c1 redis:latest "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp marks_myredis.1.epldn8afn1iqwj01bp4ispp6k
7944e3315025 redis:latest "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp marks_myredis.3.is7gaa8ra6rmef9u6k3r9jv5e
968e7f88fa05 nicolaka/netshoot:latest "tail -f /dev/null" About a minute ago Up About a minute marks_myclient.1.xebv9ws2kbo5x9de1kvq4rk7q
Notice how the service names and container names all have the stack name prepended. They are the same thing as if you had created the service using the docker service create command. So everything else I have told you regarding names still applies.
Even the part about being able to set your containers hostname can be done using a docker stack file.
version: "3.4"
services:
nginx:
image: nginx
hostname: '{{.Node.Hostname}}'
This stack file uses a filter to set the hostname. You can combine any static text along with the dynamic filter to specify a name. The .Node
has several other values you can specify. Reference docker documentation for Service Constraints and Services Templates
Now let’s deploy the stack.
$ docker stack deploy -c nginx-stack.yml mynginx
Creating network mynginx_default
Creating service mynginx_nginx
$ docker stack ps mynginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jzwvgzofh3xc mynginx_nginx.1 nginx:latest linuxkit-025000000001 Running Preparing 11 seconds ago
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ecb4b62b0fa2 nginx:latest "nginx -g 'daemon of…" 3 seconds ago Up 2 seconds 80/tcp mynginx_nginx.1.jzwvgzofh3xcca7ahuc0brhb8
bb2cf90a6379 redis:latest "docker-entrypoint.s…" 10 minutes ago Up 10 minutes 6379/tcp marks_myredis.2.jlzpn5f34g4dci4239emypnlt
d64d7af977c1 redis:latest "docker-entrypoint.s…" 10 minutes ago Up 10 minutes 6379/tcp marks_myredis.1.epldn8afn1iqwj01bp4ispp6k
7944e3315025 redis:latest "docker-entrypoint.s…" 10 minutes ago Up 10 minutes 6379/tcp marks_myredis.3.is7gaa8ra6rmef9u6k3r9jv5e
968e7f88fa05 nicolaka/netshoot:latest "tail -f /dev/null" 11 minutes ago Up 11 minutes marks_myclient.1.xebv9ws2kbo5x9de1kvq4rk7q
$ docker exec -it ecb4b62b0fa2 sh
# hostname
linuxkit-025000000001
And the output of this container’s hostname command is linuxkit-025000000001
which is the name assigned on my MacBook in the Docker for Mac installation.
Summary
Hopefully we have learned a little about containers, services, and stacks. And specifically about how names play an important part in the Docker platform by giving us the power to manipulate containers, services, and stacks. The advantage of using names is that you don’t have to remember long object IDs.
I have also shared some history of my family’s names and of Dockers former names.
The company I work for has a new name as well. We are Capstone IT. Formerly Capstone Consulting, we changed our name to capture more of the spirit of what we do. We specialize in IT Solutions and Staffing. To find out more about us, visit us at https://capstonec.com.
Mark Miller
Solutions Architect
Docker Accredited Consultant
