前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Docker for Devs:创建一个开发版镜像

Docker for Devs:创建一个开发版镜像

作者头像
Techeek
发布2018-01-08 13:34:27
1.6K0
发布2018-01-08 13:34:27
举报
文章被收录于专栏:云计算云计算

Docker for Devs 系列包括以下6篇文章,这是第二篇。

  • 容器化您的应用程序环境
  • 创建一个开发版镜像(这篇文章)
  • 容器中的模块热重载和代码更新
  • 链接容器
  • 组成多容器网络
  • 与你的团队分享镜像

在这个系列教程的第一部分中,我们为应用程序创建了一个的 基础 Docker 镜像并运行了该镜像的实例(称为容器)。我们也见证了 Grayskull 的力量......我的意思是,Docker!(PS: Grayskull 出自《He-Man and the Masters of the Universe》,上个世纪八十年代的一部动画)

我们完成了所有典型应用程序的配置和运行,但不是从我们的本地主机,而是在一个容器内运行。接下来的 Docker for Developers 系列中,我们将试着配置一个可编辑的应用程序开发环境镜像。

Docker for Developers:入门

我们在本教程的这一部分中的目标是生成一个代表我们应用程序开发版本的镜像,并为它配置一个(可运行)容器所需的必要组件,这样我们就能对文件系统进行更改并将其反映在容器中。

Live editing in container
Live editing in container

步骤1:创建一个开发版镜像

让我们在我们的应用程序的根目录中创建一个新的Docker镜像文件。以下是派生镜像的说明:

代码语言:bash
复制
# dev.dockerfile

FROM express-prod-i

ENV NODE_ENV=development

CMD [“./initialize.sh”]

我们做了什么?

我们创建了一个新的docker镜像文件:

  • 从我们的生产环境镜像 express-prod-i 获得了基本镜像...
  • ...并创建了值为 "development" 的容器本地 ENV 变量 NODE_ENV 。
  • 最后,我们指定从 WORKDIR 运行名为 "initialized.sh" 的 bash shell 脚本。

步骤2:创建我们的初始化 Bash Shell 脚本

我们不会在创建镜像时初始化应用程序,而是将其移至容器中。因此,应用程序启动步骤(例如,"npm install")将在每次容器启动时执行。

1. 在项目根目录下创建一个名为 "initialize.sh" 的文件

2. 将以下内容粘贴到 "initialize.sh"

代码语言:bash
复制
npm install
node bin/www

3. 从终端/命令提示符进入项目根目录并运行以下命令,以使 bash shell 脚本可执行:

代码语言:bash
复制
chmod +x initialize.sh 

注意:请记住,这些容器正在基于 Linux 的环境中运行,因此运行 chmod system 命令会将指定文件的权限设置为可执行文件(本例中为initialize.sh)。

我们做了什么?

还记得吗,我们在基本的 express-prod-i 镜像中指定了运行 "npm install" 命令,该命令将安装 NPM 软件包作为容器的一部分。但在这里,我们:

  1. 创建一个文件,该文件将包含每次从此镜像生成的容器启动时要运行的命令。
  2. 设置权限,以便可以从容器内执行文件,并在容器启动时执行初始化步骤(如 "npm install")

步骤3:创建应用程序开发版镜像

现在,我们拥有了一个新的 Docker 镜像文件,我们已经准备好创建一个镜像了。

步骤3a:构建开发版镜像

就像我们在上一篇教程中所做的那样,让我们创建一个新的镜像:

  1. 从终端/命令提示符进入我们的项目根目录。
  2. 在项目根目录的下执行以下命令:(PS:不要忘记最后的 空格"."
代码语言:bash
复制
docker build -t express-dev -i -f dev.dockerfile。

我们做了什么?

  1. 我们使用 Docker build 命令创建了一个新的镜像。
  2. 需要注意的是,我们使用了一个新的标志 (-f) 代表文件,以指定我们希望它使用哪个 Docker 文件。记住,默认文件名是 Dockerfile。
  3. 像之前一样用标志 (-t) 标记指定镜像名称,并为其命名 "express-dev-i"。

步骤3b:列出镜像

运行 docker images,我们可以看到所有运行着的新旧镜像:

镜像列表
镜像列表

步骤4:生成并运行挂载数据卷(Volume)的容器

我们现在有一个镜像,代表我们的应用程序的开发版本,它以我们的生产环境版本为基础。现在,我们想在运行那个容器的同时,挂载数据卷(Volume)。

一直以来,您可能一直在想如何编辑源代码,并且如果源代码驻留在容器中,它会反映在正在运行的容器中,对吗?那也是我们要完成的主要目标之一,不是吗?

我之前提到,镜像是一堆不同的只读分层文件系统。每层添加或替换下面的层。我也提到容器是镜像的一个运行实例。但事实上不止于此,容器为镜像的底层只读文件系统提供了一个读写层。

Picture of Docker Container Write Layer over Image Read-only layer
Picture of Docker Container Write Layer over Image Read-only layer

为了将这些只读层和读写层合并在一起,Docker 使用了 Union File System(联合文件系统)。但通过容器的状态变化并不会反映在镜像中,任何文件更改都严格保存在容器中。这就带来了一个问题:当一个容器脱机时,在容器实例化的底层镜像中任何改变都不会被保存。

因此,为了持久化容器所做的更改(也有其他好处),Docker 开发了 Volume,通常被称作数据卷。简而言之,数据卷是存在于 Union File System 之外的目录或文件,通常位于主机文件系统上。

步骤4a:使用数据卷创建开发版镜像

现在我们有了一个表示应用程序开发版本的镜像,我们准备在主机上创建一个容器,其中包含指向应用程序源代码本地目录的 数据卷:

重要提示:如果你已经在容器外运行了应用程序(例如,node bin/www),与我们在 shell 脚本 initialization.sh 中设置的命令相同,并且你的文件夹根目录下有一个本地的 node_modules 目录,请现在删除他们。

  1. 从终端/命令提示符进入 express 应用程序根目录。
  2. docker run –name express-dev-app -p 7000:3000 -v $(pwd):/var/app express-dev-i

注意:赋给 volume -v 标志的值被拆分为由 (:) 分隔的主机目录,后面跟着容器 WORKDIR 工作目录。这只与我们的设置有关,但指定的容器目录不一定是 WORKDIR 目录。

我们做了什么?

  1. 使用 Docker RUN 命令,我们生成并启动了一个容器(一个镜像的实例)。
  2. 并使用 -name 标志给我们的容器提供了 "express-dev-app" 的名字。
  3. 将我们的主机上7000的本地端口映射到我们使用 -p 标志公开的3000内部容器端口(与Dockerfile EXPOSE命令一起使用)。
  4. 使用 volume -v 标志,我们在主机上挂载了一个数据卷,$(pwd) 代表主机上的“当前工作目录”到容器 "/var/app" 中的一个目录(指定为 Dockerfile 中的 WORKDIR)。
  5. 最后,指定要生成的镜像"express-dev-i" ,并将其作为容器运行

提示:当容器被移除时,默认情况下不会删除数据卷。但是,您可以使用 docker remove(rm)指定 -v 标志来删除关联卷: docker rm -v [容器的名称或ID]

步骤4b:验证容器是否正在运行

如果一切按计划进行,您应该能在终端/命令提示符中看到 npm install 的结果和正在安装的 node modules 列表。我特意遗漏了这个被分开的 -d 标志,这样就可以观察到了。

我们可以通过运行 docker ps命令列出正在运行的容器,来验证是否有问题导致容器停止运行。如果没有列出,可以将 ALL -a 标志添加到上述命令中,以显示所有容器,并查看是否有“express-dev-app”容器列出的退出错误。

步骤4c:检查容器的挂载信息

在我们继续之前,我们可以通过使用下面的 INSPECT 命令来查看有关装载量的信息,这个命令会向我们显示大量的容器信息:

代码语言:bash
复制
docker inspect express-dev-app
Mount Information
Mount Information

我们做了什么?

  • 我们使用 Docker INSPECT 命令查看有关容器信息的 JSON 格式输出。
  • 它包含一个 "Mounts" 部分,列出了数据卷的来源。
  • 它指向我们在本地主机上指定的项目根目录,以及指向容器中的 WORKDIR 目录的目的地。

步骤5:在本地编辑源代码

这大概你一直在等待的时刻吧!我们将单刀直入,看看我们如何在本地进行源代码更改,并将其反映在容器中。

重要提示:请务必查看第6步,了解关于安装的本地源代码和容器的一些重要提示,命令和解释。

步骤5a:验证正在运行的 Express App

1. 在浏览器中进入http://localhost:7000

浏览器运行结果
浏览器运行结果

2. 在根目录中,导航到 /views 目录并打开 index.jade 文件

3. 找到行

代码语言:dockerfile
复制
p Welcome to #{title}

4. 修改为

代码语言:javascript
复制
p Welcome to #{title} running in a container

5. 回到浏览器中,刷新URL

刷新后
刷新后

我们做了什么?

我们不需要重建,甚至无需重新启动容器,就能看到我们对这个 express 应用的前端进行的简单而重要的改动被反映在了容器中。

步骤6:Node_Modules 驻留本地

还记得吗,我们在创建最后一个容器之前删除了本地应用程序根目录中可能存在的任何 node_modules 文件夹。但是,如果你再查看一下,会发现 node_modules 文件夹依然存在。

这是因为托管运行 node.js 应用程序所需的更改(例如安装所有依赖的 node 模块),会通过我们挂载的卷在本地反映出来。

步骤6a:与容器进行交互

我们可以通过连接到正在运行的容器来验证。在容器上打开一个 bash shell 并检查有关工作目录的信息。

我们没有以脱机模式启动容器,因此您需要停止正在运行的容器,并使用docker start命令重启,如上一个教程中所示。

或者您需要打开一个新的终端/命令提示符并通过:

  1. docker exec -it express-dev-app /bin/sh
  2. 在提示符下输入命令: ls -l
Image of bash shell interaction on Docker container
Image of bash shell interaction on Docker container

我们做了什么?

  • 我们使用 EXEC 命令连接正在运行的容器,使用 -it 标志提供交互式终端,并指定我们想要使用 /bin/sh 参数连接到bash shell。
  • 你应该注意到,当我们连接到容器时,我们将自动连接到正在工作的 WORKDIR 目录。
  • 我们使用 list 命令ls -l来显示目录内容实际上显示了本地卷挂载主机目录的内容。

结论

我们在 Docker for Developer 教程中完成的看起来很简单,但是非常高效。我们将我们的应用程序设置模块化,到一个包含应用程序必要设置的容器,同时保持对我们运行在容器中的应用程序源代码的控制。

本篇教程中,我们只是初步地在应用程序开发中应用 Docker 容器化技术。在下一个教程中,我们将抛开这些简单的例子,通过在容器中使用和运行支持热重载的通用(同构)React.js 应用程序,进行更深入的实践。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Docker for Developers:入门
    • 步骤1:创建一个开发版镜像
      • 步骤2:创建我们的初始化 Bash Shell 脚本
        • 步骤3:创建应用程序开发版镜像
          • 步骤3a:构建开发版镜像
          • 步骤3b:列出镜像
        • 步骤4:生成并运行挂载数据卷(Volume)的容器
          • 步骤4a:使用数据卷创建开发版镜像
          • 步骤4b:验证容器是否正在运行
          • 步骤4c:检查容器的挂载信息
        • 步骤5:在本地编辑源代码
          • 步骤5a:验证正在运行的 Express App
        • 步骤6:Node_Modules 驻留本地
          • 步骤6a:与容器进行交互
        • 结论
        相关产品与服务
        容器镜像服务
        容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档