# 第一章 基本概念与操作

## 建立以 Docker 为核心的概念

### 容器技术与 Docker 架构

容器通过对操作系统的资源访问进行限制，构建成独立的资源池，让应用运行在一个相对隔离的空间里，同时容器间也可以进行通信。容器技术对比虚拟化技术，容器比虚拟化更轻量级，对资源的消耗小很多。容器操作也更快捷，启动和停止都要比虚拟机快。但Docker容器需要与主机共享操作系统内核，不能像虚拟机那样运行独立的内核。

Docker 则是一个基于 Linux 容器（LXC, Linux Container) 技术构建的容器引擎。Docker 支持将应用打包进一个可以移植的容器中，重新定义了应用开发，测试，部署上线的过程。

使用 Docker，可以快速构建一个应用程序服务器、一个消息总线、一个持续集成测试环境，升值为生产或开发快速复制一套复杂的应用程序栈，可以说是让开发者成为 DevOps 的必备技能之一。

Docker 是一个 C/S 架构的程序，Docker 客户端通过向守护进程发出请求，守护进程和 Docker 容器将请求处理完成后返回响应的结果。由于 Docker 是基于 LXC 产生的，因此在其他（Mac/Windows）上不具备这样的技术时，Docker 依然能够运行则是得益于一个虚拟的宿主机，因此整个 Docker 的架构在 Linux 和其他平台上的架构略有不同，但随着如今 Docker 技术的发展，作为 Docker 的用户，我们完全不需要操心这一切，只需心中有下图即可：

![Docker 架构](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsFyU2ohtX9EJwh_%2Farch.svg?generation=1565704816910669\&alt=media)

此外，Docker 提供了一个命令行工具及一整套 RESTful API 让用户与守护进程进行交互，也就是我常说的 Docker API，这使得我们能够基于 Docker 来开发 Docker 相关的应用。

学习 Docker 需要先在脑子里建立三大基本概念，分别是：仓库、镜像、容器。

### 仓库(Registry)

仓库的概念和 `git` 里的仓库几乎完全一样。一个 Docker 仓库就相当于一个 Git 的代码仓库。DockerHub 的地位就如同 GitHub，用于托管和保存用户的镜像，因此用户可以在 DockerHub 上注册账号，管理或开源自己的镜像。

### 镜像(Image)

镜像是构建 Docker 的基础，就如同操作系统安装的 `.iso` 镜像一样，Docker 镜像就是这种类似物。镜像通过 Union 文件系统产生了层式的结构，从最基础的镜像，像盖房子一样一层层堆叠，从而完成整个镜像的构建。

### 容器(Container)

容器是从 Docker 镜像创建运行出来的一个实例（Instance），可以理解为一个虚拟的 Linux 环境。Docker 容器如同虚拟机，支持启动、停止、删除。每个容器之间互相隔离，隔离性弱于虚拟机。

> 隔离的效果由 `CGroups`和 `Namespaces` 实现，`CGroups` (Control Group, Linux 内核特性之一) 对 CPU、内存、磁盘资源等访问进行限制，而 `Namespaces` 提供了环境的隔离。

## 安装 Docker

安装 Docker 的方法在现在已经变得非常的简单了，这里不再叙述，下面是各个平台的安装方法：

* Mac: <https://docs.docker.com/docker-for-mac/>
* Linux: <https://docs.docker.com/engine/getstarted/>
* Window: <https://docs.docker.com/docker-for-windows/>

值得一提的是，现在(Docker v1.12) Mac 上的 Docker 已经不再需要使用 Docker ToolBox 了，只需要下载 Docker for Mac，就能够在 命令行工具中使用了：

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsG-711Jtdnqwk6a%2F1.png?generation=1565704816958048\&alt=media)

## 基本操作

首先要获得一个镜像，我们才能开始 Docker 的相关学习。

```bash
$ docker images           # 查看当前镜像
$ docker search ubuntu    # 搜索镜像
$ docker pull ubuntu      # 拉取一个镜像
```

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsG12JGSNEY2QXw7%2F2.png?generation=1565704817047433\&alt=media)

拿到镜像后，我们可以开始进行一些基本的操作了。

```bash
$ # 运行 docker 容器
$ docker run ubuntu echo "hello docker" 
$ # 查看运行的容器列表, 
$ # docker 在执行完命令后会主动停止, 
$ # 所以使用 -a 参数可以查看到已经停止的容器. 此外, -l 可以查看最后一次启动的容器, -q 只查看容器 ID
$ docker ps 
$ # 对于 run 命令可以使用 -it 参数进入 bash 来保持容器运行
$ docker run -i -t ubuntu /bin/bash
```

`docker run` 的 `-i` (interactive) 参数保证了容器中的 STDIN 开启，要让交互式 shell 长期运行，就必须要开启持久的标准输入；`-t` (tty) 指定让 Docker 创建一个伪 tty 终端。

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsG3KKmCUldKdYEA%2F3.png?generation=1565704817121546\&alt=media)

在 Docker 中，涉及到磁盘、网络、设备等 Linux 特权命令都无法执行，因此我们不能够执行诸如 `reboot` 之类的命令，可以通过 `exit` 退出 bash。

如果希望退出后依然保持容器运行，可以使用 `Ctrl+p` 及 `Ctrl+q` 两组快捷键，然后使用 `docker attach` 能够再次进入 bash。

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsG52ZoogH-rVEHP%2F4.png?generation=1565704817043957\&alt=media)

```bash
$ docker start <id>     # 根据 ID 启动一个容器
$ docker stop <id>      # 根据 ID 停止一个容器
$ docker restart <id>   # 根据 ID 重启一个容器
```

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsG74Zhy-CGKDSXm%2F5.png?generation=1565704817062438\&alt=media)

```bash
$ docker inspect <id>   # 查看容器或镜像内部信息
$ docker top <id>       # 查看容器中运行进程等信息
```

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsG9mneJTyh6YmXx%2F6.png?generation=1565704818356907\&alt=media) ![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsGB6jh9GT8FdWwk%2F7.png?generation=1565704816881033\&alt=media)

```bash
$ docker rm  <id>        # 删除容器
$ docker rmi <id>        # 删除镜像
```

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsGDJBBve1RawZjH%2F8.png?generation=1565704817110545\&alt=media)

## 初识 Dockerfile

Dockerfile 用于编写一个镜像，下面创建了一个新的 Docker 镜像：

```bash
from ubuntu:latest          # 基于 ubuntu:latest 
ENV HOSTNAME=changkun       # 设置 HOSTNAME 环境变量
```

通过 `docker build` 构建新镜像：

```bash
$ docker build -t changkun .
```

其中 `-t changkun` 指定了镜像的名字, `.` 标志从当前目录查找 Dockerfile。

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsGFjgbHRXohdUDr%2F9.png?generation=1565704817084191\&alt=media)

删除我们刚才创建的镜像：

![](https://1241771330-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmAGnx3SJCC0Kh7ojtM%2F-LmAGp5cNpBpcrTZgvwg%2F-LmAGsGHrVU1lGepQ-OD%2F10.png?generation=1565704817136805\&alt=media)

> **注意**: 删除 Docker 镜像时，尽管可以使用 `-f` 参数强制删除镜像，为了养成良好的运维习惯，应该先使用 `docker rm` 删除容器后，再删除镜像。

## 延伸阅读

* [理解 Docker](https://docs.docker.com/engine/understanding-docker/)
* [Docker 核心技术预览](http://www.infoq.com/cn/articles/docker-core-technology-preview)
