"但是它在我的机器上运行了!"
这是在开发者和运营团队之间的对话中经常听到的借口。即使使用复杂的工具,云中几乎没有限制的计算能力以及先进的持续集成工作流程,本地开发应用程序与在生产环境中运行应用程序之间的差异,仍然是错误和其他问题的持续来源。开发团队和运营团队通常会转向虚拟机,预先构建的映像和/或像Puppet 和Chef这样的配置管理系统,以便与基于Linux的生产环境和Mac或Windows开发环境实现更好的兼容性。
所有这些方法都可以提供帮助,但问题仍然存在。幸运的是,新的Docker for Mac测试版 提供了一个机会来创建一个更有弹性的本地环境,更好地反映生产。传统上,MacOS和Windows不支持基于Linux的容器技术,但Docker for Mac和Windows的最新版本现在更容易开始在这些环境中创建和运行容器,而且开销较小。让我们把一个简单的Node.js应用程序放在一个Docker容器中。
开发者工作站是脆弱的。升级操作系统、拙劣的安装软件包、相互冲突的依赖以及使用多种编程语言运行时的需求仍然是开发人员头疼的源头。许多特定于语言的工具,已建成管理这种复杂性,包括 virtualenv中 的Python, rbenv for Ruby和 jenv for Java。然而,Docker提供了一个优雅的新选择。
像虚拟机一样,容器提供了一种方法来隔离应用程序对主机操作系统和其他应用程序所需的复杂依赖关系。与虚拟机不同,容器的资源消耗较少,通常只需几秒钟即可启动。
Docker通过将Linux容器技术与一个专用的文件系统和命令行界面结合起来,就这样成为了开发者的宠儿,这个文件系统和命令行界面也可以借助Linux虚拟机在Mac和Windows上运行。在非Linux环境下运行Docker所需的附加要求在Docker软件的最新测试版中得到了简化,使其更易于使用。
一旦安装完毕,通常可用于Docker Hub的流行开源项目的Docker镜像,也被用来实例化,运行正在执行应用程序代码的容器。(理解容器和图像之间的区别尤为重要 - 官方Docker教程中提供了更多信息 。)
Docker镜像和容器之间的区别
新的 Docker for Mac测试版软件 是一个简单好用的安装程序,可以放弃某些依赖关系 - VirtualBox,最值得注意的是 - 使用macOS本地虚拟化解决方案来支持轻量级Linux虚拟机 。
新的Docker beta有一个Mac OS X的工具栏助手
在为Mac安装新版Docker客户端之后,可以立即开始拉取将创建容器的镜像。这可以通过使用命令行或 Kitematic GUI界面 (一个单独的下载与Mac beta配合使用)完成。
使用最新的稳定版本的Node.js的官方Docker镜像,这是一个单行命令,它使用名为“test-node”的容器中的新EcmaScript 6功能运行一些JavaScript代码:
$ docker run -it --rm --name test-node node:6.2 node -e
"console.log(\`Hi from Docker running on
\${require('os').platform()}\`)"
这个命令的输出是“ Hi from Docker running on linux
”,因为Node.js 6.2映像基于Debian Linux,从Node.js进程的角度来看,它运行在Linux上。运行Node.js 6.2所需的所有系统依赖关系都被隔离在容器映像的内部。
虽然运行单行脚本在有限的情况下很有用,但大多数应用程序都有很多外部依赖关系。 使用在Dockerfile中指定的命令,可以为需要使用节点包管理器(npm)的模块的案例—Node.js应用程序创建一个Docker映像。 这个Dockerfile例子还创建了一个特殊的非root用户来运行应用程序,因为默认情况下,Docker容器以root用户身份执行命令:
FROM node:6.2
RUN useradd --user-group --create-home --shell /bin/false app
# Where the app lives inside of the container file system
ENV HOME=/home/app
COPY package.json $HOME
RUN chown -R app:app $HOME/*
# Set user and install npm packages
USER app
WORKDIR $HOME
RUN npm install
# Set non-root permissions
USER root
COPY . $HOME
RUN chown -R app:app $HOME/*
USER app
# Run the node.js app
CMD ["node", "index.js"]
使用这个Dockerfile,你可以为从一个index.js文件开始的一个Node.js应用程序构建一个镜像 - 在这个例子中,我们将创建一个简单的HTTP服务器,使用一个npm模块来 输出ASCII字符串。遵循标准约定,我们使用用户名或组织名称,镜像名称以及应用程序的版本为图像命名空间,然后docker build
在Node.js项目目录的根目录下运行 命令:
$ docker build -t csmith/cow-service:v1.0.0 .
一旦镜像成功建立,我们可以在项目的根目录下运行容器。需要几个命令行选项来告诉Docker以守护进程的形式运行映像,将端口3000映射到主机操作系统的端口3000,将主机上存在的目录(实际的应用程序代码)挂载到容器中,并给它一个友好的名字,“奶牛服务”:
$ docker run -ti -d --name cow-service -p 3000:3000 -v
$(pwd):/home/app -v /home/app/node_modules csmith/cow-service:v1.0.0
如果容器运行成功(一个快速的 docker ps
可以验证这个),一个到localhost:3000的HTTP请求将会输出一个牛:
使用Docker for Mac和官方的Node.js镜像,一个简单的Node.js Web服务现在正在一个容器中运行。如果对应用程序代码进行了更改,只要在Docker重启的命令后面添加镜像名称: docker restart cow-service
,这样就可了。根据Dave Kerr最近发表的一篇 文章,如果你使用的是像nodemon这样的代码监视工具,新的Docker for Mac软件现在可以正确地获取更改。但是,如果npm依赖关系发生变化,则需要使用docker build
给定此Dockerfile结构的命令来重建映像 。
使用Kitematic,可以在图形界面中管理重新启动和查看日志和卷:
工作中Docker容器的路径
在这一点上,有理由怀疑,安装Docker,定义Dockerfile以及运行一系列命令来构建映像和运行容器等额外的复杂性是否值得这样一个简单的应用程序。关键是运行Node.js所需的所有依赖关系(Node.js,npm依赖关系和npm本身的正确版本)都与主机操作系统完全隔离,并打包为只读映像。
这意味着经过这个过程后,应用程序被包装在一个静态的,版本化的工件的容器图像。可以与其他团队成员共享,用于持续集成环境以运行测试,并最终部署到生产环境。值得注意的是,在容器中运行Node.js应用程序不需要对应用程序或macOS本身进行任何代码更改 - 在应用程序目录的根目录中创建的唯一文件是Dockerfile。
即使Docker没有在生产环境中运行,Docker对各种开发人员工作流也越来越有用。当你使用Docker创建支持更快更改和更频繁部署的更脆弱的开发和生产环境时,你可以要查看 New Relic APM,该软件可帮助软件团队了解更改如何影响应用性能和可靠性。