作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Radek Ostrowski's profile image

Radek Ostrowski

Radek是一名区块链工程师,对以太坊智能合约感兴趣. 他在机器学习方面也有丰富的经验.

Previously At

Sony
Share

If you like whales, 或者只是对快速、无痛地将软件持续交付到生产环境感兴趣, 那么我邀请你阅读这个介绍性的Docker教程. 一切似乎都表明,软件容器是IT的未来, 让我们和集装箱鲸一起快速畅游吧 Moby Dock and Molly.

Docker,由一个带有友好鲸鱼的标志代表

Docker,由一个带有友好鲸鱼的标志代表, 一个开放源码项目是否促进了软件容器内部应用程序的部署. 它的基本功能是通过Linux内核的资源隔离特性实现的, 但它提供了一个用户友好的API. 第一版于2013年发布, 从那以后,它变得非常受欢迎,并被许多大公司广泛使用,比如 eBay, Spotify, Baidu, and more. In the last funding round, Docker获得了9500万美元的巨额收入在美国,它正逐渐成为美国的主食 DevOps services.

Transporting Goods Analogy

Docker背后的哲学可以用下面一个简单的类比来说明. 在国际运输行业, 货物必须通过不同的方式运输,比如叉车, trucks, trains, cranes, and ships. 这些货物有不同的形状和大小,有不同的储存要求:袋装糖, milk cans, plants etc. Historically, 这是一个痛苦的过程,依赖于人工干预,在每个中转点装卸.

运货马车,小货车和运输卡车,均用于运输货物

随着多式联运集装箱的出现,一切都改变了. 因为它们有标准尺寸,制造时考虑到运输, 所有相关的机器都可以设计成以最少的人为干预来处理这些问题. 密封容器的额外好处是,它们可以保护内部环境,如敏感货物的温度和湿度. As a result, 运输业可以不再担心货物本身,而把注意力集中在把货物从A地运到B地.

使用海运集装箱的陆上和海上运输

这就是Docker发挥作用的地方它给 software industry.

它与虚拟机有何不同?

乍一看,虚拟机和Docker容器似乎很相似. 然而,当你看一下下面的图表时,它们的主要区别就会变得明显:

虚拟机与容器对比图

在虚拟机中运行的应用程序, apart from the hypervisor, 需要操作系统的完整实例和任何支持库. 另一方面,容器与主机共享操作系统. Hypervisor可以与容器引擎(在映像上表示为Docker)相媲美,因为它管理容器的生命周期. 重要的区别在于,在容器内运行的进程就像主机上的本机进程一样, 并且不要引入与管理程序执行相关的任何开销. 此外,应用程序可以重用这些库并在容器之间共享数据.

因为这两种技术有不同的优势, 将虚拟机和容器结合在一起的系统很常见. 一个完美的例子是在Docker安装部分中描述的名为Boot2Docker的工具.

Docker Architecture

Docker Architecture

在架构图的顶部是注册中心. 默认情况下,主注册表是Docker Hub,它托管公共和官方图像. 如果愿意,组织也可以托管自己的私有注册中心.

右边是图像和容器. 可以显式地从注册表下载映像(docker pull imageName)或在启动容器时隐式地. 一旦图像被下载,它就被缓存到本地.

容器是图像的实例——它们是有生命的东西. 可能有多个基于同一映像运行的容器.

At the centre, 有一个Docker守护进程负责创建, running, and monitoring containers. 它还负责构建和存储图像. 最后,在左侧有一个Docker客户端. It talks to the daemon via HTTP. 在同一台机器上使用Unix套接字,但是可以通过基于HTTP的API进行远程管理.

Installing Docker

有关最新的使用说明,请参阅 official documentation.

Docker在Linux上本机运行,因此根据目标发行版,它可以像下面这样简单 sudo apt-get install docker.io. 有关详细信息,请参阅文档. 通常在Linux中,您会在Docker命令前加上 sudo,但为了清楚起见,我们将在本文中跳过它.

因为Docker守护进程使用linux特定的内核特性, 不可能在Mac OS或Windows上运行Docker. 相反,你应该安装一个名为Boot2Docker的应用程序. 该应用程序由一个VirtualBox虚拟机组成, Docker itself, 以及Boot2Docker管理实用程序. 您可以按照官方的安装说明 MacOS and Windows 在这些平台上安装Docker.

Using Docker

让我们以一个简单的例子开始本节:

docker运行phusion/baseimage echo "Hello Moby Dock ". Hello Molly."

We should see this output:

Hello Moby Dock. Hello Molly.

然而,幕后发生的事情比你想象的要多得多:

  • 镜像phusion/baseimage是从Docker Hub下载的(如果它还没有在本地缓存中)
  • 基于此映像的容器已启动
  • The command echo was executed within the container
  • 当命令退出时,容器已停止

在第一次运行时,您可能会注意到文本在屏幕上打印之前有延迟. 如果图像是在本地缓存的,那么所有操作只需要几分之一秒的时间. 有关最后一个容器的详细信息可以通过运行来检索 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 

Taking the Next Dive

As you can tell, 在Docker中运行一个简单的命令就像直接在标准终端上运行一样简单. 来说明一个更实际的用例, 在本文的其余部分中, 我们将看到如何利用Docker来部署一个简单的web服务器应用程序. To keep things simple, 我们将编写一个Java程序来处理对' /ping '的HTTP GET请求,并使用字符串' pong\n '进行响应。.

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httperver.HttpExchange;
import com.sun.net.httperver.HttpHandler;
import com.sun.net.httperver.HttpServer;

public class PingPong {

    public static void main(String[] args)抛出异常{
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
        server.createContext("/ping", new MyHandler());
        server.setExecutor(null);
        server.start();
    }

    静态类MyHandler实现HttpHandler
        @Override
        (HttpExchange)抛出IOException {
            String response = "pong\n";
            t.sendResponseHeaders(200, response.length());
            OutputStream os = t.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }
}

Dockerfile

在开始构建自己的Docker映像之前, 首先检查Docker Hub或您可以访问的任何私有注册表中是否有现有的注册表,这是一个很好的做法. 例如,我们不需要自己安装Java,而是使用官方图片: java:8.

要构建映像,首先我们需要决定要使用的基本映像. It is denoted by FROM instruction. Here, it is an official image for Java 8 from the Docker Hub. 将其复制到Java文件中 COPY instruction. 接下来,我们将使用 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 toptal/pingpong .

Docker的官方文档中有 section dedicated to best practices regarding writing Dockerfile.

Running Containers

当图像已经建立,我们可以把它作为一个容器的生活. 有几种方法可以运行容器,但让我们从一个简单的开始:

Docker运行-d -p 8080:8080 total /乒乓

where - p [port-on-the-host]: [port-in-the-container] 表示主机和容器上的端口映射. Furthermore, 我们告诉Docker在后台将容器作为守护进程运行 -d. 您可以通过尝试访问“http://localhost:8080/ping”来测试web服务器应用程序是否正在运行。. 注意,在使用Boot2docker的平台上, 你需要将' localhost '替换为运行Docker的虚拟机的IP地址.

On Linux:

curl http://localhost:8080/ping

On platforms requiring Boot2Docker:

curl $(boot2docker ip):8080/ping

如果一切顺利,您应该看到响应:

pong

万岁,我们的第一个定制Docker容器活跃起来了! 我们还可以以交互模式启动容器 -i -t. In our case, we will override the entrypoint 命令,这样我们就会看到一个bash终端. 现在我们可以执行任何我们想要的命令,但是退出容器会阻止它:

Docker运行-i -t——entrypoint="bash" toptal/乒乓

还有更多的选项可用于启动容器. Let us cover a few more. For example, 如果我们想要在容器外保存数据, 我们可以使用命令与容器共享主机文件系统 -v. 缺省情况下,访问模式为读写模式,但可以通过追加修改为只读模式 :ro to the intra-container volume path. 当我们需要在容器内使用任何安全信息(如凭据和私钥)时,卷尤为重要, 哪些不应该存储在图像中. Additionally, 它还可以防止数据重复, 例如,通过将本地Maven存储库映射到容器来避免两次下载Internet.

Docker还具有将容器连接在一起的能力. 即使没有任何端口公开,链接的容器也可以相互通信. It can be achieved with –link other-container-name. 下面是综合上述参数的示例:

docker run -p 9999:8080 
    ——link otherContainerA——link otherContainerB 
    -v /Users/$USER/.m2/repository:/home/user/.m2/repository 
    toptal/pingpong

其他容器和映像操作

Unsurprisingly, 可以应用于容器和映像的操作列表相当长. 为了简短起见,让我们看看其中的几个:

  • stop - Stops a running container.
  • start - Starts a stopped container.
  • commit -根据容器的更改创建一个新映像.
  • rm -删除一个或多个容器.
  • rmi - Removes one or more images.
  • ps - Lists containers.
  • images - Lists images.
  • exec—在一个正在运行的容器中运行命令.

最后一个命令对于调试特别有用, 因为它可以让你连接到一个正在运行的容器的终端:

docker exec -i -t  bash

面向微服务世界的Docker Compose

如果您不止有几个相互连接的容器,那么使用像 docker-compose. In a configuration file, 您描述了如何启动容器以及它们应该如何相互链接. 不管所涉及的容器的数量及其依赖关系如何, 你可以用一个命令让它们全部启动并运行: docker-compose up.

Docker in the Wild

让我们着眼于项目生命周期的三个阶段,并看看我们友好的鲸鱼玩家是如何提供帮助的.

Development

Docker帮助您保持本地开发环境的整洁. 而不是安装不同服务(如Java)的多个版本, Kafka, Spark, Cassandra, etc.,您可以在必要时启动和停止所需的容器. 您可以更进一步,并排运行多个软件堆栈,以避免依赖版本的混淆.

使用Docker,您可以节省时间、精力和金钱. 如果你的项目设置起来非常复杂,“dockerise”它. 体验一次创建Docker映像的痛苦, 从这一点开始,每个人都可以快速启动一个容器.

您还可以在本地(或CI上)运行一个“集成环境”,并将存根替换为运行在Docker容器中的实际服务.

Testing / Continuous Integration

使用Dockerfile,很容易实现可复制的构建. Jenkins或其他CI解决方案可以配置为为每次构建创建一个Docker映像. 您可以将部分或全部映像存储在私有Docker注册表中,以备将来参考.

使用Docker,您只需测试需要测试的内容,并将环境排除在外. 在运行的容器上执行测试可以帮助保持事情的可预测性.

使用软件容器的另一个有趣的特性是,可以很容易地使用相同的开发设置派生出从属机器. 它对于集群部署的负载测试特别有用.

Production

Docker可以作为开发人员和操作人员之间的公共接口,消除摩擦的来源. 它还鼓励在管道的每一步使用相同的映像/二进制文件. Moreover, 能够部署完全经过测试的容器而不存在环境差异,有助于确保在构建过程中不会引入错误.

您可以无缝地将应用程序迁移到生产环境中. 曾经是一个繁琐而不稳定的过程,现在可以变得如此简单:

docker stop container-id; docker run new-image

如果在部署新版本时出现问题, 您总是可以快速回滚或更改到其他容器:

docker stop container-id; docker start other-container-id

保证不会留下任何混乱或让事情处于不一致的状态.

Summary

Docker自己的座右铭中有一个很好的总结: Build, Ship, Run.

  • Build - Docker允许你用微服务构建你的应用程序, 无需担心开发环境和生产环境之间的不一致性, 并且不受任何平台或语言的限制.
  • Ship - Docker可以让你设计应用程序开发的整个周期, testing, and distribution, 并使用一致的用户界面进行管理.
  • Run - Docker为您提供了在各种平台上安全可靠地部署可扩展服务的能力.

Have fun swimming with the whales!

这部作品的部分灵感来自一本优秀的书 Using Docker by Adrian Mouat.

Hire a Toptal expert on this topic.
Hire Now
Radek Ostrowski's profile image
Radek Ostrowski

Located in Phuket, Thailand

Member since September 24, 2014

About the author

Radek是一名区块链工程师,对以太坊智能合约感兴趣. 他在机器学习方面也有丰富的经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Previously At

Sony

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.