关键词:
这个《Docker入门系列》文档,是根据Docker官网(https://docs.docker.com)的帮助文档大致翻译而成。主要是作为个人学习记录。有错误的地方,Robin欢迎大家指正。分为如下几个部分:
1 Docker入门:简介
2 Docker入门:安装运行
3 Docker入门:容器(Containers)
4 Docker入门:服务(Services)
5 Docker入门:Swarms
6 Docker入门:Stacks
7 Docker入门:部署app
8 Docker入门:稍高级的话题
3.1 引言
现在我们采用Docker方式来构建一个app。这种app分为三层,从低至上分别为container、service、stack:
stack |
service |
container |
本章介绍最低层:container。再上一层是service,用于描述产品中container的行为方式,在第四章介绍。最上层是stack,定义了所有services的交互方式,在第六章介绍。
3.2 新的开发环境
以前你想着手写一个Python app,首先要做的就是在你的机器上安装Python运行时。这就在你的机器上创建了一个环境,从而使得你的app像期望中一样的运行;如果在服务器上运行你的app,也需要做同样的事情。
使用Docker后,你只需要将Python运行时作为一个映像,而不再需要安装。于是,你所构建的程序包括基本的Python映像和你的app代码。保证你的app及其依赖,以及运行时,在传送时一个都不能落下。
这种便携式的映像是由Dockerfile来定义的。
3.3 使用Dockerfile来定义容器
Dockerfile会在容器里定义一个环境,这个环境包括所有要做的事情。在这个环境里,一些资源访问(如网络接口,磁盘访问)是虚拟化的,从而和其余的系统隔离开来。因此,你可以将端口映射到外面的世界,也可以限定将哪些文件拷贝到那个环境中。于是,用Dockerfile构建的app无论在哪里运行,其行为表现都是一样的。
下面来写一个Dockerfile文件。
首先创建一个空目录,并cd到该目录下,然后在该目录下创建文件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 -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"]
警告:你是否使用了代理服务器?当代理服务器运行时,它会阻止到web app的网络连接。解决这个问题,需要在Dockerfile中加入如下代码,使用ENV命令来为代理服务器设定主机IP和端口号:
# Set proxy server, replace host:/port with values for your servers ENV http_proxy host:/port ENV https_proxy host:/port
上面的Dockerfile中引用了几个还没有创建的文件:app.py和requirements.txt,下面我们来创建它们。
3.4 app程序
在Dockerfile文件的同一目录下,创建requirements.txt和app.py两个文件。这样整个app就完成了,很简单吧。当用这个Dockerfile构建映像时,app.py和requirements.txt也都有了,这是因为Dockerfile中的ADD命令起了作用;同样,由于命令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 会安装Flask和Redis的Python库,这个app会打印环境变量NAME,并输出socket.gethostname()的调用结果。因为Redis并没有运行(因为我们只是安装了其Python库,而不是Redis本身),这么使用最终会失败并打印出错误信息。
注意:在容器中访问host的名称将会得到一个容器ID,相等于主机环境下可执行程序的进程ID。
这就OK了。你的系统里并不需要Python或者requirements.txt中的任何东西,构建或运行这个映像也不会将这些东西安装到你的系统。看起来并没有安装Python和Flask的环境,但是这个环境你有了。
3.5 构建这个app
准备构建(build)这个app。确保你还在刚创建的目录下,确认一下:
$ ls
Dockerfile app.py requirements.txt
没问题。接下来运行构建命令。这个命令将会创建一个Docker映像,在命令中使用-t来指定一个友好的名字:
docker build -t friendlyhello .
可以通过如下命令列出本地Docker映像:
[root@localhost docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest 4005a12618da 17 minutes ago 195MB
python 2.7-slim 9724e90f1f17 6 days ago 184MB
hello-world latest 05a3bd381fc2 4 weeks ago 1.84kB
也可以使用新的命令docker images ls来列出映像。
3.6 运行这个app
运行这个app,并通过-p将机器的端口4000映射到容器发布的端口80上:
[root@localhost docker]# docker run -p 4000:80 friendlyhello
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
可以看到一个提示,friendlyhello在http://0.0.0.0:80上提供服务。这个信息来自于容器内部,容器并不知道你将容器的端口80映射到4000了,使用正确的URL:http://localhost:4000。
在浏览器里运行这个URL,可以看到如下页面:
192.168.137.100是虚拟机的IP,这是在宿主主机上运行的浏览器。
注意:这里的端口重映射4000:80是为了演示两个端口的区别,一个是Dockerfile中EXPOSE的端口,另一个是docker run –p所发布的端口。后面我们只将主机的端口80映射到容器中的端口80,所以使用http://localhost就可以了。
在终端里使用CTRL+C来结束容器运行。
下面使用detached mode,在后台运行这个app:
[root@localhost docker]# docker run -d -p 4000:80 friendlyhello
59dfe83b4b1264f2d04be89f3e2a9a02e54fcac3b169a45e721dd81d6429cdbd
运行后,可以看到这个app的容器ID,然后就在后台运行了。使用命令docker container ls可以查看容器:
[root@localhost /]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
46f21d48ba94 friendlyhello "python app.py" 42 minutes ago Up42minutes 0.0.0.0:4000->80/tcp relaxed_raman
可以看到,容器ID已经简化了,并且这个ID和http://localhost:4000保持一致。
使用docker container stop来终止容器进程,命令中包括容器ID:
docker container stop 46f21d48ba94
3.7 分享映像
为了展示便携性,我们来上传构建的映像并在其他地方运行。如果你想在产品中部署容器,后面还需要学习怎样把映像推送到registry中。
一个registry就是多个repository(仓库)的集合;一个repository就是多个映像的集合。Registry的一个帐号可以创建许多repository。Docker CLI默认使用Docker的public registry。
注意:这里我们将使用Docker的public registry,因为它是免费的,并且是已经配置好的。也可以选择其他的public registry,当然,你也可以自己搭建个人的registry(使用Docker Trusted Registry)。
3.7.1 使用Docker ID登录
如果你没有Docker帐号,到cloud.docker.com上注册一个。记住用户名和密码。然后在你的本地机器上登录到public registry:
docker login
3.7.2 给映像加上标签
通过标识username/repository:tag,将一个本地映像和registry中的一个映像关联起来。tag是可选的,但是强烈推荐加上tag,因为registry使用这个机制,来给Docker映像提供版本号。提供registry和tag,例如:get-started:part2,将会把这个映像放到get-started repository中,并加上标签part2。
运行命令docker tag image,并提供用户名,repository和tag名称,然后映像就会被上传到相应的目标中。这个命令的语法是:
docker tag image username/repository:tag
例如:
docker tag friendlyhello robin/get-started:part2
运行命令docker image来查看加了标签的映像:
[root@localhost /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest 4005a12618da 25 hours ago 195MB
robin/get-started part2 4005a12618da 25 hours ago 195MB
python 2.7-slim 9724e90f1f17 7 days ago 184MB
hello-world latest 05a3bd381fc2 4 weeks ago 1.84kB
3.7.3 发布映像
使用如下命令,就可以将加了标签的映像上传到repository中:
docker push username/repository:tag
运行命令如下:
[root@localhost /]# docker push robin/get-started:part2
The push refers to a repository [docker.io/robin/get-started]
02bdd180d5f0: Pushed
bf76f22d8e58: Pushed
e82724fbda2b: Pushed
f39f8a7b1485: Mounted from library/python
5e4d4a29edae: Mounted from library/python
f46f014db30a: Mounted from library/python
c01c63c6823d: Mounted from library/python
part2: digest: sha256:5fb9a92175f6918c8baf55ef5248e9e234e1f9cc0ccb683b8d0bf49da4286724 size: 1787
一旦命令运行完成,这个映像就公开可用了。如果你登录到Docker Hub,就可以看到新提交的映像,以及相应的拉取命令。
3.7.4 从远程repository中拉取并运行映像
现在,可以在任何机器上使用如下命令来运行你的app:
docker run -p 4000:80 username/repository:tag
如果本地机器没有这个映像,Docker将会从repository中拉取。
$ docker run -p 4000:80 robin/get-started:part2
Unable to find image robin/get-started:part2' locally
part2: Pulling from robin/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 robin/get-started:part2
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
注意:无论你是在构建或者运行映像时,如果你没有在命令中给出:tag部分,假定的tag是:latest。如果没有标注tag,Docker将会使用映像的最新版本。
无论在哪里执行docker run,它将会拉取你的映像、Python以及requirements.txt中的所有依赖,并运行你的代码。所有这些东西作为一个整体一起传递,并且主机系统除了安装Docker之外,不需要安装任何其他东西。
3.8 总结
下一节,我们通过在service中运行这个容器,来学习如何使我们的程序具有可伸缩性。
3.9 本节命令列表
下面是本节的基本Docker命令,有些命令后面的部分也会用到。
docker build -t friendlyname . # Create image using this directory's Dockerfile docker run -p 4000:80 friendlyname # Run "friendlyname" mapping port 4000 to 80 docker run -d -p 4000:80 friendlyname # 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
docker容器技术基础入门(代码片段)
docker容器技术基础入门容器(Container)传统虚拟化与容器的区别:LinuxNamespacesCGroupsLXCdocker基本概念docker工作方式docker容器编排容器(Container)容器是一种基础工具;泛指任何可以用于容纳其他物品的工具,可以部分或完全封闭... 查看详情
docker容器技术基础入门(代码片段)
docker容器技术基础入门容器(Container)传统虚拟化与容器的区别Linux容器技术LinuxNamespacesCGroupsLXCdocker基本概念docker工作方式docker容器编排容器(Container)容器是一种基础工具;泛指任何可以用于容纳其他物品的工具,可以部分... 查看详情
docker入门3:基础操作
--容器删除dockerrmCONTAIN_ID|CONTAIN_NAME--镜像删除dockerrmiIMAGE_ID|IMAGE_NAME--进入容器dockerexec-it CONTAIN_ID|CONTAIN_NAMETTY示例:dockerexec-it node_server/bin/bash --容器内容考入考出考入:sudodockercphost_ 查看详情
docker入门:容器
...一个运行时程序。要操作一个Docker容器,只需要执行dockercontainer命令。可以通过help查看run运行容器基础使用:docker container run nginxDocker会到本地区查找有没有叫做nginx的镜像(镜像这篇暂不涉及)。如果没有,会在Dock... 查看详情
docker入门
... 1初步了解概念先大致了解几个概念:image镜像、container容器、Repository库、Service服务、Task任务、stack堆叠、swarm群集image镜像是一个只读模板,里面包含了完整的系统环境、应用程序和数据,可以理解为打包好的一个包con... 查看详情
docker-入门
术语1.镜像(image)与容器(container)镜像是指文件系统快照或tar包。容器是指镜像的运行态(时) 2.宿主机管理设置/配置一台物理服务器或虚拟机,以便用于运行Docker容器的过程。 3.编排/编配包括容器调度的过程、集... 查看详情
docker入门到实践——简单操作
...2.基本概念Docker包括三个基本概念:镜像(Image)容器(Container)仓库(Repository)Docker镜像Docker镜像就是一个只读的模板。镜像可以用来创建Docker容器。Docker容器Docker利用容器来运行应用。容器是从镜像创建运行 查看详情
docker入门
1Docker技术介绍DOCKER是一个基于LXC技术之上构建的container容器引擎,通过内核虚拟化技术(namespace及cgroups)来提供容器的资源隔离与安全保障,KVM是通过硬件实现的虚拟化技术,它是通过系统来实现资源隔离与安全保障,占用系... 查看详情
docker入门案例(代码片段)
...pullmysql:8.0.17#查看本机镜像dockerimages#删除docker容器dockerrm[container-idorcontainer-name]#删除docker镜像dockerrmi[container-idorimage-name]#查看docker中的容器-a全部all(无论有没有运行)dockerps-a#新建并运行镜像参数#t伪终端i交互界面p端口映射- 查看详情
docker使用入门
dockerimages 查看本地镜像dockerps-a 查询容器dockerps-l 查询最近使用容器dockerrmCONTAINER_ID 删除容器dockerrmiIMAGE_ID 删除镜像dockercp[OPTIONS]SRC_PATHCONTAINER_ID:DEST_PATH 上传文件dockerstart CONTAIN 查看详情
docker入门指南
...度很慢,自行更换国内docker加速地址。名词解释image:镜像containers:容器docker-machine:docker虚拟主机dockerstore:docker存储云术语Image和ContainerImage可以理解为一个系统镜像,Container是Image在运行时的一个状态。如果拿虚拟机 查看详情
docker学习入门(代码片段)
Docker简介:Docker包括三个基本概念镜像(Image)容器(Container)仓库(Repository)理解了这三个概念,就理解了Docker的整个生命周期。Docker镜像Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等... 查看详情
docker入门(代码片段)
...概念docker镜像(Images):用于创建Docker容器的模板;docker容器(Container):是独立运行的一个或一组应用,即镜像启动后的实例;docker客户端(Client):客户端通过命令行或者其他工具使用Docker;docker主机(Host):一个物理或者虚拟的机器用于执行Dock... 查看详情
docker入门
docker是docker.lnc公司开源的一个基于LXC技术之上构建的container容器引(openstack是基于KVM),源代码托管在GitHub上,基于Go语言并遵从Apache2.0协议开源。 docker是通过内核(linux内核)虚拟化技术(namespaces及cgroups等)(KV... 查看详情
docker入门学习
...向docker引擎的只读模板,包含了文件系统。docker容器:(container)类似于一个轻 查看详情
docker入门(代码片段)
...介Docker是Docker.Inc公司开源的一个基于LXC技术之上构建的Container容器引擎,源代码托管在GitHub上,基于Go语言并遵从Apache2.0协议开源。Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何... 查看详情
docker——入门与简介
...sp;Docker是基于容器的一种开源虚拟平台,容器在英文里叫container,有集装箱的意思,我认为集装箱这一个单词很好的表达了什么是Docker。我们可以把一些衣服,鞋子,电子商品和水果等东西包装到一个集装箱中,通过一些运输手... 查看详情
docker快速入门——docker-compose(代码片段)
...三层,分别是工程(project),服务(service)以及容器(container)。Docker-Compose运行目录 查看详情