This repository has been archived by the owner on Mar 23, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,3 +22,4 @@ Dockerfile编写 | |
dockerfile/ARG | ||
dockerfile/SHELL | ||
dockerfile/[译]Dockerfile编写最佳实践 | ||
dockerfile/多阶段构建 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
|
||
# 多阶段构建 | ||
|
||
参考:[Use multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) | ||
|
||
多阶段构建(`multi-stage`)功能是从`Docker 17.05`开始的。其有利于提升`Dockerfile`的可读性和可维护性 | ||
|
||
## 之前的构建模式 | ||
|
||
没有多阶段构建之前,通常将构建和实现分为两个阶段,利用不同的`Dockerfile`文件去实现,这有利于最终实现镜像的小体积。比如,利用`c++`代码实现`Hello World`输出 | ||
|
||
``` | ||
// main.cpp | ||
#include <iostream> | ||
#include "test.h" | ||
int main() { | ||
Test test; | ||
test.PrintHello(); | ||
std::cout << "Hello, World!" << std::endl; | ||
return 0; | ||
// test.h | ||
#ifndef MULTI_TEST_H | ||
#define MULTI_TEST_H | ||
class Test { | ||
public: | ||
void PrintHello(); | ||
}; | ||
#endif //MULTI_TEST_H | ||
// test.cpp | ||
#include "test.h" | ||
#include <iostream> | ||
void Test::PrintHello() { | ||
std::cout << "Hello World" << std::endl; | ||
} | ||
// CMakeLists.txt | ||
cmake_minimum_required(VERSION 3.1) | ||
project(multi) | ||
set(CMAKE_CXX_FLAGS "-static ${CMAKE_CXX_FLAGS}") | ||
set(CMAKE_CXX_STANDARD 11) | ||
add_executable(app main.cpp test.cpp test.h) | ||
``` | ||
|
||
需要一个构建`Dockerfile`,在`ubuntu:18.04`中实现可执行文件生成 | ||
|
||
``` | ||
// Dockerfile.build | ||
FROM ubuntu:18.04 | ||
RUN apt-get update && apt-get install -f && apt-get install -y make cmake gcc g++ | ||
COPY CMakeLists.txt main.cpp test.cpp test.h /app/ | ||
WORKDIR /app | ||
RUN cmake . && make | ||
``` | ||
|
||
同时需要一个实现镜像,使用最小的`Linux`镜像`Alpine` | ||
|
||
``` | ||
FROM alpine:latest | ||
WORKDIR /root/ | ||
COPY app . | ||
ENTRYPOINT ["./app"] | ||
``` | ||
|
||
编写构建脚本`build.sh`如下: | ||
|
||
``` | ||
#!/bin/sh | ||
echo Building zjzstu/hello:build | ||
docker build -t zjzstu/hello:build . -f Dockerfile.build | ||
docker container create --name extract zjzstu/hello:build | ||
docker container cp extract:/app/app ./app | ||
docker container rm -f extract | ||
echo Building zjzstu/hello:latest | ||
docker build --no-cache -t zjzstu/hello:latest . | ||
rm ./app | ||
``` | ||
|
||
执行构建脚本,最后得到实现镜像`zjzstu/hello:latest` | ||
|
||
``` | ||
REPOSITORY TAG IMAGE ID CREATED SIZE | ||
zjzstu/hello latest 58cfb6bb0f00 9 minutes ago 7.83MB | ||
``` | ||
|
||
## 多阶段构建 | ||
|
||
使用多阶段构建功能后,只需在单个`Dockerfile`文件中进行多个镜像的构建 | ||
|
||
### 实现 | ||
|
||
重新编写`Dockerfile`如下 | ||
|
||
``` | ||
$ cat Dockerfile | ||
FROM ubuntu:18.04 | ||
RUN apt-get update && apt-get install -f && apt-get install -y make cmake gcc g++ | ||
COPY CMakeLists.txt main.cpp test.cpp test.h /app/ | ||
WORKDIR /app | ||
RUN cmake . && make | ||
FROM alpine:latest | ||
WORKDIR /root/ | ||
COPY --from=0 /app/app . | ||
ENTRYPOINT ["./app"] | ||
``` | ||
|
||
构建镜像`zjzstu/ubuntu:latest`并输出 | ||
|
||
``` | ||
$ docker build -t zjzstu/hello:latest . | ||
Sending build context to Docker daemon 20.99kB | ||
Step 1/9 : FROM ubuntu:18.04 | ||
---> 2ca708c1c9cc | ||
Step 2/9 : RUN apt-get update && apt-get install -f && apt-get install -y make cmake gcc g++ | ||
---> Using cache | ||
---> 865ec579ba4a | ||
Step 3/9 : COPY CMakeLists.txt main.cpp test.cpp test.h /app/ | ||
---> Using cache | ||
---> 9ef7ff833894 | ||
Step 4/9 : WORKDIR /app | ||
---> Using cache | ||
---> 58842dc65b4b | ||
Step 5/9 : RUN cmake . && make | ||
---> Using cache | ||
---> 0534f6cacc59 | ||
Step 6/9 : FROM alpine:latest | ||
---> 961769676411 | ||
Step 7/9 : WORKDIR /root/ | ||
---> Using cache | ||
---> a64ae89edb81 | ||
Step 8/9 : COPY --from=0 /app/app . | ||
---> 77a7cda0d6b2 | ||
Step 9/9 : ENTRYPOINT ["./app"] | ||
---> Running in 717aa7a3a5ea | ||
Removing intermediate container 717aa7a3a5ea | ||
---> c73f340b5938 | ||
Successfully built c73f340b5938 | ||
Successfully tagged zjzstu/hello:latest | ||
$ docker run zjzstu/hello:latest | ||
Hello World | ||
Hello, World! | ||
``` | ||
|
||
最终得到的镜像和之前构建的大小一致 | ||
|
||
``` | ||
zjzstu/hello latest c73f340b5938 3 minutes ago 7.83MB | ||
``` | ||
|
||
### 解析 | ||
|
||
使用多阶段构建,可以在同一个`Dockefile`中执行多次`FROM`指令,每个`FROM`指令使用不同的基础镜像,每次都会开始新的构建阶段 | ||
|
||
后面的构建阶段可以从之前的构建镜像中复制文件,比如 | ||
|
||
``` | ||
COPY --from=0 /app/app . | ||
``` | ||
|
||
标识符`--from=0`表示从第一个构建镜像中复制 | ||
|
||
`docker build`命令中输入的参数`-t zjzstu/hello:latest`指定了最后一个镜像的标记 | ||
|
||
``` | ||
Successfully built c73f340b5938 | ||
Successfully tagged zjzstu/hello:latest | ||
``` | ||
|
||
## 命名构建阶段 | ||
|
||
默认从上到下按数字标识每个构建阶段,也可以使用`AS <name>`指定 | ||
|
||
``` | ||
FROM ubuntu:18.04 AS builder | ||
``` | ||
|
||
后面阶段复制时可以使用构建阶段命名 | ||
|
||
``` | ||
COPY --from=builder /app/app . | ||
``` | ||
|
||
## 在特定的构建阶段停止 | ||
|
||
在构建镜像时,不必构建包括每个阶段在内的整个`Dockerfile`,可以指定目标构建阶段。下面的构建命令指定了完成`builder`阶段构建后停止 | ||
|
||
``` | ||
$ docker build --target builder -t zjzstu/hello:latest . | ||
``` | ||
|
||
这种实现有如下好处: | ||
|
||
1. 调试特定生成阶段 | ||
2. 可以指定调试(`debug`)阶段(启用所有调试符号或工具)和生产(`production`)阶段 | ||
3. 指定测试(`test`)阶段,在这个阶段中,应用程序将被测试数据填充;同时指定生产阶段,使用真实数据进行测试 | ||
|
||
## 使用外部镜像 | ||
|
||
使用多阶段构建时,不局限于从先前在`Dockerfile`中创建的阶段进行复制。可以使用`COPY --from`指令从单独的镜像复制,可以使用本地镜像名、本地或`Docker`注册表上可用的标记或标记`ID`。`Docker`客户端在必要时提取镜像并从中复制工件。语法如下: | ||
|
||
``` | ||
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf | ||
``` |