Docker 入门指南
Docker 入门指南
0 Docker术语
0.1 容器(Container)
容器是指一个独立于主机上其他所有进程的沙箱进程。Docker使用kernal namespaces和cgroup来提供容器隔离。
- 通过DockerAPI 或 CLI可以运行的镜像实例,可以创建、启动、停止、移动或删除。
- 可以运行在本地主机、虚拟机、云上
- 可以运行在任何系统上
- 容器之间相互独立,每个容器的运行环境、配置、软件等也相互独立。
0.2 镜像(Image)
当运行一个容器时需要一个独立的文件系统。这个系统是容器镜像提供的。因为镜像包含容器的文件系统,因此镜像必须包含运行一个应用程序的所有依赖环境。
0.3 Volume
-
容器卷(Volumes):Volumes用于持久化数据。默认情况下,容器所有的配置是各自独立的,因此当容器被删除时,配置也会丢失。容器卷可以将容器特定文件系统路径回连到主机,如果目录被挂载,在当前目录所做的更改会同步到本地。即只要启动新容器时挂载相同的目录,就能查看到目录中的文件。
-
绑定挂载(bind mounts):bind mounts用于持久化数据。通过bind mounts可以精确控制主机上的挂载点,也可能用于为容器提供额外的数据。
对于应用程序:利用bind mounts挂载源代码到容器可以实时看到代码的变化和回包。
对于基于节点的应用程序:nodemon可以更好的监控应用程序重启时的文件变化。 -
Volumes与bind mounts对比
Named Volumes Bind Mounts 主机位置 Docker 决定 自定义 挂载示例(-v) my-volume:/usr/local/data /path/to/data:/usr/local/data 作为新卷挂载到容器 Yes No 支持Volume Drivers Yes No
1 环境准备
1.0 安装 docker
# CentOS 下
cd /etc/yum.repos.d/
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install -y docker-ce
# Ubuntu 下
sudo apt install docker.io
1.1 配置 docker 加速
vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://1nj0zren.mirror.aliyuncs.com",
"https://docker.mirrors.ustc.edu.cn",
"http://f1361db2.m.daocloud.io",
"https://registry.docker-cn.com"
]
}
systemctl daemon-reload
systemctl restart docker
1.2 获取并安装 App
git clone https://github.com/docker/getting-started.git
cd /path/to/app
docker build -t getting-started .
1.3 启动docker/getting-started
$ docker run -d -p 80:80 docker/getting-started
# 也可以执行以下命令
$ docker run -dp 80:80 docker/getting-started
-d
- 后台运行容器-p 80:80
- 映射主机80端口-P
- 将容器的端口暴露在宿主机的随机端口上docker/getting-started
- 要使用的镜像--name
- 指定名字-t
- 创建一个虚拟终端-i
- 将容器的标准输入保持打开- 退出 -
exit
、Ctrl + D
2 创建一个App容器镜像
2.1 下载APP源代码
- 确保前面设置的docker容器正常运行。
- 下载地址:http://yourIP/assets/app.zip
- 解压app.zip。
2.2 编写Dockerfile
Dockerfile 类似一个创建容器镜像的文本脚本说明书。
-
在
package.json
同个目录下创建一个名称为Dockerfile
的文件,内容如下:FROM node:12-alpine # Adding build tools to make yarn install work on Apple silicon / arm64 machines RUN apk add --no-cache python2 g++ make WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"]
注:Dockerfile文件不能有任何后缀,否则会报错。
-
创建容器镜像
docker build -t getting-started .
- 之所以会下载许多的
layers
,是因为我们指定了要从node:12-alpine
开始创建,由于我们系统中不存在,所以需要下载。 - 下载完成后,首先复制应用程序,其次使用
yarn
安装应用程序的依赖,以上命令使用了默认的命令从该镜像中启动一个容器。 -t
参数:命令该镜像为:getting-started
。.
告诉Docker使用本地的Dockerfile
。
- 之所以会下载许多的
-
启动App容器
docker run -dp 3000:3000 getting-started
-
访问
http://yourIP:3000/
可以访问创建好的App,并创建两个Items
3 更新App源代码
-
在
src/static/js/app.js
文件中更新内容如下:# 将56行数据修改成以下内容 // <p className="text-center">No items yet! Add one above!</p> <p className="text-center">You have no todo items yet! Add one above!</p>
-
创建App更新后的image版本
docker build -t getting-started .
-
启动一个新的App容器
# 需要先删除旧版本的App容器 # 停止App容器 docker stop <the-container-id> # 删除App容器 docker rm <the-container-id> # 一次命令停止并删除App容器 docker rm -f <the-container-id> docker run -dp 3000:3000 getting-started
4 共享App镜像
4.1 创建一个repo
在共享一个镜像之前,我们需要在Docker Hub上创建一个repo
-
选择
Create Repository
-
repo名称:
getting-started
-
确保可见为公共后点击创建
docker push fcarey/getting-started:tagname
4.2 推送App镜像
-
命令行模式
docker push fcarey/getting-started:tagname The push refers to repository [docker.io/fcarey/getting-started] An image does not exist locally with the tag: fcarey/getting-started
提示错误:该命令将会查找本地
fcarey/getting-started
镜像,但是没有找到。可以通过docker image ls
查看本地存在的镜像。要解决该问题,需要给已经存在的镜像打tag。 -
命令行登录
Docker Hub
docker login -u YOUR-USER-NAME
-
使用
docker tag
命令给getting-started
镜像打tag,记得前缀换成你自己的Docker ID。docker tag getting-started YOUR-USER-NAME/getting-started
-
如果不想配置镜像的tagname,默认为:
latest
docker push YOUR-USER-NAME/getting-started
4.3 运行App镜像新实例
-
浏览器打开labs.play-with-docker.com,并登录
-
点击左侧"+ ADD NEW INSTANCE"
-
在终端中启动刚刚推送的App镜像
docker run -dp 3000:3000 YOUR-USER-NAME/getting-started
-
点击"Open Port" 按钮,输入3000查看你所修改的App。
5 持久化数据
更新镜像后,新的App容器中并没有旧App容器所创建的数据,为什么呢?
5.1 容器的文件系统
当容器运行时,会使用不同layers,每个容器都会创建各自的暂存空间,即使使用相同的镜像,每个容器也是互不影响。
-
启动一个新容器
docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
- 启动新容器的同时,开启一个bash shell ,并插入两个命令
shuf -i 1-10000 -n 1 -o /data.txt
为随机生成1个在1-10000之间的数并保存在/data.txt文件中tail -f /dev/null
查看/dev/null最新的日志记录
-
命令行查看
/data.txt
内容,会看到一个随机的数字sudo docker exec <container-id> cat /data.txt
-
使用相同的镜像启动另外一个ubuntu容器,并容器中并不存在
data.txt
文件docker run -it ubuntu ls /
-
删除第一个容器
docker rm -f <container-id>
-
结论:容器所有的配置是各自独立的,因此当容器被删除时,配置也会丢失。
5.2 持久化ToDo数据
默认情况下,App容器的数据保存在 /etc/todos/todo.db
,即SQLite数据库中。通过创建volume并挂载相应目录可以实现持久化数据。
5.2.1 named volume
适用于只是保存数据但并不关心数据的存储位置。
-
创建volume
docker volume create todo-db
-
停止并删除之前所创建的App容器
docker rm -f <container-id>
-
启动新的App容器,命令如下:
docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
-v
:指定要挂载的卷名:要挂载到容器的目录
-
启动容器后,打开App并添加一些ToDo清单
-
删除刚刚创建的App容器
docker rm -f <container-id>
-
使用第3步命令启动一个新的App容器
-
打开App后,我们之前创建的数据还在
-
查看named volume实际存放数据的位置
docker volume inspect todo-db
5.2.2 bind mounts
适用于保存数据并指定数据存储位置;通过物理主机修改容器中相应程序的配置文件。
-
以创建一个开发模式的容器为例
创建开发模式的容器的条件: 1. 挂载源码到容器 2. 安装所有依赖,包括"dev"依赖 3. 开启nodemon监控文件系统改变
-
在源码文件目录下运行以下命令:
docker run -dp 3000:3000 \ -w /app -v "$(pwd):/app" \ node:12-alpine \ sh -c "yarn install && yarn run dev"
-dp 3000:3000
:后台运行并将主机3000端口映射到容器3000端口。-w /app
:指定运行命令的工作目录。-v "$(pwd):/app"
:绑定挂载主机当前的工作目录到容器的/app
目录。sh -c "yarn install && yarn run dev"
:使用shell运行yarn install
安装所有依赖,然后运行yarn run dev
。此时查看package.json
可以看到dev
脚本启动了nodemon
-
查看运行日志:按
Ctrl
+C
退出┌──(root💀kali)-[/docker/app] └─# docker logs -f 2f788837b11a yarn install v1.22.17 [1/4] Resolving packages... warning Resolution field "ansi-regex@5.0.1" is incompatible with requested version "ansi-regex@^2.0.0" warning Resolution field "ansi-regex@5.0.1" is incompatible with requested version "ansi-regex@^3.0.0" warning sqlite3 > node-gyp > tar@2.2.2: This version of tar is no longer supported, and will not receive security updates. Please upgrade asap. warning sqlite3 > node-gyp > request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142 warning sqlite3 > node-gyp > request > har-validator@5.1.5: this library is no longer supported warning sqlite3 > node-gyp > request > uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. Done in 36.74s. yarn run v1.22.17 $ nodemon src/index.js [nodemon] 2.0.13 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mjs,json [nodemon] starting `node src/index.js` Using sqlite database at /etc/todos/todo.db Listening on port 3000
-
在
app/src/static/js/app.js
文件中,将"Add Item" 改成 "Add"- {submitting ? 'Adding...' : 'Add Item'} + {submitting ? 'Adding...' : 'Add'}
-
重新刷新页面可以看到修改成功
6 多容器 Apps
6.1 配置原则:让每个容器只做好一件事
独立功能的容器:
- 更方便的使用不同的数据库扩展API和前端
- 同时运行存在不同版本或更新版本的app容器。
- 在生产环境中使用数据库托管服务,独立发布应用。
- 一个容器内运行多个进程程序(每个容器只有一个进行),增加了容器的启动或关闭的复杂性。
6.2 配置多容器运行应用
配置App应用工作模式如下:
6.2.1 配置Mysql容器
-
创建容器网络
docker network create todo-app
-
启动一个Mysql容器配置如下,并连接到上面创建的容器网络中
docker run -d \ --network todo-app --network-alias mysql \ -v todo-mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ -e MYSQL_DATABASE=todos \ mysql:5.7
- 使用一个名称为
todo-mysql-data
的volume挂载到Mysql数据存储路径/var/lib/mysql
。若本地没有该name volume,docker会自动创建。
- 使用一个名称为
-
确认mysql是否正常运行:可以看上去创建的todos数据库
docker exec -it 710257e08335 mysql -p # 密码为:secret mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | todos | +--------------------+ 5 rows in set (0.00 sec)
6.2.2 MySQL容器网络状态
-
使用 nicolaka/netshoot 调试网络:利用
nicolaka/netshoot
镜像创建一个容器,并确保连接到同一个网络。docker run -it --network todo-app nicolaka/netshoot
-
在刚刚创建的容器中使用
dig或ping
命令查看主机名为MySQL的IP。# dig mysql ; <<>> DiG 9.16.22 <<>> mysql ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21154 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;mysql. IN A ;; ANSWER SECTION: mysql. 600 IN A 172.18.0.2 ;; Query time: 0 msec ;; SERVER: 127.0.0.11#53(127.0.0.11) ;; WHEN: Thu Mar 17 09:15:55 UTC 2022 ;; MSG SIZE rcvd: 44 # ping -c 1 mysql PING mysql (172.18.0.2) 56(84) bytes of data. 64 bytes from mysql.todo-app (172.18.0.2): icmp_seq=1 ttl=64 time=0.144 ms
6.2.3 利用MySQL容器运行App程序
docker中所支持的MySQL环境变量:
MYSQL_HOST
:MySQL容器主机名MYSQL_USER
:连接MySQL的用户名MYSQL_PASSWORD
:连接MySQL的用户密码MYSQL_DB
:指定要连接MySQL数据库名称
-
启动新容器并配置如下:
docker run -dp 3000:3000 \ -w /app -v "$(pwd):/app" \ --network todo-app \ -e MYSQL_HOST=mysql \ -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ node:12-alpine \ sh -c "yarn install && yarn run dev"
-
查看运行日志:
docker logs <container-id> yarn install v1.22.17 [1/4] Resolving packages... warning Resolution field "ansi-regex@5.0.1" is incompatible with requested version "ansi-regex@^2.0.0" warning Resolution field "ansi-regex@5.0.1" is incompatible with requested version "ansi-regex@^3.0.0" success Already up-to-date. Done in 0.68s. yarn run v1.22.17 $ nodemon src/index.js [nodemon] 2.0.13 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mjs,json [nodemon] starting `node src/index.js` Waiting for mysql:3306. Connected! Connected to mysql db at host mysql Listening on port 3000
-
浏览器打开app程序并添加几个todo list
-
连接mysql数据库验证数据写入在MySQL数据库中
docker exec -it <mysql-container-id> mysql -p todos mysql> use todos mysql> select * from todo_items; +--------------------------------------+--------+-----------+ | id | name | completed | +--------------------------------------+--------+-----------+ | 1883d865-3eae-45f1-ac72-3ff9b2e8f4cd | test01 | 0 | | 4db8cd95-f647-455b-87a6-380afa0208c9 | test02 | 0 | +--------------------------------------+--------+-----------+ 2 rows in set (0.00 sec)
7 使用Docker Compose
Docker Compose 是用于帮助定义并共享多容器应用程序的工具。通过Compose可以创建YAML文件来通过一条命令定义服务。Compose的优点是可以在保存在项目repo根中的YAML文件中定义你的应用程序的栈,并可以让任何人建设,克隆并使用你的项目。
7.1 创建Compose文件
-
首先确认已安装Docker Compose
# docker-compose version docker-compose version 1.29.2, build unknown docker-py version: 5.0.0 CPython version: 3.9.10 OpenSSL version: OpenSSL 1.1.1m 14 Dec 2021
-
在app项目根目录下,创建一个名称为
docker-compose.yml
的文件,并配置如下内容定义schema版本:version: "3.8" services:
- 配置参考指南: Compose file reference
-
后面我们将迁移一个要运行服务或容器作为App应用程序的一部分
7.2 定义App服务
-
在前面定义app容器时,所使用的命令:
docker run -dp 3000:3000 \ -w /app -v "$(pwd):/app" \ --network todo-app \ -e MYSQL_HOST=mysql \ -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ node:12-alpine \ sh -c "yarn install && yarn run dev"
-
定义服务入口和容器的镜像,可以为服务指定一个名称,该名称将自动成为该容器的网络别名。
version: "3.8" services: app: image: node:12-alpine
-
迁移定义app容器时的命令
version: "3.8" services: app: image: node:12-alpine command: sh -c "yarn install && yarn run dev"
-
迁移端口映射配置:此处使用的是 short syntax ,更详细的请参考 long syntax
version: "3.8" services: app: image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000
-
迁移工作目录:
working_dir
定义工作目录(-w /app
);volumes
定义volume映射-v "$(pwd):/app"
version: "3.8" services: app: image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app
-
迁移环境变量设定
version: "3.8" services: app: image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app environment: MYSQL_HOST: mysql MYSQL_USER: root MYSQL_PASSWORD: secret MYSQL_DB: todos
7.3 定义MySQL服务
-
在前面定义MySQL容器时,所使用的命令:
docker run -d \ --network todo-app --network-alias mysql \ -v todo-mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ -e MYSQL_DATABASE=todos \ mysql:5.7
-
定义服务入口并命名为
mysql
version: "3.8" services: app: # The app service definition mysql: image: mysql:5.7
-
定义volume映射:在Compose中不会像使用
docker run
命令会自动创建named volume,因此需要使用lvolumes:
区块定义volume,并在服务配置中指定挂载点,若提供volume名,将使用默认选项,更多选项参考。version: "3.8" services: app: # The app service definition mysql: image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql volumes: todo-mysql-data:
-
定义环境变量
version: "3.8" services: app: # The app service definition mysql: image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: todos volumes: todo-mysql-data:
-
最终的配置文件
docker-compose.yml
内容如下:version: "3.8" services: app: image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app environment: MYSQL_HOST: mysql MYSQL_USER: root MYSQL_PASSWORD: secret MYSQL_DB: todos mysql: image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: todos volumes: todo-mysql-data:
7.4 运行应用程序栈
-
删除之前所所创建的app与mysql容器
docker rm -f <CONTAINER-ID>
-
运行应用程序栈
docker-compose up -d Creating network "app_default" with the default driver Creating volume "app_todo-mysql-data" with default driver Creating app_app_1 ... done Creating app_mysql_1 ... done
-
查看运行日志:查看特定服务的日志
docker-compose logs -f app
# docker-compose logs -f Attaching to app_mysql_1, app_app_1 app_1 | yarn install v1.22.17 app_1 | [1/4] Resolving packages... app_1 | warning Resolution field "ansi-regex@5.0.1" is incompatible with requested version "ansi-regex@^2.0.0" app_1 | warning Resolution field "ansi-regex@5.0.1" is incompatible with requested version "ansi-regex@^3.0.0" app_1 | success Already up-to-date. app_1 | Done in 0.78s. app_1 | yarn run v1.22.17 app_1 | $ nodemon src/index.js app_1 | [nodemon] 2.0.13 app_1 | [nodemon] to restart at any time, enter `rs` app_1 | [nodemon] watching path(s): *.* app_1 | [nodemon] watching extensions: js,mjs,json app_1 | [nodemon] starting `node src/index.js` app_1 | Waiting for mysql:3306........... app_1 | Connected! app_1 | Connected to mysql db at host mysql app_1 | Listening on port 3000
-
删除所有的容器
# docker-compose down Stopping app_mysql_1 ... done Stopping app_app_1 ... done Removing app_mysql_1 ... done Removing app_app_1 ... done Removing network app_default
8 创建image最佳实践
8.1 image层
-
查看image文件组成
# docker image history getting-started IMAGE CREATED CREATED BY SIZE COMMENT 77ca1987abde 10 days ago /bin/sh -c #(nop) CMD ["node" "src/index.js… 0B 947863fe4b66 10 days ago /bin/sh -c yarn install --production 86.1MB 5738a14f4b25 10 days ago /bin/sh -c #(nop) COPY dir:b43b032ba51228260… 4.6MB 0909fcbc4c2e 10 days ago /bin/sh -c #(nop) WORKDIR /app 0B 37afb0a6e721 10 days ago /bin/sh -c apk add --no-cache python2 g++ ma… 223MB 1b156b4c3ee8 5 weeks ago /bin/sh -c #(nop) CMD ["node"] 0B <missing> 5 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B <missing> 5 weeks ago /bin/sh -c #(nop) COPY file:4d192565a7220e13… 388B <missing> 5 weeks ago /bin/sh -c apk add --no-cache --virtual .bui… 7.84MB <missing> 5 weeks ago /bin/sh -c #(nop) ENV YARN_VERSION=1.22.17 0B <missing> 5 weeks ago /bin/sh -c addgroup -g 1000 node && addu… 77.6MB <missing> 5 weeks ago /bin/sh -c #(nop) ENV NODE_VERSION=12.22.10 0B <missing> 3 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 3 months ago /bin/sh -c #(nop) ADD file:9233f6f2237d79659… 5.59MB
--no-trunc
:查看完整输出内容
8.2 层缓存
-
当改变一个层,所有的下游层都必须要重建。可以重构Dockerfile以支持依赖的缓存。对于所有基于节点的应用,
package.json
定义了所有依赖,如果一开始复制了package.json
文件,安装依赖,再复制其他文件,然后只有当package.json
文件更改时才会重建yarn依赖。 -
查看我们所使用的Dockerfile
FROM node:12-alpine RUN apk add --no-cache python2 g++ make WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"]
-
修改Dockerfile内容:先复制
package.json
文件,再安装依赖,最后复制所有文件内容FROM node:12-alpine RUN apk add --no-cache python2 g++ make WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --production COPY . . CMD ["node", "src/index.js"]
-
在Dockerfile文件同目录下创建
.dockerignore
文件,内容如下:node_modules
.dockerignore
文件是有选择的复制只与image相关的文件的一个简单方法。在本例中,node_modules
文件夹会在第二次复制操作时被忽略,它可能会覆盖由RUN步骤中的命令创建的文件。
-
使用
docker build
创建一个新镜像# docker build -t getting-started . Sending build context to Docker daemon 4.656MB Step 1/7 : FROM node:12-alpine ---> 1b156b4c3ee8 Step 2/7 : RUN apk add --no-cache python2 g++ make ---> Using cache ---> 37afb0a6e721 Step 3/7 : WORKDIR /app ---> Using cache ---> 0909fcbc4c2e Step 4/7 : COPY package.json yarn.lock ./ ---> 129fe0efb187 Step 5/7 : COPY . . ---> 9d237c50ec6c Step 6/7 : RUN yarn install --production ---> Running in d1951023995a yarn install v1.22.17 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... Done in 10.30s. Removing intermediate container d1951023995a ---> 9236fb3caf32 Step 7/7 : CMD ["node", "src/index.js"] ---> Running in f4748415315d Removing intermediate container f4748415315d ---> 1b87ea345300 Successfully built 1b87ea345300 Successfully tagged getting-started:latest
-
任意修改
src/static/index.html
内容:如修改<title>
为"The Awesome Todo App" -
重新创建Docker image
#docker build -t getting-started . Sending build context to Docker daemon 4.656MB Step 1/7 : FROM node:12-alpine ---> 1b156b4c3ee8 Step 2/7 : RUN apk add --no-cache python2 g++ make ---> Using cache ---> 37afb0a6e721 Step 3/7 : WORKDIR /app ---> Using cache ---> 0909fcbc4c2e Step 4/7 : COPY package.json yarn.lock ./ ---> Using cache ---> 129fe0efb187 Step 5/7 : COPY . . ---> def0ab2c3473 Step 6/7 : RUN yarn install --production ---> Running in b371033126a4 yarn install v1.22.17 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... Done in 28.41s. Removing intermediate container b371033126a4 ---> 483d314e3860 Step 7/7 : CMD ["node", "src/index.js"] ---> Running in 6a2070d91e4b Removing intermediate container 6a2070d91e4b ---> 4c7cf643078a Successfully built 4c7cf643078a Successfully tagged getting-started:latest
- 可以看到 2-3 使用了cache