4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

go mod vendorについて調べてみる

4
Last updated at Posted at 2025-06-15

インターン先でvendorを使うことがあり、go mod tidyとは何が違うのかわからなかったため、vendorとはどんな仕組みなのか調べてみました。

vendorとは

vendorとは、goの依存パッケージをローカルのプロジェクト内に保存する仕組みです。
go mod vendorと打つことでプロジェクト内に.vendorディレクトリが生成され、そこにプロジェクト内で依存しているパッケージをダウンロードしてくれます。

goにおけるパッケージのダウンロードは、go getgo mod tidyを使用して、$GOPATH/pkg/modにダウんロードすることになります。
このパスはローカルキャッシュとして、他プロジェクトにも同じように使い回していくことができます。

vendorでは、他プロジェクトに使い回すことができず、.vendorディレクトリがあるファイル配下のみでパッケージが機能します。

vendorのメリット

vendorを行うことでプロジェクトが依存しているモジュールの依存関係固定化を行ってくれます。

ただ、go.sumでversion等は固定されているので、vendorを知らない方は依存関係固定化なんていらなくないか?と思うことがあると思います。

go mod tidy等でダウンロードしたモジュールは$GOPATH/pkg/modにダウンロードされますが、全プロジェクトのパッケージをキャッシュしてしまっているので、プロジェクトに依存しているパッケージだけ抽出してみるということが大分難しいです。

vendorを使うと上のような問題が解決するのですが、大分想像しづらいと思うので、もう少し具体的な例を出してみようと思います。

1.オフライン環境下におけるビルド

オフライン環境でビルドしないといけず、ビルド環境に該当パッケージがダウンロードされていないとビルドを行うことができません。
また、モジュールのversionが上がると適宜ビルド環境で特定のモジュールを入れ替えるという作業が必要になります。

そのためvendorディレクトリを作成して、ビルド環境に持っていくことで、依存するパッケージのみが全てビルド環境に入っている状況にすることができます。

2.ビルド時間の短縮

上に大分関係していますが、vendorディレクトリにモジュールが入っていることによって、モジュールのダウンロードが不要になるため、結果ビルドする際にかかる時間が少なくなります。

3.privateなパッケージの再利用

GitHubのプライベートリポジトリで管理されているGoのパッケージをインストールするには、そのリポジトリへのアクセス権が必要です。ローカルでの開発では自身のGitアカウントを使用できますが、Gitアカウントが紐付いていない環境、例えばGitHub ActionsのようなCI/CD環境でGoモジュールをダウンロードする必要がある場合はどうでしょうか?

GitHub Actionsで自動ビルドやテストを行う際、ホステッドランナー上でGoモジュールをダウンロードする必要があります。このダウンロードには、プライベートリポジトリからクローンするためのPersonal Access Token (PAT) が必要となり、そのPATをGitHub Actionsで設定したり、DockerイメージでのビルドではDockerfileに渡したりする必要があります。

しかし、PATが外部に漏洩すると、そのプライベートリポジトリが悪用される重大なリスクがあります。 このセキュリティリスクを軽減するため、GitHub Actionsでは一時的にPATを発行する actions/create-github-app-token などのActionが提供されています。

このActionを通して発行されたPATを使用してプライベートなモジュールをクローンした後、vendorディレクトリを利用することで、Goモジュールの管理とデプロイをより効率的に行えます。
vendorディレクトリには依存関係にあるモジュールのコピーが保存されるため、Dockerイメージをビルドする際に、自身のストレージに入っているGoモジュールキャッシュ全体をコピーする代わりにvendorディレクトリのみをコピーするだけで済みます。これにより、Dockerfileの記述が簡単にされ、最終的なイメージサイズも小さくなります。

vendorを実際に使ってみる

今回は上の話を踏まえてvendorを実際に使ってみようと思います。

vendorディレクトリを生成

vendorディレクトリを生成してみます。

go mod vendor

こうすることでvendorディレクトリが生成されます。

ls
go.mod  go.sum  main.go vendor

vendorディレクトリの中を見てみると下のようになっていました。

❯ tree
.
├── github.com
│   └── maooz4426
│       └── hello
│           └── hello.go
└── modules.txt

actionsでvendorのメリットを実感してみる

vendorのメリットの方で書いたprivateなパッケージの再利用を実感するためにactionsでvendorを使ったビルドを行ってみようと思います。

まずgo mod vendorを実行してみる

privateなパッケージをgo.modに依存関係として追加するために下のように環境変数を追加します。

export GOPRIVATE="github.com/maooz4426/hello"

これを行うことでダウンロードできます。

go mod tidy

go.modに依存関係を追加したのでpushします。

まず失敗する例をworkflowで構築してみます。

.github.com/workflows/build.yml
name: build
on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
      - name: Set up Go
        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
        with:
          go-version-file: "go.mod"
      - name: Install dependencies
        run: go mod vendor
      - name: go build
        run: go build -mod=vendor -v ./...

すると下のように失敗します。
image.png

Actionsにはprivateリポジトリの閲覧権限が存在しないため怒られました。

Run go mod download
  go mod download
  shell: /usr/bin/bash -e {0}
go: github.com/maooz4426/hello@v1.0.2: reading github.com/maooz4426/hello/go.mod at revision v1.0.2: git ls-remote -q origin in /home/runner/go/pkg/mod/cache/vcs/18842ccf53f7f60310496984429b6bd1a1db7a42f058f8dba917eb73175a7732: exit status 128:
	fatal: could not read Username for 'https://github.com': terminal prompts disabled
Confirm the import path was entered correctly.
If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.

Github Appの準備

上のビルドを成功させるためにGithubAppを準備します。
Developers settingsからGithubAppを作成します。
image.png

下のように該当Appを作ってみます。
image.png

Webhookはオンになっていますが、最初はいらないのでチェックは外します。
image.png

image.png

権限はcontentsをread-onlyにします。
image.png

上でAppを作成した後下のように作成したAppを該当リポジトリにインストールします。
image.png

ダウンロードするモジュールが存在するリポジトリも追加しておきます。
image.png

repositoryの設定

Github AppのIDをvariablesとして登録します。

IDは該当GithubAppの下の部分に書かれています。
スクリーンショット 2025-06-15 22.43.11.png

これをvariablesに設定します。
スクリーンショット 2025-06-15 22.44.57.png

次に秘密鍵を生成します。
image.png

catコマンドで秘密鍵が下の形式で表示されるのでそれをコピーします。

-----BEGIN RSA PRIVATE KEY-----

-----END RSA PRIVATE KEY-----

これをrepositoryのsecretsに保存します。
image.png

ビルドが成功するようにworkflowを組む

下のようにworkflowsを組んで見ました。
privateリポジトリを見るためtokenを発行するactionを使うことでパッケージのインストールを可能にしています。

.github.com/workflows/build.yml
name: build
on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
      - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e #v2.0.6
        id: generate-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}
      - name: Configure Git for private modules
        run: |
          git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf "https://github.com"
        env:
          GOPRIVATE: github.com/maooz4426/*
      - name: Set up Go
        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
        with:
          go-version-file: "go.mod"
      - name: Install dependencies
        run: go mod vendor
      - name: go build
        run: go build -mod=vendor -v ./...

上のように組んでみるとビルドが成功します。

image.png

vendorをDockerfileに渡してみる

実際にDockerfileにvendorをコピーさせてみます。

FROM public.ecr.aws/docker/library/golang:1.24.2

WORKDIR /app

COPY go.mod go.sum ./
COPY vendor/ ./vendor/
COPY *.go ./

RUN go build -mod=vendor -o main .

workflowsは下のように修正してみました。

.github/workflows/build.yml
name: build
on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
      - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e #v2.0.6
        id: generate-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}
      - name: Configure Git for private modules
        run: |
          git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf "https://github.com"
        env:
          GOPRIVATE: github.com/maooz4426/*
      - name: Set up Go
        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
        with:
          go-version-file: "go.mod"
      - name: Install dependencies
        run: go mod vendor
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb

      - name: Build and push
        uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
        with:
          context: .
          push: false 
          tags: |
            myapp:latest
            myapp:pr-${{ github.event.number }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

先述しましたが、もしvendorを使っていなかった場合は、Dockerfileでgo mod tidyをする必要があ離ますが、その場合は閲覧権限のあるPATを渡さなければなりません。またGOPATH配下のモジュールキャッシュを使うこともできますが、GOPATHを指定するとイメージサイズが大きくなりますし、特定のパッケージだけコピーするように記述するのも大分めんどくさいため制限があります。

このような問題をvendorを使うことで解決できます。

まとめ

vendorを使用することで安全にprivateリポジトリからのgoパッケージのクローン、そこからactions等でのビルドが行えるようになります。

個人開発ではあまり出会うことは少ないと思いますが、vendorを使う機会があったら一度調べてみると面白いと思います。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?