部分导航:

p-guide.png

概念

1 初识Docker

Docker是基于Go语言实现的云开源项目,诞生于2013年初。

1.1 为什么要使用Docker

Docker的主要目标是“Build, Ship and Run Any App, Anywhere.”,即通过对应用组件的封装(Packaging)、分发(Distribution)、部署(Deployment)、运行(Runtime)等生命周期的管理,达到应用组件级别的“一次封装,到处运行”。这里的应用组件,既可以是一个Web应用,也可以是一套数据库服务,甚至是一个操作系统或编译器。

Linux容器技术

Docker引擎的基础是Linux容器(Linux Containers, LXC)技术。

IBM Developer Works给出了关于容器技术的描述:容器有效地将由单个操作系统管理的资源划分到鼓励的组中,以便更好地在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,这样既不需要指令级模拟,也不需要即时编译。容器可以在核心CPU本地运行指令,而不需要任何专门的解释机制。此外,也避免了准虚拟化(paravirtualization)和系统调用替换中的复杂性。

从Linux容器到Docker

Docker进一步优化了LXC容器的使用体验,它提供了各种容器管理工具(如分发、版本、移植等)让用户无需关注底层的操作,可以简单明了地管理和使用容器。用户操作Docker就像操作一个轻量级的虚拟机一样简单。

可以简单地将Docker容器理解为一种沙盒(Sandbox)。每个容器内运行一个应用,不同的容器相互隔离,容器之间也可以建立通信机制。容器的创建和停止都十分快速,容器自身对资源的需求也十分有限,远远低于虚拟机。

1.2 为什么要使用Docker

Docker容器虚拟化的好处

现在开发者需要能方便地创建运行在云平台上的应用,也就是说应用必须能够脱离底层机器,而且同时必须是“任何时间任何地点”可获取的,因此开发者人需要一种创建分布式应用程序的方式->Docker。

Docker在开发和运维中的优势

  • 更快速的交付和部署。
  • 更高效的资源利用。它是内核级的虚拟化,可以实现更高的性能,同时对资源的额外需求很低
  • 更轻松的迁移和拓展。
  • 更简单的更新管理。使用Dockerfile。

Docker与虚拟机比较

  • Docker容器更快
  • Docker容器对系统资源需求很少
  • Docker通过类似Git的操作来方便用户获取、分发和更新应用镜像,指令简明,学习成本较低。
  • Docker通过Dockerfile配置文件来支持灵活的自动化创建和部署机制,提高工作效率
特性 容器 虚拟机
启动速度 秒级 分钟级
硬盘使用 一般为MB 一般为GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个
隔离性 安全隔离 完全隔离

1.3 虚拟化与Docker

虚拟化技术是一个通用的概念,在不同领域有不同的理解。在计算领域,一般指的是计算虚拟化(Computing Virtualization),或通常说的服务器虚拟化。

在计算机技术中,虚拟化(Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以用比原生的组态更好的方式来应用这些资源。 ——wiki

虚拟化的核心是对资源进行抽象。从大类上分,虚拟化技术可分为基于硬件的虚拟化和基于软件的虚拟化。

基于软件的虚拟化从对象所在的层次,又可以分为应用虚拟化和平台虚拟化。

平台虚拟化可以细分为如下几个子类:

  • 完全虚拟化。虚拟机模拟完整的底层硬件环境和特权指令的执行过程,客户操作系统无需进行修改。如VMware Workstation,VirtualBox、QEMU等。
  • 硬件辅助虚拟化。利用硬件(主要是CPU)辅助支持处理敏感指令来实现完全虚拟化的功能,客户操作系统无需修改,如VMware Workstation、Xen、KVM。
  • 部分虚拟化。只针对部分硬件资源进行虚拟化,客户操作系统需要进行修改。
  • 超虚拟化(Paravirtualization)。部分硬件接口以软件的形式提供给客户机操作系统,客户操作系统需要进行修改,如早期的Xen。
  • 操作系统级虚拟化。内核通过创建多个虚拟的操作系统实例(内核和库)来隔离不同的进程。Docker及其他容器相关技术在这个范畴。

1-1

2 Docker的核心概念和安装

2.1 核心概念

Docker的三大核心概念:

  • 镜像(Image)
  • 容器(Container)
  • 仓库(Repository)

Docker镜像

Docker镜像类似于虚拟机镜像。可以将它理解为一个面向Docker引擎的只读模板,包含了文件系统。

Docker容器

Docker容器类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用。
容器是从镜像创建的应用运行实例,可以将其启动、开始、停止、删除,而这些容器都是相互隔离、互不可见的。

可以把容器看做一个简易版的Linux系统环境(包括root用户权限、进程空间、用户空间和网络空间等),以及运行在其中的应用程序打包而成的应用盒子。

镜像自身是子都的。容器从镜像启动的时候,Docker会在镜像的最上层创建一个可写层,镜像本身将保持不变。

Docker仓库

Docker仓库类似于代码仓库,是Docker集中存放镜像文件的场所。
注册服务器是存放仓库的地方,其上往往存放着多个仓库,每个仓库集中存放某一类镜像,往往包括多个镜像文件,通过不同的标签来进行区分。根据所存储的镜像公开分享与否,Docker仓库可以分为公开仓库(Public)和私有仓库(Private)两种形式。

目前,最大的公开仓库是Docker Hub,存放了数量庞大的镜像提供用户下载。国内的公开仓库包括Docker Pool等,可以提供稳定的国内访问。当然如果用户徐希望公开分享自己的镜像文件,Docker也支持用户再本地网络内创建一个只能自己访问的私有仓库。

2.2 安装Docker

Docker在Linux平台上是原生支持,体验最好。

具体安装在此就不介绍了。

2.3 Docker总体架构

2-1.jpg

其中:

  • distribution 负责与docker registry交互,上传洗澡镜像以及v2 registry 有关的源数据
  • registry负责docker registry有关的身份认证、镜像查找、镜像验证以及管理registry mirror等交互操作
  • image 负责与镜像源数据有关的存储、查找,镜像层的索引、查找以及镜像tar包有关的导入、导出操作
  • reference负责存储本地所有镜像的repository和tag名,并维护与镜像id之间的映射关系
  • layer模块负责与镜像层和容器层源数据有关的增删改查,并负责将镜像层的增删改查映射到实际存储镜像层文件的graphdriver模块
  • graghdriver是所有与容器镜像相关操作的执行者

3 镜像

Docker运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker会尝试先从默认镜像仓库下载(默认使用Docker Hub公共注册服务器中的仓库),用户也可以通过配置,使用自定义的镜像仓库。

3.1 获取镜像

可以使用docker pull命令从网络上下周镜像。格式

1
docker pull NAME[:TAG]

如果不显式地指定TAG,则默认会选择latest标签(最新版本)。


1
sudo docker pull ubuntu

其中下载过程中可以看出,镜像文件一般由若干层组成,下载过程中会获取并输出镜像的各层信息。层(Layer)其实是AUFS(Advanced Union File System, 一种联合文件系统)中的重要概念,是实现增量保存与更新的基础。

下载镜像到本地后,即可随时使用该镜像了,例如利用该镜像创建一个容器,在其中运行bash应用。

1
sudo docker run -t -i ubuntu /bin/bash

3.2 查看镜像信息

使用docker images命令可以列出本地主机上已有的镜像。

1
sudo docker images

在列出信息中,可以看到几个字段信息:

  • 来自于那个仓库,比如Ubuntu仓库
  • 镜像的标签信息,比如14.04
  • 镜像的ID号
  • 创建时间
  • 镜像大小

TAG信息用于标记来自同一个仓库的不同镜像。为了方便子啊后续工作中使用这个镜像,还可以使用docker tag命令为本地镜像添加新的标签,如

1
sudo docker tag dl.dockerpool.com:5000/ubuntu:latest ubuntu:latest

使用docker inspect命令可以获取该镜像的详细信息,它返回的是一个JSON格式的消息,如果我们只要其中一项内容时,可以使用-f参数来指定。

3.3 搜寻镜像

使用docker search命令可以搜索远端仓库中共享的镜像,默认搜索Docker Hub官方仓库中的镜像。使用方法为

1
docker search TERM

支持的参数包括:

  • --automated=false:仅显示自动创建的镜像
  • --no-trunc=false:输出信息不截断显示
  • -s--stars=0:指定仅显示评价为指定星级以上的镜像。

3.4 删除镜像

使用docker rmi命令可以删除镜像,格式为

1
docker rmi IMAGE[IMAGE...]

其中IMAGE可以为标签或ID。

例如要删除dl.dockerpool.com:5000/ubuntu:latest镜像,可以使用如下命令:

1
sudo docker rmi dl.dockerpool.com:5000/ubuntu

当同一个镜像拥有多个标签的时候,docker rmi命令只是删除了该镜像多个标签中的指定标签而已,并不影响镜像文件。但当镜像只剩下一个标签的时候就要小心了,此时再使用docker rmi命令会彻底删除该镜像。

使用镜像ID删除镜像

当使用docker rmi命令后面跟上镜像的ID(或ID部分前缀)时,会先尝试删除所有指向该镜像的标签,然后删除该镜像文件本身。

注意,当有该镜像创建的容器存在时,镜像文件默认是无法被删除的。如果要强行删除镜像,可以使用-f参数,但并不推荐。推荐先删除依赖该镜像的所有容器,再来删除镜像

3.5 创建镜像

创建镜像的方法有三种:基于已有镜像的容器创建、基于本地模板导入、基于Dockerfile创建。

基于已有镜像的容器创建

该方法主要是使用docker commit命令,命令格式

1
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

主要选项为:

  • -a--author="":作者信息
  • -m--message="":提交信息
  • -p--pause=true:提交时暂停容器运行

如下创建一个新镜像:
首先启动一个镜像,并在其中进行修改操作

1
2
3
sudo docker run -ti ubuntu:14.04 /bin/bash
touch test # 创建一个test文件
exit # 退出

提交为一个新的镜像,并用ID或名称来指定容器:

1
sudo docker commit -m "added a new file" -a "Docker newbee" a925cb3923gd test

顺利的话会返回新创建的镜像的ID信息

基于本地模板导入

比如使用OpenVZ提供的模板来创建(下载地址:http://openva.org/Download/templates/precreated)。

比如现已下载了一个Ubuntu14.04的模板压缩包后,可以使用以下命令导入:

1
sudo cat ubuntu-14.040x86_64-minimal.tar.gz |docker import -ubuntu:14.04

3.6 存出和载入镜像

可以使用docker save和docker load命令来存出和载入镜像。

存出镜像

如果要存出镜像到本地文件,可以使用docker save命令


1
sudo docker save -o ubuntu_14.04.tar ubuntu:14.04

载入镜像

可以使用docker load从存出的本地文件中再导入到本地镜像库,如

1
sudo docker load --input ubuntu_14.04.tar


1
sudo docker load < ubuntu_14.04.tar

3.7 上传镜像

可以使用docker push命令上传镜像到仓库,默认上传到DockerHub官方仓库,格式

1
docker push NAME[:TAG]

用户在DockerHub网站注册后,即可上传自制的镜像。如

1
2
sudo docker tag test:latest user/test:latest
sudo docker push user/test:latest

4 容器

容器是镜像的一个运行实例,所不同的是,它带有额外的可写文件层。

4.1 创建容器

新建容器

可以使用docker create命令新建一个容器,如

1
sudo docker create -it ubuntu:latest

查看

1
sudo docker ps -a

使用docker create命令新建的容器处于停止状态,可以使用docker start命令来启动它。

新建并启动容器

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。所需要的命令主要为docker run,等价于先执行docker create命令,再执行docker start命令。

当利用docker run来创建并启动容器时,Docker在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载。
  • 利用镜像创建并启动一个容器。
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层。
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口倒容器中去。
  • 从地址池配置一个IP地址给容器。
  • 执行用户指定的应用程序。
  • 执行完毕后容器被终止。

下面的命令则启动一个bash终端,允许用户进行交互:

1
sudo docker run -t -i ubuntu:14.04 /bin/bash

其中,-t选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,-i则让容器的标准输入保持打开。

在交互模式下,用户可以通过所创建的终端来输入命令,例如:

1
2
pwd
ls

用户可以按Ctrl+d或输入exit命令来退出容器

对于所创建的bash容器,当使用exit命令退出之后,该容器就自动处于终止状态了。这是因为对于Docker容器来说,当运行的应用退出后,容器也就没有继续运行的必要了。

守护态运行

更多的时候,需要让Docker容器在后台以守护态(Daemonized)形式运行。用户可以通过添加-d参数来实现。

如:

1
sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello; sleep 1; done"

容器启动后会返回一个唯一的ID,也可以通过docker ps命令来查看容器信息,要获取容器的输出信息,可以通过docker logs命令

4.2 终止容器

可以使用docker stop来终止一个运行中的容器,命令的格式为:

1
docker stop [-t|--time[=10]]

它会首先向容器发送SIGTERM信号,等待一段时间后(默认为10s),再发送SIGKILL信号终止容器。

此外,当Docker容器中指定的应用终结时,容器也自动终止。例如对于上一节中只启动了一个终端的容器,用户通过exit命令或Ctrl+d来退出终端时,所创建的容器立刻终止。

此外,可以使用docker stop来终止一个运行中的容器:

1
sudo docker stop ce5

docker kill命令会直接发送SIGKILL信号来强行终止容器。

可以使用docker ps -a -q命令看到处于终止状态的容器的ID信息。处于终止状态的容器,可以通过docker start命令来重新启动。

此外,docker restart命令会将一个运行态的容器终止,然后再重新启动它。

4.3 进入容器

attach命令

4-1.jpg

使用attach命令有时候并不方便,当多个窗口同时attach到同一个容器的时候,所有窗口都会同步显示。当某个窗口因命令阻塞时,其他窗口也无法执行操作了。

exec命令(v1.3起)

如进入到刚创建的容器中并启动一个bash:

1
sudo docker exec -ti 243c32535da7 /bin/bash

nsenter工具

4.4 删除容器

可以使用docker rm命令删除处于终止状态的容器,命令格式为:

1
docker rm [OPTIONS] CONTAINER [CONTAINER...]

支持的选项:

  • -f/--force=false:强行终止并删除一个运行中的容器。
  • -l/--link=false:删除容器的连接,但保留容器。
  • -v/--volumes=false:删除容器挂载的数据券。

4.5 导入和导出容器

导出容器

导出容器是指导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态。可以使用docker export命令,命令格式为:

1
docker export CONTAINER

如:

1
sudo docker export ce5 >test_for_run.tar

导入容器

导出的文件又可以使用docker import 命令导入,成为镜像。如

1
2
cat test_for_run.tar | sudo docker import - test/ubuntu:v1.0
sudo docker images

import和load命令很像,都可以用来导入一个镜像文件到本地。
只不过load命令来导入镜像存储文件到本地的镜像库,import命令来导入一个容器快照到本地镜像库。区别在与容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态)。而镜像存储文件将保存完整记录,体积也要打。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

5 仓库

仓库(Repository)是集中存放镜像的地方。仓库又分为公共仓库和私有仓库。

5.1 Docker Hub

Docker官方维护的一个公共仓库https://hub.docker.com

登录

可以通过执行docker login命令来输入用户名、密码和邮箱来完成注册和登录。注册成功后本地用户目录的.dockercfg中将保存用户的认证信息。

基本操作

  • docker search:查找官方仓库中的镜像(无需登录)。
  • docker pull:下载镜像到本地。
  • docker push:将本地镜像推送到Docker Hub。

5.2 Docker Pool

技术社区

5.3 创建和使用私有仓库

使用registry镜像创建私有仓库

安装Docker后,可以通过官方提供的registry镜像来简单搭建一套本地私有仓库环境:

1
sudo docker run -d -p 5000:5000 registry

这将自动下载并启动一个registry容器,创建本地的私有仓库服务。

默认情况下,会将仓库创建在容器的/tmp/registry目录下,可以通过-v参数来将镜像文件存放在本地的指定路径上。

如下面的例子将上传的镜像放到/opt/data/registry目录:

1
sudo docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry

此时,在本地将启动一个私有仓库服务,监听端口为5000。

管理私有仓库镜像

5-1.jpg

6 数据管理

用户在使用Docker的过程中,往往需要能查看容器内应用产生的数据,或者需要把容器内的数据进行备份,甚至多个容器之间进行数据的共享,这必然涉及容器的数据管理操作。

容器中管理数据主要有两种方式:

  • 数据卷(Data Volumes)
  • 数据卷容器(Data Volume Dontainers)

6.1 数据卷

数据卷是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用。
  • 对数据卷的修改会立马生效。
  • 对数据卷的更新,不会影响镜像。
  • 卷会一直存在,直到没有容器使用。

数据卷的使用,类似于Linux下对目录或文件进行mount操作。

在容器内创建一个数据卷

在使用docker run命令的时候,使用-v标记可以在容器内创建一个数据卷。多次使用-v标记可以创建多个数据卷。

如,使用training/webapp镜像创建一个Web容器,并创建一个数据卷挂载到容器的/webapp目录:

1
sudo docker run -d -P --name web -v /webapp training/webapp python app.py

-P是允许外部访问容器需要暴露的端口。

挂载一个主机目录作为数据卷

使用-v标记也可以指定挂载一个本地的已有目录到容器中去作为数据卷:

1
sudo docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

上面的命令加载主机的/src/webapp目录到容器的/opt/webapp目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序或数据到本地目录中,然后在容器内运行和使用。另外,本地目录的路径必须是绝对路径,如果目录不存在,Docker会自动创建。

Docker挂载数据卷的默认权限是读写(rw),用户也可以通过,ro指定为只读:

1
2
sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro
training/webapp python app.py

加了:ro之后,容器内挂载的数据卷的数据就无法修改了。

挂载一个本地主机文件作为数据卷

-v标记也可以从主机挂载耽搁文件到容器中作为数据卷:

1
sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

这样就可以记录在容器输入过的命令历史了。

6.2 数据卷容器

如果用户需要再容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器其实是一个普通的容器,专门用它提供数据卷供其他容器挂载使用。方法如下。

首先创建一个数据卷容器dbdata,并在其中创建一个数据卷挂载到/dbdata。

1
sudo docker run -it -v /dbdata --name dbdata ubuntu

然后可以子啊其他容器中使用--volumes-from来挂载dbdata容器中的数据卷,例如创建db1和db2两个容器,并从dbdata容器挂载数据卷:

1
2
sudo docker run -it --volumes-form dbdata --name db1 ubuntu
sudo docker run -it --volumes-form dbdata --name db2 ubuntu

此时,容器db1和db2都挂载同一个数据卷到相同的/dbdata目录。三个容器任何一方在该目录下的写入,其他容器都可以看到。

例如,在dbdata容器中创建一个test文件:

1
2
3
cd /dbdata
touch test
ls

可以多次使用--volumes-form参数来从多个容器挂载多个数据卷。还可以从其他已经挂载了容器卷的容器来挂载数据卷:

1
sudo docker run -d --name db3 --volumes-form db1 training/postgres

使用--volumes-form参数所挂载数据卷的容器本身并不需要保持在运行状态。

如果删除了了挂载的容器(包括dbdata、db1和db2),数据卷并不会被自动删除。如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时显式使用docker rm -v命令来指定同时删除关联的容器。

使用数据卷容器可以让用户在容器之间自由地升级和移动数据卷。

6.3 利用数据卷容器迁移数据

可以利用数据卷容器对其中的数据卷进行备份、恢复,以实现数据的迁移。

备份

如:

1
sudo docker run --volumes-form dbdata -v $(pwd):/backup --name worker ubuntu tar cvf /backup/backup.tar /dbdata

这个命令首先利用Ubuntu镜像创建了一个容器worker,使用--volumes-from dbdata参数来让worker容器挂载dbdata容器的数据卷(即dbdata数据卷);使用-v $(pwd):/backup参数来挂载本地的当前目录到worker容器的/backup目录。worker容器启动后,使用了tar cvf /backup/backup.tar /dbdata命令来将/dbdata下内容备份为容器内的/backup/backup.tar,即宿主主机当前目录下的backup.tar。

恢复

首先创建一个带有数据卷的容器dbdata2:

1
sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/sh

然后创建另一个新的容器,挂载dbdata2的容器,并使用untar解压备份文件到所挂载的容器卷中即可:

1
sudo docker run --volumes-form dbdata2 -v $(pwd):/backup busybos tar xvf /backup/backup.tar

7 网络基础配置

Docker目前提供了映射容器端口到宿主主机和容器互联机制来为容器提供网络服务。

7.1 端口映射实现访问容器

从外部访问容器应用

在启动容器的时候,如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。

当容器中运行一些网络应用,要让外部访问这些应用时,可以通过-P-p参数来指定端口映射。当使用-P标记时,Docker会随机映射一个49000~49900的端口至容器内部开放的网络端口

如:

1
sudo docker run -d -P training/webapp python app.py

此时,可以使用docker ps看到,本地主机的49155被映射到了容器的5000端口。访问宿主主机的49155端口即可访问容器内Web应用提供的界面。

同样,可以通过docker logs命令来查看应用的信息:

1
sudo docker logs -f nostalgic_morse

-p则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。

映射所有接口地址

使用hostPort:containerPort格式将本地的5000端口映射到容器的5000端口,可以执行如下命令:

1
sudo docker run -d -p 5000:5000 training/webapp python app.python

此时默认会绑定本地所有接口上的所有地址。多次使用-p标记可以绑定多个端口。如

1
sudo docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.python

映射到指定地址的指定端口

可以使用ip:hostPort:containerPort格式指定映射使用搞一个特定地址,比如localhost地址127.0.0.1:

1
sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py

映射到指定地址的任意端口

使用ip::containerPort绑定localhost的任意端口到容器的5000端口,本地主机会自动分配一个端口:

1
sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.python

还可以使用udp标记来指定udp端口:

1
sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.python

查看映射端口配置

使用docker port来查看当前映射的端口配置,也可以查看到绑定的地址:

1
sudo docker port nosealgic_morse 5000

容器有自己的内部网络和IP地址(使用docker inspect+容器ID可以获取所有的变量值)

7.2 容器互联实现容器间通信

容器的连接(linking)系统是除了端口映射外另一种可以与容器中应用进行交互的方式。它会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。

自定义容器命令

使用--name标记可以为容器自定义命名:

1
sudo docker run -d -P --name web training/webapp python app.py

容器的名称是唯一的。如果已经命名了一个叫web的容器,当你要再次使用web这个名称的时候,需要先用docker rm来删除之前创建的同名容器。

在执行docker run的时候如果添加--rm标记,则容器在终止后会立刻删除。(--rm-d不能同时使用)

容器互联

使用--link参数可以让容器之间安全的进行交互。

先创建一个新的数据库容器:

1
sudo docker run -d --name db training/postgres

删除之前创建的web容器

1
sudo docker rm -f web

然后创建一个新的web容器,并将它连接到db容器:

1
sudo docker run -d -P --name web --link db:db training/webapp python app.py

此时,db容器和web容器简历互联关系。

--link参数的格式为--link name:alias,其中name是要链接的容器的名称,alias是这个链接的别名。

Docker在两个互联的容器之间创建了一个安全隧道,并且不用映射它们的端口到宿主主机上。在启动db容器的时候并没有使用-p-P标记,从而避免了保留数据库端口到外部网络上。

Docker通过两种方式为容器公开连接信息:

  • 环境变量
  • 更新/etc/hosts/文件

8 使用Dockerfile创建镜像

Dockerfile是一个文本格式的配置文件,用户可以使用Dockerfile快速创建自定义的镜像。

8.1 基本结构

Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。

一般而言,Dockerfile分为4部分:基础镜像信息、维护信息、镜像操作指令和容器启动时执行指令。如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# dockerfile

# 第一行必须制定基于的基础镜像
FROM ubuntu

# 维护者信息
MAINTAINER docker_user docker_user@email.com

# 镜像的操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# 容器启动时执行命令
CMD /usr/sbin/nginx

8.2 指令

指令的一般格式为INSTRUCTION arguments,指令包括FROM、MAINTAINER、RUN等。

FROM

格式为FROM <image>FROM<image>:<tag>

第一条指令必须为FROM指令。并且,如果在同一个Dockerfiel中创建多个镜像时,可以使用多个FROM指令(每个镜像一次)。

MAINTAINER

格式为MAINTAINER <name>,指定维护者信息

RUN

格式为RUN <command>RUN ["executable", "param1", "param2"]
前者将在shell终端中运行命令,即/bin/sh -c;后者则使用exec执行。指定使用其他终端可以通过第二种方式实现,例如RUM ["/bin/bash", "-c", "echo hello"]

每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用\来换行。

CMD

支持三种格式:

  • CMD ["executable", "param1", "param2"]使用exec执行,推荐方式。
  • CMD command param1 param2在/bin/sh中执行,提供给需要交互的应用。
  • CMD ["param1", "param2"]提供给ENTRYPOINT的默认参数。

指定启动容器时执行的命令,每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行。

如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。

EXPOSE

格式为EXPOSE <port> [<port>...]

告诉DOcker服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过-P,Docker主机会自动分配一个端口转发到指定的端口;使用-p,则可以具体指定那个本地端口映射过来。

ENV

格式为ENV <key> <value>。指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持。

ADD

格式为ADD <src> <dest>。该命令将复制指定的到容器中的。其中可以是DOckerfile所在目录的一个相对路径(文件或目录);也可以是一个URL;还可以是一个tar文件(自动解压为目录)。

COPY

格式为COPY <src> <dest>。复制本地主机的(为Dockerfile所在目录的相对路径,文件或目录)为容器中的。目标路径不存在时,会自动构建。

ENTRYPOINT

有两种格式:

  • ENTRYPOINT ["executable", "param1", "param2"]
  • ENTRYPOINT command param1 param2

配置容器启动后执行的命令,并且不可被docker run提供的参数覆盖。
每个Dockerfile中只能有一个ENTRYPOINT,当指定多个ENTRYPOINT时,只有最后一个生效。

VOLUME

格式为VOLUME ["/data"]。创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

USER

格式为USER daemon。指定运行容器时的用户名或UID,后续的RUN也会使用指定用户。

当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户。例如RUN groupadd -r postgres && useradd -r -g postgres postgres。要临时获取管理员权限可以使用gosu,而不推荐sudo。

WORKDIR

格式为WORKDIR /path/to/workdir。为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。
可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。如

1
2
3
4
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为/a/b/c。

ONBUILD

格式为ONBUILD [INSTRUCTION]。配置当所创建的镜像作为其他新创建镜像的基础镜像时,所执行的操作指令。

例如,Dockerfile使用如下的内容创建了镜像image-A。

1
2
3
4
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于image-A创建新的镜像时,新的Dockerfile中使用FROM image-A指定基础镜像时,会自动执行ONBUILD指令内容,等价于在后面添加了两条指令。

8.3 创建镜像

编写完Dockerfile之后,可以通过docker build命令来创建镜像。

格式为:

1
docker build [选项] 路径

该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下所有内容发送给Docker服务端,由服务端来创建镜像。因此一般建议防止Dockerfile的目录为空目录。

另外,可以通过.dockerugnore文件(每一行添加一条匹配模式)来让Docker忽略路径下的目录和文件。

要指定镜像的标签信息,可以通过-t选项。


1
sudo docker build -t build_repo/first_image /tmp/docker_builder

指定Dockerfile所在路径为/tmp/docker_builder/,并且希望生成镜像标签为build_repo/first_image。