Docker入门,Part 2:容器

要求

  • 已安装Dockers1.13或更高版本
  • 完成Part 1的阅读
  • 在机器环境上快速启动运行一个容器保证所有都配置正确docker run hello-world

介绍

是时候开始使用Docker方式来构建一个应用了。在本节我们将基于一个应用垂直结构的底层开始构建容器。在这之上是服务,我们将在Part3第三节介绍基于容器的服务是如何在生产环境运行的。最后,在最顶层是栈,定义了所有服务的交互行为,我们将在Part5第五节进行介绍说明。

  • Stack栈
  • Services服务
  • Container容器(本节的介绍内容)

新的开发环境

在以往,如果我们要开始着手写一个Python应用,要做的第一件事就是在机器上安装Python的运行环境。但是,这样会造成一个问题,就是我们搭建的环境要完美的适配应用程序的需求,同时也要匹配将来生产环境的要求。
使用Docker,我们仅仅需要做的就是构建一个便捷的Python运行环境镜像,不再需要而外安装其他东西。基于一个通用的镜像我们将代码和运行环境一起构建为一个专用镜像,保证所有的依赖、运行环境、代码全都在一起。
通过使用Dockerfile我们能够定义这些非常便捷的镜像。

使用Dockerfile定义容器

Dockerfile定义了容器是如何运转的。类似于网络接口和数据硬盘驱动等这些资源的使用在环境中都是虚拟化的,并且与系统其他程序是相互隔离的,因此我们需要将端口映射到外部,确定文件是如何来实现共通。我们期望通过Dockerfile定义配置的应用容器能够准确无误在任何地方运行。

Dockerfile

  • 创建一个空目录
  • 切换工作路径到这个新目录
  • 在目录下新建Dockerfile,将下文中的内容复制到Dockerfile中,然后保存文件(请详细查看每个语句的注释部分,解释了每个语句具体的含义)
# 使用官方的Python运行环境作为基础镜像
FROM python:2.7-slim

# 设置工作路径为/app
WORKDIR /app

# 复制当前目录下的内容到容器的/app下
ADD . /app

# 安装在文件requirements.txt指定的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 将80端口进行对外开放,这样就能从容器外部通过映射访问容器内部的80端口
EXPOSE 80

# 定义环境变量
ENV NAME World

# 容器启动后运行 app.py程序
CMD ["python", "app.py"]

Dockerfile中提到了有几个我们还没有创建的文件,例如app.py和requirements.txt。我们接下来进行逐一的创建

应用程序

我们再创建两个文件,分别为requirements.txt和app.py,并将他们保存到跟Dockerfile一致的目录。这样我们就非常简单的完成了应用。当上文提到的Dockerfile构建为一个镜像后,app.py和requirements.txt也会在镜像中,因为我们使用了Dockerfile的ADD命令,并且因为使用的EXPOSE命令我们能够使用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将按照Flask和Redis包,在应用程序中会将环境变量NAME进行打印输出,同时还会输出socket.gethostname()的内容。最后应为Redis并没有运行(因为我们并没有启用任何的Redis服务),因此在程序中尝试使用Redistribution会出错同时也会输出相关的错误信息。

注意:在容器中获取机器的名称会返回一个类似于程序进程ID一样的容器ID

如此简单,不需要在机器上安装Python和相关的依赖,也不需要在机器上构建安装这些镜像。看上去就像我们没有配置Python和Flask,一样,实际上我们已经配置好了。

构建应用程序

通过上面的配置,我们已经准备好构建需要的所有。确保你的工作目录是处于之前创建的新目录。通过ls命令我们可以看到

$ ls
Dockerfile     app.py    requirements.txt

运行构建命令。将会新建一个Docker镜像,我们可以通过使用-t参数为镜像取一个友好的名字
docker build -t friendlyhello .
我们构建的镜像在哪里呢?它位于机器上的本地Docker镜像仓库中,

docker image ls
REPOSITORY    TAG     IMAGE ID
friendlyhello latest   ******

Linux 用户可能遇到的问题
代理服务设置
代理服务在启动运行时能够阻止(修改)网络连接。如果你的机器上运行了一个代理服务器,需要将下面的相关指令添加到Dockerfile,通过使用ENV指令能够指定代理服务的主机和端口

# 配置代理服务,修改host:port为你自己机器上的对应值
EVN http_proxy host:port
EVN https_proxy host:port

DNS设置
错误的DNS设置会给pip的使用带来问题。为确保pip的正常使用,需要设置DNS服务地址。我们可以通过修改Docker守护进程里的DNS信息。通过在编辑(或者新建)配置文件(/etc/docker/daemon.json)添加dns信息

{
  "dns": ["你的DNS地址", "8.8.8.8"]
}
保存对daemon.json文件的修改,重启docker服务`sudo service docker restart`

问题修复后,重新使用build进行构建

运行应用

使用-p参数进行端口映射将主机的4000端口映射为容器对外发布的80端口,docker run -p 4000:80 friendlyhello
通过控制台我们可以看到容器启动的相关信息,可以看到Python监听了80端口启动了一个HTTP服务。但是这个信息是来自于容器内容,因此80端口也是指的容器内的端口,在运行启动时我们通过端口映射将主机的4000端口映射到容器内的80,因此我们可以在本机通过http://localhost:4000来访问服务。如果一起启动正常我们可以在浏览器看到相关的信息

注意
如果是在windows7上通过Docker Toolbox来运行容器复苏的话,需要将localhost替换为Docker Machine的IP。可以通过docker machine ip来查看Docker Machine的IP地址

在终端中使用CTRL+C,可以退出

在windows操作系统中,需要明确停止容器
在windows操作系统中,使用CTRL+C不会停止容器,只会退出命令终端。因此我们需要通过CTRL+C退出命令终端(此时通过docker container ls查看还有哪些容器在运行),然后使用docker container stop <Container NAME od ID>来停止容器

接下来,我们需要应用在后台运行,以静默的方式在后台运行
docker run -d -p 4000:80 friendlyhello
使用-d参数,可以让容器在后台运行,通过docker container ls查看正在运行的容器和相关信息,在后台运行的的容器可以使用docker container stop <Container NAME od ID>来停止容器

发布共享镜像

为演示我们刚刚运行的应用的可移植性,我们可以上传构建的镜像然后就可以在任意地方运行了。在部署容器到其他环境(生产环境、测试环境)前,需要知道如何将镜像推送上传到登记注册处。
一个登记注册处是仓库的集合,仓库是镜像的集合——就像GitHub的仓库一样。登记注册处的一个账号可以创建多个仓库。docker命令默认使用Dockers的公共注册处

注意
使用默认的注册处是因为它已经做好了默认的配置而且是免费的。当然还有很多其他的公共注册处提供选择,我们也可以自己创建私有的注册处

使用Docker账号登陆

如果还没有Docker账号,可以在http://hub.docker.com进行注册。在本机登陆公共的Docker登记注册处docker login,根据提示信息输入账号密码,完成登陆

为镜像打标签

仓库上的镜像与本地镜像的关联格式是username/repository:tag,tag是可选的,但是建议每次都附上tag,通过tag我们可以了解到docker镜像的版本。通过镜像功能和相关信息为repository:tag选择有意义的名称,例如get-started:part2
使用我们自己的username,repository和tag运行docker tag image,命令的语法格式为:
docker tag image username/repository:tag
例如
docker tag friendlyhello gordon/get-started:part2
运行docker image ls查看刚刚打上tag的镜像

推送发布镜像

上传打上标签的镜像到仓库
docker push username/repository:tag,例如docker push gordon/get-started:part2
上传完成后,这个镜像就成为公共的,如果我们登陆Docker Hub,就可以看到刚刚上传的镜像

从远端拉取镜像并运行

至此,可以通过使用 docker run在任何其他支持docker的机器上运行应用
docker run -p 4000:80 username/repository:tag
如果这个镜像在本机没有检测到,Docker就会从远端仓库哦查找获取这个镜像。无论docker run在哪里运行,他都会获取到我们上传的镜像,在镜像中包含有Python、相关的依赖和运行的代码。它把所有需要的代码依赖组件等打包在一起,而在本地机器上我们不需要任何安装而外的东西就能够运行整个应用。

本节总结

本节通一个实际的案例讲解了Dockefile和Docker Hub的相关知识,在下一节,将会学习如何以服务的方式运行容器来扩展我们的应用

温故知新

下面的终端记录小视频记录了本节的所涉及的相关操作

下面列出了本节使用到的一些命令

docker build -t friendlyhello # 使用当前目录下的Dockerfile新建镜像
docker run -p 4000:80 friendlyhello # 运行“friendlyhello”,并将本机的4000端口映射到容器的80端口
docker run -d -p 4000:80 friendlyhello # 以后台静默方式运行容器,并将本机的4000端口映射到容器的80端口
docker container ls # 列出所有正在运行的容器
docker container ls -a # 列出所有容器,包括已停止的
docker container stop <hash> # 友好的停止指定的容器
docker contailer kill <hash> # 强制停止指定的容器
docker container rm <hash> # 从机器上删除指定的容器
docker container rm (docker container ls -a -q) # 删除所有容器 docker image ls -a # 列出本机上所有的镜像 docker image rm <image_id> # 从本机上删除指定的镜像 docker image rm(docker image ls -a -q) # 删除本机上所有的镜像
docker login # 使用Docker账号进行登陆
docker tag <image> username/repository:tag # 为待上传的镜像打标签
docker push username/repository:tag # 上传镜像到仓库
docker run username/repository:tag # 运行镜像(如果本机没有该镜像,就会从仓库下载镜像并允许)

了解更多

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容