3.

Docker+Go+gRPCで簡単なWebアプリを作る(その1)|環境構築とproto定義

編集

このチュートリアルでは、Docker でコンテナ化された開発環境のうえで、Go 言語と gRPC を使った簡単な Web アプリケーションの土台を作ります。具体的には、プロジェクトの作成から Docker 環境の用意、Protocol Buffers による API 定義、コード生成、gRPC サーバの雛形までを、Windows 環境を例にハンズオン形式で進めていきます。実際にリクエストを処理して動かす実装は次の段階で扱う想定で、本記事では「動かす前の土台」を一通り組み上げることを目標にします。

この記事の要点
  • この記事で学ぶこと:Go モジュールの初期化、Docker(Dockerfile / docker-compose)による開発環境の構築、.proto ファイルによる gRPC サービスの定義、protoc によるコード生成、gRPC サーバの雛形の組み立て。
  • 作るもの:名前を受け取って挨拶を返す「Greeter」サービスを題材に、gRPC サーバを起動できる最小構成。実際の応答処理の実装は次の段階で扱います。
  • 前提環境:Windows OS、Docker Desktop、Go(比較的新しいバージョン推奨)、protoc(Protocol Buffers コンパイラ)。Go や gRPC の基本的な用語に軽く触れたことがある読者を想定しています。
  • 補足:コマンドやパスは執筆時点(2026 年)の一般的な手順です。Mac / Linux では一部コマンドが異なるため、お使いの環境に合わせて読み替えてください。

 

全体像

gRPC は、Protocol Buffers(以下 proto)で定義したインターフェースをもとに、サーバとクライアント双方のコードを自動生成して通信する仕組みです。本記事で組み立てる流れは、おおむね次のようになります。

手順 やること 成果物
1. プロジェクト作成 go mod init でモジュールを初期化 go.mod
2. Docker 環境 Dockerfile と docker-compose を用意 Dockerfile / docker-compose.yml
3. proto 定義 サービスとメッセージを記述 .proto ファイル
4. コード生成 protoc で Go コードを生成 *.pb.go
5. サーバ雛形 生成コードを使ってサーバを起動 main.go

最終的なディレクトリ構成は、おおよそ次のような形を目指します。

my-web-app/
├── go.mod
├── go.sum
├── main.go
├── Dockerfile
├── docker-compose.yml
└── pb/
    ├── greeter.proto
    ├── greeter.pb.go
    └── greeter_grpc.pb.go

 

事前準備:必要なツールのインストール

手順に入る前に、次の 3 つを用意します。

  • Docker DesktopDocker Desktop for Windows からダウンロードしてインストールします。
  • GoGo の公式ウェブサイト からインストーラを入手します。インストール後、go version でバージョンを確認できます。
  • protoc(Protocol Buffers コンパイラ)Protocol Buffers のリリースページ から Windows 向けの zip(protoc-win64.zip など)をダウンロードして解凍し、bin ディレクトリのパスを環境変数 Path に追加します。

protoc 本体に加えて、Go コードを生成するためのプラグインも必要です。次のコマンドでインストールします(PowerShell またはコマンドプロンプトを再起動してから実行してください)。

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

インストール先は $(go env GOPATH)\bin(多くの環境で %USERPROFILE%\go\bin)です。protoc がプラグインを見つけられるよう、このディレクトリも環境変数 Path に通しておきます。protoc-gen-goprotoc-gen-go-grpc は、それぞれメッセージ用コードと gRPC サービス用コードを生成する別々のプラグインである点に注意してください。

 

手順1:プロジェクトの作成(go mod init)

作業用のディレクトリを作り、その中で Go モジュールを初期化します。PowerShell またはコマンドプロンプトを開き、次のように実行します。

mkdir my-web-app
cd my-web-app
go mod init example.com/myproject

ここで指定した example.com/myproject が、このプロジェクトのモジュールパスになります。以降、生成コードを import する際の基点になるため、後で出てくるパスと食い違わないよう、自分で決めた値を一貫して使ってください。実際の公開を想定する場合は、リポジトリの URL(例:github.com/yourname/my-web-app)に合わせておくと扱いやすくなります。

gRPC 関連の依存も追加しておきます。コードを書いてから go mod tidy でまとめて解決する方法でも構いません。

go get google.golang.org/grpc@latest
go get google.golang.org/protobuf@latest

 

手順2:Docker 環境の構築(Dockerfile / docker-compose)

開発環境をコンテナ化しておくと、手元の OS 構成に左右されにくくなります。まずプロジェクト直下に Dockerfile を作成します。ここではビルド用と実行用を分けるマルチステージ構成にして、最終イメージを軽くしています。

# Dockerfile

# ---- ビルドステージ ----

FROM golang:1.22 AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o server .

# ---- 実行ステージ ----

FROM gcr.io/distroless/base-debian12

WORKDIR /app
COPY --from=builder /app/server .

EXPOSE 50051

CMD ["./server"]

ベースイメージのバージョン(上記の golang:1.22)は、お使いの環境に合わせて読み替えてください。gRPC は HTTP/2 上で動作する独自プロトコルのため、Web ブラウザで直接開く一般的な HTTP サーバとはポートの扱いが異なります。ここでは慣例的に使われることの多い 50051 番を公開しています。

続いて、起動を簡単にするための docker-compose.yml を用意します。

# docker-compose.yml

services:
  app:
    build: .
    ports:
      - "50051:50051"

これで docker compose up --build によるビルドと起動ができるようになります。ただし、この段階では main.go や生成コードがまだ無いためビルドは通りません。コンテナのビルド確認は、手順 5 まで進めてから行ってください。

 

手順3:proto ファイルの定義

pb ディレクトリを作り、その中に greeter.proto を作成します。名前(name)を受け取り、挨拶メッセージ(message)を返す Greeter サービスを定義します。

// pb/greeter.proto

syntax = "proto3";

package pb;

option go_package = "example.com/myproject/pb";

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

ポイントは option go_package です。ここには手順 1 で決めたモジュールパスに /pb を続けた値を指定します。この値が生成コードの import パスを決めるため、モジュールパスと一致していないと、後でサーバ側から生成コードを参照できなくなります。フィールドに付いている = 1 などの番号は、各フィールドを識別するためのフィールド番号で、メッセージ内で一意である必要があります。

 

手順4:protoc によるコード生成

proto 定義から Go コードを生成します。プロジェクトのルートディレクトリで、次のコマンドを実行します。

protoc --go_out=. --go_opt=paths=source_relative ^
  --go-grpc_out=. --go-grpc_opt=paths=source_relative ^
  pb/greeter.proto

上記はコマンドプロンプト向けに行末を ^ で継続しています。PowerShell では継続文字がバッククォート(`)になるため、1 行で書くか継続文字を読み替えてください。成功すると pb ディレクトリに greeter.pb.go(メッセージ型)と greeter_grpc.pb.go(サービス用インターフェース)が生成されます。

  • --go_out / --go-grpc_out:生成コードの出力先。. は現在のディレクトリを起点にする指定です。
  • paths=source_relative.proto ファイルの位置に合わせて出力先を決めるオプションです。これにより pb 内にそのまま生成されます。

生成後、依存関係を整理しておきます。

go mod tidy

 

手順5:gRPC サーバの雛形

生成された Greeter サービス用のインターフェースを実装し、gRPC サーバを起動する main.go を作成します。ここでは「サーバが起動してリクエストを待ち受ける」ところまでを組み立てます。

// main.go

package main

import (
  "context"
  "log"
  "net"

  "google.golang.org/grpc"
  pb "example.com/myproject/pb"
)

// 生成されたインターフェースを満たすための型
type greeterServer struct {
  pb.UnimplementedGreeterServer
}

// SayHello はリクエストの name を使って挨拶を返す
func (s *greeterServer) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
  msg := "Hello, " + req.GetName()
  return &pb.HelloResponse{Message: msg}, nil
}

func main() {
  lis, err := net.Listen("tcp", ":50051")
  if err != nil {
    log.Fatalf("failed to listen: %v", err)
  }

  s := grpc.NewServer()
  pb.RegisterGreeterServer(s, &greeterServer{})

  log.Println("gRPC server listening on :50051")
  if err := s.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %v", err)
  }
}

import の example.com/myproject/pb は、手順 1 のモジュールパスと手順 3 の go_package に合わせています。構造体に pb.UnimplementedGreeterServer を埋め込んでいるのは、将来サービスにメソッドが追加されてもコンパイルが壊れにくくするための、生成コード側が用意した仕組みです。

ここまでできたら、ビルドして起動を確認します。

# ローカルで実行する場合
go run .

# Docker で実行する場合
docker compose up --build

ログに gRPC server listening on :50051 と表示されれば、サーバの雛形は完成です。gRPC は HTTP/2 上の独自プロトコルのため、Web ブラウザで http://localhost:50051 を開いても期待した表示にはなりません。動作確認には gRPC 用のクライアントツール(例:grpcurl など)を使うのが一般的です。クライアント側の実装や呼び出しの確認は、続く段階で扱うのが分かりやすいでしょう。

ここまでで、Docker でコンテナ化された環境のうえに、proto 定義から生成したコードを使う gRPC サーバの土台が用意できました。次の段階では、このサーバへ実際にリクエストを送り、応答が返ることを確認していく流れになります。

 

つまずきやすいポイント

症状 原因と対処
protoc が見つからない protoc 本体の bin を環境変数 Path に追加していない可能性があります。追加後はターミナルを開き直してから再実行します。
protoc-gen-go 系のプラグインが見つからない プラグインのインストール先($(go env GOPATH)\bin)が Path に通っていないと、protoc がプラグインを呼び出せません。このディレクトリを追加してください。
生成コードを import できない モジュールパス(go mod init)、go_packagemain.go の import が食い違っていることが多いです。3 箇所を同じ基点に揃えます。
ポートに接続できない/競合する EXPOSE やコード内のポート、docker-compose の ports マッピングがずれていないか確認します。既に使用中のポートなら別番号に変更します。
Docker ビルド時に依存解決で失敗する go.sum が古い、または go mod tidy 未実行のことがあります。ローカルで go mod tidy を実行してから再ビルドします。

 

よくある質問

Q1. 一般的な HTTP サーバと gRPC は何が違うのですか?

HTTP サーバは多くの場合 HTTP/1.1 上でテキストや JSON をやり取りし、ブラウザから直接開いて確認できます。一方 gRPC は HTTP/2 上で Protocol Buffers によるバイナリ形式を用い、proto で定義したインターフェースに沿って通信します。型付きの API を双方のコードとして生成できる点が特徴で、ブラウザで直接開く用途には向きません。確認には専用のクライアントを使います。

Q2. protoc-gen-goprotoc-gen-go-grpc は両方とも必要ですか?

はい、役割が分かれています。protoc-gen-go はメッセージ型(HelloRequest など)を生成し、protoc-gen-go-grpc はサービス用のインターフェースや登録関数(RegisterGreeterServer など)を生成します。gRPC サーバを組み立てるには両方が必要なので、事前準備の段階でどちらもインストールしておいてください。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. Protocol Buffers
  2. 詳細説明付きクイックスタート
  3. Docker + Go言語 + gRPC で簡単なWebアプリケーションを作る その1

最近更新/作成されたページ