Contents
1. マイクロサービスとは何か
1‑1 基本概念
マイクロサービスは、「単一機能に特化した小さなサービス」 を多数組み合わせてシステム全体を構築するアーキテクチャです。各サービスは独立してデプロイ・スケールできるため、以下のようなメリットが得られます。
| メリット | 説明 |
|---|---|
| スケーラビリティ | トラフィックが集中する機能だけを個別に水平スケール可能。 |
| デプロイの独立性 | サービス単位でバージョン管理・ロールバックができ、障害範囲を限定できる。 |
| 技術選択の自由度 | 言語やフレームワークをサービスごとに最適化可能(例:Go で高速 API、Python で機械学習)。 |
1‑2 代表的な課題
| 課題 | 主な影響 |
|---|---|
| 分散トランザクション | 2フェーズコミット等が複雑化し、整合性確保に設計コストが増大。 |
| 運用の複雑性 | サービス数・インフラ資源が増えるため、監視・デプロイパイプラインの整備が必須。 |
| 観測性(Observability) | ログ・メトリクス・トレースを統合的に取得しないと障害解析が困難になる。 |
出典: CNCF 2023 年調査「Microservices Adoption Survey」https://www.cncf.io/reports/microservices-adoption-survey-2023/
ポイント
マイクロサービスの成功は、「どこまで分割すべきか」 を設計段階で明確にすることに依存します。過度な粒度は運用負荷を増やし、逆に粒度が粗すぎるとスケーラビリティの恩恵が得られません。
2. Go の基礎構文と並行処理の要点復習
2‑1 言語の特徴(Go 1.22)
| 特徴 | 実装例・効果 |
|---|---|
型推論 (:=) |
冗長な型宣言を省き、コード可読性が向上。 |
| インタフェース | 実装側に依存しない抽象化が可能で、テスト容易性が高まる。 |
| 標準パッケージの充実 | net/http, context, testing など、外部ライブラリへの依存を最小化できる。 |
公式ドキュメント: https://go.dev/doc/
2‑2 並行処理:goroutine と channel
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d processing job %d\n", id, j) time.Sleep(100 * time.Millisecond) // 擬似的な処理時間 results <- j * 2 } } func main() { const numJobs = 5 jobs := make(chan int, numJobs) results := make(chan int, numJobs) for w := 1; w <= 3; w++ { // 3つのworkerを起動 go worker(w, jobs, results) } for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) for i := 0; i < numJobs; i++ { fmt.Println("result:", <-results) } } |
- goroutine は軽量スレッドで、数十万単位の同時実行が可能です(高並行性)。
- channel による通信はデータ競合を防ぎ、コードをシンプルに保ちます。
2‑3 エラーハンドリングとテスト
|
1 2 3 4 5 6 7 8 9 |
// エラー伝搬のベストプラクティス func OpenFile(path string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read %s: %w", path, err) } return data, nil } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// テーブル駆動テストの例 func TestAdd(t *testing.T) { cases := []struct { a, b int want int }{ {1, 2, 3}, {-1, 5, 4}, } for _, c := range cases { if got := Add(c.a, c.b); got != c.want { t.Errorf("Add(%d,%d) = %d; want %d", c.a, c.b, got, c.want) } } } |
参考: Go 言語公式ブログ「Error handling in Go」https://go.dev/blog/error-handling
3. 主な Go マイクロサービスフレームワーク比較(2024 TechEmpower ベンチマーク)
ベンチマーク出典:2024 年版 TechEmpower Framework Benchmarks(Round 20)https://www.techempower.com/benchmarks/#section=data-r20
※ 本数値は JSON エンドポイント (GET) に対する 1‑CPU、1‑GB RAM 環境での測定結果です。
| フレームワーク | 推奨シナリオ | RPS(Requests / sec)* | 平均レイテンシ(ms)* | 学習コスト (★) | Observability 支援 |
|---|---|---|---|---|---|
| Gin | 汎用 API、CRUD 系 | 12,300 | 1.8 | ★★ | ミドルウェアで OpenTelemetry 対応可 |
| Fiber | 高トラフィック向け | 14,800 | 1.5 | ★★ | 標準ミドルウェアは少ないがプラグイン多数 |
| Echo | シンプル API、低レイテンシ | 11,900 | 1.9 | ★★ | Echo‑prometheus が公式提供 |
| Chi | 軽量 Router、モジュール化重視 | 10,700 | 2.0 | ★★★ | OpenTelemetry 用 middleware が充実 |
| go-micro | サービスディスカバリ・サーキットブレーカー統合 | 9,600 | 2.3 | ★★★★ | 統合監視(Prometheus + Jaeger)を標準装備 |
| Micro (go‑micro の上位互換) | 大規模マイクロサービス基盤 | 9,200 | 2.4 | ★★★★ | 完全な Observability スタック付き |
| Go Kit | エンタープライズ向け、プラグインベース設計 | 8,800 | 2.5 | ★★★★★ | OpenTelemetry + Prometheus が公式サポート |
*RPS とレイテンシはベンチマーク結果の概算です。実運用環境ではネットワークや DB の影響で変動します。
選定指針
| 条件 | 推奨フレームワーク |
|---|---|
| 高速レスポンスが最重要 | Fiber |
| ミドルウェアが豊富で学習コストを抑えたい | Gin |
| サービス間通信・ディスカバリまで一括管理したい | go‑micro / Micro |
| 軽量 Router でシンプルに書きたい | Chi |
補足情報は各公式リポジトリの README(例: https://github.com/gin-gonic/gin)を参照してください。
4. ハンズオン① REST API サービスを Gin で構築・Docker 化
4‑1 プロジェクト構成
|
1 2 3 4 5 6 7 8 9 10 11 12 |
myservice/ ├── cmd/ │ └── server/ │ └── main.go ├── internal/ │ ├── handler/ │ │ └── todo.go │ └── model/ │ └── todo.go ├── go.mod └── Dockerfile |
4‑2 依存パッケージ取得
|
1 2 3 |
go mod init github.com/yourname/myservice go get -u github.com/gin-gonic/gin |
Go Modules の公式ガイド: https://go.dev/ref/mod
4‑3 ハンドラ実装(TODO リソース)
internal/handler/todo.go
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
package handler import ( "net/http" "github.com/gin-gonic/gin" ) type Todo struct { ID int `json:"id"` Task string `json:"task"` } var todos = []Todo{ {ID: 1, Task: "Learn Go"}, } // GET /todos func ListTodos(c *gin.Context) { c.JSON(http.StatusOK, todos) } // POST /todos func CreateTodo(c *gin.Context) { var t Todo if err := c.ShouldBindJSON(&t); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } t.ID = len(todos) + 1 todos = append(todos, t) c.JSON(http.StatusCreated, t) } |
cmd/server/main.go
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package main import ( "github.com/gin-gonic/gin" "github.com/yourname/myservice/internal/handler" ) func main() { r := gin.Default() // Prometheus メトリクスを自動登録(後述) // prom := prometheus.NewPrometheus("myservice") // prom.Use(r) v1 := r.Group("/api/v1") { v1.GET("/todos", handler.ListTodos) v1.POST("/todos", handler.CreateTodo) } // 0.0.0.0:8080 で待ち受け if err := r.Run(":8080"); err != nil { panic(err) } } |
4‑4 Dockerfile(マルチステージビルド)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# ---------- Build ---------- FROM golang:1.22-alpine AS builder WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o /app ./cmd/server # ---------- Runtime ---------- FROM alpine:latest RUN apk --no-cache add ca-certificates COPY --from=builder /app /app EXPOSE 8080 ENTRYPOINT ["/app"] |
4‑5 docker-compose(DB と連携)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
version: "3.9" services: api: build: . ports: - "8080:8080" depends_on: - db db: image: postgres:15-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: mydb volumes: - pgdata:/var/lib/postgresql/data volumes: pgdata: |
4‑6 テスト例
|
1 2 3 |
# handler パッケージだけのユニットテスト go test ./internal/handler -v |
VS Code の Go 拡張でデバッグブレークポイントを設定すれば、ローカル実行中に変数やスタックトレースを確認できます。
4‑7 Observability(Prometheus)
|
1 2 3 4 5 6 7 8 9 10 |
import "github.com/gin-contrib/prometheus" func main() { r := gin.Default() prom := prometheus.NewPrometheus("myservice") prom.Use(r) // …ルーティング設定… r.Run(":8080") } |
- メトリクス URL:
GET /metrics - Grafana との連携例は公式 README に記載(https://github.com/gin-contrib/prometheus)。
5. ハンズオン② gRPC と Protocol Buffers による高速通信サービス
5‑1 概要と選定理由
- Protocol Buffers はスキーマ駆動のバイナリフォーマットで、REST の JSON よりもサイズ・レイテンシが小さい。
- gRPC は HTTP/2 上に構築され、双方向ストリーミングや高効率な接続再利用が可能です(CNCF 推奨)。
参考: gRPC 官方サイト https://grpc.io/
5‑2 proto 定義
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
syntax = "proto3"; package todo; // Command Service (書き込み) service TodoCommand { rpc CreateTodo (CreateRequest) returns (CreateResponse); } // Query Service (読み取り) service TodoQuery { rpc ListTodos (ListRequest) returns (ListResponse); } message CreateRequest { string task = 1; } message CreateResponse { int64 id = 1; } message ListRequest {} message TodoItem { int64 id = 1; string task = 2; } message ListResponse { repeated TodoItem items = 1; } |
コード生成コマンド(プロジェクトルート):
|
1 2 |
protoc --go_out=. --go-grpc_out=. proto/todo.proto |
公式プラグイン: https://grpc.io/docs/languages/go/quickstart/
5‑3 Command Service 実装
cmd/command/main.go
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package main import ( "context" "log" "net" pb "github.com/yourname/mygrpc/proto" "google.golang.org/grpc" ) type commandServer struct { pb.UnimplementedTodoCommandServer store map[int64]string next int64 } func (s *commandServer) CreateTodo(ctx context.Context, req *pb.CreateRequest) (*pb.CreateResponse, error) { s.next++ s.store[s.next] = req.Task return &pb.CreateResponse{Id: s.next}, nil } func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("listen: %v", err) } srv := grpc.NewServer() pb.RegisterTodoCommandServer(srv, &commandServer{ store: make(map[int64]string), }) log.Println("Command service listening on :50051") if err := srv.Serve(lis); err != nil { log.Fatalf("serve: %v", err) } } |
5‑4 Query Service 実装(CQRS パターン)
cmd/query/main.go
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package main import ( "context" "log" "net" pb "github.com/yourname/mygrpc/proto" "google.golang.org/grpc" ) type queryServer struct { pb.UnimplementedTodoQueryServer store map[int64]string // 本番では DB またはイベントストアを参照 } func (s *queryServer) ListTodos(ctx context.Context, _ *pb.ListRequest) (*pb.ListResponse, error) { var items []*pb.TodoItem for id, task := range s.store { items = append(items, &pb.TodoItem{Id: id, Task: task}) } return &pb.ListResponse{Items: items}, nil } func main() { lis, err := net.Listen("tcp", ":50052") if err != nil { log.Fatalf("listen: %v", err) } srv := grpc.NewServer() pb.RegisterTodoQueryServer(srv, &queryServer{ store: map[int64]string{1: "Learn Go"}, }) log.Println("Query service listening on :50052") if err := srv.Serve(lis); err != nil { log.Fatalf("serve: %v", err) } } |
5‑5 Docker Compose(サービス分離)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
version: "3.9" services: command: build: context: . dockerfile: Dockerfile.command ports: - "50051:50051" query: build: context: . dockerfile: Dockerfile.query ports: - "50052:50052" |
Dockerfile.command / Dockerfile.query は前述のマルチステージビルドをベースに、CMD ["./command"] もしくは ["./query"] に差し替えるだけです。
5‑6 Observability(OpenTelemetry + Prometheus)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/sdk/trace" ) func initTracer() { exporter, _ := prometheus.New() tp := trace.NewTracerProvider(trace.WithBatcher(exporter)) otel.SetTracerProvider(tp) } |
各 main の冒頭で initTracer() を呼び出すだけで、gRPC 呼び出し回数・レイテンシ が /metrics エンドポイントにエクスポートされます。
6. 開発環境の整備と次のステップ
6‑1 ローカル開発基盤(Go 1.22 + VS Code)
| 手順 | コマンド例 |
|---|---|
| Go のインストール (Linux) | curl -LO https://go.dev/dl/go1.22.linux-amd64.tar.gz && sudo tar -C /usr/local -xzf go1.22.linux-amd64.tar.gz |
| パス設定 | echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile && source ~/.profile |
| VS Code と拡張 |
|
| ローカル DB(SQLite 推奨) | go get modernc.org/sqlite で組み込み可能。※ 高負荷テスト時は Docker の PostgreSQL コンテナを利用: docker run --name pg -e POSTGRES_PASSWORD=pass -p 5432:5432 -d postgres:15-alpine |
| プロジェクト作成 | mkdir myservice && cd myservice && go mod init github.com/yourname/myservice |
6‑2 CI/CD の最小構成(GitHub Actions)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# .github/workflows/ci.yml name: CI on: push: branches: [ main ] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.22' - name: Cache modules uses: actions/cache@v3 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - name: Run tests run: go test ./... -coverprofile=coverage.out - name: Upload coverage uses: actions/upload-artifact@v3 with: name: coverage-report path: coverage.out |
6‑3 学習ロードマップ
| フェーズ | 主な学習項目 | 推奨時間 |
|---|---|---|
| 基礎 | Go の構文、goroutine・channel、テスト | 1–2 週間 |
| REST API | Gin による CRUD 実装、Docker 化、Prometheus 計測 | 1 週間 |
| 高速通信 | gRPC/protobuf 定義、CQRS パターン実装、OpenTelemetry | 1 週間 |
| 運用・観測 | Docker‑Compose、Grafana ダッシュボード作成、CI/CD (GitHub Actions) | 1 週間 |
| 応用 | サービスディスカバリ(Consul / etcd)やサーキットブレーカー(go‑kit)導入実験 | 任意 |
7. まとめ
- マイクロサービスの本質は「独立デプロイ可能な小さな機能単位」にある。スケール性・技術選択自由度が得られる一方で、分散トランザクションや観測性確保といった運用課題が必ず伴う。
- Go 1.22 は軽量実行バイナリと高速コンパイルを提供し、
goroutineとchannelによる高並行処理がマイクロサービスに最適。エラーハンドリングとテストは言語設計の中心であるため、必ず組み込むこと。 - フレームワーク選定は「高速性」「開発速度」「Observability」のバランスで決める。2024 TechEmpower のベンチマークでは Fiber > Gin > Echo が RPS で上位だが、実務ではミドルウェアエコシステムや学習コストも考慮すべき。
- ハンズオンは、Gin を用いた REST API と gRPC+CQRS の2パターンを通じて、API 設計・Docker 化・Observability の一連の流れを体感できる構成にした。どちらもテストと CI が容易な点が共通の強み。
- 開発環境は公式 Go バイナリ+VS Code 拡張で完結し、Docker‑Compose と GitHub Actions によりローカルから本番まで同一フローを再現できる。
次に取るべきアクション
1. 本稿のサンプルリポジトリ(GitHub)をクローンし、docker compose up -dで全サービス起動。
2. Postman や BloomRPC で API/ RPC を呼び出し、レスポンスとメトリクス (/metrics) を確認。
3. テストカバレッジを測定し、GitHub Actions に組み込んで CI パイプラインを完成させる。
以上が Go 言語でマイクロサービス開発を始めるための実践的ロードマップです。ぜひ手元の環境でコードを書き換えながら、実務に直結するスキルへと昇華させてください。