1 Docker简介
1.1 什么是Docker
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。
Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。
Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。
在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。
下面的图片比较了 Docker 和传统虚拟化方式的不同之处,可见容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层面实现。
1.2 为什么Docker如此受欢迎
Docker 是一种新兴的虚拟化方式,与传统虚拟化相比,它具有许多优势。
启动速度快
首先,Docker 容器的启动可以在秒级实现,这比传统的虚拟机快得多。
高效利用资源
Docker 对系统资源的利用率非常高,一台主机上可以同时运行数千个 Docker 容器。容器除了运行其中的应用外,几乎不消耗额外的系统资源,从而保证了高性能和低开销。传统虚拟机方式需要为每个应用启动一个虚拟机,而 Docker 只需启动相应数量的隔离应用即可。
快速交付和部署
对于开发和运维人员(DevOps)来说,Docker 提供了更快的交付和部署方式。开发者可以使用一个标准的镜像来构建开发容器,开发完成后,运维人员可以直接使用该容器部署代码。Docker 能快速创建容器,快速迭代应用程序,并使整个过程透明易见,方便团队成员理解应用程序的创建和工作方式。
高效虚拟化
Docker 容器运行不需要额外的 hypervisor 支持,它是内核级虚拟化,能够实现更高的性能和效率。
轻松迁移和扩展
Docker 容器几乎可以在任何平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑和服务器。这种兼容性使得应用程序可以在不同平台之间轻松迁移。
简单管理
使用 Docker,只需进行小幅度修改即可替代过去大量的更新工作。所有修改都以增量方式被分发和更新,实现了自动化和高效管理。
对比传统虚拟机
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为 MB | 一般为 GB |
性能 | 接近原生 | 弱于原生 |
系统支持量 | 单机支持上千个容器 | 一般支持几十个虚拟机 |
总的来说,Docker 在启动速度、资源利用率、交付和部署速度、虚拟化效率、迁移和扩展能力以及管理简便性方面,都显著优于传统虚拟机。
2 基本概念
Docker 包括镜像(Image)、容器(Container)、仓库(Repository) 三个基本概念
2.1 镜像
Docker 镜像就是一个只读的模板。
例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了 Apache 或用户需要的其它应用程序。
镜像可以用来创建 Docker 容器。
Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。
2.2 容器
Docker 利用容器来运行应用。
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
2.3 仓库
仓库是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务器(Registry)混为一谈,并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。 国内的公开仓库包括 Docker Pool 等,可以提供大陆用户更稳定快速的访问。
当然,用户也可以在本地网络内创建一个私有仓库。
当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了。
3 Ubuntu 安装 Docker
$ sudo apt-get update
$ sudo apt-get install -y docker.io
$ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker
$ sudo sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker.io
4 镜像
4.1 获取镜像
从仓库获取所需要的镜像,可以使用如下方法,下面的例子将从 Docker Hub 仓库下载一个 Ubuntu 22.04 操作系统的镜像
$ sudo docker pull ubuntu:22.04
下载过程中,会输出获取镜像的每一层信息。
该命令实际上相当于 $ sudo docker pull registry.hub.docker.com/ubuntu:22.04
命令,即从注册服务器 registry.hub.docker.com 中的 ubuntu 仓库来下载标记为 22.04 的镜像。
如果要从指定的服务器地址下载,使用如下命令
$ sudo docker pull your_server_url/ubuntu:22.04
完成后,即可随时使用该镜像了,例如创建一个容器,让其中运行 bash 应用
$ sudo docker run -t -i ubuntu:22.04 /bin/bash
root@fe7fc4bd8fc9:/#
4.2 列出
使用 docker images
显示本地已有的镜像。
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 22.04 74fe38d11401 4 weeks ago 209.6 MB
ubuntu precise 74fe38d11401 4 weeks ago 209.6 MB
ubuntu 14.04 99ec81b80c55 4 weeks ago 266 MB
ubuntu latest 99ec81b80c55 4 weeks ago 266 MB
ubuntu trusty 99ec81b80c55 4 weeks ago 266 MB
在列出信息中,可以看到几个字段信息
- 来自于哪个仓库,比如 ubuntu
- 镜像的标记,比如 14.04
- 它的 ID 号(唯一)
- 创建时间
- 镜像大小
其中镜像的 ID 唯一标识了镜像,注意到 ubuntu:14.04 和 ubuntu:trusty 具有相同的镜像 ID,说明它们实际上是同一镜像。
TAG 信息用来标记来自同一个仓库的不同镜像。例如 ubuntu 仓库中有多个镜像,通过 TAG 信息来区分发行版本,例如 10.04、12.04、12.10、13.04、14.04 等。
4.3 创建镜像
有两种创建镜像的方法,分别是从 Docker Hub 获取已有镜像并更新,第二种是利用本地文件系统创建一个。
修改已有镜像
先使用下载的镜像启动容器
$ sudo docker run -t -i training/sinatra /bin/bash
root@0b2616b0e5a8:/#
在容器中添加 json 和 gem 两个应用
root@0b2616b0e5a8:/# gem install json
当结束后,我们使用 exit 来退出,现在我们的容器已经被我们改变了,使用 docker commit 命令来提交更新后的副本
$ sudo docker commit -m "Added json gem" -a "Docker Newbee" 0b2616b0e5a8 ouruser/sinatra:v2
4f177bd27a9ff0f6dc2a830403925b5360bfe0b93d476f7fc3231110e7f71b1c
其中,-m
来指定提交的说明信息,跟我们使用的版本控制工具一样;-a
可以指定更新的用户信息;之后是用来创建镜像的容器的 ID;最后指定目标镜像的仓库名和 tag 信息。创建成功后会返回这个镜像的 ID 信息
使用 docker images
来查看新创建的镜像
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
training/sinatra latest 5bc342fa0b91 10 hours ago 446.7 MB
ouruser/sinatra v2 3c59e02ddd1a 10 hours ago 446.7 MB
ouruser/sinatra latest 5db5f8471261 10 hours ago 446.7 MB
之后,可以使用新的镜像来启动容器
$ sudo docker run -t -i ouruser/sinatra:v2 /bin/bash
root@78e82f680994:/#
利用 Dockerfile 来创建镜像
使用 docker commit
来扩展一个镜像比较简单,但是不方便在一个团队中分享。我们可以使用 docker build
来创建一个新的镜像。为此,首先需要创建一个 Dockerfile,包含一些如何创建镜像的指令。
新建一个目录和一个 Dockerfile
$ mkdir sinatra
$ cd sinatra
$ touch Dockerfile
Dockerfile 中每一条指令都创建镜像的一层,例如:
# This is a comment
FROM ubuntu:14.04
MAINTAINER Docker Newbee <newbee@docker.com>
RUN apt-get -qq update
RUN apt-get -qqy install ruby ruby-dev
RUN gem install sinatra
Dockerfile 基本的语法是
- 使用#来注释
FROM
指令告诉 Docker 使用哪个镜像作为基础- 接着是维护者的信息
RUN
开头的指令会在创建中运行,比如安装一个软件包,在这里使用apt-get
来安装了一些软件
编写完成 Dockerfile 后可以使用 docker build 来生成镜像。
$ sudo docker build -t="ouruser/sinatra:v2" .
Uploading context 2.56 kB
Uploading context
Step 0 : FROM ubuntu:14.04---> 99ec81b80c55
Step 1 : MAINTAINER Newbee <newbee@docker.com>---> Running in 7c5664a8a0c1---> 2fa8ca4e2a13
Removing intermediate container 7c5664a8a0c1
Step 2 : RUN apt-get -qq update---> Running in b07cc3fb4256---> 50d21070ec0c
Removing intermediate container b07cc3fb4256
Step 3 : RUN apt-get -qqy install ruby ruby-dev---> Running in a5b038dd127e
Selecting previously unselected package libasan0:amd64.
(Reading database ... 11518 files and directories currently installed.)
Preparing to unpack .../libasan0_4.8.2-19ubuntu1_amd64.deb ...
Setting up ruby (1:1.9.3.4) ...
Setting up ruby1.9.1 (1.9.3.484-2ubuntu1) ...
Processing triggers for libc-bin (2.19-0ubuntu6) ...---> 2acb20f17878
Removing intermediate container a5b038dd127e
Step 4 : RUN gem install sinatra---> Running in 5e9d0065c1f7
. . .
Successfully installed rack-protection-1.5.3
Successfully installed sinatra-1.4.5
4 gems installed---> 324104cde6ad
Removing intermediate container 5e9d0065c1f7
Successfully built 324104cde6ad
其中 -t
标记来添加 tag,指定新的镜像的用户信息。 “.” 是 Dockerfile 所在的路径(当前目录),也可以替换为一个具体的 Dockerfile 的路径。
可以看到 build 进程在执行操作。它要做的第一件事情就是上传这个 Dockerfile 内容,因为所有的操作都要依据 Dockerfile 来进行。 然后,Dockfile 中的指令被一条一条的执行。每一步都创建了一个新的容器,在容器中执行指令并提交修改(就跟之前介绍过的 docker commit
一样)。当所有的指令都执行完毕之后,返回了最终的镜像 id。所有的中间步骤所产生的容器都被删除和清理了。
上传镜像
用户可以通过 docker push
命令,把自己创建的镜像上传到仓库中来共享。例如,用户在 Docker Hub 上完成注册后,可以推送自己的镜像到仓库中。
$ sudo docker push ouruser/sinatra
The push refers to a repository [ouruser/sinatra] (len: 1)
Sending image list
Pushing repository ouruser/sinatra (3 tags)
4.4 存出和载入镜像
存出镜像
如果要导出镜像到本地文件,可以使用docker save
命令。
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 14.04 c4ff7513909d 5 weeks ago 225.4 MB
...
$sudo docker save -o ubuntu_14.04.tar ubuntu:14.04
载入镜像
可以使用 docker load
从导出的本地文件中再导入到本地镜像库,例如
$ sudo docker load --input ubuntu_14.04.tar
4.5 移除镜像
如果要移除本地的镜像,可以使用 ·docker rmi· 命令。注意 ·docker rm· 命令是移除容器
$ sudo docker rmi training/sinatra
Untagged: training/sinatra:latest
Deleted: 5bc342fa0b91cabf65246837015197eecfa24b2213ed6a51a8974ae250fedd8d
Deleted: ed0fffdcdae5eb2c3a55549857a8be7fc8bc4241fb19ad714364cbfd7a56b22f
Deleted: 5c58979d73ae448df5af1d8142436d81116187a7633082650549c52c3a2418f0
*注意:在删除镜像之前要先用 docker rm
删掉依赖于这个镜像的所有容器。
5 容器
5.1 启动容器
运行容器的命令是docker run
例如,下面的命令启动一个 bash 终端,允许用户进行交互。
$ sudo docker run -t -i ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#
其中,-t
选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i
则让容器的标准输入保持打开。
当利用 docker run
来创建容器时,Docker 在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
5.2 后台运行容器
更多的时候,需要让 Docker 容器在后台以守护态(Daemonized)形式运行。此时,可以通过添加 -d 参数来实现。
例如下面的命令会在后台运行容器。
$ sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
1e5535038e285177d5214659a068137486f96ee5c2e85a4ac52dc83f2ebe4147
容器启动后会返回一个唯一的 id,也可以通过docker ps
命令来查看容器信息。
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e5535038e28 ubuntu:14.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute insane_babbage
要获取容器的输出信息,可以通过 ·docker logs ·命令。
$ sudo docker logs insane_babbage
hello world
hello world
hello world
. . .
5.3 终止容器
可以使用 docker stop
来终止一个运行中的容器。
此外,当Docker容器中指定的应用终结时,容器也自动终止。 例如对于上一章节中只启动了一个终端的容器,用户通过 exit
命令或Ctrl+d
来退出终端时,所创建的容器立刻终止。
终止状态的容器可以用 docker ps -a
命令看到。例如
sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton
98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike
处于终止状态的容器,可以通过 docker start
命令来重新启动。
此外,docker restart
命令会将一个运行态的容器终止,然后再重新启动它。
5.4 删除容器
可以使用docker rm
来删除一个处于终止状态的容器。 例如
$sudo docker rm trusting_newton
trusting_newton
如果要删除一个运行中的容器,可以添加 -f·
参数。Docker 会发送 SIGKILL 信号给容器。
6 仓库
Docker Hub
目前 Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了超过 15,000 的镜像。大部分需求,都可以通过在 Docker Hub 中直接下载镜像来实现。
基本操作
用户无需登录即可通过 docker search
命令来查找官方仓库中的镜像,并利用 docker pull
命令来将它下载到本地。
例如以 centos 为关键词进行搜索:
$ sudo docker search centos
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
centos The official build of CentOS. 465 [OK]
tianon/centos CentOS 5 and 6, created using rinse instea... 28
blalor/centos Bare-bones base CentOS 6.5 image 6 [OK]
saltstack/centos-6-minimal 6 [OK]
tutum/centos-6.4 DEPRECATED. Use tutum/centos:6.4 instead. ... 5 [OK]
可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、星级(表示该镜像的受欢迎程度)、是否官方创建、是否自动创建。 官方的镜像说明是官方项目组创建和维护的,automated 资源允许用户验证镜像的来源和内容。
根据是否是官方提供,可将镜像资源分为两类。 一种是类似 centos 这样的基础镜像,被称为基础或根镜像。这些基础镜像是由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。 还有一种类型,比如 tianon/centos 镜像,它是由 Docker 的用户创建并维护的,往往带有用户名称前缀。可以通过前缀 user_name/ 来指定使用某个用户提供的镜像,比如 tianon 用户。
另外,在查找的时候通过-s N
参数可以指定仅显示评价为 N 星以上的镜像。
下载官方 centos 镜像到本地。
$ sudo docker pull centos
Pulling repository centos
0b443ba03958: Download complete
539c0211cd76: Download complete
511136ea3c5a: Download complete
7064731afe90: Download complete