And now we return to our previously-scheduled blog series “Docker: Containers for the Masses”. To bring the reader up to speed with what episodes have already aired:
- Introduction – this episode introduced the reader to the storyline and characters: what are containers, how they work containers vs. virtual machines, what is Docker.
- Installation – this episode featured information about installing Docker
In today’s episode, the real excitement begins with actually using Docker. The reader will be shown how to perform tasks with Docker, how to build images by hand, and using a Dockerfile to build Docker images. Also starring in this episode are images that a user can build that have services running as well as ENTRYPOINT
and CMD
instructions.
The Docker CLI
The Docker CLI, the tool that one uses to interact with Docker, is very intuitive and has a well-documented help facility that can simply be invoked by running:
$ docker help subcommand
For instance, if one were to need to find out the options on building images:
$ docker help build
Usage: docker build [OPTIONS] PATH | URL | -
Build a new container image from the source code at PATH
--no-cache=false Do not use cache when building the image
-q, --quiet=false Suppress the verbose output generated by the containers
--rm=true Remove intermediate containers after a successful build
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
The following shows basic usage of Docker and some common tasks that a user would perform.
Pulling the base Ubuntu image
The first thing to do is to build images based off of Ubuntu.
(Note: it is simply a preference of the author of this blog to use Ubuntu. There are other base images available for other Linux distributions.)
To obtain a base image, and in this case, the Ubuntu base image, conduct a search to find out the image tag in order to pull the Ubuntu image from the Docker image repository:
$ docker search ubuntu
NAME DESCRIPTION STARS OFFICIAL TRUSTED
ubuntu Official Ubuntu base image 171
stackbrew/ubuntu Barebone ubuntu images 36
crashsystems/gitlab-docker A trusted, regularly updated build of GitL... 19 [OK]
dockerfile/ubuntu Trusted Ubuntu (http://www.ubuntu.com/) Bu... 13 [OK]
...<snip>...
In this example, the list is extensive. The first one from above is selected:
$ docker pull ubuntu
Pulling repository ubuntu
3db9c44f4520: Download complete
6006e6343fad: Download complete
d2099a5ba6c5: Pulling dependent layers
5cf8fd909c6c: Pulling dependent layers
7656cbf56a8c: Pulling dependent layers
cc0067db4f11: Pulling dependent layers
511136ea3c5a: Download complete
086a54ed1641: Download complete
5e9838970f44: Download complete
c47d897f1a44: Download complete
845a7ff0bf5a: Downloading 2.027 MB/40.16 MB 3m59s
e41f31e92d60: Downloading 1.986 MB/39.53 MB 4m6s
101e9f33c3dc: Downloading 7.08 MB/39.17 MB 1m51s
6cfa4d1f33fb: Download complete
35f6dd4dd141: Downloading 1.582 MB/67.48 MB 4m42s
7baf0ef6f14a: Download complete
e497c7c1bfbb: Download complete
... <snip> ...
Launching a container
Once the base Ubuntu image is pulled, it is possible to launch a container using it. In this example, the flags -i -t
are specified to indicate running interactively using a pseudo terminal.
$ docker -it run ubuntu /bin/bash
root@849e71a53c2c:/#
(Note: later in this post, it will be shown how to run a container that runs sshd that will allow a user to log into.)
At this point, changes can be made to the container and any commits will create images representative of the changes made.
One the session is exited, the container also exits.
Analyzing your container
It is now possible to verify the container as well as find out the container ID of the container that is running. In another terminal, run the following:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
849e71a53c2c ubuntu:14.04 /bin/bash 2 minutes ago Up 2 minutes loving_bardeen
Running docker ps
with a -a
flag can also be used to see all containers, both running and terminated:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7f5881636580 ubuntu:14.04 /bin/bash 3 seconds ago Exited (0) 1 seconds ago clever_shockley
94d4341afefa ubuntu:14.04 /bin/bash 7 seconds ago Exited (0) 4 seconds ago happy_bardeen
849e71a53c2c ubuntu:14.04 /bin/bash 17 hours ago Up 17 hours loving_bardeen
As shown above in the output, the container currently running is still shown as running– the author of this blog has left it up for 17 hours so fire while writing this part of the post!
Connecting to a container
When a virtual machine is run, one commonly expects to be able to ssh into it. If Docker is run as in the above example, interactively, you see that it is running in a shell that allows you to do work inside of your container. The container can also be can also be connected to it using the following command:
$ docker attach 849e71a53c2c
root@849e71a53c2c:/#
Inspecting a container
One command that will be often used is the inspection command. This command, when run, will produce a large JSON data structure with every bit of information you would ever want to know about the container. Most often, the primary interest of the author is to use this command to find out either the IP address or what ports docker uses for exposed ports:
$ docker inspect 849e71a53c2c
[{
"ID": "849e71a53c2ce2d696cb71782bb25528e503c87a150ae87efa71c6265c027879",
"Created": "2014-06-01T11:44:12.117743232Z",
"Path": "/bin/bash",
"Args": [],
"Config": {
"Hostname": "849e71a53c2c",
"Domainname": "",
"User": "",
"Memory": 0,
"MemorySwap": 0,
"CpuShares": 0,
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"PortSpecs": null,
"ExposedPorts": null,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"HOME=/",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"Image": "ubuntu",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"NetworkDisabled": false,
"OnBuild": null
},
"State": {
"Running": false,
"Pid": 0,
"ExitCode": 0,
"StartedAt": "2014-06-01T11:44:12.393165329Z",
"FinishedAt": "2014-06-02T08:27:24.095030218Z"
},
"Image": "5cf8fd909c6ccc61199df6dbeb165767b83c23842ef49ca3ef3b81ece1bdce4f",
"NetworkSettings": {
"IPAddress": "",
"IPPrefixLen": 0,
"Gateway": "",
"Bridge": "",
"PortMapping": null,
"Ports": null
},
"ResolvConfPath": "/etc/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/849e71a53c2ce2d696cb71782bb25528e503c87a150ae87efa71c6265c027879/hostname",
"HostsPath": "/var/lib/docker/containers/849e71a53c2ce2d696cb71782bb25528e503c87a150ae87efa71c6265c027879/hosts",
"Name": "/loving_bardeen",
"Driver": "aufs",
"ExecDriver": "native-0.2",
"MountLabel": "",
"ProcessLabel": "",
"Volumes": {},
"VolumesRW": {},
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LxcConf": [],
"Privileged": false,
"PortBindings": {},
"Links": null,
"PublishAllPorts": false,
"Dns": null,
"DnsSearch": null,
"VolumesFrom": null,
"NetworkMode": "bridge"
}
}]
Committing a container and creating images
As mentioned in the previous example, it would have been possible to commit the container to an image. In order to do this, one will need the container ID. To find this, one of the most used commands you will familiarize yourself with is the docker ps command:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
849e71a53c2c ubuntu:14.04 /bin/bash 2 minutes ago Up 2 minutes loving_bardeen
Once the container ID is known, the container can be committed to an image:
$ docker commit 849e71a53c2c capttofu/ubuntu_example
f54ee24a549a4680966d12db4071da61470ff2c979b0177405f07f860d08ab63
To verify that there is now an image created from the previous step
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
capttofu/ubuntu_example latest f54ee24a549a 9 minutes ago 274.3 MB
ubuntu 12.10 6006e6343fad 2 days ago 172.2 MB
ubuntu quantal 6006e6343fad 2 days ago 172.2 MB
ubuntu 13.10 d2099a5ba6c5 2 days ago 180.2 MB
ubuntu saucy d2099a5ba6c5 2 days ago 180.2 MB
ubuntu 14.04 5cf8fd909c6c 2 days ago 274.3 MB
ubuntu latest 5cf8fd909c6c 2 days ago 274.3 MB
ubuntu trusty 5cf8fd909c6c 2 days ago 274.3 MB
ubuntu 13.04 7656cbf56a8c 2 days ago 169.4 MB
ubuntu raring 7656cbf56a8c 2 days ago 169.4 MB
ubuntu 12.04 cc0067db4f11 2 days ago 210.1 MB
ubuntu precise cc0067db4f11 2 days ago 210.1 MB
It’s also possible to see the history of this newly-created image, showing what changes it is comprised of:
$ docker history capttofu/ubuntu_example
IMAGE CREATED CREATED BY SIZE
f54ee24a549a 13 minutes ago /bin/bash 0 B
5cf8fd909c6c 2 days ago /bin/sh -c apt-get update && apt-get install 81.61 MB
e497c7c1bfbb 2 days ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.903 kB
7baf0ef6f14a 2 days ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 194.5 kB
35f6dd4dd141 2 days ago /bin/sh -c #(nop) ADD file:8162d8bad0054f4038 192.5 MB
511136ea3c5a 11 months ago
Pushing up your image
Once an image is built, a way to think of it is that it is ready for sharing. It can be pushed to the public Docker image registry:
$ docker push capttofu/ubuntu_example
$ docker push capttofu/ubuntu_example
The push refers to a repository [capttofu/ubuntu_example] (len: 1)
Sending image list
Pushing repository capttofu/ubuntu_example (1 tags)
Image 511136ea3c5a already pushed, skipping
Image 35f6dd4dd141 already pushed, skipping
Image 7baf0ef6f14a already pushed, skipping
Image e497c7c1bfbb already pushed, skipping
Image 5cf8fd909c6c already pushed, skipping
f54ee24a549a: Image successfully pushed
Pushing tag for rev [f54ee24a549a] on {https://registry-1.docker.io/v1/repositories/capttofu/ubuntu_example/tags/latest}
Building images using a Dockerfile
While it is certainly easy to launch a base image, modify it and then commit the changes, it’s not something tenable for the long-term purposes of automation and everyday work. This is where using a “Dockerfile” is the solution. A “Dockerfile” is another great example on the utility of Docker and analogous to a Makefile. It is a simple text file that describes a set of instructions for how an image is to be built.
For instance, below is a simple Dockerfile that can be used to install Percona XtraDB Cluster:
# Percona XtraDB Cluster Dockerfile
#
# VERSION 0.0.1
#
FROM ubuntu:13.04
MAINTAINER Patrick aka CaptTofu Galbraith , patg@patg.net
# Add the Percona apt repository
RUN apt-key adv --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A
ADD percona.list /etc/apt/sources.list.d/percona.list
# Update distribution
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get clean
# Install ssh, vim and PXC packages
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y ssh vim percona-xtradb-cluster-client-5.6 percona-xtradb-cluster-server-5.6
# set up ssh and authorized keys so one can ssh into this system
RUN mkdir /var/run/sshd
RUN mkdir /root/.ssh
RUN chmod 700 /root/.ssh
ADD docker.pem.pub /root/.ssh/authorized_keys
RUN chown -R root:root /root/.ssh
# add entrypoint script used to start processes
ADD entrypoint.sh /usr/local/sbin/entrypoint.sh
# Expose SSH and MySQL ports
EXPOSE 22 3306 4444 4567 9200
# Set the entrypoint to entrypoint.sh
ENTRYPOINT ["/usr/local/sbin/entrypoint.sh"]
As can be seen, there are several instructions in this Dockerfile that one would commonly use. The RUN
instruction is used to set up apt repositories and update the system. The ADD
instruction adds a file to the specific location and name, in this case a script used as an ENTRYPOINT
instruction, which is what the container be executed as. The EXPOSE
instruction is used to inform docker which ports the container will listen on: in this case ports needed for ssh and Galera replication and automatic failover setup.
Do take note that since the ENTRYPOINT
instruction is used, this container will be run differently than the previous example above. This will be explained later in this post.
Building an image using a Dockerfile
Building the image is painfully simple:
$ docker build -t "capttofu/pxc" /my/path/to/pxc/dockerfile/
Uploading context 6.656 kB
Uploading context
Step 0 : FROM ubuntu
---> 5cf8fd909c6c
Step 1 : MAINTAINER Patrick aka CaptTofu Galbraith , patg@patg.net
---> Running in 3d950f3de14f
---> 5767d785bbcd
Removing intermediate container 3d950f3de14f
Step 2 : RUN apt-key adv --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A
---> Running in e59f5947a662
Executing: gpg --ignore-time-conflict --no-options --no-default-keyring --homedir /tmp/tmp.UqCbfHSVSa --no-auto-check-trustdb --trust-model always --keyring /etc/apt/trusted.gpg --primary-keyring /etc/apt/trusted.gpg --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A
gpg: requesting key CD2EFD2A from hkp server keys.gnupg.net
gpg: key CD2EFD2A: public key "Percona MySQL Development Team <mysql-dev@percona.com>" imported
gpg: Total number processed: 1
gpg: imported: 1
---> bfee11a71dae
Removing intermediate container e59f5947a662
Step 3 : ADD percona.list /etc/apt/sources.list.d/percona.list
---> fb16cc14e7d1
Removing intermediate container 79beea842c16
Step 4 : RUN apt-get update && apt-get upgrade -y && apt-get clean
---> Running in 8cadef9f201f
Ign http://archive.ubuntu.com trusty InRelease
Get:1 http://repo.percona.com raring InRelease [15.3 kB]
< ... snip ... many lines of apt-get update output .. >
Processing triggers for ureadahead (0.100.0-16) ...
Processing triggers for initramfs-tools (0.103ubuntu4.1) ...
---> f439c7087235
Removing intermediate container 8cadef9f201f
Step 5 : RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libterm-readline-gnu-perl ssh vim percona-xtradb-cluster-client-5.6 percona-xtradb-cluster-server-5.6
---> Running in 21b9a5ebcac5
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
ca-certificates iproute krb5-locales libaio1 libck-connector0
< ... snip ... many lines of apt-get install output ... >
Processing triggers for libc-bin (2.19-0ubuntu6) ...
Processing triggers for ca-certificates (20130906ubuntu2) ...
Updating certificates in /etc/ssl/certs... 164 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.
Processing triggers for ureadahead (0.100.0-16) ...
---> 096ffef048d9
Removing intermediate container 21b9a5ebcac5
Step 6 : RUN mkdir /var/run/sshd
---> Running in 3a4fcf5d7f29
---> 072bb5f6ba3b
Removing intermediate container 3a4fcf5d7f29
Step 7 : RUN mkdir /root/.ssh
---> Running in 022b27983f27
---> 64fef47cb18b
Removing intermediate container 022b27983f27
Step 8 : RUN chmod 700 /root/.ssh
---> Running in 49aedf1a4343
---> 8fc7cf6bd075
Removing intermediate container 49aedf1a4343
Step 9 : ADD docker.pem.pub /root/.ssh/authorized_keys
---> a7e9f80410b5
Removing intermediate container 4d44374c82fe
Step 10 : RUN chown -R root:root /root/.ssh
---> Running in 424c151e8281
---> 6edc721b9876
Removing intermediate container 424c151e8281
Step 11 : ADD entrypoint.sh /usr/local/sbin/entrypoint.sh
---> d9f9a1a92d99
Removing intermediate container 26f08f5661e3
Step 12 : EXPOSE 22 3306 4444 4567 9200
---> Running in f873a3b78049
---> cfb3e7275caa
Removing intermediate container f873a3b78049
Step 13 : ENTRYPOINT ["/usr/local/sbin/entrypoint.sh"]
---> Running in ec5b19d1bbd0
---> 9a9aa0bb48b8
Removing intermediate container ec5b19d1bbd0
Successfully built 289923fa1196
After this build command has completed, there will exist an image tagged as capttofu/pxc
to use. The docker images
command can be run in order to see it.
Building images with running services
More pragmatic examples are building containers with running services, particularly sshd, that allow one to access a running container either through SSH, a database connection (think PaaS applications, DBaaS, etc) or any other method of interaction with a service that a client would make. To do this, one needs to ensure the service in question is started when the container runs. As seen in previous exemples in this post, a shell script called “entrypoint.sh” is used.
An example of a simple entrypoint script that starts ssh is:
#!/bin/bash
/usr/sbin/sshd -D
If this script is added to the container, set with appropriate permissions, it will run when the container is launched, allowing one to ssh into the container. Note that other commands can be added to this script. For instance, in the case of the Percona XtraDB Cluster container, “service start mysql” is also added.
To launch images with entrypoints, docker run
is issued differently, using the -d
flag, which means to run detached. Two examples are:
$ docker run -d --name=pxc1 capttofu/pxc
$ docker run -d --name pxc1 289923
(Note: In the example above, the second example used only the first 6 characters of the image ID. As long as the string used in any Docker command that pertains to an ID is unique, it will work. Had there been another image ID with the same 6 characters, it would have been ambiguous and failed.)
The next thing that needs to be done is to ensure the container is running:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 59b7425cd5cf 289923fa1196 "/bin/sh -c "/usr/lo 2 seconds ago Up 1 seconds 22/tcp, 3306/tcp, 4444/tcp, 4567/tcp, 9200/tcp pxc1
Then find out the IP address of the container by grepping the output of docker inspect
:
$ docker inspect pxc1|grep IPA
"IPAddress": "172.17.0.2",
Now one can ssh into the container!
$ ssh root@172.17.0.2
root@59b7425cd5cf:~# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4444 628 ? Ss 11:25 0:00 /bin/sh -c "/usr/local/sbin/entrypoint.sh" /bin/sh -c #(n
root 11 0.0 0.0 17908 1436 ? S 11:25 0:00 /bin/bash /usr/local/sbin/entrypoint.sh
root 12 0.0 0.0 52256 2876 ? S 11:25 0:00 /usr/sbin/sshd -D
root 13 0.0 0.0 75648 3720 ? Ss 11:25 0:00 sshd: root@pts/0
root 25 0.0 0.0 18168 2080 pts/0 Ss 11:25 0:00 -bash
root 72 0.0 0.0 4444 776 pts/0 S 11:33 0:00 /bin/sh /usr/bin/mysqld_safe
mysql 145 2.3 8.9 1143332 461120 pts/0 Sl 11:33 0:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql
root 186 0.0 0.0 15572 1196 pts/0 R+ 11:33 0:00 ps aux
See above the output of ps showing running processes in the container. This provides a good example of how when within a container, it’s apparent how the numer of processes is limited to only the application in question (and SSH).
The author sees this as an excellent example of how this is great technology works, and furthermore could have used something like this years ago to solve numerous problems!
ENTRYPOINT
s and CMD
s
As promised earlier in this post, it’s worth explaining what ENTRYPOINT
and CMD
instructions are and what the difference is between the two. ENTRYPOINT
is essentially the binary that you are running the container as. CMD
is the same thing, except that ENTRYPOINT
is more difficult to override and you have to explicitely use --entrypoint
command line option to override the executable ENTRYPOINT
specifies whereas with CMD
, you simply provide that as the CMD
argument on the command line.
An example provides a better explaination.
A Dockerfile defining an entrypoint using some arbitrary command:
ENTRYPOINT: ["/usr/bin/mycommand", "arg1", "argN"]
If a container using the image myimage
was run, it would execute as /usr/bin/mycommand arg1 argN
when run as:
$ docker run -d --name=container1 myimage
If the need was to run the container interactively, perhaps the developer also makes changes to the image manually, it would (have to be) run this as:
$ docker run -it --name=container1 --entrypoint /bin/bash myimage "-"
If CMD
had instead been used in the Dockerfile:
CMD: ["/usr/bin/mycommand", "arg1", "argN"]
The same is true in that the container would run the binary /usr/bin/mycommand arg1 argN
. If one needed to run the container interactively, simply providing the binary would work:
$ docker run -it --name=container1 myimage /bin/bash
In other words, if you think you are going to iteratively develop and make changes to a particular image that you plan to run backgrounded, CMD
may work best for you since it’s more straighforward to override. If you think you have the image nailed down and don’t plan to run the container interactily, use ENTRYPOINT
.
About ports exposure and publishing
The next bit of information to cover pertains to ports and how they are exposed. In an upcoming post detailing work the author completed with Docker and Ansible, the output from a [demonstration][moonshot] that the author gave during the recent Ansible Fest 2014 in New York is provided demonstrating managing Docker across 45 Moonshot cartridges (in other words, bare-metal servers). To be able to reach Docker containers from external hosts, the setting shown in the previous post whereby the command option --ip
is used to specify that containers, when launched, will run bound to 0.0.0.0. The other required piece of this functionality requires having Docker bind a given port of the container to a port on the Docker host. For instance, one will want to be able to SSH into containers from an external host. An example would be to ssh from HostA to any on of the containers running on HostB, each running sshd on port 22. How would one ssh to any one of these containers and know what port to use to reach the correct container? The following example shows how this is done.
First, one can specify publish ports with the -p
flag:
$ docker run -d -p 48522:22 --name pxc1 289923
05253564fe90ae9e473f1ac34104e61f53c5db86d24e21266c8fefb6386477bb
Or, allow Docker to publish all exposed ports, automatically chosing the port:
$ docker run -d -P --name pxc2 289923
ddcceffb801ae6b2e9c1004187a784eaa2e8f9f66f9965898ef86227533c0271
To view the ports mapped on the host to the Docker ports for port 22:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ddcceffb801a 289923fa1196 "/bin/sh -c "/usr/lo 2 seconds ago Up 2 seconds 0.0.0.0:49180->22/tcp, 0.0.0.0:49181->3306/tcp, 0.0.0.0:49182->4444/tcp, 0.0.0.0:49183->4567/tcp, 0.0.0.0:49184->9200/tcp pxc2
05253564fe90 289923fa1196 "/bin/sh -c "/usr/lo 22 seconds ago Up 22 seconds 3306/tcp, 4444/tcp, 4567/tcp, 9200/tcp, 0.0.0.0:48522->22/tcp pxc1
As one can see, one would use port 48522 on the host OS to connect to the container pxc1
and port 49180 on the container pxc2
.
docker inspect
can also be used, though is much more verbose. The port mapping found within several entries of the output for the container in question.
(Note: output formatted and reduced for instructional purposes)
$ docker inspect pxc1
[{
"ID": "05253564fe90ae9e473f1ac34104e61f53c5db86d24e21266c8fefb6386477bb",
"NetworkSettings": {
"IPAddress": "172.17.0.2",
"Ports": {
"22/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "48522"
}
],
"3306/tcp": null,
"4444/tcp": null,
"4567/tcp": null,
"9200/tcp": null
}
},
"HostConfig": {
"PortBindings": {
"22/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "48522"
}
],
"3306/tcp": null,
"4444/tcp": null,
"4567/tcp": null,
"9200/tcp": null
},
}]
Cleanup
Finally, there are several ways to stop containers, as well as perform some good house-keeping:
The following command docker stop
sends a SIGTERM
, and after a grace period, SIGKILL
:
$ docker stop <container>
docker kill
Sends a SIGKILL
:
$ docker kill <container>
The above two, you can restart with
$ docker start <container>
If it is found that by running docker ps -a
that there are a lot of stopped containers that are no longer needed and need to clean up to reclaim disk space on the host, docker rm
is run:
$ docker rm <container>
If laziness is desired (and caveat emptor– the author is not responsible!)
$ docker rm $(docker ps -a -q)
Also, one may wish to clean up old containers.
$ docker rmi <image>
Summary
This post was meant provide the reader with information and practical examples on how to use Docker. At this point, the reader should feel confident that using Docker is very simple and have a good basis for beginning to build their own images and run containers to do useful work!
The next post will feature using Ansible to manage Docker: build images and run containers, using some useful Ansible Docker modules and plugins.