Docker 入门 第二部分: 容器

Docker 入门 第二部分: 容器

先决条件

  • 安装 Docker 1.13 或更高版本

  • 阅读第一部分

  • 给你的环境做一个快速运行测试,以确保你已经准备好了

    docker run hello-world
    

介绍

现在到了以docker的方式去构建一个应用了。我们从整个应用的架构的底层开始,这里是一个容器,我们会在本页介绍它。在此层级之上是一个服务,它定义了容器在生产环境中会如何工作,我们会在第三部分介绍。最后,最顶层的是堆栈,定义所有服务的交互行为,我们会在第五部分介绍。

  • Stack 堆栈
  • Services 服务
  • Container 容器 (现在你在这里)

你的新开发环境

在过去,如果你要写一个ptyhon应用,首先你要在你的机器上安装一套ptyhon的运行环境。但是,这就产生了一种情况,为了让你的应用按照预期运行,你机器上的环境需要精确部署,而且还需要和你的生产环境相匹配。
使用Dcoker,你只需要将一个便携的python运行时(就是一套可执行的python)作为镜像,而没有安装的必要。然后,你构建的镜像可以包括基本的Python镜像和你的应用代码。以确保你的应用、应用的依赖和运行环境全部在一起。
这些由某些内容定义的便携的镜像叫做 Dockerfile

使用 Dockerfile 定义一个容器

Dockerfile 定义容器内环境中的内容。对注入网络接口和磁盘驱动的访问都在这个环境中被虚拟化。以和系统的其余部分分离开来,因此你需要将端口映射到外部,并具体指明你想要将什么文件“复制到”该环境。

对诸如网络接口和磁盘驱动器等资源的访问在这个环境中被虚拟化, 这与系统的其余部分隔离开来, 因此您需要将端口映射到外部世界, 并具体要 "复制" 哪些文件到该环境。不管怎样,可以保证在这个 Dockerfile中构建定义的应用可以在任何地方都可以一样的运行。

Dockerfile

创建一个空目录。进入该目录,并创建一个名为 Dockerfile 的文件,把以下内容复制粘贴到该文件中,并保存。注意dockerfile文件中解释每一条语句的注释内容。

# 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"]

这个 Dockerfile 文件引用了我们还没有创建的文件,app.pyrequirements.txt。 稍后我们会创建它们。

应用本身

再创建两个文件,app.pyrequirements.txt,并把他们放入和 Dcokerfile 相同的目录里。这样我们就完成了应用,你可以看到,这很简单。当上面的 Dcokerfile 被构建到一个镜像中时,app.pyrequirements.txt 也会存在于这个镜像,这是因为 DockerfileADD 命令,并且因为有 EXPOSE 命令, app.py 的输出可以通过 HTTP 访问。

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 和Redis库,应用程序打印了环境变量NAME,以及调用socket.gethostname()后的输出。最终,因为Redis没有运行(我们只安装了Python库,没有安装redis),正常的话,我们可以看到尝试使用redis失败,产生的错误信息。

注意:当在容器内部检索容器ID时,访问的主机名就像一个正在运行的进程的ID

就是这样,你的系统中不需要 python 或 requirements.txt 中的任何内容,构建和运行这个镜像时也不需要在你的系统上安装它们。看起来你并没有安装Python和Flask的环境,但你已经有了。

构建应用

我们已经准备好了去构建应用。确保你一直都在新建目录的最上级。你可以看到如下内容:

$ ls
Dockerfile		app.py			requirements.txt

现在运行构建命令,这会创建一个Docker镜像,我们可以使用 -t 参数给它指定一个友好的名字。

docker build -t friendlyhello .

构建的镜像保存在哪里呢?镜像会保存在你机器上本地的Docker镜像registry里:

$ docker image ls

REPOSITORY            TAG                 IMAGE ID
friendlyhello         latest              326387cea398

Linux用户故障排查
代理服务器设置
代理服务器启动运行后,可以阻塞与web应用的链接。如果你使用了代理服务器,把下面的内容添加到你的Dockerfile,使用ENV命令指定你的代理服务器的主机和端口:

# Set proxy server, replace host:port with values for your servers
ENV http_proxy host:port
ENV https_proxy host:port

DNS设置
DNS配置错误可以导致使用pip时出现问题。你需要设置你自己的DNS服务器地址来pip正常工作。可能你想修改 Docker守护进程的DNS设置,你可以编辑(或创建)配置文件/etc/docker/daemon.json,设置 dns关键字,内容如下:

{
  "dns": ["your_dns_address", "8.8.8.8"]
}

上面的例子中,列表的第一个元素是DNS服务器的地址,第二个是Google的DNS,当第一个地址无法使用时,会使用第二个。
在继续之前,先保存 daemon.json 并重启docker服务。

sudo service docker restart

问题修复后,重新尝试build命令

运行应用

运行应用,使用-p参数将你的机器的4000端口映射到容器的发布端口80上。

docker run -p 4000:80 friendlyhello

你会在http://0.0.0.0:80看到一个Pyhton正常为你的应用服务的消息。但是这个消息来自容器内部,它不知道你将该容器的80端口映射到4000端口,需要输入正确的URL http://localhost:4000

在浏览器中访问正确的URL,以查看网页中显示的内容。

图片名称

注意:如果你在Windows7中使用Docker Toolbox,要使用Docker机器的IP替换localhost。例如,http://192.168.99.100:4000/。使用 docker-machine ip 命令可以找到这个IP地址。

你也可以在shell中使用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>

这里重新映射 4000:80 演示了和 DockerfiileEXPOSE的不同之处,而且在运行 docker run -p 时设置了发布的值。在后面的步骤中,映射了主机的4000端口到容器的80端口,并使用了 http://localhost

在你的终端中使用 CTRL+C 来退出。

在Windows中,显示的关闭容器
在windows系统,CTRL+C 不会关闭容器。所以,首先输入 CTRL+C 返回提示符(或者新打开一个shell),然后输入 docker container ls 来列出运行中的容器,随后通过 docker container stop <Container NAME or ID>来关闭容器。否则,在稍后的步骤中,当你尝试重新运行容器会得到一个来自守护进程错误的响应。

现在我们以分离模式在后台运行应用:

docker run -d -p 4000:80 friendlyhello

你会获得应用容器的长ID并返回终端,容器会在后台运行,你还可以荣国 docker container ls 查看缩写的容器ID()

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

注意 容器IDhttp://localhost:4000上的内容相匹配。
现在使用 docker container stop 指定 容器ID 来结束进程,如下:

docker container stop 1fa4ab2cf395

共享你的镜像

为了证明我们刚刚创建的镜像的可移植性,我们可以上传构建的镜像并在任何地方运行它。毕竟,当你想把容器部署到生产环境中时,你需要知道如何推送到 registries。

一个registry 是一个仓库的集合,一个仓库是一个镜像的集合——类似于GitHub的仓库。只是代码已经构建好了。registry上的一个用户可以创建多个仓库。docker CLI默认使用Docker的公共registry。

注意: 这里我们使用Docker的公共registry是因为它是免费的和预置的,不过我们还是有很多公共的registry可以选择,甚至你可以使用 Docker Trusted Registry来搭建你自己私有的registry。

使用你的docker ID进行登录

如果你没有docker用户,可以在hub.docker.com注册一个。记下你的用户名。在你的本地机器上登录到DOcker公共的registry。

$ docker login

为镜像加标签

关联本地镜像与registry仓库的方法是 username/repository:tag 。标签是可选的,但建议设置上,因为这是registry为docekr镜像提供版本的机制,根据上下文给仓库和标签起一个有意义的名字,例如:get-started:part2。这里我们将镜像放在 get-started仓库,标签名字为 part2
现在,把它们放在一起来标记镜像。使用in的用户名、仓库和标签名来运行 docker tag image,以便将镜像上传到你想要上传的位置,命令语法如下:

docker tag image username/repository:tag

例如:

docker tag friendlyhello gordon/get-started:part2

运行docker image ls来查看你信打标签的镜像:

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

发布镜像

把打过标签的镜像上传到仓库:

docker push username/repository:tag

完成之后,上传之的镜像会被公开发布。如果你登录到了 Docker Hub,你可以使用pull命令,在Docker Hub看到最新的镜像。

从远程仓库拉取并运行镜像

从现在开始,你可以在在任何机器上使用docker run运行你的应用:

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

如果镜像在本地机器上不可用,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)

现在无论你在哪里运行docker run,他会拉取镜像,Pyhton、requirements.txt中指定的依赖和你的代码 会在一起运行,他们全部打包在一个小包裹里(镜像),你不需要在你的机器上安装任何东西,Docker 就可以运行它了。

第二部分总结

这就是本页所有内容,下一节,我们会通过在服务中运行这个容器来学习如何扩展我们的应用程序。

回顾和备忘【可选】

官方在这里提供了一段视频,但我这里观看有问题,我就找把视频录制下来放到了B站,地址如下:

官方视频

B站的“分享视频”中给的代码放在博客园无法播放,只能在这里写一个地址了。

下面是本页中关于docker的基本命令列表,以及一些相关的命令,在继续后面的学习前可以研究一下。

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

posted on 2018-08-30 14:15  R_e  阅读(615)  评论(0编辑  收藏  举报

导航