はじめに
SONiCになんらか追加機能を加えようとするとき、ビルド環境にも手を加えなければならない場面が出てくることがあります。今回はビルドに必要なツールが現在ビルド環境にない場合、どうやって追加すればいいかについて説明します。
ビルド環境の整備
SONiCをソースコードからビルドするには、まず環境の整備が必要です。General Building Guideによればpip
、jinja2
、j2cli
に加えてDockerが必要とのことです(make
とgit
も必要です)。まずは無変更でビルドできることを確認して、そこがスタート地点です。本当に無変更だと下手すると半日以上かかりますので、焦らず放置して待ちましょう。
ビルドに必要なツールがあるか確認する
SONiCのビルド環境はDockerコンテナで構成され必要なツールがそこにインストールされますので、指定されたバージョンのC++コンパイラを予めインストールすると言った事前の作業は不要です。下記のファイルを使ってビルド環境のDockerコンテナは作成されます。
sonic-slave-buster/Dockerfile.j2
sonic-slave-jessie/Dockerfile.j2
sonic-slave-stretch/Dockerfile.j2
.j2
というサフィックスはjinja2テンプレートを意味していて、ファイル内の{%- ... %}
で囲まれた部分をj2
コマンド等で処理することで、本来ファイル内に固定文字列で記述される部分を差し替えたりできるというものです。C言語のプリプロセッサのようなものと思っていただければいいかと思います。今回はテンプレート部分には手を付けませんので、無視して普通のDockerfile
と思っていただいて問題ありません。
中を見ると、apt-get -y install
で様々なツールがインストールされていることがわかります。もし使いたいツールが含まれていれば、以降の作業は不要です。
ビルドに必要なツールを加える
先ほどのDockerfile.j2
を変更することで、ビルドに必要なツールを追加します。2020年12月時点での最新のSONiCでは、buster (Debian10)ベースのdebパッケージと stretch (Debian9)ベースのdebパッケージが混在しています。ビルドするdebパッケージがどちらを使うかによって適切なDockerfile.j2
を変更します。
あくまでビルドの際に使われるということで、ビルドの結果できあがったSONiCのバイナリイメージには当然ですが加えたツールは含まれないことに注意してください。
apt-get
でインストールできるツール
apt-get install -y
の行に加えます。
## Make apt-get non-interactive
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
apt-utils \
default-jre-headless \
openssh-server \
curl \
wget \
unzip \
git \
build-essential \
アーカイブをダウンロードして展開するツール
幸いなことに、Go言語コンパイラがこのタイプです。これを参考にするといいでしょう。
# For gobgp and telemetry build
RUN export VERSION=1.14.2 \
{%- if CONFIGURED_ARCH == "armhf" %}
&& wget https://storage.googleapis.com/golang/go$VERSION.linux-armv6l.tar.gz \
&& tar -C /usr/local -xzf go$VERSION.linux-armv6l.tar.gz \
{%- elif CONFIGURED_ARCH == "arm64" %}
&& wget https://storage.googleapis.com/golang/go$VERSION.linux-arm64.tar.gz \
&& tar -C /usr/local -xzf go$VERSION.linux-arm64.tar.gz \
{%- else %}
&& wget https://storage.googleapis.com/golang/go$VERSION.linux-amd64.tar.gz \
&& tar -C /usr/local -xzf go$VERSION.linux-amd64.tar.gz \
{%- endif %}
&& echo 'export GOROOT=/usr/local/go' >> /etc/bash.bashrc \
&& echo 'export PATH=$PATH:$GOROOT/bin' >> /etc/bash.bashrc \
&& rm go$VERSION.linux-*.tar.gz
インストーラを実行するタイプのツール
curl
やwget
でインストーラを取得して、RUN
コマンドで実行します。
実際に追加してみる(例)
Rustコンパイラ
Rust言語の公式ページによると、一番簡単なインストール方法は
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
です。やってみるとわかりますが、これをそのまま実行すると、対話形式でどうするか問い合わせがあり答える必要があります。
default host triple: x86_64-unknown-linux-gnu
default toolchain: stable (default)
profile: default
modify PATH variable: yes
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>
dockedr build
の中でこれを実行すると、対話できないためエラーになります。実行されるrust-init.sh
スクリプトは、パラメータとして-y
を指定すると問い合わせなくインストールを実行できます。直接パイプでsh
に食わせる場合、sh -s -- -y
とします。
実際にビルドでrustc
やcargo
などを呼び出す場合、PATH
を通しておく必要があります。golang
と同様に、/etc/bash.bashrc
に書き加えます。
Rustの場合、これらをDockerfile.j2
の末尾に加えるとうまく動きません。
Rustツール群はにコンパイラだけでなくダウンロードしたモジュールなどもユーザのホームディレクトリの下に置くよう作られています。Dockerfile.j2
を使ってDockerイメージを構築しているときはSONiCビルド時のユーザアカウントではなくroot
で構築されるため、本来インストールしたかった場所に配置されずアクセス権限もRustツール群の想定外となるというのが動かない理由です。
Dockerfile.j2
でdocker build
する際にはビルド時のユーザアカウント情報がないためうまく処理できません。同じディレクトリにDockerfile.user
があり、そちらはユーザアカウントに関連するあれこれが書かれてますので、そちらに追加するのが素直かと思います。ファイル末尾付近、USER $user
の前に追加します。(USER $user
の後ろだと/etc/
への書き込み権限が失われるため、Dockerイメージの作成に失敗します)
まとめるとこうなります。
# Install Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | su $user -c "sh -s -- -y" \
&& echo 'export PATH=$PATH:$HOME/.cargo/bin' >> /etc/bash.bashrc
USER $user
ちゃんとやるならgolang
と同じようにターゲットのアーキテクチャに対応するバイナリを取得する必要がありますが、今回は手抜きです。
もしarm64
やarmhf
でトライされる場合は、RustドキュメントのOther installation methodsを参考にするといいでしょう。rust-init.sh
にオプションを指定すればターゲットを変更できるので、対応自体はそれほど難しくないと思いますが、Dockerfile.user
はテンプレート化されていませんのでjinja2のテンプレート置換が使えません。Dockerイメージの作成はMakefile.work
で処理されますので、そちらを変更してユーザ名などの情報を渡すようにすれば、Dockerfile.j2
への変更で綺麗にまとめられると思います。
DDlogコンパイラ
Differential Datalog(Ddlog)コンパイラのバイナリリリースはamd64
しか提供されていません。
arm64
やarmhf
でDDlogを利用するためにはソースコードからビルドする必要がありますが、DDlogのソースコードはRust
で書かれている部分もあればHaskell
で書かれている部分もありJDK
も使うようです。
手順通りにやればビルドできるとは思いますが、arm64
やarmhf
のターゲットマシンを持ってないため確認できないこともあり、ここはRust
と同様、手抜きさせていただきます。
/usr/local/ddlog/
に展開していますので、PATH
を通しておきます。また、公式のインストール手順に書かれているとおり、環境変数DDLOG_HOME
も設定しておきます。
RUN wget https://github.com/vmware/differential-datalog/releases/download/v0.32.0/ddlog-v0.32.0-20201212054437-linux.tar.gz \
&& tar -C /usr/local -xzf ddlog-v0.32.0-20201212054437-linux.tar.gz \
&& echo 'export PATH=$PATH:/usr/local/ddlog/bin' >> /etc/bash.bashrc \
&& echo 'export DDLOG_HOME=/usr/local/ddlog' >> /etc/bash.bashrc \
&& rm ddlog-v0.32.0-20201212054437-linux.tar.gz
これをDockerfile.j2
の末尾に加えます。
ビルド環境に組み込めたかの確認
今回はツールを加えただけですが、この状態でSONiCをビルドすると、ビルド環境のDockerコンテナ再構築の際にRustコンパイラやDDlogコンパイラがインストールされる様子を見ることができます。
ビルドログ
---> bb0f00a37f11
Step 18/19 : RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | su $user -c "sh -s -- -y" && echo 'export PATH=$PATH:$HOME/.cargo/bin' >> /etc/bash.bashrcwnloading installer
info: profile set to 'default'
info: default host triple is x86_64-unknown-linux-gnu
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2020-11-19, rust version 1.48.0 (7eac88abb 2
020-11-16)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: using up to 500.0 MiB of RAM to unpack components
info: installing component 'clippy'
info: installing component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
info: default toolchain set to 'stable-x86_64-unknown-linux-gnu'
stable-x86_64-unknown-linux-gnu installed - rustc 1.48.0 (7eac88abb 2020-11-16)
Rust is installed now. Great!
To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH
environment variable. Next time you log in this will be done
automatically.
To configure your current shell, run:
source $HOME/.cargo/env
---> 192e550224ee
Step 62/62 : RUN wget https://github.com/vmware/differential-datalog/releases/download/v0.32.0/ddlog-v0.32.0-20201212054437-linux.tar.gz && tar -C /usr/local -xzf ddlog-v0.32.0-20201212054437-linux.tar.gz && echo 'export PATH=$PATH:/usr/local/ddlog/bin' >> /etc/bash.bashrc && echo 'export DDLOG_HOME=/usr/local/ddlog' >> /etc/bash.bashrc && rm ddlog-v0.32.0-20201212054437-linux.tar.gz
---> Running in 5b01001431b8--2020-12-14 06:11:44-- https://github.com/vmware/differential-datalog/releases/download/v0.32.0/ddlog-v0.32.0-20201212054437-linux.tar.gz
Resolving github.com (github.com)... 52.192.72.89
Connecting to github.com (github.com)|52.192.72.89|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://github-production-release-asset-2e65be.s3.amazonaws.com/126076620/1b406580-3bfa-11eb-9790-bb8d226f5ef3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20201214%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20201214T061144Z&X-Amz-Expires=300&X-Amz-Signature=5d4b80217d6d631cebeecca5cde3de602be37b94ef45c722eee98b5c9c2fd6d6&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=126076620&response-content-disposition=attachment%3B%20filename%3Dddlog-v0.32.0-20201212054437-linux.tar.gz&response-content-type=application%2Foctet-stream [following]
--2020-12-14 06:11:44-- https://github-production-release-asset-2e65be.s3.amazonaws.com/126076620/1b406580-3bfa-11eb-9790-bb8d226f5ef3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20201214%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20201214T061144Z&X-Amz-Expires=300&X-Amz-Signature=5d4b80217d6d631cebeecca5cde3de602be37b94ef45c722eee98b5c9c2fd6d6&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=126076620&response-content-disposition=attachment%3B%20filename%3Dddlog-v0.32.0-20201212054437-linux.tar.gz&response-content-type=application%2Foctet-stream
Resolving github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)... 52.216.145.123
Resolving github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)... 52.216.145.123
Connecting to github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)|52.216.145.123|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 30424097 (29M) [application/octet-stream]
Saving to: 'ddlog-v0.32.0-20201212054437-linux.tar.gz'
また、docker exec
等でコンテナ内に入って、動かせるかの確認ができます。
Dockerコンテナでの操作ログ
$ docker run -it sonic-slave-buster-masaru:a1ccaf7d3a8 bash
masaru@4ea4efaa90e0:/$ cargo
Rust's package manager
USAGE:
cargo [+toolchain] [OPTIONS] [SUBCOMMAND]
OPTIONS:
-V, --version Print version info and exit
--list List installed commands
--explain <CODE> Run `rustc --explain CODE`
-v, --verbose Use verbose output (-vv very verbose/build.rs output)
-q, --quiet No output printed to stdout
--color <WHEN> Coloring: auto, always, never
--frozen Require Cargo.lock and cache are up to date
--locked Require Cargo.lock is up to date
--offline Run without accessing the network
-Z <FLAG>... Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
-h, --help Prints help information
Some common cargo commands are (see all commands with --list):
build, b Compile the current package
check, c Analyze the current package and report errors, but don't build object files
clean Remove the target directory
doc Build this package's and its dependencies' documentation
new Create a new cargo package
init Create a new cargo package in an existing directory
run, r Run a binary or example of the local package
test, t Run the tests
bench Run the benchmarks
update Update dependencies listed in Cargo.lock
search Search registry for crates
publish Package and upload this package to the registry
install Install a Rust binary. Default location is $HOME/.cargo/bin
uninstall Uninstall a Rust binary
See 'cargo help <command>' for more information on a specific command.
masaru@4ea4efaa90e0:/$ ddlog
Usage: ddlog [OPTION...]
-h --help Display help message.
-v --version Display DDlog version.
-i FILE DDlog program to compile.
--action=ACTION Action: [validate, compile]
-L PATH Extra DDlog library directory.
-o DIR --output-dir=DIR Output directory (default based on program name).
--dynlib Generate dynamic library.
-j --java Generate Java bindings. Implies '--rust-flatbuffers'.
--output-internal-relations All non-input relations are marked as output relations.
--output-input-relations=PREFIX Mirror each input relation into an output relation named by prepending the prefix.
--no-dynlib Do not generate dynamic library (default).
--staticlib Generate static library (default).
--no-staticlib Do not generate static library.
-g Enable debugging hooks.
--pp-flattened Dump the source after compilation pass 1 (flattening module hierarchy) to PROG.flat.ast.
--pp-validated Dump the source after compilation pass 2 (validation, including several source transformations) to PROG.valid.ast.
--pp-debug Dump the source after compilation pass 3 (injecting debugging hooks) to FILE.debug.ast. If the '-g' option is not specified, then pass 3 is a no-op and will produce identical output to pass 2.
--pp-optimized Dump the source after compilation pass 4 (optimization) to FILE.opt.ast.
--re-validate [developers only] Re-validate the program after type inference and optimization passes.
--omit-profile Skip adding a Cargo profile (silences warnings for some rust builds, included by default)
--omit-workspace Skip adding a Cargo workspace (silences errors for some rust builds, included by default)
--run-rustfmt Run rustfmt on the generated code
--rust-flatbuffers Build flatbuffers bindings for Rust
--nested-ts-32 Use 32-bit instead of 16-bit nested timestamps. Supports recursive programs that may perform >65,536 iterations. Slightly increases the memory footprint of the program.
ddlog: input file not specified
masaru@4ea4efaa90e0:/$ echo $DDLOG_HOME
/usr/local/ddlog
masaru@4ea4efaa90e0:/$
おわりに
SONiCのビルド環境として構築されるDockerコンテナは、一度ビルドを走らせると次回以降再利用されます。それによってビルド時間が短縮されるのですが、今回のようにDockefile.j2
に手を加えるとコンテナイメージは作り直しになります(make
が勝手にやってくれます)。最初の無変更ビルドで時間がかかるのは実感されてると思いますが、気長に待ちましょう。
追加したビルドツールを呼び出す件については、別途記事を用意したいと思います。