Docker是什么
Docker 是一个开源的应用容器引擎,属于Linux容器的一种封装,提供简单易用的容器使用接口,是目前最流行的 Linux 容器解决方案。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,获得高度的灵活性,从而实现对容器的高效创建、部署及复制。
Docker的原理
Docker 技术使用Linux内核和内核功能(例如 Cgroups 和 namespaces)来分隔进程,以便各进程相互独立运行。这种独立性正是采用容器的目的所在;它可以独立运行多种进程、多个应用,更加充分地发挥基础设施的作用,同时保持各个独立系统的安全性。
容器工具(包括 Docker)可提供基于镜像的部署模式。这使得它能够轻松跨多种环境,与其依赖程序共享应用或服务组。Docker 还可在这一容器环境中自动部署应用(或者合并多种流程,以构建单个应用)。
此外,由于这些工具基于 Linux 容器构建,使得 Docker 既易于使用,又别具一格。它可为用户提供前所未有的高度应用程访问权限、快速部署以及版本控制和分发能力。
Docker的优势
在理解Docker的优势之前,首先我们需要先理解两个概念:容器和虚拟机。
-
虚拟机(如VMware、VisualBox)需要模拟整台机器包括硬件,每台虚拟机包含包括应用,必要的二进制和库,以及一个完整的用户操作系统,虚拟机一旦被开启,预分配给它的资源将全部被占用。
-
容器技术是和我们的宿主机共享硬件资源及操作系统,实现资源的动态分配。容器包含应用和其所有的依赖包,但是与其他容器共享内核。容器在宿主机操作系统中,在用户空间以分离的进程运行。本质上,容器其实是一种特殊的进程。
相比于传统的虚拟机,Docker的优势很明显,它启动时间很快,秒级,而且对资源的利用率很高(一台主机可以同时运行几千个Docker容器)。它占的空间很小,虚拟机一般要几GB到几十GB,而容器只需要MB级甚至KB级。
特性 | 容器 | 虚拟机 |
---|---|---|
隔离级别 | 进程级 | 操作系统级 |
隔离策略 | CGroups | Hypervisor |
系统资源 | 0-5% | 5-15% |
启动时间 | 秒级 | 分钟级 |
镜像存储 | KB-MB | GB-TB |
集群规模 | 上千(单机) | 上百(单机) |
性能 | 接近原生 | 弱于 |
高可用策略 | 弹性、负载、动态 | 备份、容灾、迁移 |
Docker的架构
Docker 包括三个基本概念:
-
镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
-
容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
-
仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
Docker 容器通过 Docker 镜像来创建。
概念 | 说明 |
Docker 镜像(Images) | Docker 镜像是用于创建 Docker 容器的模板,比如 Ubuntu 系统。 |
Docker 容器(Container) | 容器是独立运行的一个或一组应用,是镜像运行时的实体。 |
Docker 客户端(Client) | Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。 |
Docker 主机(Host) | 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。 |
Docker Registry | Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。 一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。 通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。 |
Docker Machine | Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。 |
Docker的安装
Docker 是一个开源的商业产品,有两个版本:社区版(Community Edition,缩写为 CE)和企业版(Enterprise Edition,缩写为 EE)。企业版包含了一些收费服务,个人开发者一般用不到。下面的介绍都针对社区版。
Docker CE 的安装请参考官方文档。
安装完成后,运行下面的命令,验证是否安装成功。
$ docker version
# 或者
$ docker info
Docker镜像加速
国内从 DockerHub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
-
科大镜像:https://docker.mirrors.ustc.edu.cn/
-
网易:https://hub-mirror.c.163.com/
-
阿里云:https://<你的ID>.mirror.aliyuncs.com
-
七牛云加速器:https://reg-mirror.qiniu.com
当配置某一个加速器地址之后,若发现拉取不到镜像,请切换到另一个加速器地址。国内各大云服务商均提供了 Docker 镜像加速服务,建议根据运行 Docker 的云平台选择对应的镜像加速服务。
阿里云镜像获取地址:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors,登陆后,左侧菜单选中镜像加速器就可以看到你的专属地址了:
我们可以多添加几个国内的镜像,如果有不能使用的,会切换到可以使用的镜像来拉取。
Ubuntu14.04、Debian7
对于使用 upstart 的系统而言,编辑 /etc/default/docker 文件,在其中的 DOCKER_OPTS 中配置加速器地址:
DOCKER_OPTS="--registry-mirror=https://registry.docker-cn.com"
重新启动服务:
$ sudo service docker restart
Ubuntu16.04+、Debian8+、CentOS7
对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件):
{"registry-mirrors":["https://reg-mirror.qiniu.com/"]}
之后重新启动服务:
$ sudo systemctl daemon-reload $ sudo systemctl restart docker
Windows 10
对于使用 Windows 10 的系统,在系统右下角托盘 Docker 图标内右键菜单选择 Settings,打开配置窗口后左侧导航菜单选择 Daemon。在 Registrymirrors 一栏中填写加速器地址 https://docker.mirrors.ustc.edu.cn/ ,之后点击 Apply 保存后 Docker 就会重启并应用配置的镜像地址了。
Mac OS X
对于使用 Mac OS X 的用户,在任务栏点击 Docker for mac 应用图标-> Perferences...-> Daemon-> Registrymirrors。在列表中填写加速器地址 https://reg-mirror.qiniu.com 。修改完成之后,点击 Apply&Restart 按钮,Docker 就会重启并应用配置的镜像地址了。
检查加速器是否生效
检查加速器是否生效配置加速器之后,如果拉取镜像仍然十分缓慢,请手动检查加速器配置是否生效,在命令行执行 docker info,如果从结果中看到了如下内容,说明配置成功。
$ docker info Registry Mirrors: https://reg-mirror.qiniu.com
Docker Hello World
首先,运行下面的命令,将 image 文件从仓库抓取到本地。
$ docker pull library/hello-world
docker image pull 是抓取 image 文件的命令。
library/hello-world是 image 文件在仓库里面的位置,其中
library是 image 文件所在的组,
hello-world是 image 文件的名字。
由于 Docker 官方提供的 image 文件,都放在library组里面,所以它的是默认组,可以省略。因此,上面的命令可以写成下面这样。
$ docker pull hello-world
抓取成功以后,就可以在本机看到这个 image 文件了。
$ docker images
现在,运行这个 image 文件。
$ docker run hello-world
docker container run命令会从 image 文件,生成一个正在运行的容器实例。
注意,docker run命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。因此,前面的docker pull命令并不是必需的步骤。
如果运行成功,你会在屏幕上读到下面的输出。
输出这段提示以后,hello world 就会停止运行,容器自动终止。
有些容器不会自动终止,因为提供的是服务。比如,安装运行 Ubuntu 的 image,就可以在命令行体验 Ubuntu 系统。
$ docker run -it ubuntu bash
对于那些不会自动终止的容器,必须使用docker kill命令手动终止。
$ docker kill [containID]
Docker常用命令
容器生命周期管理
容器操作
容器rootfs命令
镜像仓库
本地镜像管理
info|version
Docker应用容器化
Docker的核心思想就是如何将应用整合到容器中,并且能在容器中实际运行。
将应用整合到容器中并且运行起来的这个过程,称为“容器化”(Containerizing),有时也叫作“Docker化”(Dockerizing)。容器是为应用而生的,具体来说,容器能够简化应用的构建、部署和运行过程。
完整的应用容器化过程主要分为以下几个步骤。
-
编写应用代码。
-
创建一个 Dockerfile,其中包括当前应用的描述、依赖以及该如何运行这个应用。
-
对该 Dockerfile 执行 docker image build 命令。
-
等待 Docker 将应用程序构建到 Docker 镜像中。
一旦应用容器化完成(即应用被打包为一个 Docker 镜像),就能以镜像的形式交付并以容器的方式运行了。
Dockerfile简介
使用 Docker中的docker image build命令会读取 Dockerfile,并将应用程序容器化。
Dockerfile 由一行行命令语句组成,并支持以 # 开头的注释行。例如:
# Test web-app to use with Pluralsight courses and Docker Deep Dive book
# Linux x64
FROM alpine
LABEL maintainer="nigelpoulton@hotmail.com"
# Install Node and NPM
RUN apk add --update nodejs nodejs-npm
# Copy app to /src
COPY . /src
WORKDIR /src
# Install dependencies
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]
这个文件中的一些关键步骤概述:以 alpine 镜像作为当前镜像基础,指定维护者(maintainer)为“nigelpoultion@hotmail.com”,安装 Node.js 和 NPM,将应用的代码复制到镜像当中,设置新的工作目录,安装依赖包,记录应用的网络端口,最后将 app.js 设置为默认运行的应用。
下面分析一下每一步的作用:
每个 Dockerfile 文件第一行都是 FROM 指令。
FROM 指令指定的镜像,会作为当前镜像的一个基础镜像层,当前应用的剩余内容会作为新增镜像层添加到基础镜像层之上。
本例中的应用基于 Linux 操作系统,所以在 FROM 指令当中所引用的也是一个 Linux 基础镜像;如果要容器化的应用是一个基于 Windows 操作系统的应用,就需要指定一个像 microsoft/aspnetcore-build 这样的 Windows 基础镜像了。
截至目前,基础镜像的结构如下图所示。
接下来,Dockerfile 中通过标签(LABLE)方式指定了当前镜像的维护者为“nigelpoulton@hotmail. com”。
每个标签其实是一个键值对(Key-Value),在一个镜像当中可以通过增加标签的方式来为镜像添加自定义元数据。备注维护者信息有助于为该镜像的潜在使用者提供沟通途径,这是一种值得提倡的做法。
RUN apk add --update nodejs nodejs-npm
指令使用 alpine 的 apk 包管理器将 nodejs 和 nodejs-npm 安装到当前镜像之中。
RUN 指令用于在镜像中执行命令,会在 FROM 指定的 alpine 基础镜像之上,新建一个镜像层来存储这些安装内容,每个 RUN 指令创建一个新的镜像层。当前镜像的结构如下图所示。
COPY. / src 指令将应用相关文件从构建上下文复制到了当前镜像中,并且新建一个镜像层来存储。COPY 执行结束之后,当前镜像共包含 3 层,如下图所示。
下一步,Dockerfile 通过 WORKDIR 指令,为 Dockerfile 中尚未执行的指令设置工作目录。
该目录与镜像相关,并且会作为元数据记录到镜像配置中,但不会创建新的镜像层。
然后,
RUN npm install
指令会根据 package.json 中的配置信息,使用 npm 来安装当前应用的相关依赖包。
npm 命令会在前文设置的工作目录中执行,并且在镜像中新建镜像层来保存相应的依赖文件。
目前镜像一共包含 4 层,如下图所示。
因为当前应用需要通过 TCP 端口 8080 对外提供一个 Web 服务,所以在 Dockerfile 中通过 EXPOSE 8080 指令来完成相应端口的设置,Dockerfile 中的 EXPOSE 指令用于记录应用所使用的网络端口。
这个配置信息会作为镜像的元数据被保存下来,并不会产生新的镜像层。
最终,通过 ENTRYPOINT 指令用于指定镜像以容器方式启动后默认运行的程序。ENTRYPOINT 指定的配置信息也是通过镜像元数据的形式保存下来,而不是新增镜像层。
其他的 Dockerfile 指令还有 LABEL、ENV、ONBUILD、HEALTHCHECK、CMD 等。
构建镜像
下面的命令会构建并生成一个名为 web:latest 的镜像。命令最后的点(.)表示 Docker 在进行构建的时候,使用当前目录作为构建上下文。
使用当前目录的 Dockerfile 创建镜像,标签为 web:lastest
docker build -t web:lastest .
命令执行结束后,检查本地 Docker 镜像库是否包含了刚才构建的镜像。
$ docker image ls REPO TAG IMAGE ID CREATED SIZE web latest fc69fdc4c18e 10 seconds ago 64.4MB
接下来就可以运行这个镜像了。
推送镜像到仓库
在创建一个镜像之后,可以将其保存在镜像仓库服务,例如:Docker Hub。
docker image push
命令默认的推送地址即为Docker Hub。
在推送镜像之前,需要先使用 Docker ID 登录 Docker Hub。除此之外,还需要为待推送的镜像打上合适的标签。
接下来介绍一下如何登录 Docker Hub,并将镜像推送到其中。
在后续的例子中,需要用自己的 Docker ID 替换示例中所使用的 ID。所以每当看到“nigelpoulton”时,记得替换为自己的 Docker ID。
$ docker login Login with **your** Docker ID to push and pull images from Docker Hub... Username: nigelpoulton Password: Login Succeeded
推送 Docker 镜像之前,还需要为镜像打标签。这是因为 Docker 在镜像推送的过程中需要如下信息。
-
Registry(镜像仓库服务)。
-
Repository(镜像仓库)。
-
Tag(镜像标签)。
无须为 Registry 和 Tag 指定值。当没有为上述信息指定具体值的时候,Docker 会默认 Registry=docker.io、Tag=latest。
但是 Docker 并没有给 Repository 提供默认值,而是从被推送镜像中的 REPOSITORY 属性值获取。
这一点可能不好理解,下面会通过一个完整的例子来介绍如何向 Docker Hub 中推送一个镜像。
在前面的例子中执行了 docker image ls 命令。在该命令对应的输出内容中可以看到,镜像仓库的名称是 web。
这意味着执行 docker image push命令,会尝试将镜像推送到 docker.io/web:latest 中。
但是其实 nigelpoulton 这个用户并没有 web 这个镜像仓库的访问权限,所以只能尝试推送到 nigelpoulton 这个二级命名空间(Namespace)之下。
因此需要使用 nigelpoulton 这个 ID,为当前镜像重新打一个标签。
$ docker image tag web:latest nigelpoulton/web:latest
为镜像打标签命令的格式是docker image tag <current-tag> <new-tag>,其作用是为指定的镜像添加一个额外的标签,并且不需要覆盖已经存在的标签。
再次执行 docker image ls 命令,可以看到这个镜像现在有了两个标签,其中一个包含 Docker ID nigelpoulton。
$ docker image ls REPO TAG IMAGE ID CREATED SIZE web latest fc69fdc4c18e 10 secs ago 64.4MB nigelpoulton/web latest fc69fdc4c18e 10 secs ago 64.4MB
现在将该镜像推送到 Docker Hub。
$ docker image push nigelpoulton/web:latest The push refers to repository [docker.io/nigelpoulton/web] 2444b4ec39ad: Pushed ed8142d2affb: Pushed d77e2754766d: Pushed cd7100a72410: Mounted from library/alpine latest: digest: sha256:68c2dea730...f8cf7478 size: 1160
下图展示了 Docker 如何确定镜像所要推送的目的仓库。
因为权限问题,所以需要把上面例子中出现的 ID(nigelpoulton)替换为自己的 Docker ID,才能进行推送操作。
在接下来的例子当中,将使用 web:latest 这个标签。
相关链接
- Docker 官网:https://www.docker.com
- Github Docker 源码:https://github.com/docker/docker-ce
- Docker入门实践(精讲版):http://c.biancheng.net/docker/
- 菜鸟教程-Docker教程:https://www.runoob.com/docker/docker-tutorial.html
- 阮一峰-Docker 入门教程:http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html