Day03-开始使用Docker吧(DockerFile)

前言

今天的文章要来介绍如何产生 Docker 的映像档了,有了上一篇文章的介绍相信读者对於 Docker 已经有了初步的了解了,应该知道要有映像档是可以用来虚拟化应用程序及其执行环境的档案,但执行环境基本上不用自己写因为 DockerHub 上面都有,所以今天这篇文章主要会介绍如何产生应用程序的映像档。

不晓得大家有没有写过 C++,要产生一个可以执行的 .out 档通常都会写一个 Makefile 最後再下 make 的指令来产生 .out 档,之所以要先讲这个跟 Docker 毫无相关的原因是因为 Docker 要产生映像档的方式跟这个几乎是一样的做法,而要产生映像档时要写的档案就叫做 Dockerfile 也是本篇文章要讲的重要内容。

由於笔者本身是前端出身因此这篇文章会来介绍如何产生前端的应用程序,接下来就正式进入本篇文章的重点吧!

Dockerfile 写法

Dockerfile 的写法简单来说分为四个阶段:

  • FROM:哪个映像档(通常会抓取应用程序的执行环境,例如前端通常都是写 JavaScript 所以就会利用 Node 作为执行环境)

  • WORKDIR:应用程序执行位置

  • ADD/COPY:新增或复制档案到应用程序执行位置

  • RUN:执行应用程序

所以简单的写法就会像下面这样:

接下来就简单的用白话文来解释一下上面每一行在做的事情:

  1. 第一行在做的事情就是我要抓取 node:10-alpine 映像档,所以在这个 Dockerfile 产生的映像档中就可以利用 node 的相关指令来做事情。
  2. 接下来就是我要让这个应用程序执行在 /app 的资料夹中,但要如何可以让这个 /app 的资料夹中有程序码可以让我执行呢?
  3. 所以我就要把本地端资料夹内的所有档案都复制到这个 /app 中,这就是 COPY . /app 的作用。
  4. 最後则是执行 npm install 并且利用 node index.js 来执行这个 index.js 档。

其实除了上面的写法外,Dockerfile 还有以下几个比较进阶的写法,分别是:

  • ARG:在 build 的过程中,可以从外部带入一些参数进来作为後续的环境变数使用。

  • ENV:在 build 的过程中,可以定义一些环境变数,让後面指令在执行时候可以使用,但前提是这个环境变数必须要从 ARG 中先被定义。

  • CMD:Docker 在启动容器的时候会执行的指令,不过这个指令只能执行一次,也就是假如你的 Dockerfile 中写了多个 CMD,最後只会执行最後写的那个 CMD

  • ENTRYPOINT:Docker 在启动容器的时候会优先执行的指令,所以比起 CMD 来说 ENTRYPOINT 会更优先执行。

上面的 CMDENTRYPOINT 看起来很像但其实两者是不太一样的,除了优先执行顺序外还有一个很重要的观念是,当今天 Dockerfile 只设定 CMD 而没有设定 ENTRYPOINT 时,Docker 在启动容器时会先使用预设的 ENTRYPOINT 指令,而预设的指令为 /bin/sh -c,所以假如今天要让容器不用这种方式启动的话,就要记得使用 ENTRYPOINT 这个写法喔!

Dockerfile 重要观念

其实 Dockerfile 还有一个很重要的观念要跟读者说,就是 Dockerfile 在每次要产生一个新的映像档前都会先看 Dockerfile 中有没有哪个是被修改过的,有点像是 Git 在检查档案有没有被修改的道理,如果发现该行的结果其实是不需要修改的话,Docker 就会很聪明的直接利用上一次产生的映像档当成是 cache 来当作是该行的结果。

举例来说:在上面的例子每一次都一定会拉到正确的 node:10-alpine 所以每次在产生映像档前我就不需要重新下载从之前已经下载好的 node:10-alpine 中拿过来用就好,也因为这个特性可以让 Dockerfile 在写法上可是有着效能上的差距,接下来我们就来一起看一下如何改善上面的 Dockerfile 写法吧!

改善 Dockerfile 写法

首先我们先来看原本的写法:

上面这份 Dockerfile 最大的问题就在於 COPY . /app 以及 RUN npm i && node index.js 这两行,每次只要我资料夹内有档案变动,我就要重新复制所有的档案到 /app 这个资料夹,乍看之下好像没什麽问题,但熟悉前端的人就会知道这些档案也包括了 package.json 以及 package-lock.json 这两个用来管理 mode_modules 的档案,也就会造成每次我复制档案时都会重新跑底下 npm install 这就会让整个 build 的过程变得相当久。

所以我们可以简单调整一下,先复制 package.json 以及 package-lock.json 这两个档案,然後跑 npm install,这样只要我没有变动到安装套件就不用花很长的时间再重新安装套件了,最终改善後的写法就像下面这样:

虽然行数变多了,但我们来看一下执行结果:

可以发现只要我没有动到 package.json 也就是没有安装新的套件,我就不会执行 npm i 这个指令,也因此让 Docker 可以更有效率的产生这个映像档,不用每次都重新安装套件。

multi-stage build

multi-stage build 可以想像成是一个档案有多个阶段的 build,也就是我要把某一阶段 build 好的档案传给另一阶段让他可以利用,举个例子来说:前端通常都会利用 webpack 进行专案内容打包的工作,但打包完会变成一个又一个的静态档,这时候就必须要利用 multi-stage build 的观念,把第一个 build 也就是经由 webpack 打包好的静态档传给下一个 build 可能是利用 nginx 来 serve 这些静态档。

multi-stage build 的写法也很简单,只要用到 as 这个关键字就好,透过 as 就可以把这个映像档的内容快速地提供给该 Dockerfile 下的其他映像档使用,写法如下:

可以发现这个 as 的关键字最主要的做法就是要把这个 FROM 的阶段当作是一个标签可以让其他阶段在进行操作时可以用 --from=标签 的方式进行使用,所以 multi-stage build 最主要的作用就是帮助一个复杂的 Dockerfile 可以有效的管理各个阶段的 build 并且可以让下一个阶段的 build 能使用上一个阶段的产物。

Docker 指令集

  • docker build Dockerfile -t imageName:编译 Dockerfile 并产生相对应 imageName 的映像档, -t 代表的是 tag 的意思。

  • docker images:列出该机器下的所有映像档。

  • docker run -d image:产生容器,并执行於背景。

  • docker container ps -a:列出该机器下的所有容器。

  • docker stop CONTAINER_ID:暂停 container,也就是关闭的意思。

  • docker image rm IMAGE_ID:移除该 IMAGE_ID 的映像档。

    但有时候可能会出现这个画面:

    这是因为这个映像档正在被某个容器使用中,因此想要移除该映像档前必须要先移除该容器。

  • docker container rm CONTAINER_ID:移除该 CONTAINER_ID 的容器

    但有时候可能会出现这个画面:

    这是因为这个容器正在使用中,因此想要移除该容器前必须要先把该容器暂停。

小结

今天介绍了如何撰写 Dockerfile 来产生 Docker 映像档,但假如我今天有多个档案每个档案都必须要执行,那我不就要连续下三次 docker build 以及 docker run 的指令,这样也太麻烦,即便我用 shellscript 的方式来统整这些指令还是很不方便,因此在明天的文章笔者要来介绍一个进阶的 Docker 用法叫 Docker Compose,就敬请期待明天的文章吧XD

如果对於文章有什麽问题都欢迎在下面留言给笔者,让我们就明天的文章见吧!


<<:  Day 6.来建立第一个专案吧

>>:  JavaScript入门 Day13_如何使用数字5

[Day14]C# 鸡础观念- 不同层次的阵列~二维阵列

既然空间有维度, 阵列也像是空间一样, 他是拥有维度的, 就让我们探索看看吧 二维阵列 就如同象棋棋...

DNS over TLS (DoT)

20201228 bind > 9.17.7 , dnsdist > 1.3.0 bin...

【从零开始的Swift开发心路历程-Day12】打造自己的私房美食名单Part1

昨天和前天我们分别介绍了UITableView和XIB,今天我们就来利用这两个工具来实做一个能显示餐...

Day 03 安装python、需要的package以及VS Code等环境建置

环境建置 安装python 至python官网下载并开启python安装档 https://www....

[ 卡卡 DAY 2 ] - React Native 是什麽? 原理?优点?

起源 於 2015 年 3 月 Facebook 开放了 React Native 的原始码,让使...