創造一個最小的binary執行檔Docker容器


今天我們想要製造一個Docker容器來幫忙計算第N個斐波那契数,並決定用C++來撰寫執行程式。
下面是一個時間複雜度O(N)的計算程式:
fib.cpp

#include <iostream>
#include <exception>
using namespace std;
int fib(int n)
{

    int f[] = {1, 0};
    while (n)
    {
        auto temp = f[1];
        f[1] = f[0] + f[1];
        f[0] = temp;
        --n;
    }
    return f[1];
}
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        cerr << "Please give a positive number.(<=40)" << endl;
        return 1;
    }
    int num;
    try
    {
        num = stoi(argv[1]);
    }
    catch (...)
    {
        cerr << "Please give a positive number.(<=40)" << endl;
        return 0;
    }
    if (num <= 0 || num > 40)
    {
        cerr << "Please give a positive number.(<=40)" << endl;
        return 1;
    }
    cout << "The number " << num << " of fibonacci sequence is " << fib(num) << "." << endl;
    return 0;
}

最直接的方式是用gcc image來幫助我們完成所要的容器:
Dockerfile

FROM gcc
COPY fib.cpp fib.cpp
RUN g++ fib.cpp -o fib
ENTRYPOINT ["/fib"]

build 及 run 的結果:

$ docker build -t fib .
$ docker run --rm fib 10
The number 10 of fibonacci sequence is 55.

這是一個簡單的計算程式,然而這樣的image卻佔有1gb以上,如果要分享這個image給其他人,實在太大了。
仔細思考,我們其實只要最終編譯的二進制執行檔,gcc image裡的編譯程式等都不需要。所以換個方式,先用gcc image編譯,再把編譯好的二進制檔案放到幾乎甚麼都沒有的scratch image裡面。 我們可以利用Docker提供的multi-stage build方式來完成:

Dockerfile

FROM gcc AS builder
COPY fib.cpp fib.cpp
RUN g++ -static fib.cpp -o fib

FROM scratch
COPY --from=builder fib /
ENTRYPOINT ["/fib"]

記得g++後頭要加-static來避免執行時的dynamic linking,因為scratch image裡無法提供所需的東西。

最後,build後我們可以得到約2.4MB的image,比先前的小非常多!

docker image

docker  cpp 

See also