Get Started with Docker Today
STEP 1: Install Docker Engine - Community, a.k.a. docker-ce
See the full list of supported platforms here: https://docs.docker.com/install/#supported-platforms
Examples:
- Install
docker-ce
on Ubuntu: https://docs.docker.com/install/linux/docker-ce/ubuntu/ - Install
docker-ce
on CentOS: https://docs.docker.com/install/linux/docker-ce/centos/
Follow the official installation instructions for your OS. Use the links above.
Subset of installation commands on Ubuntu:
STEP 2: Test your installation
Based on: https://docs.docker.com/get-started/part2/
If you've successfully installed Docker, you can test it using the following command:
$ sudo docker run hello-world
If successful, you should see something like the following as output:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
TIP: If you can't run it the first time, try starting the Docker Daemon, dockerd
, and then give it another shot:
$ sudo systemctl start docker
TIP: If you wish to enable the Docker Daemon to start at boot, you can use systemd or your init system to do so. For example, using systemd:
$ sudo systemctl enable docker
Create a containerized Python app
STEP 3: Create a Dockerfile
Create a new, empty directory for your app:
$ mkdir new_app ; cd new_app
Create a 'Dockerfile'. This is used to define your app's environment, install dependencies, and so on.
Paste the following into your Dockerfile:
# Use an official Python runtime as a parent image
FROM python:3.7.4-buster
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Set proxy server, replace host:port with values for your servers
# ENV http_proxy host:port
# ENV https_proxy host:port
# Install any needed packages specified in requirements.txt
RUN pip3 install --trusted-host pypi.python.org -r requirements.txt
# Make ports 80 available to the world outside this container
EXPOSE 80
# Define environment variable
ENV NAME World
# Run app.py when the container launches
CMD ["python", "app.py"]
STEP 4: Define your dependencies
Define your dependencies for this Python app by listing them out in a new file called 'requirements.txt'.
Paste the following into 'requirements.txt':
Flask
Redis
STEP 5: Create the Python app
Create your app. Call it 'app.py'.
Paste the following into 'app.py':
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname: </b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
Breakdown
- Dockerfile defines the environment in which your app runs.
- This includes the version of Python to use, in this case the
python:3.7.4-buster
image.
- This includes the version of Python to use, in this case the
- Docker installs the dependencies as instructed in your Dockerfile.
- Pip is used to install the required packages listed in requirements.txt:
Flask
andRedis
. - The app prints the environment variable
NAME
as well as the output from a call tosocket.gethostname()
.
Gotchas
- Redis isn't actually running yet. We installed the Python library, but not Redis itself.
- Therefore, when running this app, we may get an error along the lines of:
Note: Accessing the name of the host when inside a container retrieves the container ID, which is like the process ID for a running executable.
Docker Magic
- Without modifying the host environment or installing any additional packages on your system (besides Docker), we have layed the groundwork for a new Python app.
- The next step is to build the Docker image.
- NOTE: Neither building nor running the image will install anything extra on our host system.
STEP 6: Build the image
From the top level of your app directory (new_app
), you should have:
$ ls
Dockerfile app.py requirements.txt
Now you can build the Docker image. Give it a name using the --tag
option.
The full command to build your image is:
$ sudo docker build --tag=friendlyhello .
If you need to make any modifications, you can run the above command again to overwrite the previous build.
Or, you can append a version number to the tag/image name, for example:
--tag=friendlyhello:v0.0.1
Once you've built your image, list it out, along with any other images you may have on your system, using $ sudo docker image ls
. Example output:
REPOSITORY TAG IMAGE ID SIZE
friendlyhello latest 0bf4ae1c31e8 928MB
python 3.7.4-buster 02d2bb146b3b 918MB
hello-world latest fce289e99eb9 1.84kB
STEP 7: Run the app
Run the app:
$ sudo docker run friendlyhello
- If you get any errors relating to pip/pip3, or failures to install packages, you may need to modify your DNS settings for the Docker Daemon:
- Edit your configuration file
/etc/docker/daemon.json
and append adns
key. - The example below shows Google's DNS servers:
{
"dns": ["8.8.8.8", "8.8.4.4"]
}
- Save the changes and then restart the Docker Daemon:
$ sudo systemctl restart docker
- Now try running your app again:
$ sudo docker run friendlyhello
TIP: Hit Ctrl+C
to kill the app.
TIP: In order to access your app over the web or through your browser, you will need to map the specified HTTP port 80 for the container, to a port on the host system that is open to incoming connections.
STEP 8: Map to another port on the host machine
- The container itself advertises that a service is running at the specified IP address using port 80. However, the host machine does not know or care about this (yet).
- If you map the container port (80) to the host port (80), you will be able to 'visit' your app from your browser and see it running.
- You can also map the app to a separate port on the host machine if you prefer.
Run your app image with the -p
flag to indicate the desired port:
$ sudo docker run -p 80:80 friendlyhello
$ sudo docker run -p 4000:80 friendlyhello
STEP 9: Test the app
Now that you've initialized the app, you can visit the URL using the mapped port.
In your browser on the host machine, enter http://localhost:<PORT>
to see your app running.
NOTE: If you are testing this on a web server or VPS, make sure the designated port is open to incoming connections, then try navigating to your app using the Public IP address of your server:
http://<PUBLIC-IP-ADDR>:<PORT>
OPTIONAL STEPS
- Create a Docker Hub account: https://hub.docker.com/
- Create a security token for login: https://blog.docker.com/2019/09/docker-hub-new-personal-access-tokens/
- Create a repository for your new app
tag
andpush
the image to your repositorypull
andrun
the image on a separate system to test your app
Notes for users on a VPN:
From a local Linux machine running a VPN, I was not able to start the Docker Daemon and run the hello-world
image initially. Read on to learn how to resolve this issue without modifying your current network or VPN configuration.
When I first tried running $ sudo docker run hello-world
I got an error message about the daemon not running yet.
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
So I tried $ sudo systemctl start docker
, but got more errors:
systemd[1]: docker.service: Start request repeated too quickly.
systemd[1]: Failed to start Docker Application Container Engine.
Unit docker.service has failed
Eventually, I found the suggestion to check the daemon logs under /var/log/daemon.log
. There I found more revealing messages about what was going wrong with the dockerd
:
dockerd[12144]: failed to start daemon:
Error initializing network controller: list bridge addresses failed:
PredefinedLocalScopeDefaultNetworks List:
[172.17.0.0/16 172.18.0.0/16 172.19.0.0/16 172.20.0.0/16 172.21.0.0/16 172.22.0.0/16 172.23.0.0/16 172.24.0.0/16 172.25.0.0/16 172.26.0.0/16 172.27.0.0/16 172.28.0.0/16 172.29.0.0/16 172.30.0.0/16 172.31.0.0/16 192.168.0.0/20 192.168.16.0/20 192.168.32.0/20 192.168.48.0/20 192.168.64.0/20 192.168.80.0/20 192.168.96.0/20 192.168.112.0/20 192.168.128.0/20 192.168.144.0/20 192.168.160.0/20 192.168.176.0/20 192.168.192.0/20 192.168.208.0/20 192.168.224.0/20 192.168.240.0/20]:
no available network
And:
systemd-udevd[12309]: Could not generate persistent MAC address for docker0: No such file or directory
Fortunately, user kinglion811 shared this suggestion on Docker GitHub issue #123 [see: original comment]. He wrote:
ip link add name docker0 type bridge
ip addr add dev docker0 172.17.0.1/16
can solve this issue
So I checked my network interfaces on this local Linux machine...
# ip a
:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state [...]
inet 127.0.0.1/8 scope host lo
2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc [...]
inet 192.168.<REDACTED>/<REDACTED> brd 192.168.<REDACTED> [...]
13: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> <REDACTED>
inet <REDACTED>/<REDACTED> scope global tun0
tun0
is a VPN network interface. But most importantly here: There is no docker0
interface. So let's create one as kinglion811
suggested:
# ip link add name docker0 type bridge
# ip addr add dev docker0 172.17.0.1/16
Now when I run ip a
again I see a new interface for Docker:
[...]
14: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
Having created the docker0
interface and given it an IP address to use, I could start dockerd
without issue: $ sudo systemctl start docker
.
This solution worked for me on Debian; for others on Ubuntu it also solves the problem. See: https://github.com/docker/for-linux/issues/123
Up and docker run
ning..!
If you have any questions or suggestions to improve this article, hit me up at [email protected]
or on Twitter @kernelmastery (DMs are open).