本文概述
如果你喜欢鲸鱼, 或者只是对快速, 轻松地将软件连续交付生产感兴趣, 那么我邀请你阅读此入门Docker教程。一切似乎都表明软件容器是IT的未来, 因此让我们快速了解一下容器鲸Moby Dock和Molly。
Docker以开放的鲸鱼徽标为标志, 是一个开源项目, 可促进在软件容器内部部署应用程序。它的基本功能由Linux内核的资源隔离功能启用, 但它在其之上提供了用户友好的API。第一个版本于2013年发布, 此后非常流行, 并被eBay, Spotify, 百度等许多大型公司广泛使用。在上一轮融资中, Docker已经获得了9500万美元的巨额投资, 并且有望成为DevOps服务的主要内容。
运输货物类比
可以通过以下简单的类比说明Docker背后的哲学。在国际运输业中, 必须通过不同的方式来运输货物, 例如叉车, 卡车, 火车, 起重机和轮船。这些货物具有不同的形状和大小, 并且具有不同的存储要求:一袋糖, 牛奶罐, 植物等。从历史上看, 这是一个痛苦的过程, 取决于每个装卸点的手动干预。
随着联运集装箱的使用, 这一切都发生了变化。由于它们具有标准尺寸, 并且在制造时考虑到运输, 因此所有相关的机器都可以设计为以最小的人工干预来处理这些机器。密封容器的另一个好处是, 它们可以保护敏感货物的内部环境, 例如温度和湿度。结果, 运输行业可以不再担心货物本身, 而将精力集中在将货物从A运送到B上。
这就是Docker进来的地方, 它为软件行业带来了类似的好处。
与虚拟机有何不同?
乍一看, 虚拟机和Docker容器似乎很相似。但是, 当你查看下图时, 它们的主要区别将变得显而易见:
除了虚拟机管理程序外, 在虚拟机中运行的应用程序还需要完整的操作系统实例和任何支持库。另一方面, 容器与主机共享操作系统。从某种意义上说, Hypervisor在管理容器的生命周期方面与容器引擎(在镜像上表示为Docker)具有可比性。重要的区别在于, 容器内部运行的进程就像主机上的本机进程一样, 并且不会引入与管理程序执行相关的任何开销。此外, 应用程序可以重用这些库并在容器之间共享数据。
由于两种技术具有不同的优势, 因此通常会找到结合虚拟机和容器的系统。一个完美的示例是Docker安装部分中描述的名为Boot2Docker的工具。
Docker架构
体系结构图的顶部是注册表。默认情况下, 主注册表是Docker Hub, 它托管公共和官方映像。组织也可以根据需要托管其私人注册表。
在右侧, 我们有图像和容器。可以在启动容器时从注册表显式下载图像(docker pull imageName)或隐式下载图像。映像下载后, 将被本地缓存。
容器是图像的实例-它们是生物。可能有多个容器基于同一图像运行。
在中心, 有Docker守护程序负责创建, 运行和监视容器。它还负责构建和存储图像。最后, 在左侧有一个Docker客户端。它通过HTTP与守护程序对话。在同一台机器上使用Unix套接字, 但是可以通过基于HTTP的API进行远程管理。
安装Docker
有关最新说明, 请始终参考官方文档。
Docker在Linux上本地运行, 因此取决于目标发行版, 它可能像sudo apt-get install docker.io一样容易。有关详细信息, 请参阅文档。通常, 在Linux中, 你在Docker命令前添加了sudo, 但是为了清楚起见, 我们将在本文中略过它。
由于Docker守护程序使用特定于Linux的内核功能, 因此无法在Mac OS或Windows中本地运行Docker。相反, 你应该安装一个名为Boot2Docker的应用程序。该应用程序由VirtualBox虚拟机, Docker本身和Boot2Docker管理实用程序组成。你可以按照MacOS和Windows的官方安装说明在这些平台上安装Docker。
使用Docker
让我们以一个简单的示例开始本节:
docker run phusion/baseimage echo "Hello Moby Dock. Hello Molly."
我们应该看到以下输出:
Hello Moby Dock. Hello Molly.
但是, 幕后发生的事情比你想象的要多得多:
- 图像” phusion / baseimage”是从Docker Hub下载的(如果尚未在本地缓存中)
- 基于该图像的容器已启动
- 命令回显在容器内执行
- 命令退出时容器已停止
在第一次运行时, 你可能会注意到文本在屏幕上打印之前的延迟。如果图像已在本地缓存, 则所有内容将花费一秒钟的时间。通过运行docker ps -l可以检索有关最后一个容器的详细信息:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
af14bec37930 phusion/baseimage:latest "echo 'Hello Moby Do 2 minutes ago Exited (0) 3 seconds ago stoic_bardeen
进行下一次潜水
如你所知, 在Docker中运行简单命令就像直接在标准终端上运行一样简单。为了说明一个更实际的用例, 在本文的其余部分中, 我们将看到如何利用Docker部署简单的Web服务器应用程序。为简单起见, 我们将编写一个Java程序来处理对” / ping”的HTTP GET请求, 并使用字符串” pong \ n”进行响应。
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class PingPong {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/ping", new MyHandler());
server.setExecutor(null);
server.start();
}
static class MyHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
String response = "pong\n";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
Docker文件
在开始构建自己的Docker映像之前, 最好先检查Docker Hub中是否存在现有映像或你有权访问的任何私有注册表。例如, 我们将使用一个官方映像:java:8, 而不是自己安装Java。
要构建图像, 首先我们需要确定要使用的基本图像。它由FROM指令表示。在这里, 它是来自Docker Hub的Java 8的官方映像。我们将通过发出COPY指令将其复制到Java文件中。接下来, 我们将使用RUN对其进行编译。 EXPOSE指令表示该映像将在特定端口上提供服务。 ENTRYPOINT是当基于此映像的容器启动并且CMD指示要传递给它的默认参数时, 我们要执行的指令。
FROM java:8
COPY PingPong.java /
RUN javac PingPong.java
EXPOSE 8080
ENTRYPOINT ["java"]
CMD ["PingPong"]
将这些指令保存在名为” Dockerfile”的文件中之后, 我们可以通过执行以下操作来构建相应的Docker映像:
docker build -t srcmini/pingpong .
Docker的官方文档中有一节专门介绍有关编写Dockerfile的最佳实践。
运行容器
构建图像后, 我们可以将其作为一个容器来实现。有几种方法可以运行容器, 但让我们从一个简单的方法开始:
docker run -d -p 8080:8080 srcmini/pingpong
-p [主机上端口]:[容器中端口]分别表示主机和容器上的端口映射。此外, 我们通过指定-d告诉Docker在后台将容器作为守护进程运行。你可以通过尝试访问” http:// localhost:8080 / ping”来测试Web服务器应用程序是否正在运行。请注意, 在使用Boot2docker的平台上, 你将需要用运行Docker的虚拟机的IP地址替换” localhost”。
在Linux上:
curl http://localhost:8080/ping
在需要Boot2Docker的平台上:
curl $(boot2docker ip):8080/ping
如果一切顺利, 你应该看到响应:
pong
Hurray, 我们的第一个自定义Docker容器还活着, 在游泳!我们还可以以交互模式-i -t启动容器。在我们的例子中, 我们将覆盖entrypoint命令, 以便为我们提供一个bash终端。现在, 我们可以执行所需的任何命令, 但是退出容器将停止它:
docker run -i -t --entrypoint="bash" srcmini/pingpong
还有许多其他选项可用于启动容器。让我们再介绍一些。例如, 如果我们要在容器外部保留数据, 则可以使用-v与容器共享主机文件系统。默认情况下, 访问模式为读写模式, 但可以通过在容器内卷路径后附加:ro更改为只读模式。当我们需要在容器内使用任何安全信息(例如凭据和私钥)时, 卷尤为重要, 这些信息不应该存储在映像中。此外, 它还可以防止数据重复, 例如, 通过将本地Maven存储库映射到容器, 可以避免两次下载Internet。
Docker还具有将容器链接在一起的功能。即使没有暴露任何端口, 链接的容器也可以互相通信。可以使用–link other-container-name来实现。以下是结合上述参数的示例:
docker run -p 9999:8080
--link otherContainerA --link otherContainerB
-v /Users/$USER/.m2/repository:/home/user/.m2/repository
srcmini/pingpong
其他容器和映像操作
毫不奇怪, 可以应用于容器和图像的操作列表很长。为简便起见, 让我们看一下其中的几个:
- stop-停止正在运行的容器。
- start-启动一个停止的容器。
- commit-根据容器的更改创建新图像。
- rm-删除一个或多个容器。
- rmi-删除一个或多个图像。
- ps-列出容器。
- images-列出图像。
- exec-在正在运行的容器中运行命令。
Last命令对于调试目的可能特别有用, 因为它使你可以连接到正在运行的容器的终端:
docker exec -i -t <container-id> bash
面向微服务世界的Docker Compose
如果你不仅有几个相互连接的容器, 那么可以使用docker-compose之类的工具。在配置文件中, 你描述了如何启动容器以及如何将它们彼此链接。无论涉及的容器数量及其依赖性如何, 你都可以使用一个命令启动并运行所有容器:docker-compose up。
野外的Docker
让我们看一下项目生命周期的三个阶段, 看看我们的友好鲸鱼将如何提供帮助。
发展历程
Docker帮助你保持本地开发环境的整洁。无需安装多个版本的不同服务, 例如Java, Kafka, Spark, Cassandra等, 你只需在必要时启动和停止所需的容器即可。你可以更进一步, 并排运行多个软件堆栈, 避免混淆依赖版本。
使用Docker, 你可以节省时间, 精力和金钱。如果你的项目设置非常复杂, 请对其进行” dockerize”。经历一次创建Docker映像的痛苦, 从这一点开始, 每个人都可以快速启动一个容器。
你还可以在本地(或在CI上)运行一个”集成环境”, 并用在Docker容器中运行的真实服务替换存根。
测试/持续集成
使用Dockerfile, 可以轻松实现可复制的构建。可以配置Jenkins或其他CI解决方案以为每个构建都创建一个Docker映像。你可以将部分或全部映像存储在私有Docker注册表中, 以备将来参考。
使用Docker, 你只需测试需要测试的内容, 并排除环境因素。在正在运行的容器上执行测试可以帮助使事情更加可预测。
拥有软件容器的另一个有趣的功能是, 使用相同的开发设置很容易衍生出从属机器。对于集群部署的负载测试, 它特别有用。
生产
Docker可以成为开发人员和运营人员之间的通用接口, 从而消除了摩擦。它还鼓励在流水线的每个步骤使用相同的映像/二进制文件。此外, 能够在没有环境差异的情况下部署经过全面测试的容器有助于确保在构建过程中不会引入任何错误。
你可以将应用程序无缝迁移到生产中。曾经繁琐而脆弱的过程现在可以变得很简单:
docker stop container-id; docker run new-image
而且, 如果在部署新版本时出现问题, 则始终可以快速回滚或更改为其他容器:
docker stop container-id; docker start other-container-id
……保证不会留下任何混乱或使事物处于不一致状态。
摘要
Docker所做的一个很好的总结包含在自己的座右铭中:Build, Ship, Run。
- 构建-Docker允许你从微服务组成你的应用程序, 而不必担心开发和生产环境之间的不一致, 也无需锁定任何平台或语言。
- 发货-Docker使你可以设计应用程序开发, 测试和分发的整个周期, 并使用一致的用户界面进行管理。
- 运行-Docker使你能够在各种平台上安全可靠地部署可扩展服务。
享受与鲸鱼畅游的乐趣!
这项工作的一部分是由Adrian Mouat撰写的一本出色的书《使用Docker》所启发。