Docker:Containers
Prerequisites
- Install Docker version 1.13 or higher.
- Read the orientation in Part 1.
-
Give your environment a quick test run to make sure you’re all set up:
docker run hello-world
Introduction
It’s time to begin building an app the Docker way.
We start at the bottom of the hierarchy of such an app, which is a container, which we cover on this page.
Above this level is a service, which defines how containers behave in production, covered in Part 3.
Finally, at the top level is the stack, defining the interactions of all the services, covered in Part 5.
- Stack
- Services
- Container (you are here)
Your new development environment
In the past, if you were to start writing a Python app,your first order of business was to install a Python runtime onto your machine. But, that creates a situation where the environment on your machine needs to be perfect for your app to run as expected, and also needs to match your production environment.
With Docker, you can just grab a portable Python runtime as an image, no installation necessary.
Then, your build can include the base Python image right alongside your app code, ensuring that your app, its dependencies, and the runtime, all travel together.
These portable images are defined by something called a
Dockerfile
.Define a container with
Dockerfile
Dockerfile
defines what goes on in the environment inside your container.Access to resources like networking interfaces and disk drives is virtualized inside this environment, which is isolated from the rest of your system,
so you need to map ports to the outside world,
and be specific about what files you want to “copy in” to that environment.
However, after doing that, you can expect that the build of your app defined in this
Dockerfile
behaves exactly the same wherever it runs.Dockerfile
Create an empty directory. Change directories (
cd
) into the new directory, create a file calledDockerfile
, copy-and-paste the following content into that file, and save it. Take note of the comments that explain each statement in your new Dockerfile.1234567891011121314151617181920# Use an official Python runtime as a parent image
FROM
python:
2
.
7
-slim
# Set the working directory to /app
WORKDIR
/app
# Copy the current directory contents into the container at /app
ADD
. /app
# Install any needed packages specified in requirements.txt
RUN
pip install --trusted-host pypi.python.org -r requirements.txt
# Make port 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"
]
This
Dockerfile
refers to a couple of files we haven’t created yet, namelyapp.py
andrequirements.txt
.Let’s create those next.
The app itself
Create two more files,
requirements.txt
andapp.py
, and put them in the same folder with theDockerfile
. This completes our app, which as you can see is quite simple.When the above
Dockerfile
is built into an image,app.py
andrequirements.txt
is present because of thatDockerfile
’sADD
command,and the output from
app.py
is accessible over HTTP thanks to theEXPOSE
command.requirements.txt
Flask Redis
app.py
123456789101112131415161718192021222324from 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
)
Now we see that
pip install -r requirements.txt
installs the Flask and Redis libraries for Python,and the app prints the environment variable
NAME
,as well as the output of a call to
socket.gethostname()
.Finally, because Redis isn’t running (as we’ve only installed the Python library, and not Redis itself), we should expect that the attempt to use it here fails and produces the error message.
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.
That’s it! You don’t need Python or anything in
requirements.txt
on your system, nor does building or running this image install them on your system.It doesn’t seem like you’ve really set up an environment with Python and Flask, but you have(in image or container,but not in system).
Build the app
We are ready to build the app. Make sure you are still at the top level of your new directory. Here’s what
ls
should show:$ ls Dockerfile app.py requirements.txt
Now run the build command. This creates a Docker image, which we’re going to tag using
-t
so it has a friendly name.docker build -t friendlyhello .
Where is your built image? It’s in your machine’s local Docker image registry:
$ docker image ls REPOSITORY TAG IMAGE ID friendlyhello latest 326387cea398
Troubleshooting for Linux users
Proxy server settings
Proxy servers can block connections to your web app once it’s up and running.
If you are behind a proxy server, add the following lines to your Dockerfile, using the
ENV
command to specify the host and port for your proxy servers:123# Set proxy server, replace host:port with values for your servers
ENV
http_proxy host
:port
ENV
https_proxy host
:port
DNS settings
DNS misconfigurations can generate problems with
pip
.You need to set your own DNS server address to make
pip
work properly.You might want to change the DNS settings of the Docker daemon. You can edit (or create) the configuration file at
/etc/docker/daemon.json
with thedns
key, as following:123{
"dns"
: [
"your_dns_address"
,
"8.8.8.8"
]
}
In the example above,
the first element of the list is the address of your DNS server.
The second item is the Google’s DNS which can be used when the first one is not available.
Before proceeding, save
daemon.json
and restart the docker service.sudo service docker restart
Once fixed, retry to run the
build
command.Run the app
Run the app, mapping your machine’s port 4000 to the container’s published port 80 using
-p
:docker run -p 4000:80 friendlyhello
You should see a message that Python is serving your app at
http://0.0.0.0:80
. But that message is coming from inside the container, which doesn’t know you mapped port 80 of that container to 4000, making the correct URLhttp://localhost:4000
.Go to that URL in a web browser to see the display content served up on a web page.
Note: If you are using Docker Toolbox on Windows 7, use the Docker Machine IP instead of
localhost
. For example, http://192.168.99.100:4000/. To find the IP address, use the commanddocker-machine ip
.You can also use the
curl
command in a shell to view the same content.$ curl http://localhost:4000 <h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i> This port remapping of 4000:80 is to demonstrate the difference between what you EXPOSE within the Dockerfile, and what you publish using docker run -p. In later steps, we just map port 80 on the host to port 80 in the container and use http://localhost. 4000:80 EXPOSE Dockerfile publish docker run -p http://localhost
Hit
CTRL+C
in your terminal to quit.On Windows, explicitly stop the container
On Windows systems,
CTRL+C
does not stop the container.So, first type
CTRL+C
to get the prompt back (or open another shell),then type
docker container ls
to list the running containers,followed by
docker container stop <Container NAME or ID>
to stop the container. Otherwise, you get an error response from the daemon when you try to re-run the container in the next step.Now let’s run the app in the background, in detached mode:
docker run -d -p 4000:80 friendlyhello
You get the long container ID for your app and then are kicked back to your terminal.
Your container is running in the background.
You can also see the abbreviated container ID with
docker container ls
(and both work interchangeably when running commands):123$ docker container ls
CONTAINER
ID
IMAGE
COMMAND
CREATED
1fa4ab2cf395 friendlyhello
"python app.py"
28
seconds ago
Notice that
CONTAINER ID
matches what’s onhttp://localhost:4000
.Now use
docker container stop
to end the process, using theCONTAINER ID
, like so:1docker container stop 1fa4ab2cf395
Share your image
To demonstrate the portability of what we just created, let’s upload our built image and run it somewhere else.
After all, you need to know how to push to registries when you want to deploy containers to production.
A registry is a collection of repositories, and a repository is a collection of images—sort of like a GitHub repository, except the code is already built.
An account on a registry can create many repositories.
The
docker
CLI uses Docker’s public registry by default.Note: We use Docker’s public registry here just because it’s free and pre-configured,
but there are many public ones to choose from,
and you can even set up your own private registry using Docker Trusted Registry.
Log in with your Docker ID
If you don’t have a Docker account, sign up for one at hub.docker.com. Make note of your username.
Log in to the Docker public registry on your local machine.
$ docker login
Tag the image
The notation for associating a local image with a repository on a registry is
username/repository:tag
.The tag is optional, but recommended, since it is the mechanism that registries use to give Docker images a version.
Give the repository and tag meaningful names for the context, such as
get-started:part2
. This puts the image in theget-started
repository and tag it aspart2
.Now, put it all together to tag the image. Run
docker tag image
with your username, repository, and tag names so that the image uploads to your desired destination.The syntax of the command is:
docker tag image username/repository:tag
For example:
docker tag friendlyhello gordon/get-started:part2
Run docker image ls to see your newly tagged image.
1234567$ docker image ls
REPOSITORY
TAG
IMAGE
ID
CREATED
SIZE
friendlyhello latest d9e555c53008
3
minutes ago
195MB
gordon/get-started part2 d9e555c53008
3
minutes ago
195MB
python
2
.
7
-slim 1c7128a655f6
5
days ago
183MB
...
Publish the image
Upload your tagged image to the repository:
1docker push username/repository
:tag
Once complete, the results of this upload are publicly available.
If you log in to Docker Hub, you see the new image there, with its pull command.
Pull and run the image from the remote repository
From now on, you can use
docker run
and run your app on any machine with this command:1docker run -p
4000
:
80
username/repository
:tag
If the image isn’t available locally on the machine, Docker pulls it from the repository.
12345678910111213$ docker run -p
4000
:
80
gordon/get-started
:part2
Unable to find image
'gordon/get-started:part2'
locally
part2: Pulling from gordon/get-started
10a267c67f42: Already exists
f68a39a6a5e4: Already exists
9beaffc0cf19: Already exists
3c1fe835fb6b: Already exists
4c9f1fa8fcb8: Already exists
ee7d8f576a14: Already exists
fbccdcced46e: Already exists
Digest: sha256:0601c866aab2adcc6498200efd0f754037e909e5fd42069adeff72d1e2439068
Status: Downloaded newer image
for
gordon/get-started
:part2
* Running on http://
0
.
0
.
0
.
0
:
80
/ (Press
CTRL
+
C
to quit)
No matter where
docker run
executes, it pulls your image, along with Python and all the dependencies fromrequirements.txt
, and runs your code.It all travels together in a neat little package, and you don’t need to install anything on the host machine for Docker to run it.
Conclusion of part two
That’s all for this page. In the next section, we learn how to scale our application by running this container in a service.
Recap and cheat sheet (optional)
Here’s a terminal recording of what was covered on this page:
Here is a list of the basic Docker commands from this page,
and some related ones if you’d like to explore a bit before moving on.
12345678910111213141516docker build -t friendlyhello .
# Create image using this directory's Dockerfile
docker run -p
4000
:
80
friendlyhello
# Run "friendlyname" mapping port 4000 to 80
docker run -d -p
4000
:
80
friendlyhello
# Same thing, but in detached mode
docker container ls
# List all running containers
docker container ls -a
# List all containers, even those not running
docker container stop <hash>
# Gracefully stop the specified container
docker container kill <hash>
# Force shutdown of the specified container
docker container rm <hash>
# Remove specified container from this machine
docker container rm $(docker container ls -a -q)
# Remove all containers
docker image ls -a
# List all images on this machine
docker image rm <image id>
# Remove specified image from this machine
docker image rm $(docker image ls -a -q)
# Remove all images from this machine
docker login
# Log in this CLI session using your Docker credentials
docker tag <image> username/repository
:tag
# Tag <image> for upload to registry
docker push username/repository
:tag
# Upload tagged image to registry
docker run username/repository
:tag
# Run image from a registry
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
2016-06-20 命令模式
2016-06-20 合成/聚合复用原则,桥接模式