clangでビルドしたduckdbのdockerイメージ作成のメモ
たまにしかdockerを触らないため、よく記憶から抜け落ちる。
clangでビルドしたduckdbのdockerイメージを介して作成の記録を残しておく。
※一連の手順は、CHATGPT大先生の指導のもとに実施しています。
duckdbのソースからのビルドに必要なもの
- llvm
- cmake
- ninja
- duckdb公式で使用が推奨されている
手元の開発環境で、最新版のパーケージ取得が確認できているlinux版homebrewを使う。
幸いdockerhub.ioにて公式提供されている。
今回はhomebrew/brewを使用する。
段階を踏んでイメージを作成するため、マルチステージビルドを採用する
- ステージ1 (dev)- 開発環境の構築まで
- ステージ2 (build) - duckdbのビルド・インストールまで
- 最終ステージ - 後々必要となる成果物のみを残したイメージの作成
- homebrewイメージをベース
- 後々使う際に
aptでは必要なバージョンのパッケージを取得できなかった -
linux版homebrewは最新版のパーケージ取得が確認できた
- 後々使う際に
- nodejs
- これも後々Zigの導入で使用する
- 時短のため組み込んでおく
- これも後々Zigの導入で使用する
- homebrewイメージをベース
ステージ1
以下のDockerfileを記述した。
FROM homebrew/brew as dev
# Update and install common packages
RUN brew update
RUN brew install llvm && \
brew install cmake
# Default command
CMD ["bash"]
-
docker build -t tmp_build_duckdb:latest .でビルドできた -
docker run -it tmp_build_duckdb:latestでコンテナ内に入れた - コンテナ内で、
brew list- 期待通りパッケージがインストールされていることを確認した
上で、ninjaの導入を忘れていたため追加でインストールする
-
docker ps- 該当のコンテナを記録する - `docker start -i <CONTAINER ID>
- コンテナを停止していたため再起動
- バックグラウンド起動で停止していなければ、
docker exec -it <CONTAINER ID>
- コンテナ内で
brew install ninja - コンテナから脱出
ステージ2
手順再確認のため、コンテナ内に入りStp by Stepでビルドを進める。
- duckdbのclone
git clone --depth=1 https://github.com/duckdb/duckdb.git
- モンキーパッチを当てる
target_file="duckdb/extension/jemalloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h" \
&& line_number=485 \
&& sed -i "${line_number}s/^\/\/ #define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE/#define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE/" "$target_file"
issue 12657のため、linux環境下でのclangでのビルドに失敗するため。
- 2024-7-15時点の対応。該当ファイルはしばしば変更されるため行番号は変更するだろう
- モンキーパッチが適用されたかどうかの確認
cat duckdb/extension/jemalloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h | head -n 485 | tail -n 1
viがいなかったためcatで代用。
- homebrewのパスを環境変数に入れておく
BREW_PREFIX="/home/linuxbrew/.linuxbrew/opt"
echo $BREW_PREFIX
- configureまでの実行
mkdir -p duckdb/_build \
&& cd duckdb/_build \
&& cmake -GNinja \
-DCMAKE_C_COMPILER=$BREW_PREFIX/llvm/bin/clang \
-DCMAKE_CXX_COMPILER=$BREW_PREFIX/llvm/bin/clang++ \
-DCMAKE_BUILD_TYPE=Release \
-DINSTALL_LIB_DIR=../_install/lib \
-DINSTALL_INCLUDE_DIR=../_install/include \
-DINSTALL_BIN_DIR=../_install/bin \
-DINSTALL_CMAKE_DIR=../_install/lib/cmake/DuckDB \
..
- ビルドの実行(めっちゃ長い)
ninja
- 成果物のインストール
cmake --install .
5.の設定より、duckdb/_installフォルダに展開される
フォルダ構成
- bin
- duckdb - 対話環境
- lib
- cmake
- libduckdb.so
- libduckdb_fastpforlib.a
- libduckdb_fmt.a
- libduckdb_fsst.a
- libduckdb_hyperloglog.a
- libduckdb_mbedtls.a
- libduckdb_miniz.a
- libduckdb_pg_query.a
- libduckdb_re2.a
- libduckdb_skiplistlib.a
- libduckdb_static.a
- libduckdb_utf8proc.a
- libduckdb_yyjson.a
- libjemalloc_extension.a
- libparquet_extension.a
- include
- duckdb - Parser, Binder, Plannerなどのコア機能を直接さわるためのAPI
- duckdb.h - C用クライアントAPI
- duckdb.hpp - C++用クライアントAPI
- ここまでの分を一度コミットする
docker commit <CONTAINER ID> tmp_duckdb_image
ステージ2まとめ
手順をDockerfileに転記
# (snip) stage 1
FROM dev as build
WORKDIR /home/linuxbrew
# download duckdb source repository
RUN git clone --depth=1 https://github.com/duckdb/duckdb.git
# apply monky patch from issue 12657 (https://github.com/duckdb/duckdb/issues/12657)
RUN target_file="duckdb/extension/jemalloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h" \
&& line_number=485 \
&& sed -i "${line_number}s/^\/\/ #define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE/#define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE/" "$target_file"
# add homebre prefix to env
RUN BREW_PREFIX="/home/linuxbrew/.linuxbrew/opt"
# build duckdb
RUN mkdir -p duckdb/_build \
&& cd duckdb/_build \
&& cmake -GNinja \
-DCMAKE_C_COMPILER=$BREW_PREFIX/llvm/bin/clang \
-DCMAKE_CXX_COMPILER=$BREW_PREFIX/llvm/bin/clang++ \
-DCMAKE_BUILD_TYPE=Release \
-DINSTALL_LIB_DIR=../_install/lib \
-DINSTALL_INCLUDE_DIR=../_install/include \
-DINSTALL_BIN_DIR=../_install/bin \
-DINSTALL_CMAKE_DIR=../_install/lib/cmake/DuckDB \
..
# install artifacts
RUN cmake --install .
CMD [bash]
WORKDIR コマンドを使用して、作業ディレクトリを明示的に設定すると、コマンドの実行やファイルのコピーがより簡潔になります。
とのこと。
ステージ3
最終ビルドの構築をStep by Stepで
ステージ2の成果物を最終ステージに取り込むため、管理対象のためのvolumeを作成する
- ボリュームを作成する
docker volume create duckdb_volume
- ボリュームのマウントポイントを取得
VOLUME_MOUNTPOINT=$(docker volume inspect --format '{{.Mountpoint}}' duckdb_volume)
docker volume inspectの実行結果は以下のようになった。
[
{
"CreatedAt": "2024-07-15T08:16:50Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/duckdb_volume/_data",
"Name": "duckdb_volume",
"Options": null,
"Scope": "local"
}
]
配列形式なのは、複数のボリュームを同時に指定できるようにするため。
docker volume inspect volume_a volume_b
3-a. ステージ2のコンテナを起動する(すでに起動済みの場合はスキップ)
docker run -d --name tmp_duckdb_container tmp_duckdb_image
コンテナ -> ボリューム間で成果物を複製するため、起動しておく必要がある。
なんか起動後即終了する。
ttyでコンテナ内には入れる。
3-b. コンテナ内からボリュームにコピーするため、ボリュームをマウントした上でttyでコンテナに入る。
docker run -it --name tmp_duckdb_container -v duckdb_volume:/mnt tmp_duckdb_image bash
マウントしたボリュームに成果物をコピー
sudo cp -r duckdb/_install/. /mnt
duckdb/_install/.の指定、今まで知らんかった。さすCHATGPT先生。
確認
$ ls -ld /mnt/*
drwxr-xr-x 2 root root 4096 Jul 15 08:57 /mnt/bin
drwxr-xr-x 3 root root 4096 Jul 15 08:57 /mnt/include
drwxr-xr-x 3 root root 4096 Jul 15 08:57 /mnt/lib
ステージ3 (後半戦)
homebrew/brewベースの新規のコンテナを起動し、ttyで入る。
docker run -it --name tmp_final_container -v duckdb_volume:/mnt homebrew/brew bash
``
成果物を取り込む
cp -r /mnt duckdb
nodejsをインストールする
brew install node
最終的なDockerfile
# Build tool setup
FROM homebrew/brew as dev
# Update and install common packages
RUN brew update
RUN brew install llvm && \
brew install cmake && \
brew install ninja
# Build stage
FROM dev as build
WORKDIR /home/linuxbrew
# download duckdb source repository
RUN git clone --depth=1 https://github.com/duckdb/duckdb.git
# apply monky patch from issue 12657 (https://github.com/duckdb/duckdb/issues/12657)
RUN target_file="duckdb/extension/jemalloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h" \
&& line_number=485 \
&& sed -i "${line_number}s/^\/\/ #define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE/#define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE/" "$target_file"
# add homebre prefix to env
RUN BREW_PREFIX="/home/linuxbrew/.linuxbrew/opt"
# build duckdb
RUN mkdir -p duckdb/_build \
&& cd duckdb/_build \
&& cmake -GNinja \
-DCMAKE_C_COMPILER=$BREW_PREFIX/llvm/bin/clang \
-DCMAKE_CXX_COMPILER=$BREW_PREFIX/llvm/bin/clang++ \
-DCMAKE_BUILD_TYPE=Release \
-DINSTALL_LIB_DIR=../_install/lib \
-DINSTALL_INCLUDE_DIR=../_install/include \
-DINSTALL_BIN_DIR=../_install/bin \
-DINSTALL_CMAKE_DIR=../_install/lib/cmake/DuckDB \
..
# install artifacts
RUN cmake --install .
# Last stage
FROM homebrew/brew
WORKDIR /home/linuxbrew
# copy artifact
COPY /home/linuxbrew/duckdb/_install/. /home/linuxbrew/duckdb
# install package
RUN brew update && \
brew install node