Skip to content
This repository has been archived by the owner on Mar 23, 2021. It is now read-only.

Commit

Permalink
docs(docker): 多阶段构建
Browse files Browse the repository at this point in the history
  • Loading branch information
zjZSTU committed Sep 26, 2019
1 parent f460f0f commit 328da43
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/source/docker/dockerfile.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ Dockerfile编写
dockerfile/ARG
dockerfile/SHELL
dockerfile/[译]Dockerfile编写最佳实践
dockerfile/多阶段构建
213 changes: 213 additions & 0 deletions docs/source/docker/dockerfile/多阶段构建.md
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
```

0 comments on commit 328da43

Please sign in to comment.