C/C++の軽量なDocker実行環境の作り方

# 概要
C/C++のプログラムをDockerコンテナでビルド、動作させる際の軽量な方法を紹介します。Dockerの実行環境の構築方法としては、Go、Python、Javaなどに関しては情報が多くあるのですが、C/C++に対しての良いDocker環境の構築方法の記事がなかったので共有します。

最近は、Dockerにmulti stage buildという機能があり、２段階でのDockerイメージの作成を行います。主に、Dockerfile内で一段階目で実行ファイルを作成するためのビルド環境を作成し、二段階目で、作成した実行ファイルを実行するためだけの軽量ランタイム環境を最終的なDockerイメージとして保存することが主流になってきています。これらをC/C++プログラムにも適用します。

また、最近ではdistolessイメージという、実行に特化して機能が限定された、軽量でセキュアなDockerイメージをGoogleが公開しております。詳しくは、[こちら](https://qiita.com/some-nyan/items/90b624b0f148231748f0)で詳しく解説している方がいらっしゃいます。この環境を使いたいのですが、C/C++では実行時にライブラリをリンクさせる動的リンクライブラリを使用する場合があり、その場合は、追加でパッケージを入れることが難しいdistoless環境ではプログラムの実行ができません。そのため、ここでは、他に軽量と言われていて、パッケージ追加など基本的な機能があるディストリビューションのAlpine Linuxを使います。Alpine Linuxについて詳しくは、[こちら](https://qiita.com/ryuichi1208/items/6020cfabc92bd8153654)で解説されている方がいらっしゃいます。

もし、より良いDockerの実行環境があればコメント頂ければ幸いです。

# Dockerfileと実行方法

まず、Dockerfileとその実行方法を示して、そのあとにDockerfileの意味を一つ一つ説明していきます。以下のDockerfileを作成します。

```
# ---------------
# Image creation stage
FROM alpine AS dev
# Copy required files
COPY ./simulation.cpp /app/
# Set the working directory
WORKDIR /app
# Install required packages
RUN apk add --quiet --no-cache \
                librdkafka-dev \
                alpine-sdk  \
                nlohmann-json
# Build
RUN g++ simulation.cpp -o simulation -lrdkafka++

# ---------------
# Image creation stage
FROM alpine AS prod
# Set the working directory
WORKDIR /app
# Copy required files
COPY --from=dev /app/simulation /app/simulation 
# Install required packages
RUN apk add --quiet --no-cache \
                librdkafka-dev 

# ---------------
# Execution
CMD ["./simulation"]
```

Dockerイメージのビルドと起動を行います。同じディレクトリにコンパイル、実行したいプログラム`simulation.cpp`を置いておきます。

```
docker build -t simulation:1.0 .
docker run -it simulation:1.0
```

作成された実行環境のDockerイメージが以下の`simulation`で、参考として、`<none>`がビルド環境、`ubuntu`がubuntuイメージです。`simulation`が非常に軽量なのがわかります。

```
$ docker images
REPOSITORY    TAG      IMAGE ID       CREATED         　　 SIZE
simulation    1.0      3593bd8b0d91   About a minute ago   13.1MB
<none>        <none>   64d875c1b27f   11 minutes ago       227MB
ubuntu        latest   7e0aa2d69a15   3 weeks ago          72.7MB
```

#Dockerfileの説明

alpineのイメージを作成し、`dev`とタグを付けます。

```
# Image creation stage
FROM alpine AS dev
```

ローカルの現在のディレクトリからdevの/app/のディレクトリに対してsimulation.cppをコピーします。

```
# Copy required files
COPY ./simulation.cpp /app/
```

作業用ディレクトリを/appに変更します。（cdのようなものです。）

```
# Set the working directory
WORKDIR /app
```

ビルドに必要なパッケージをインストールします。私の`simulation.cpp`のビルドに必要なパッケージを記載しているだけなので、パッケージ部分は任意に書き換えて下さい。`no-cashe`を指定することで、パッケージをキャッシュしない設定となり、通常イメージサイズ削減のため指定する。`quiet`は特に`apk add`の出力などが必要ない時に指定する。

```
# Install required packages
RUN apk add --quiet --no-cache \
                librdkafka-dev \
                alpine-sdk  \
                nlohmann-json
```

Docker内でコンパイルを行う。`-lrdkafka++`により動的リンクするライブラリを指定する。

```
# Build
RUN g++ simulation.cpp -o simulation -lrdkafka++
```

次に、実行用のDockerイメージを作成する。`prod`とタグを付ける。

```
# Image creation stage
FROM alpine AS prod
```

作業用ディレクトリを`prod`の/appとする。

```
# Set the working directory
WORKDIR /app
```

`dev`のイメージから実行ファイルを`prod`にコピーする。

```
# Copy required files
COPY --from=dev /app/simulation /app/simulation 
```

動的リンクが必要なライブラリのみをインストールする。

```
# Install required packages
RUN apk add --quiet --no-cache\
                librdkafka-dev 
```

実行ファイルを実行する。

```
# Execution
CMD ["./simulation"]
```

以上です。