多阶段构建
多阶段构建(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 .
这种实现有如下好处:
- 调试特定生成阶段
- 可以指定调试(
debug
)阶段(启用所有调试符号或工具)和生产(production
)阶段 - 指定测试(
test
)阶段,在这个阶段中,应用程序将被测试数据填充;同时指定生产阶段,使用真实数据进行测试
使用外部镜像
使用多阶段构建时,不局限于从先前在Dockerfile
中创建的阶段进行复制。可以使用COPY --from
指令从单独的镜像复制,可以使用本地镜像名、本地或Docker
注册表上可用的标记或标记ID
。Docker
客户端在必要时提取镜像并从中复制工件。语法如下:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf