无论是钢琴还是泰迪熊,容器都提供了制造商和运输商可以依赖的一致性。正如集装箱彻底改变了进出口行业一样,您可能听说过 Docker 正在技术领域做着同样的事情。
它像野火一样蔓延开来,因为它有相关且可识别的用例,而且任何人都可以轻松使用。
了解 Docker
容器是一种提供与开发和运维可以依赖的相同一致性的方法。Docker 使用容器作为隔离一个或多个进程的方式。主机上的进程 (PID) 需要内存、CPU、网络访问和磁盘访问。应用程序需要一个微妙的环境才能运行。这包括特定的可执行文件、特定的库和特定的标准 C 库 (libc)。显然,内核是许多这些组件的幕后功臣,但就 PID 而言,这些资源通常被认为是理所当然的,并且对内核的访问通过 libc 进行抽象。
有多种方法可以为 PID 设置环境。如果您需要 libc 的替代版本,更改根目录 (chroot) 是一个基本选项。如果需要约束内存,内核控制组 (cgroups) 是一个功能日益丰富的选项。虚拟机 (VM) 可以是答案,但这引入了一个虚拟机监控器来封装整个包含的运行时,并且需要为机器的可用性进行完整的初始化/启动。如果 PID 仅用于作业或任务,则 VM 可能是资源昂贵的途径。为了获得具有 cgroups、隔离和易于共享的根文件系统的更改根目录感觉,请转向 Docker。它实现了所有这些,同时还提供了一种以编程方式创建新根文件系统的方法!
Dotcloud, Inc. 的开发人员于 2013 年 1 月开始 Docker 的工作,并在同年 3 月公开发布。在创建 Docker 之前,Dotcloud 一直是一个平台即服务 (PaaS),因此像 Docker 这样的工具,允许在任何地方构建 PaaS 环境,对他们来说是非常合乎逻辑的下一步。Docker 的 GitHub 页面反映了自公开发布以来的狂热兴奋。仅在一年内,该项目就获得了 500 多名贡献者,其中不到 10 名是 Docker 的员工。超过 70 个版本、13,000 多个星标和 2,000 多个 fork 对于一个刚起步的开源项目来说是惊人的活动量。在 2014 年 6 月的首届 DockerCon 及时到来之际,Docker 发布了 1.0.0 版本,吹嘘其稳定性和生产就绪性,可以运行 linux-amd64 容器。
Docker 的受众是开发人员和系统管理员,重点是应对他们面临的挑战。有些问题对于开发人员和系统管理员来说非常熟悉,以至于它们融入了日常例行公事。
开发的困境可能是
- 在迭代新构建时保持一致的环境
- 编写文档,以便其他人可以像您已经知道的那样使用您的代码
- 安装依赖软件而不会与主机版本冲突
- 一致的测试环境
- 可追溯的数据和可追踪的存储
运维的困境可能是
- 正确记录被监控的软件
- 进程微管理和资源配额
- 应用程序要求的网络配置
- 可追溯的数据和可追踪的存储
通过拥有标准化的镜像格式,开发人员看到所有服务器都一样,而运维人员看到所有容器都一样。这些是 Docker 直接解决的困境。首先有必要区分 Docker 环境中“镜像”和“容器”的用法。
镜像
可移植:它们可以被推送到注册中心,或保存为 tar 存档。
分层:生成镜像的步骤是分层添加的。通过这种方式,除了最后几个步骤外,大部分相同的镜像可以通过共享父层来减少磁盘使用量。
静态:内容是不可更改的,除非制作新镜像。
容器
运行时:PID 的环境。
可写:它本质上是一种临时存储。
分层:它位于镜像之上。
这些术语将在各种上下文中出现,重要的是要了解它们彼此之间的关系,但也都是独立的实体。有了这个基础,就可以探索其基本的 Docker 应用程序和功能了。
Docker 101
$> docker run busybox echo 'Hello, World!' Hello, World!
让我们拆解这个简单的例子。首先,命令是 docker,并告诉 docker `run`
'busybox' 镜像。如果 'busybox' 镜像不存在,那么 docker 将尝试从公共 Docker Hub 获取名为 'busybox' 的镜像。然后 Docker 设置此镜像的层,此容器环境的所有 cgroup 和命名空间,并执行 `echo 'Hello, World!'`
。
$> docker pull fedora
这是一种从公共 Docker Hub 获取名为 'fedora' 的镜像的直接方法。
$> docker images
这显示了本地可用的镜像。还有许多类似的 docker 命令。要查看所有这些命令,请参阅 `docker help`
。可以交互式地运行 docker 镜像,并在以后决定将该容器提交为镜像以供将来使用。
$> $ docker run fedora touch file $> docker ps -l CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES 9626763a35f9 fedora:20 touch file 6s ago Exited (0) 3s prickly_goldstine $> docker commit 9626763a35f9 my-touched-file $> docker run my-touched-file ls -s /file 0 /file
对于以编程方式提交新镜像,有 "Dockerfile" 格式。Dockerfile 是包含准备镜像步骤的文件。乍一看,它看起来像一个带有少量元数据的 shell 脚本。
FROM fedora RUN yum install -y mongodb-server && mkdir -p /data/db EXPOSE 27017 VOLUME ["/data/db"] CMD mongod
我们正在 'fedora' 镜像的基础上构建,运行 shell 命令来安装软件并创建目录,然后声明要发布的端口和要存储的目录。最后,我们声明此容器运行时要执行的命令(例如 'mongod')。
在名为 "Dockerfile" 的文件中包含这些指令,运行
$> docker build -t mongodb .
所有步骤的输出都是可见的,一旦成功完成,'mongodb' 镜像就可用了。
运行服务容器 使用这个用 Dockerfile 构建的新 'mongodb' 镜像,启动并查看它。
$> docker run -it -p 127.0.0.1:27017:27017 -v $(pwd)/db:/data/db mongodb
mongodb 服务器将启动并预先分配所需的文件。
以下是对 “Hello, World!” 示例以来新标志的解释
`-it`
连接终端,以便 <ctrl>+c 可以杀死附加的进程。`-p 127.0.0.1:27017:27017`
将容器内部的端口 27017 附加到主机上的 127.0.0.1:27017`-v $(pwd)/db:/data/db`
将容器内部的 '/data/db' 卷挂载到主机上的 $(pwd)/db。
现在,随着 mongodb 容器运行,在单独的终端中运行以下命令
$> docker ps CONTAINER ID IMAGE COMMAND CREATED cb5346e10a69 mongodb:latest /bin/sh -c mongod 2min ago STATUS PORTS NAMES Up 2min 127.0.0.1:27017->27017/tcp hopeful_torvalds
将出现运行在我们 'mongodb' 镜像上的容器的运行时信息。要结束此过程,可以 <ctrl>+c 原始终端,或者 `docker kill cb5346e10a69`
该容器。由于该临时容器写入的唯一数据在 /data/db 目录中,该目录已挂载到主机目录,因此可以重新启动同一容器,并看到所有预先分配的数据都被重用,即使这个新的 `docker run`
本身也是一个全新的开始。
本地共享
除了上述公共 docker 镜像注册中心 Docker Hub 之外,还有 Docker Registry,它允许托管本地 Docker 镜像。虽然许多 Linux 发行版已经打包了 docker-registry,但使用 docker 本身来运行 Docker Registry 更容易。
$> docker run -d -p 5000:5000 -v $(pwd)/registry-data:/tmp registry:latest
现在我们有了一个本地服务,Docker 镜像可以推送到该服务并从中拉取。由于该服务正在监听主机的 "0.0.0.0",因此镜像需要使用主机的 IP 地址或 DNS 名称进行标记。建议使用 DNS 名称,因此无需为新的 IP 地址重新标记本地 docker 镜像。
要使用此本地注册中心,镜像需要在镜像名称中包含它。这可以在 `docker build`
时设置,也可以标记为这样。假设 DNS 名称为 "image-storage.local.lan"(这完全是任意的)指向主机的 IP 地址,运行
$> docker tag mongodb image-storage.local.lan:5000/mongodb $> docker push image-storage.local.lan:5000/mongodb
现在本地网络上的其他人可以通过引用这个新的标记名称来拉取和运行此镜像。
一致的开发人员环境
许多 Docker 用例都围绕生产服务和可伸缩性,但更相关的用例是开发人员的环境。Docker 镜像的可用性可以创建遗留代码、当前代码和前沿代码所需的环境。现在无需 VM 虚拟机监控器,也无需在网络上访问“具有所有正确版本的构建机器”。此外,一旦准备好这些开发镜像并在本地注册中心共享,那么任何能够运行镜像的机器都将成为可能的工作站。
就像前面提到的分层一样,以下 Dockerfile 可以拆分以制作一个公共基础镜像,然后允许为各个用户名构建最后步骤。
FROM fedora:20 # setup and install common tools RUN yum groupinstall -y "Development tools" RUN yum install -y vim-enhanced git CMD bash -l # this part could be per-user RUN useradd -m -u 1000 vbatts ENV HOME /home/vbatts WORKDIR /home/vbatts USER vbatts
构建它,并为本地注册中心标记它
$> docker build -t image-storage.local.lan:5000/$USER/devel:f20 . [...] $> docker push image-storage.local.lan:5000/$USER/devel:f20
现在使用此构建。此开发镜像允许主机保持源代码和主目录不变,同时围绕活动 shell 嫁接此标准化开发镜像。
function devel() { docker run \ --rm \ -it \ --hostname=$(hostname)-devel \ -v $HOME:$HOME \ -v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK \ --env SSH_AUTH_SOCK \ --env PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin \ --workdir $(pwd) \ ${1+"$@"} \ image-storage.local.lan:5000/$USER/devel:f20 }
这是一个 `~/.bashrc`
的 bash 函数。这定义了一个名为 `devel`
的函数,它将进入刚刚构建的开发镜像的 bash shell。
以下是对传递给 `docker run`
的所有标志的逐步解释
`--rm`
此清理操作在 shell 退出后删除容器`-it`
创建一个交互式和附加的终端`--hostname=$(hostname)-devel`
在容器内部设置主机名,但附加 'devel' 以帮助区分`-v $HOME:$HOME`
将当前主目录挂载到容器内部的相同位置`-v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK`
在容器内部挂载活动 ssh 代理套接字`--env SSH_AUTH_SOCK`
传入 ssh 代理环境变量`--env PATH=...`
清理 PATH 状态`--workdir $(pwd)`
在与当前工作目录相同的目录中启动 bash shell(例如 ~/src/my/big/project/)`${1+"$@"}`
使`devel`
的附加标志在此函数中着陆
vbatts@noyee ~ $> cd src/my/big/project vbatts@noyee ~/src/my/big/project $> devel vbatts@noyee-devel ~/src/my/big/project $> make [...]
这保持了无缝体验,而无需担心弄乱或杂乱主机,主机上只需要特定项目的工具和库。
CI / 测试
使用持续集成 (CI) 可以为软件的构建和测试提供引擎。构建 Dockerfile 可以将此包装到单个操作中,如果成功,则留下可用的镜像。
[...] RUN cd /src/myapp && git pull --force origin master && \ autoreconf && \ ./configure && \ make RUN cd /src/myapp && \ make test
如果 Dockerfile 中的任何内容以非零退出代码退出,则镜像构建失败。此外,通过从基础镜像构建 Dockerfile,并通过能够自定义其自身的环境,可以减轻共享构建主机上的构建要求、冲突版本以及任何数量的复杂情况。
通过这些示例,可以识别许多日常用例。不难看出为什么这项技术正在开发到生产的生命周期中点燃火焰。虽然它是一个低级构建块,但它同时也在彻底改变计算环境的编排。加入对话,帮助培育 Docker 正在发展的容器生态系统。
评论已关闭。