1 docker出现的背景
在docker出现之前,一台机器上部署多个应用,得通过虚拟机技术来实现。虚拟机技术有如下缺点:
- 启动慢
- 系统资源耗费大
2 docker能够做的事情
docker是一种新的虚拟化技术,docker相对于虚拟机技术,是更加轻量的虚拟化技术。此外,docker通过image镜像的分发,能够做到更加快速的构建和部署。具体的的应用场景包括:
- 部署应用程序更加节省资源
相比较于虚拟机技术,docker占用的资源较少。 - 部署应用程序更加快速
docker可以做到秒级启动,而虚拟机技术是分钟级启动。 - 保证一致的环境
通过镜像,可以保证除了内核之外,线下和线上一致的运行环境。
正因为docker启动快速和节省资源,因此,在很多场景下应用很广泛。
3 概念
镜像(image):镜像可以认为是运行中的应用程序的一个快照。镜像包含了命令、依赖包、环境变量和配置文件。类似于面向对象编程中的类。
容器(container):容器是镜像的一个实例,具有运行状态。类似于面向对象编程中的对象。
注册服务器(registry):注册服务器类似于远程仓库,用于存储镜像。在本地创建完镜像之后,可以提交到注册服务器供其他人使用。
仓库(repository):仓库用于存放镜像,一般用于存放某个镜像的多个版本。可以认为仓库位于注册服务器上面。
4 docker的具体命令
安装docker以及配置镜像源
在mac上安装docker请参考:Install Docker for Mac
mac上配置镜像源,请参考:docker for mac更换国内镜像源
镜像的CRUD
- 搜索远程注册服务器的镜像
语法:> docker search {image_name}image_name:镜像的名字
示例:
> docker search python NAME DESCRIPTION STARS OFFICIAL AUTOMATED python Python is an interpreted, interactive, objec… 4664 [OK] django Django is a free web application framework, … 896 [OK] pypy PyPy is a fast, compliant alternative implem… 217 [OK]注意:使用
docker {cmd} --help可以查看该命令{cmd}的具体用法。 - 拉取远程仓库的镜像
拉取镜像是指从注册服务器的仓库下载一个镜像。
语法:docker pull [repository]:[tag]repository:表示镜像的仓库名
tag:表示镜像的仓库标签。一个仓库里面可以有多镜像的不同版本,可以通过该标签来区分。比如ubuntu有18.04版本和15.10等多个版本。示例:
> docker pull ubuntu:18.04 -
查看本地的镜像
执行
docker images可以查看本地的所有镜像> docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 18.04 775349758637 9 days ago 64.2MB hello-world latest fce289e99eb9 10 months ago 1.84kBREPOSITORY:表示镜像的仓库名
TAG:表示镜像的仓库标签。一个仓库里面可以有多镜像的不同版本,可以通过该标签来区分。比如ubuntu有18.04版本和15.10等多个版本。 IMAGE ID:表示镜像的id
CREATED:表示镜像的创建时间
SIZE:表示镜像的大小 -
运行镜像
语法:
下面的语法,运行一个镜像。> docker run [-t] [-i] [repository]:[tag] [your_cmd]参数解释:
-t:可省略参数。在新容器内指定一个伪终端或终端。加上该参数,运行完命令之后,就会进入该容器的终端。-i:可省略参数。允许用户对容器内的标准输入 (STDIN) 进行交互。
your_cmd:启动容器之后执行的命令,需要与容器搭配使用。例如这里启动一个ubuntu容器,就可以终端命令/bin/bash。如果启动的是python容器环境,则使用/bin/bash会报错。
repository和tag的解释见前面的介绍。示例:
下面的例子,启动容器之后,执行命令/bin/echo "hello docker"> docker run ubuntu:18.04 /bin/echo "hello docker" hello docker下面的例子,启动容器之后,执行命令
/bin/bash "hello docker"。结果是打开一个终端,系统为ubuntu。> docker run -i -t ubuntu:18.04 /bin/bash -
删除本地的镜像
语法:
> docker rmi {image_name}注释:rmi的英文全称是
removeimage。示例:
> docker rmi python
container的CRUD
-
创建container
语法:
> docker create --name <container_name> <image_name>:<tag>示例:
下面的例子,使用镜像ubuntu:18.04创建一个名为ubuntuContainer的容器> docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 18.04 775349758637 9 days ago 64.2MB hello-world latest fce289e99eb9 10 months ago 1.84kB > docker create --name ubuntuContainer ubuntu:18.04 7ae6cda2f3094b8265c0574a8d46f78e992d2845f4bb9d0857bfa60ec1ccac8a -
查询container
查看所有的容器
> docker ps -a查看活跃的容器
> docker ps -
启动container
下面的示例从镜像创建容器并启动,然后打开一个
/bin/bash终端。> docker run -it ubuntu /bin/bash启动一个已经停止的容器
> docker start <container_id>container_id:容器的id。注意不能只输入容器前缀。
例如下面的例子启动某个停止的容器。
> docker ps -al CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8f688b7ec9b5 ubuntu:18.04 "/bin/echo 'hello do…" 26 minutes ago Exited (0) 2 minutes ago pensive_shannon > docker start 8f688b7ec9b5 8f688b7ec9b5让容器后台运行
加上-d这个参数即可。例如下面的例子会创建一个容器,但不会打开一个终端。> docker run -itd ubuntu:18.04 /bin/bash重启一个已经停止或者正在运行的容器
> docker restart <container_id> -
停止运行中的container
> docker stop <container_id> -
进入容器
当容器正在后台运行时,可以进入通过命令进入该容器,从而进行交互。注意当容器没有正在运行时,进入该容器则会报错。
语法:
> docker exec -it <container_id> <cmd>cmd:进入容器之后执行的命令
示例:
> docker start cbeda6b012eb cbeda6b012eb > docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cbeda6b012eb ubuntu:18.04 "/bin/bash" 4 hours ago Up 5 seconds hungry_perlman > docker exec -it cbeda6b012eb /bin/bash root@cbeda6b012eb:/# -
删除容器
语法:
> docker rm -f <container_id>示例:
> docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cbeda6b012eb ubuntu:18.04 "/bin/bash" 4 hours ago Up 3 minutes hungry_perlman > docker rm -f cbeda6b012eb cbeda6b012eb下面的命令会删除所有处于停止状态的容器
> docker container prune
镜像从创建到提交
当远程仓库里的镜像无法满足我们的需求时,我们可以有如下两种做法:
- 从远程仓库中拉取镜像,进行更改。
- 使用Dockerfile指令来创建新的镜像。
下面看看这两种方式。
-
更新镜像
步骤如下:
- 从仓库拉取镜像
- 为镜像创建一个容器
下面的命令拉取一个镜像,创建一个容器并启动,然后进入该容器> docker run -it ubuntu:18.04 /bin/bash - 对容器进行更新
下面的命令,更新ubuntu上的包管理器apt-get,更新的信息是其的源索引信息。> apt-get update > exit更新容器信息之后,然后使用
exit退出容器。 - 提交容器副本
使用docker commit来提交容器副本。> docker commit -m=<your_message> -a=<author_name> <container_id> <your_image_name>:<tag>示例:
> docker commit -m="docker demo" -a="chris" fc14a9f41d64 chris/ubuntu:v1 sha256:b4912972ed4a490a9ac276c5e4f0250e85175e7438c1dc8b6b391cb971cc093e > docker images REPOSITORY TAG IMAGE ID CREATED SIZE chris/ubuntu v1 b4912972ed4a 11 seconds ago 80.4MB
-
新建镜像
Dockerfile是一个由一堆命令+参数构成的脚本,使用docker build来构建脚本创建镜像。通过Dockerfile可以让脚本自动的完成一些任务。Dockerfile的格式为:
# Comment FROM <base_image> RUN <instruction> <arguments> ...规定如下:
第一行必须为
FROM <base_image>,指定基础镜像下面为Dockerfile的一个示例。
- 新建一个名为Dockerfile的文件
写入内容如下
> cat Dockerfile FROM ubuntu:18.04 MAINTAINER chris "chris@qq.com" RUN mkdir a可以看出,基础镜像为ubuntu:18.04,执行的任务主要是在根目录创建一个名为a的文件夹。
- 构建镜像
> docker build -t chris/ubuntu:v2 . Sending build context to Docker daemon 228.9kB Step 1/3 : FROM ubuntu:18.04 ---> 775349758637 Step 2/3 : MAINTAINER chris "chris@qq.com" ---> Running in fb148b02a6f0 Removing intermediate container fb148b02a6f0 ---> ace7f89e7560 Step 3/3 : RUN mkdir a ---> Running in c51b0d3cf690 Removing intermediate container c51b0d3cf690 ---> 81baf38da9d3 Successfully built 81baf38da9d3 Successfully tagged chris/ubuntu:v2
经过以上两步,就新建了一个镜像。下面验证我们的确创建了。
> docker images REPOSITORY TAG IMAGE ID CREATED SIZE chris/ubuntu v2 81baf38da9d3 4 seconds ago 64.2MB > docker run -it chris/ubuntu:v2 /bin/bash > ls a bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr可以看出,根目录多了文件夹a。
- 新建一个名为Dockerfile的文件
写入内容如下
5 docker的原理
先看看虚拟机的实现原理,然后看docker虚拟化的实现原理。

在上图中,虚拟机主要的部分是Guest OS和Hypervisor虚拟机管理程序。Guest OS是一个模拟的操作系统,具有完整的操作系统内核,就像linux等系统内核一样。Hypervisor虚拟机管理程序模拟了硬件环境,主要是模拟(虚拟化)了CPU、内存和IO设备,虚拟的技术主要是将虚拟机的设备映射到物理的设备上。
- 虚拟化CPU: 为了模拟CPU,需要模拟CPU的寄存器等结构
- 虚拟化内存:虚拟机物理内存 -> 真实的宿主机的物理内存
- 虚拟化IO设备:虚拟机物理内存 -> IO设备。虚拟机会将某个物理内存地址映射到一个IO设备,当虚拟机上的应用程序向该物理内存地址进行IO操作时,就会映射到宿主机的IO设备。
可以看出,虚拟机是很重量级的,需要一个模拟硬件环境的Hypervisor和多个操作系统内核。

上图是docker来实现虚拟化环境的架构图。docker实现虚拟化并没有实际模拟真实的环境,而是通过linux提供的namespaces(命名空间)、Cgroup(control group)和UnionFS(镜像管理文件系统)来实现资源隔离。
- namespaces(命名空间):实现进程、网络和文件系统的隔离
- Cgroup:实现CPU、内存和网络等物理资源的隔离
- UnionFS(镜像管理文件系统):用于实现镜像和容器环境的管理
关于docker原理的部分,具体可以参考Docker 核心技术与实现原理
最后是虚拟机和docker的比较
| 内容 | docker | 虚拟机 |
|---|---|---|
| 启动时间 | 秒级 | 分钟级 |
| 占用资源 | 磁盘占用一般为MB | 磁盘占用一般为GB |
| 性能 | 接近原生 | 弱于原生 |
| 单机可以支持的数量 | 单机可以支持上千个容器 | 一般为几十个 |
6 参考
从 0 开始了解 Docker
Difference between Docker registry and repository
introduction
Docker与虚拟机的区别
Docker 核心技术与实现原理