https://docs.docker.com/get-started/part2/#define-a-container-with-dockerfile

Get Started, Part 2: Containers

介绍

是时候通过docker的方式来构建一个app了。我们从一个APP的底层开始,即容器container,也就是本页内容。更高一层级是service,它定义了容器在生产的运转情况,part 3中内容。最后,最高层及是stack,它定义了许多services的交互,part 5内容。

即:

Stack

Services

Container(你现在在这里)

你的新开发环境

过去,你开始写一个python app,第一件事就是在你的机器上安装python的环境。但,这种机器上环境既要使你的app完美开发运行,又要匹配生产环境的需要。

通过docker,你紧需要获取一个轻便的python运行环境制作成镜像,不必进行安装。然后,你的构建文件可以包含python镜像和app代码,确保app,其依赖性,运行环境一起运行。

这些轻便的镜像被定义成的配置称为为一个Dockerfile.

用Dockerfile定义一个Container容器

dockerfile定义了容器内部的环境。容器内部环境对network网络接口和disk硬盘是虚拟化的,它和你系统其他部分是隔离的,所以你需要映射端口到外面环境,拷贝特定的文件到内部环境。然后,在这之后,你就可以指望这个定义在dockerfile中的你的app构建在任何地方运行达到相同的效果。

Dockerfile

创建一个新目录,进入该目录,创建一个叫Dockerfile的文件,将下列内容拷贝粘贴,并保存。注意每个语句的注释。

hub docker下载 docker官网下载_docker

这个dockerfile关联了一对文件我们没有创建,app.py和requirements.txt.我们在下面创建它们。

The app itself

再创建两个文件,requirements.txt和app.py,将他们放在和Dockerfile相同的目录。这就是我们app的全部,像你所看到的非常的简单。当Dockerfile被创建放入镜像,因为copy命令,同时也包括了app.py和requirements.txt,感谢EXPOSE 命令,http访问可以看到app.py中的输出。

 

requirements.txt

Flask
Redis

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)

我们可以看动pip install -r requirements.txt 安装了python的Flask and Redis依赖, app打印出了环境变量Name和 socket.gethostname()的输出。最后,因为redis没有运行(我们只装了python的依赖模块,没有装redis本身) ,我们能够预料到程序运行时这里会产生对应的错误信息。

Note:container中获取主机name时为container id,类似于执行程序的进程id。 

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.

就这样,你不需要python或requirements.txt中其他存在你系统中。也无需进行创建和安装镜像到你系统。看起来不像已经拥有了python和flask的环境,但是你已经有了。

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:

我们已经准备创建app。确保你在新建目录的根节点。ls命令应显示以下:

$ 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.

现在我们要运行build命令了,这会创建一个docker镜像,这里我们用到了-t参数给镜像起名。

docker build -t friendlyhello .

Where is your built image? It’s in your machine’s local Docker image registry:

创建的镜像在哪?在你机器本地的docker的镜像注册中心:

$ docker image ls

REPOSITORY            TAG                 IMAGE ID
friendlyhello         latest              326387cea398

Troubleshooting for Linux users

linux用户问题解决

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:

代理服务器会阻断web app连接。如果你用到代理,添加以下到dockerfile,通过ENV命令指定主机和端口。

# 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 the dns key, as following:

不合适的dns配置pip会有问题。你需要设置你自己的dns是pip正确工作。你可能需要改变Docker daemon的dns配置。你可以编辑或创建新的配置文件,在 /etc/docker/daemon.json 路径下。dns作为key如下:

{
  "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.

像上面代码一样,第一个是你自己的dns,第二个是google dns作为替代。

Before proceeding, save daemon.json and restart the docker service.

保存daemon.json 重启docker服务。

sudo service docker restart

Once fixed, retry to run the build command.

修改后,重新执行build命令。

 

Run the app

Run the app, mapping your machine’s port 4000 to the container’s published port 80 using -p:

运行app,通过-p参数,映射机器端口4000到容器80端口。

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 URL http://localhost:4000.

你可以看到app的路径信息http://0.0.0.0:80。但是信息是容器内部的,它不知道你已经映射端口4000,访问正确的路径:http://localhost:4000

Go to that URL in a web browser to see the display content served up on a web page.

浏览器访问地址可以看到以下内容:

hub docker下载 docker官网下载_python_02

 

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 command docker-machine ip.

如果你使用了win7 Docker Toolbox,需要访问docker机器ip和端口,而不是localhost。如http://192.168.99.100:4000/.  docker-machine ip命令查询docker ip.

 

You can also use the curl command in a shell to view the same content

你还可以通过curl命令访问地址,看到相同内容:

$ 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 demonstrates the difference between EXPOSE within the Dockerfile and what the publish value is set to when running docker run -p. In later steps, map port 4000 on the host to port 80 in the container and use http://localhost.

这端口的重新映射4000:80展示dockerfile expose端口和发布命令中docker run -p中的端口的区别。后续步骤中同此。

Hit CTRL+C in your terminal to quit.

 终端CTRL+C退出

 

Now let’s run the app in the background, in detached mode:

静默方式运行app

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):

你会得到一个container id在终端,你的container在后台运行中。你可的通过docker container ls命令获取简短container id(运行命令中可互换)

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED
1fa4ab2cf395        friendlyhello       "python app.py"     28 seconds ago

Notice that CONTAINER ID matches what’s on http://localhost:4000.

注意CONTAINER ID和访问输出一致

Now use docker container stop to end the process, using the CONTAINER ID, like so:

你可以用docker container stop 指定containerid结束运行,像这样

docker 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.

为了演示刚才创建镜像的可移植,我们上传镜像,其他地方运行。终究,当你要部署container到生产,你需要知道怎样push到注册中心。

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.

注册中心是一个仓库集合,仓库是各种镜像集合-像GitHub仓库,除了代码已经创建。注册中心一个账号可以有多个repositories。docker CLI使用默认的Docker’s public registry。

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.

注意:我们这里使用 Docker’s public registry因为其免费和配置好的,此外存在许多其他公有registry,你甚至可以创建自己的私有registry通过 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.

没有docker account,就去hub.docker.com.注册一个吧。记住你的用户名。(坑呀!!!注册不到,不知是否需要vpn才行)

Log in to the Docker public registry on your local machine.

本机登录Docker public registry

$ docker login

Tag the image

The notation for associating a local image with a repository on a registry isusername/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 the get-started repository and tag it as part2.

将本地镜像和注册中心镜像联系的是username/repository:tag代码注解。tag是可选的,但是推荐使用,因为注册中心会根据tag做镜像版本。起一个有含义的名字,如get-started:part2.这样讲把镜像放置在get-started仓库并tag是part2.

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加上你的用户名,仓库,tag名,镜像会上传相应目的地。语法命令是: 

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.

docker image ls 查看你刚标记的image

$ 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:

上传你的镜像

docker 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.

成功后,上传是公开可见的。你如果登录 Docker Hub上,你可以看到它,还有相应的pull命令。

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:

现在开始,在任意机器你可以通过docker run运行app。

docker run -p 4000:80 username/repository:tag

 If the image isn’t available locally on the machine, Docker pulls it from the repository.

如果image本地不可用,Docker会从仓库获取。

$ 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 from requirements.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.

无论在何地运行,它会获取你的镜像,python和requirements.txt中全部的依赖,并运行你的代码。所有的只是一个轻便的package,无需安装任何文件docker就可以运行。

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.

docker 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