この 一連のブログ投稿では、最適化されたコンテナー化された Go 開発環境を導入する方法について説明します。 パート 1 では、ローカルの Go 開発用にコンテナ化された開発環境を開始する方法を説明し、さまざまなプラットフォーム用のサンプル CLI ツールを構築しました。第 2 回 では、Go の依存関係を追加する方法、ビルドと単体テストを高速化するためのキャッシュについて説明しました。 この最後の 3 番目の部分では、コード リンター、GitHub アクション CI、およびいくつかの追加のビルド最適化を追加する方法を示します。
リンターの追加
優れたプログラミングプラクティスのチェックを可能な限り自動化したいので、セットアップにリンターを追加しましょう。 最初のステップは、ドッカーファイルを変更することです。
# syntax = docker/dockerfile:1-experimental
FROM --platform=${BUILDPLATFORM} golang:1.14.3-alpine AS base
WORKDIR /src
ENV CGO_ENABLED=0
COPY go.* .
RUN go mod download
COPY . .
FROM base AS build
ARG TARGETOS
ARG TARGETARCH
RUN --mount=type=cache,target=/root/.cache/go-build \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /out/example .
FROM base AS unit-test
RUN --mount=type=cache,target=/root/.cache/go-build \
go test -v .
FROM golangci/golangci-lint:v1.27-alpine AS lint-base
FROM base AS lint
COPY --from=lint-base /usr/bin/golangci-lint /usr/bin/golangci-lint
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/root/.cache/golangci-lint \
golangci-lint run --timeout 10m0s ./...
FROM scratch AS bin-unix
COPY --from=build /out/example /
...
これで、使用したいリンターを含むゴランチリント画像のエイリアスであるリントベースステージができました。 次に、lintを実行するlintステージを用意し、キャッシュを正しい場所にマウントします。
単体テストに関しては、リンティング用のリントルールをMakefileに追加できます。 テストルールにエイリアスを付けて、リンターテストと単体テストを実行することもできます。
all: bin/example
test: lint unit-test
PLATFORM=local
.PHONY: bin/example
bin/example:
@docker build . --target bin \
--output bin/ \
--platform ${PLATFORM}
.PHONY: unit-test
unit-test:
@docker build . --target unit-test
.PHONY: lint
lint:
@docker build . --target lint
CI の追加
開発プラットフォームをコンテナ化したので、プロジェクトにCIを追加するのは本当に簡単です。 Dockerビルドを実行するか、CIスクリプトからコマンドを実行するだけで済みます。 これを実証するために、GitHub アクションを使用します。 これを設定するには、次の .github/workflows/ci.yaml
ファイルを使用できます。
name: Continuous Integration
on: [push]
jobs:
ci:
name: CI
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: "1"
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run linter
run: make lint
- name: Run unit tests
run: make unit-test
- name: Build Linux binary
run: make PLATFORM=linux/amd64
- name: Build Windows binary
run: make PLATFORM=windows/amd64
CIで実行するコマンドは、ローカルで使用するコマンドと同じであり、すべてがDockerfileですでに定義されているため、ツールチェーンの構成を行う必要がないことに注意してください。
最後の最適化
COPYを実行すると、コンテナイメージに追加のレイヤーが作成され、処理が遅くなり、余分なディスク容量が使用されます。 これは、ビルドコンテキスト、ステージ、またはイメージからマウントを使用して RUN --mount
バインドすることで回避できます。 このパターンを採用すると、結果の Dockerfile は次のようになります。
# syntax = docker/dockerfile:1-experimental
FROM --platform=${BUILDPLATFORM} golang:1.14.3-alpine AS base
WORKDIR /src
ENV CGO_ENABLED=0
COPY go.* .
RUN go mod download
FROM base AS build
ARG TARGETOS
ARG TARGETARCH
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /out/example .
FROM base AS unit-test
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
go test -v .
FROM golangci/golangci-lint:v1.27-alpine AS lint-base
FROM base AS lint
RUN--mount=target=. \
--mount=from=lint-base,src=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/root/.cache/golangci-lint \
golangci-lint run --timeout 10m0s ./...
FROM scratch AS bin-unix
COPY --from=build /out/example /
FROM bin-unix AS bin-linux
FROM bin-unix AS bin-darwin
FROM scratch AS bin-windows
COPY --from=build /out/example /example.exe
FROM bin-${TARGETOS} AS bin
デフォルトのマウントタイプは、コマンドで docker build
渡すコンテキストからの読み取り専用バインドマウントです。 これは、 COPY . .
と RUN --mount=target=.
コマンドを実行するためにコンテキストからのファイルが必要であるが、最終的なイメージに保持する必要がない場合。
Goモジュールのダウンロードを分離する代わりに、これを削除して、の /go/pkg/mod
キャッシュマウントを使用できます。
結論
この 一連の投稿 では、最適化されたコンテナー化された Go 開発環境を導入する方法と、CI でこれと同じ環境を使用する方法について説明しました。 そのようなプロジェクトで開発したい人にとっての唯一の依存関係はDockerとmakeです-後者はオプションで別のスクリプト言語に置き換えられます。
この例のソースは、私のGitHubにあります https://github.com/chris-crone/containerized-go-dev
実験的なDockerfile構文の詳細については、こちらをご覧ください。 https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md
Docker でのビルドに興味がある場合は、Buildx リポジトリをご覧ください。 https://github.com/docker/buildx