経緯
GoReplayのリポジトリにあったDockerfileはIntel Macだとうまく動いたが、Appleシリコン(M1) Macではうまく動かなかった。 Dockerfile: https://github.com/buger/goreplay/blob/1.3.3/Dockerfile 実行時に以下のエラーが発生。
tunl0: SIOCETHTOOL(ETHTOOL_GLINK) ioctl failed: Function not implemented
--platform
オプションでamd64
用にビルドして実行してみたが、結果は同様だった。
たまにこういうことはありそう?
The emulation is not perfect https://github.com/docker/for-mac/issues/5328
結論
以下のDockerfileを書いた。これならIntel MacでもAppleシリコン(M1) Macでもうまく動く。
FROM golang:1.19 as builder RUN apt-get update && apt-get install ruby vim-common -y RUN apt-get install flex bison -y RUN wget http://www.tcpdump.org/release/libpcap-1.10.0.tar.gz && tar xzf libpcap-1.10.0.tar.gz && cd libpcap-1.10.0 && ./configure && make install WORKDIR /go/src/github.com/buger/goreplay/ RUN wget https://github.com/buger/goreplay/archive/refs/tags/v2.0.0-rc2.tar.gz -O gor.tar.gz && tar xzf gor.tar.gz -C . --strip=1 RUN go mod vendor RUN go build -mod=vendor -tags netgo -o gor -ldflags "-extldflags \"-static\"" FROM alpine:3.17 RUN apk add bash RUN apk add --no-cache ca-certificates openssl COPY --from=builder /go/src/github.com/buger/goreplay/gor / ENTRYPOINT ["./gor"]
build & run
# build $ docker build --tag goreplay:2.0.0 . # run $ docker run --rm -it goreplay:2.0.0 -h Gor is a simple http traffic replication tool written in Go. Its main goal is to replay traffic from production servers to staging and dev environments.
なぜ元のDockerfileだとダメか
以下で取得してきているGoReplayのプログラムがファイル名でもわかるようにx64のCPUアーキテクチャ用にビルドされたものなのが原因。
RUN wget https://github.com/buger/goreplay/releases/download/${RELEASE_VERSION}/gor_${RELEASE_VERSION}_x64.tar.gz -O gor.tar.gz
Dockerfile: https://github.com/buger/goreplay/blob/1.3.3/Dockerfile
--platform
オプションをつけずにdocker buildコマンドを実行するとホストのCPUアーキテクチャ用のbuildする。
- Intel Mac -> amd64 (= x86 https://ja.wikipedia.org/wiki/X64)
- Appleシリコン(M1) Mac -> arm64v8
dockerで使われるCPUアーキテクチャの種類: https://github.com/docker-library/official-images#architectures-other-than-amd64
AppleシリコンでBuildした場合、OSはarm64v8のCPUを想定するが、元のDockerfileだとx86用のgoプログラムを取得しているのでミスマッチが起こっている。
どう直したのか
go build
コマンドもdocker buildと同様にdefaultは実行ホストのCPUアーキテクチャ用にbuildする。なので、Dockerfileにgo buildコマンドを入れた。
buildコマンドはMakefileから必要なオプションを持ってきた。 https://github.com/buger/goreplay/blob/1.3.3/Makefile
ちなみにGOOS=linux GOARCH=amd64 go build
のようにすれば成果物のCPUアーキテクチャの指定もできるが、今回は指定しないほうが都合が良かったので特に指定していない。buildするCPUアーキテクチャとと実行するCPUアーキテクチャが違う時は指定が必要。
Makefileはbuild用のDockerコンテナ内でビルドする作りになっていたが、docker in dockerをしたくなかったのでマルチステージビルドを使って書いてみた。golangイメージでビルドして、alpineイメージで実行。goのプログラムは実行ファイルだけあれば実行できるのですごい。