Docker 相关概念
docker 是一套 Linux 的 LXC 管理工具.
LXC (Linux Containers) 的概念类似于虚拟机, 但是形式上有区别. 虚拟机更像是提供"机器环境", 而 LXC 则是提供"运行环境". 单看概念它们的区别有些微妙, 但是 LXC 的特点就是, 同样是提供一套完全隔离的操作系统环境, 它很快, 超级快.
在使用上, 概念上的区别就是, 用虚拟机, 可能更多是想, 做一个电脑, 让它跑起来, 然后再说让这台电脑干什么事.
而 docker 的使用, 则是做一个环境(一组进程, 还有虚拟的设备等), 用环境来运行一个命令. 环境-命令 是一体的(一个容器), 命令一旦执行完成, 那么一个容器实例的任务就结束了. 所以如果我们想让一个容器像一台电脑那样, 通常我们给它一个像 /usr/bin/sshd -D
这样的命令, 这个容器像一台电脑, 是因为这个命令一直"没有执行完成"的效果.
虚拟机的使用, 一个镜像就是一个安装好的系统. 而 docker 中, 镜像更像是一张安装光盘(刻盘后不能更改), 容器才是安装好的系统. 这种两层式结构相较于传统的"快照"机制, 要好用得多. (如何从真正的系统安装 ISO 镜像得到 docker 镜像, 我现在也不清楚怎么做)
更具体一些, 使用 docker , 就是在指定的环境中执行一条命令, 比如:
docker run ubuntu:14.04 /bin/bash
意思就是, 在 ubuntu:14.04 这个环境中, 执行 /bin/bash 这条命令. 当然, 这条命令显然是瞬间就执行完了的. 如果和这个命令立即有交互, 那么需要一些参数:
docker run -t -i ubuntu:14.04 /bin/bash
-t
是分配一个虚拟终端, -i
是获取当前的输入. 这样你可以立即使用一个终端来和这个环境交互了.
虽然终端是正常使用电脑最常用的交互方式, 不过真实使用 docker 的场景中, 会这样用的机会不会很多的. 通常是直接执行我们要做的事, 比如运行一个服务, 或者执行一系列的计算.
使用 nc
来作一个例子:
docker run -t ubuntu:14.04 nc -l 8000
加上 -t
是因为需要一个终端来显示输出, 否则 nc
的标准输出就没地方去了.
docker 安装之后, 会自动在系统中作一个网桥配置, run
出来的每个容器实例, 都会分配一个桥接的 IP 地址. 上面的命令执行之后, 已经有容器在运行了, 并且在其中, nc
正监听着 8000
端口. 我们要接上去试试, 首先需要找到这个容器实例的 IP 地址.
先看当前运行着的实例列表:
docker ps
你先看到下面的信息:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e1d0c9193025 ubuntu:14.04 nc -l 8000 5 seconds ago Up 4 seconds silly_curie
记下 ID , 不需要全部, 前面几位就可以了, 然后查看指定容器实例的细节:
docker inspect e1d0
会输出一串 json 字符串, 里面会有这样一节:
"NetworkSettings": { "Bridge": "docker0", "Gateway": "172.17.42.1", "IPAddress": "172.17.0.82", "IPPrefixLen": 16, "PortMapping": null, "Ports": {} },
可以看到, 容器的 IP 是 172.17.0.82
, 上面的网关 IP 172.17.42.1
就是你"实体机"的.
在实体机使用 telnet
连上去:
telnet 172.17.0.82 8000
现在能看到效果了.
^]
然后 quit
退出, nc
那边结束执行, 命令执行完, 之前的那个容器实例也就关闭了. 你使用 docker ps
看不到. 但是使用:
docker ps -a
就能看到所有的容器实例. 在这里列出的实例是真实存在的, 只是当前并没有运行起来而已. 你可以随时让其中的实例再运行起来:
docker start e1d0c9193025
这样你又可以 telnet
, 只是没地方看输出.
回顾上面的过程, 提出两个概念, 镜像 和 容器 .
镜像 指上面的 ubuntu:14.04
这种, 嗯, 这种环境, 这种系统. 后面会讲如何做一个自己的 镜像 .
容器 是在具体的 镜像 上 run
具体的命令, 得到的一个"绑定状态". run
命令执行时的一些参数(比如和实体机的端口映射), 也是"状态"的一部分, run
过之后就不能更改了.
它们的关系, 有些像编程语言中的 类 和 实例 . run
时的命令就像是类实例化时的参数. 后面会提到, 你可以删除 容器 , 也可以删除 镜像 . 当你想删除 镜像 , 但是使用它的 容器 还存在时, 你会得到操作失败的提示.
类 有继承关系, 得利于 AUFS 这些的层级文件系统, 镜像 的构建也是这种层层封装的结果.