原文链接:http://www.dubby.cn/detail.html?id=8734

准备

  • 已经安装好Docker 1.13或者以上的版本。
  • 读完的上一篇文章(基本引导)。
  • 简单的测试一下你的本地环境是否已经OK了:docker run hello-world

介绍

让我们开始构建一个Docker应用吧。这篇文章介绍的是,Docker整个结构层次中最底层的一个,那就是容器。上一个层级是服务,服务定义了容器的行为,这会在下一篇文章中介绍。最后一个,也是最高的一个层级,是堆栈,定义了所有服务的交互,这在之后也会被介绍。

  • Stack(堆栈)
  • Services(服务)
  • Container(容器)——你正在看的

走进新的开发环境

在没有Docker的时候,如果你想写一个Python应用,你首先要做的是安装一个Python的运行环境。但是,你的机器的环境必须是这样,才能使得应用可以正常执行,而且,服务器也必须有这样的环境。

有了Docker,你可以直接通过镜像来获得一个Python的运行环境,不需要安装哦。然后把你的代码和这个镜像放到一起,当然还要加上你的代码所需的依赖。

这些融合在一起,就形成了一个新的镜像,定义这个融合逻辑的就是Dockerfile

使用Dockerfile来定义一个容器

Dockerfile是用来定义你的容器。对于一些资源访问,比如网卡、磁盘,在容器里都是虚拟化的,和宿主机都是隔离的,因此,你必须把容器内的资源和宿主机的资源做一个映射;还需要指定你想要把哪些文件copy到你的容器里(比如你的代码)。当然,你还希望这个写好的Dockerfile在任何地方运行都是一样的。

Dockerfile

创建一个空目录,然后在里面新建一个文件Dockerfile

# 使用Python官方镜像作为镜像的基础
FROM python:2.7-slim

# 设置工作空间为/app
WORKDIR /app

# 把当前目录下的文件拷贝到 容器里的/app里
ADD . /app

# 安装requirements.txt中指定的依赖
RUN pip install -r requirements.txt

# 开放80端口
EXPOSE 80

# 设置 NAME 这个环境变量
ENV NAME World

# 当容器启动时,运行app.py
CMD ["python", "app.py"]

这个Dockerfile中需要的几个文件还没有准备好,app.pyrequirements.txt,所以我们继续吧。

应用

Dockerfile同一个目录下,创建这两个文件。因为ADD命令需要把这些拷贝进容器里。而且app.py中的服务器输入端口也正好是80,这会因为配置了EXPOSE而暴露出来。

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安装了Flask和Redis的库,应用打印出NAME这个环境变量和socket.gethostname。最后,因为Redis没有运行(pip安装的只是使用Redis的库,并不是Redis本身),所以我们应该做尝试连接Redis,如果连接失败就打印错误信息。

注意:在容器内部访问主机名时,会检索容器的ID,类似于进程ID。

构建应用

就这样!你不需要安装Python,不需要安装requirements.txt中指定的任何依赖。看起来,你什么都没做,但是其实你已经做完了。

执行ls(默认为类Unix环境):

$ ls
➜  demo ls
Dockerfile       app.py           requirements.txt

现在来执行构建命令。这个会创建一个Docker镜像,-t可以让你给镜像自定义一个名字:

docker build -t friendlyhello .

你肯定要问要,你的镜像在哪里?他就在你的本地Docker镜像注册中心:

$ docker images

REPOSITORY            TAG                 IMAGE ID
friendlyhello         latest              326387cea398

运行你的应用

运行,当然还要映射端口号,我们需要使用-p

docker run -p 4000:80 friendlyhello

你可以看到Python启动是提醒你访问http://0.0.0.0:80。但是不要忘记这个响应是从容器内返回的哦,而我们把容器内的80端口映射到宿主机的4000端口了,所以,我们应该访问http://localhost:4000

打开浏览器,可以看到:

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

按键CTRL+C可以退出Docker。

现在试试在后台运行这个Docker应用:

docker run -d -p 4000:80 friendlyhello

执行完后台运行的命令后,你可以获得一个容器的ID

➜  demo docker run -d -p 4000:80 friendlyhello
b0054baaca448f3b30b01004de98be45fbb9aaccd850137629220bbc730b0a1a

你也可以使用docker ps来看到缩短的容器ID:

➜  demo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
b0054baaca44        friendlyhello       "python app.py"     About an hour ago   Up About an hour    0.0.0.0:4000->80/tcp   keen_beaver

当然也可以在http://localhost:4000上看到这个容器ID。你可以使用docker stop来结束这个进程,后面要加上容器ID哦:

➜  demo docker stop b0054baaca44
b0054baaca44

分享你的镜像

为了展示Docker的便捷性,我们来尝试上传这个镜像吧,然后再证明可以run anywhere

注册中心就是一个镜像的仓库的集合——有点类似GitHub(代码仓库)。一个账号可以创建很多个仓库。docker默认情况下就是Docker公共注册中心。

我们这里使用Docker的公共注册中心,仅仅是因为他是免费而且是默认的。不过还是有很多公开的注册中心可以选择的,甚至你可以设置一个私服

登录

如果你没有账号,那你需要先注册一个

然后在你的本机上使用:

docker login

给镜像打标签

镜像的标记一般这样命名username/repository:tag。tag是可选的,不过推荐使用tag,这样可以给镜像加一个版本。建议把仓库名和标签名都起得有意义一些,比如get-started:part1。这样会把镜像放到get-started仓库里,打上part1的标签。

语法是docker tag image username/repository:tag,现在开始用命令来尝试打标签吧:

➜  demo docker tag friendlyhello dubbyyoung/get-started:part1
➜  demo docker images                                        
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
dubbyyoung/get-started   part1               4ac5012183e0        4 hours ago         194 MB
friendlyhello            latest              4ac5012183e0        4 hours ago         194 MB
python                   2.7-slim            d962f7a9f2f1        2 days ago          182 MB
hello-world              latest              1815c82652c0        9 days ago          1.84 kB
➜  demo 

推送到远端

推送的命令是:

docker push username/repository:tag

我执行的结果是:

➜  demo docker push dubbyyoung/get-started:part1
The push refers to a repository [docker.io/dubbyyoung/get-started]
b57286e58751: Pushed 
7b71bca6a91c: Pushed 
cdc93fd01629: Pushed 
553c628e7577: Mounted from library/python 
8f02c55c4e74: Mounted from library/python 
15d2fe96bb43: Mounted from library/python 
0d960f1d4fba: Mounted from library/python 
part1: digest: sha256:6d816f3a86c74e8855cef96750ff1da98eb11747c4a31cbb486b92dd20e075e6 size: 1787

随时随地都可以执行了

到了这一步,你可以使用docker run在任何一个机器上运行了:

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

如果本地没有这个镜像,Docker会自动去远端拉取:

➜  demo docker run -p 4000:80 dubbyyoung/get-started:part1
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

注意:如果你不指定标签:tag,那么缺省的就是:latest,不管是构建的时候还是运行的时候。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐