Git (docker/docker) から clone をしてはみたけれど、何だかよく分からない。とりあえず「Docker のソースの構成についてのメモ - Qiita」で、トップディレクトリの分類はしてみた。
そして、docker/ が CLI プログラムのメインと見うけられる。そもそも Go 言語のビルドシステムや作法が分からないので、いろいろと分からない。
まあ、やってみるか。
Docker を Docker 上でビルド
Docker のビルドは、Docker 上で行う。この点は簡単で助かる。依存地獄知らず。
ビルド用の Dockerfile が用意されているので、とりあえずそれを build してみる。右記を参考にした。 ∥ Etsukata blog: Docker のビルド方法に見る Golang の利点
docker run
のオプションについては、Dockerfile 内に注記がある。これ、なんで --privileged
が要るのかな?
$ git clone https://github.com/docker/docker.git
$ cd docker/
$ docker build -t docker .
$ docker run -it --rm -v $PWD:/go/src/github.com/docker/docker --privileged docker bash
# pwd
/go/src/github.com/docker/docker
# hack/make.sh
なんと、この Docker 環境はクロスコンパイルをするよ、すごいね。いや、Go 言語がすごいのか。
# ls -l /usr/local/go/bin/
total 12576
drwxr-xr-x 2 root root 4096 Jan 11 09:56 darwin_386
drwxr-xr-x 2 root root 4096 Jan 11 09:56 darwin_amd64
drwxr-xr-x 2 root root 4096 Jan 11 09:56 freebsd_386
drwxr-xr-x 2 root root 4096 Jan 11 09:56 freebsd_amd64
drwxr-xr-x 2 root root 4096 Jan 11 09:56 freebsd_arm
-rwxr-xr-x 1 root root 9350136 Jan 11 09:56 go
-rwxr-xr-x 1 root root 3496688 Jan 11 09:56 gofmt
drwxr-xr-x 2 root root 4096 Jan 11 09:55 linux_386
drwxr-xr-x 2 root root 4096 Jan 11 09:55 linux_arm
# hack/make.sh cross
すると、以下のように。ちなみに環境としては、Mac 上の Boot2docker で Docker している。コンテナとしては、Dockerfile に FROM ubuntu:14.04
とあるので、Ubuntu 14.04 TLS ですね。
# ls -d bundles/1.3.2-dev/cross/*/* | cat
bundles/1.3.2-dev/cross/darwin/386
bundles/1.3.2-dev/cross/darwin/amd64
bundles/1.3.2-dev/cross/freebsd/386
bundles/1.3.2-dev/cross/freebsd/amd64
bundles/1.3.2-dev/cross/freebsd/arm
bundles/1.3.2-dev/cross/linux/386
bundles/1.3.2-dev/cross/linux/arm
# bundles/1.3.2-dev/cross/darwin/386/docker version
Client version: 1.3.2-dev
Client API version: 1.16
Go version (client): go1.3.3
Git commit (client): 353ff40
OS/Arch (client): darwin/386
FATA[0000] Get http:///var/run/docker.sock/v1.16/version: dial unix /var/run/docker.sock: no such file or directory. Are you trying to connect to a TLS-enabled daemon without TLS?
# bundles/1.3.2-dev/cross/darwin/amd64/docker version
Client version: 1.3.2-dev
Client API version: 1.16
Go version (client): go1.3.3
Git commit (client): 353ff40
OS/Arch (client): darwin/amd64
FATA[0000] Get http:///var/run/docker.sock/v1.16/version: dial unix /var/run/docker.sock: no such file or directory. Are you trying to connect to a TLS-enabled daemon without TLS?
# bundles/1.3.2-dev/cross/darwin/amd64/docker version
bash: bundles/1.3.2-dev/cross/darwin/amd64/docker: cannot execute binary file: Exec format error
# bundles/1.3.2-dev/cross/freebsd/arm/docker version
bash: bundles/1.3.2-dev/cross/freebsd/arm/docker: cannot execute binary file: Exec format error
当然のことながら、Docker ホスト(というか、Boot2docker ホスト)である Mac(vboxsf で、VirtualBox の仮想マシンとフォルダを共有している)では、普通に Darwin のバイナリが実行できる。
$ bundles/1.3.2-dev/cross/darwin/amd64/docker version
Client version: 1.3.2-dev
Client API version: 1.16
Go version (client): go1.3.3
Git commit (client): 353ff40
OS/Arch (client): darwin/amd64
FATA[0000] Get http:///var/run/docker.sock/v1.16/version: dial unix /var/run/docker.sock: no such file or directory. Are you trying to connect to a TLS-enabled daemon without TLS?
クロスコンパイルと、daemon
ビルドタグの扱い
hack/make.sh が、引数に指定されたバンドル(=ターゲット?)をターゲットとしてビルドする。無指定であれば、全部やるっぽい。
後に cross
を見るが、これは binary
を、ターゲットの OS/arch 指定で呼んでいる。無指定ならば linux/amd64
になるってことなんだろう。
...
DEFAULT_BUNDLES=(
validate-dco
validate-gofmt
binary
test-unit
test-integration
test-integration-cli
test-docker-py
dynbinary
dyntest-unit
dyntest-integration
cover
cross
tgz
ubuntu
)
...
bundle() {
bundlescript=$1
bundle=$(basename $bundlescript)
echo "---> Making bundle: $bundle (in bundles/$VERSION/$bundle)"
mkdir -p bundles/$VERSION/$bundle
source "$bundlescript" "$(pwd)/bundles/$VERSION/$bundle"
}
main() {
# We want this to fail if the bundles already exist and cannot be removed.
# This is to avoid mixing bundles from different versions of the code.
mkdir -p bundles
if [ -e "bundles/$VERSION" ]; then
echo "bundles/$VERSION already exists. Removing."
rm -fr bundles/$VERSION && mkdir bundles/$VERSION || exit 1
echo
fi
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ $# -lt 1 ]; then
bundles=(${DEFAULT_BUNDLES[@]})
else
bundles=($@)
fi
for bundle in ${bundles[@]}; do
bundle $SCRIPTDIR/make/$bundle
echo
done
}
main "$@"
以下では、cross
バンドルに注目する。
$SCRIPTDIR を見ると、hack/make/ の下から、バンドルごとにバンドル名と同名のスクリプトを source
する。よって、環境変数(export していないけれど)は、ここ(上)で指定したまま、下位のスクリプトに受け渡される。
下記が cross
bundle のスクリプト。デーモンをサポートしているのは linux/amd64 のみであることが分かる。下記に、「remove the "daemon" build tag from platforms that aren't supported」とあるとおり。
...
daemonSupporting=(
[linux/amd64]=1
)
...
for platform in $DOCKER_CROSSPLATFORMS; do
(
mkdir -p "$DEST/$platform" # bundles/VERSION/cross/GOOS/GOARCH/docker-VERSION
export GOOS=${platform%/*}
export GOARCH=${platform##*/}
if [ -z "${daemonSupporting[$platform]}" ]; then
export LDFLAGS_STATIC_DOCKER="" # we just need a simple client for these platforms
export BUILDFLAGS=( "${ORIG_BUILDFLAGS[@]/ daemon/}" ) # remove the "daemon" build tag from platforms that aren't supported
fi
source "$(dirname "$BASH_SOURCE")/binary" "$DEST/$platform"
)
done
これがどう効いてくれかと言うと、たとえばプラットフォームによって、クライアント・Engine 両対応のバイナリを出力するか、あるいはクライアント専用のバイナリを出力するかの条件ビルドをする場合など。
Docker で言うと、main.mainDaemon() の定義が、docker/client.go と docker/daemon.go の二箇所にある。client.go は、クライアントオンリーバイナリの場合に結合されるソースだ。
docker/client.go// +build !daemon ... func mainDaemon() { log.Fatal("This is a client-only binary - running the Docker daemon is not supported.") } ...
Mac で実行してみる。
$ docker -d 2015/01/11 23:54:30 This is a client-only binary - running the Docker daemon is not supported.
詳しくは、「build - The Go Programming Language 」あたりを参照。
ビルド制約はビルドタグとしても知られており、 以下に示す文字列で始まる行コメントです。
// +build
この後にそのファイルがパッケージに含められる条件を列挙します。
ビルドできる OS/プラットフォームのリストである $DOCKER_CROSSPLATFORMS は、下記のように Dockerfile でコンテナイメージを作る際に指定している。
...
# Compile Go for cross compilation
ENV DOCKER_CROSSPLATFORMS \
linux/386 linux/arm \
darwin/amd64 darwin/386 \
freebsd/amd64 freebsd/386 freebsd/arm \
windows/amd64 windows/386
...
では、hack/make.sh では、daemon
フラグはどう扱われているか。
if [ -z "$DOCKER_CLIENTONLY" ]; then
DOCKER_BUILDTAGS+=" daemon"
fi
...
ORIG_BUILDFLAGS=( -a -tags "netgo static_build $DOCKER_BUILDTAGS" )
BUILDFLAGS=( $BUILDFLAGS "${ORIG_BUILDFLAGS[@]}" )
上記のように、フラグ(「ビルドタグ」)を -tags "aaa bbb ..."
と設定する。
project/make.sh → project/make/cross → project/make/binary と source
されていって、最終的には、下記でビルドされる。
...
go build \
-o "$DEST/$BINARY_FULLNAME" \
"${BUILDFLAGS[@]}" \
-ldflags "
$LDFLAGS
$LDFLAGS_STATIC_DOCKER
" \
./docker
...
なるほどね。
ところで、差分ビルドってできるの?
部分ビルドのしかたは分からないが(たぶん go biuld
が差分ビルドをしてくれている)、コンテナ内の「docker/」で go build
すれば、CLI ツールをコンパイルして出力してくれる。
# cd docker/
# vi flags.go
で、
# go build