Google Distributed Cloudで始めるおうちGKE
はじめに
明けましておめでとうございます。
あかいけです。
投稿時点で新年を迎えており完全に遅刻していますが、
この記事は「クラスメソッド Google Cloud Advent Calendar 2025」の23日目の記事です。
Google Cloud の最新情報や実践的な技術 Tipsを知りたい方は、
ぜひ本アドベントカレンダーをチェックしてみてください。
モチベーション
突然ですが、おうちでKubernetesを運用していますか?
私は運用しています。
ただ、おうちでKubernetesを立てる際のハードルを高く感じ、
実際に構築しようとした時に選択肢が多すぎて困る方は一定数いらっしゃるのではないでしょうか?
例えばクラスタ作成/管理のツール一つとっても、minikube?Docker Desktop?kind?kubeadm?Rancher?k3s?MicroK8s?...など無数の選択肢があります。
その結果どれを選べばいいのか分からず、私は夜な夜なドキュメントを読み漁ることになりました。
そんなとき、ふと思ったのです。
「自宅でGKEと同じ体験ができたら最高なのに...」 と。
そしてGoogle Cloudコンソールからクラスタを管理でき、Cloud LoggingやCloud Monitoringが使える夢のような環境が、 Google Distributed Cloud(GDC) で実現できることを知りました。
というわけで今回は、GDCを使っておうちでGKEライクなKubernetesクラスタを構築してみました。
またアプリの外部公開や、おそらく実運用で使うであろうCloud LoggingやArtifact Registryとの連携も試してみます。
また以前 Amazon EKS Anywhere で同じようなことをしているので、EKSが気になる方はこちらをご参照ください。
Google Distributed Cloudってなんだろう?
Google Distributed Cloud(以降GDC)は、
「オンプレミスやエッジ環境でGKEベースのKubernetesクラスタを構築・運用できるサービス」 です。
単にKubernetesが動くだけではなく、
以下のようなGoogle Cloudとの統合機能が使えるのが大きな特徴です。
- Google Cloudコンソールからのクラスタ管理
- GKEと同じUIでオンプレミスクラスタを管理
- Cloud Logging/Cloud Monitoring連携
- ログやメトリクスをGoogle Cloudに集約
- フリート管理
- 複数のクラスタを論理的にグループ化して一元管理
つまり、「オンプレミスにあるけど、GKEと同じように運用できる」 というのがGDCの魅力です。
種類について
Google Distributed Cloudは、
サービス提供形態として以下の4種類があります。
| カテゴリ | 方式 | 概要 |
|---|---|---|
| フルマネージド | Distributed Cloud コネクテッド | Googleが専用ハードウェアとソフトウェアを提供・運用。6〜24台の物理マシンとネットワーク機器で構成されるラック形式。 |
| フルマネージド | Distributed Cloud エアギャップ | インターネット接続のない完全閉域環境向け。コンプライアンス要件が厳しい環境に対応。 |
| ソフトウェアのみ | Distributed Cloud ベアメタル版 | 既存の物理マシン上でGKEベースのクラスタを実行。GPU等のハードウェアアクセラレーション対応。 |
| ソフトウェアのみ | Distributed Cloud VMware版 | vSphere環境内のオンプレミスで実行。既存の仮想化基盤を活用可能。 |
「フルマネージド」はGoogleがハードウェアごと提供・運用してくれる方式で、
「ソフトウェアのみ」は自前のハードウェア上にGoogleのソフトウェアをインストールする方式です。
今回は ソフトウェアのみ の ベアメタル版 を使って、自宅の仮想化基盤(Proxmox)上にクラスタを構築していきます。
料金について
「でも、お高いんでしょう…?」
と費用が気になる方も多いと思うので、先に料金体系を確認しましょう。
ソフトウェアのみのGDCベアメタル版は vCPU単位の従量課金 となっています。
Google Distributed Cloud を使用して作成されたオンプレミス クラスタは、vCPU ごとに課金されます。課金を有効にするには、 Google Cloudプロジェクトで Anthos API を有効にします。
| 項目 | 料金 |
|---|---|
| オンプレミスGKE(ベアメタル/VMware) | $0.03288/vCPU/時間 |
| 月額換算(1 vCPUあたり) | 約$24/月 |
注意事項:
- コントロールプレーンノードは課金対象外です (ワーカーノードのみ課金)
- ハイパースレッディング有効時は1物理コア=2 vCPUとしてカウントされます
- 上記の従量課金とは別に、自動的に利用する別サービス(Cloud Loggingなど)のサービス利用料も発生する
詳細は後ほどご紹介しますが今回構築する構成(4 vCPU × コントロールプレーン兼ワーカーノード3台)の場合、
課金対象は12 vCPUとなり、$288/月額 程度の費用が発生する見込みです。
ご参考までに、本記事の構成で約半日動かして750円ぐらいでした。

無料クレジットについて
Google Cloudの新規アカウントであれば90日間は $300分の無料クレジット が利用可能です。
もちろんGKEも対象となっているので、これを使えば90日間限定で$300以内であれば無料で試すことができます。
GKE の無料トライアル
GKE のすべての機能を 90 日間無料でお試しいただけます。フリートをベースにしたチーム管理
GitOps ベースのマネージド構成管理
ハイブリッド クラウドとマルチクラウド上の GKE
前提条件について
構築するにあたり、ノードとして使うための前提条件を確認しておきましょう。
まず全てのノードに共通して、
CPUマイクロアーキテクチャレベルv3(x86-64-v3)以降のx86-64 CPUおよびvCPUのみサポートとなります。
管理ワークステーション
管理ワークステーションは、クラスタの作成や管理を行うためのマシンです。
bmctlコマンドを実行してクラスタを構築・管理する際に使用します。
ハードウェア要件
| 項目 | 最小構成 | 推奨構成 |
|---|---|---|
| CPU | 2コア | 4コア |
| RAM(Ubuntu) | 4 GiB | 8 GiB |
| RAM(RHEL) | 6 GiB | 12 GiB |
| ストレージ | 128 GiB | 256 GiB |
なおバックアップ/復元操作を行う場合は、
コントロールプレーンノードごとに3〜5 GiBのメモリが必要です。
etcd データベースのサイズとコントロール プレーン ノードの数によっては、クラスタのバックアップと復元のオペレーションで大量の RAM が消費されます。バックアップに必要な RAM の概算量は、コントロール プレーン ノードごとに 3~5 GiB です。メモリが不足しているためバックアップ プロセスが失敗します。
ソフトウェア要件
- サポートされているLinuxディストリビューション
- Docker 19.03以降 (cgroup v2使用時は20.10.0以降)
- Google Cloud CLI
- kubectl
- bmctl
ネットワーク要件
- Google Cloudへのインターネットアクセス
- すべてのクラスタノードへのレイヤ3接続
- クラスタノードへのパスワードなしSSHアクセス(rootまたはsudo権限ユーザー)
- IP転送の有効化(
/proc/sys/net/ipv4/ip_forwardが1)
クラスタノードマシン
クラスタノードは、実際にワークロードが動作するマシンです。
ここにコントロールプレーンノードとワーカーノードが含まれます。
デフォルトプロファイル
| 項目 | 最小構成 | 推奨構成 |
|---|---|---|
| CPU | 4コア | 8コア |
| RAM | 16 GiB | 32 GiB |
| ストレージ | 128 GiB | 256 GiB |
エッジプロファイル(スタンドアロンクラスタのみ)
リソースを抑えたい場合はエッジプロファイルが使えますが、スタンドアロンクラスタでのみ利用可能です。
| 項目 | 最小構成 | 推奨構成 |
|---|---|---|
| CPU | 2コア | 4コア |
| RAM(Ubuntu) | 5 GiB | 8 GiB |
| RAM(RHEL) | 6 GiB | 12 GiB |
| ストレージ | 128 GiB | 256 GiB |
ソフトウェア要件
- サポートされているLinuxディストリビューション
- NTPサービス(chrony、ntp、ntpdate、systemd-timesyncdのいずれか)が有効
- iptables-nftまたはnf_tablesカーネルモジュールの読み込み
- UbuntuではUFW(Uncomplicated Firewall)の無効化が必須
ネットワーク要件
- インターネットアクセス(Google Cloudへの接続)
- 他のすべてのノードへのレイヤ3接続
- DNSサーバーが設定済み
本記事でのシステム構成について
構成図
全体の構成は以下のとおりです。

クラスタからGoogle Cloudのサービスへは、インターネット経由でアクセスしています、
また管理ワークステーションはいつも使っている開発端末(CPU:8コア/16スレッド、メモリ:32GB)を利用しています。
クラスタ構成
クラスタ構成について幾つかのデプロイモデルがありますが、
今回は スタンドアロンクラスタ を構築します。
スタンドアロンクラスタは各ノードがコントロールプレーンとワーカーノードを担い、
またエッジプロファイルが利用可能なため、リソースが限られた小規模環境に向いています。
各ノードのスペックは、エッジプロファイルの推奨要件を設定しています。
| 名称 | IP | vCPU | メモリ | ディスク | 用途 |
|---|---|---|---|---|---|
| gdc-node-1 | 192.168.10.210 | 4 | 8GB | 256GB | スタンドアロンクラスタ ノード1 |
| gdc-node-2 | 192.168.10.211 | 4 | 8GB | 256GB | スタンドアロンクラスタ ノード2 |
| gdc-node-3 | 192.168.10.212 | 4 | 8GB | 256GB | スタンドアロンクラスタ ノード3 |
ネットワーク構成
本来であればProxmox内のVMのネットワークは個別に切ったほうがよさそうですが、
今回は簡略化のため物理サーバーと同じネットワークCIDRでIPを割り振っています。
また、LoadBalancer型Serviceに割り当てられるIPについても、同じネットワークCIDRから使用する設定にしています。
| 名称 | CIDR/IP | 用途 |
|---|---|---|
| クラスタノードマシン ノード1 | 192.168.10.210 | クラスタを構成するノードのIP |
| クラスタノードマシン ノード2 | 192.168.10.211 | クラスタを構成するノードのIP |
| クラスタノードマシン ノード3 | 192.168.10.212 | クラスタを構成するノードのIP |
| コントロールプレーンVIP | 192.168.10.220 | Kubernetes APIサーバーへのアクセス用仮想IP |
| Ingress VIP | 192.168.10.230 | バンドル版Ingressコントローラー用仮想IP |
| LoadBalancerアドレスプール | 192.168.10.230-192.168.10.254 | LoadBalancer型Serviceに割り当てられるIPの範囲 |
| Pod CIDR | 10.244.0.0/16 | Pod間通信用の内部ネットワーク |
| Service CIDR | 10.96.0.0/20 | ClusterIP型Service用の内部ネットワーク |
設計のポイント:
- Pod CIDRとService CIDRは、既存ネットワークのIPアドレスと重複しないように設計します
- Ingress VIPはLoadBalancerアドレスプールの範囲内に含める必要があります
構築してみる
VM構築
まずはクラスタを構成するノード用のVMを作成します。
本記事ではProxmoxを使ってVMを準備しますが、お好きなサーバーでOKです。
以下はTerraformでProxmox上にVMを作成する例で、今回はbpg/proxmoxプロバイダーを使っています。
terraform {
required_version = ">= 1.14.0"
required_providers {
proxmox = {
source = "bpg/proxmox"
version = ">= 0.90.0"
}
}
}
provider "proxmox" {
endpoint = var.pm_api_url
api_token = "${var.pm_api_token_id}=${var.pm_api_token_secret}"
insecure = true
ssh {
agent = false
username = var.pm_ssh_username
password = var.pm_ssh_password
}
}
locals {
cloud_init_templates = {
cluster-node = "${path.module}/config/cloud-init/cluster-node.yaml.tftpl"
}
cloud_init_vars = {
username = var.vm_user
password_hash = var.vm_password_hash
}
}
# ------------------------------------------------------------------------------
# Cloud-Init Configuration (Snippets)
# Role-based cloud-init configuration using external template files
# ------------------------------------------------------------------------------
resource "proxmox_virtual_environment_file" "cloud_init_user_data" {
for_each = var.vms
content_type = "snippets"
datastore_id = "local"
node_name = each.value.node_name
source_raw {
data = templatefile(
local.cloud_init_templates[each.value.role],
merge(local.cloud_init_vars, {
hostname = each.value.hostname
})
)
file_name = "cloud-init-${each.key}.yaml"
}
}
# ------------------------------------------------------------------------------
# Cloud Image Download
# Downloads Ubuntu cloud image to each node for VM creation
# ------------------------------------------------------------------------------
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
for_each = toset([for vm in var.vms : vm.node_name])
content_type = "iso"
datastore_id = "local"
node_name = each.value
url = var.ubuntu_cloud_image_url
file_name = "ubuntu-24.04-cloudimg-amd64.img"
overwrite = false
}
# ------------------------------------------------------------------------------
# Cloud-Init VM
# Ubuntu VM with Cloud-Init configuration for GDC
# ------------------------------------------------------------------------------
resource "proxmox_virtual_environment_vm" "this" {
for_each = var.vms
node_name = each.value.node_name
vm_id = each.value.vm_id
name = each.value.hostname
description = "GDC ${each.value.role} - ${each.value.hostname}"
tags = ["gdc", each.value.role, "terraform"]
on_boot = each.value.started
started = each.value.started
agent {
enabled = true
}
cpu {
cores = each.value.cpu_cores
sockets = 1
type = "host"
}
memory {
dedicated = each.value.memory
}
disk {
datastore_id = "local-lvm"
file_id = proxmox_virtual_environment_download_file.ubuntu_cloud_image[each.value.node_name].id
interface = "scsi0"
size = each.value.disk_size
discard = "on"
iothread = true
}
scsi_hardware = "virtio-scsi-single"
network_device {
bridge = "vmbr0"
model = "virtio"
mtu = 1450
}
vga {
type = "serial0"
}
serial_device {}
initialization {
# Cloud-Init設定(カスタムsnippets使用)
user_data_file_id = proxmox_virtual_environment_file.cloud_init_user_data[each.key].id
datastore_id = "local-lvm"
ip_config {
ipv4 {
address = each.value.ip_address
gateway = each.value.gateway
}
}
dns {
servers = ["8.8.8.8", "8.8.4.4"]
}
}
}
# ------------------------------------------------------------------------------
# Outputs
# ------------------------------------------------------------------------------
output "vm_ip_addresses" {
description = "IP addresses of created VMs"
value = {
for k, v in proxmox_virtual_environment_vm.this : k => v.ipv4_addresses
}
}
output "vm_roles" {
description = "VM roles mapping"
value = {
for k, v in var.vms : k => {
hostname = v.hostname
role = v.role
ip = v.ip_address
}
}
}
# ------------------------------------------------------------------------------
# Proxmox Nodes Configuration
# ------------------------------------------------------------------------------
variable "pm_api_url" {
description = "Proxmox API URL"
type = string
}
variable "pm_api_token_id" {
description = "Proxmox API token ID"
type = string
}
variable "pm_api_token_secret" {
description = "Proxmox API token secret"
type = string
sensitive = true
}
variable "pm_ssh_username" {
description = "SSH username for Proxmox nodes"
type = string
default = "root"
}
variable "pm_ssh_password" {
description = "SSH password for Proxmox nodes"
type = string
sensitive = true
}
# ------------------------------------------------------------------------------
# VM Configuration
# ------------------------------------------------------------------------------
variable "ubuntu_cloud_image_url" {
description = "URL for Ubuntu cloud image download"
type = string
default = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
}
variable "vm_user" {
description = "Default user name for VMs"
type = string
default = "ubuntu"
}
variable "vm_password_hash" {
description = "Hashed password for VM user (generate with: openssl passwd -6 'yourpassword')"
type = string
sensitive = true
}
variable "vms" {
description = "VM definitions with Cloud-Init for GDC"
type = map(object({
node_name = optional(string, "pve-01")
hostname = string
vm_id = number
ip_address = string
gateway = string
role = string
cpu_cores = optional(number, 2)
memory = optional(number, 2048)
disk_size = optional(number, 20)
started = optional(bool, true)
}))
}
以下はtfvarsの設定例です。
pm_api_token_id と pm_api_token_secret は事前に作成した、Proxmox上でのユーザーとトークンです。
pm_api_token_id = "user01@pam!user01-token"
pm_api_token_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
pm_api_url = "https://192.168.10.155:8006/api2/json"
pm_ssh_password = "password"
vm_password_hash = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
vms = {
"gdc-node-1" = {
node_name = "pve-01"
hostname = "gdc-node-1"
vm_id = 210
ip_address = "192.168.10.210/24"
gateway = "192.168.10.1"
role = "cluster-node"
cpu_cores = 4
memory = 8192
disk_size = 256
}
"gdc-node-2" = {
node_name = "pve-02"
hostname = "gdc-node-2"
vm_id = 211
ip_address = "192.168.10.211/24"
gateway = "192.168.10.1"
role = "cluster-node"
cpu_cores = 4
memory = 8192
disk_size = 256
}
"gdc-node-3" = {
node_name = "pve-03"
hostname = "gdc-node-3"
vm_id = 212
ip_address = "192.168.10.212/24"
gateway = "192.168.10.1"
role = "cluster-node"
cpu_cores = 4
memory = 8192
disk_size = 256
}
}
また前提条件として必要となるOS設定やソフトウェアなどは、
可能な限りcloud initでVM作成時に設定しています。
#cloud-config
# Cluster Node configuration for Google Distributed Cloud
# Used for Control Plane and Worker nodes
# Requirements: NTP (chrony), kernel tuning, UFW disabled, working package manager
# Note: containerd is installed by bmctl during cluster creation
hostname: ${hostname}
manage_etc_hosts: true
timezone: Asia/Tokyo
users:
- name: ${username}
groups: [adm, sudo]
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: false
passwd: ${password_hash}
ssh_pwauth: true
write_files:
# Enable SSH root login and password authentication
- path: /etc/ssh/sshd_config.d/99-gdc.conf
content: |
PermitRootLogin yes
PasswordAuthentication yes
permissions: "0644"
# Kernel parameters for GDC
- path: /etc/sysctl.d/99-gdc-node.conf
content: |
# inotify limits for Kubernetes (required for Ubuntu 22.04+)
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288
permissions: "0644"
# Load kernel modules for networking
- path: /etc/modules-load.d/gdc.conf
content: |
overlay
br_netfilter
nf_tables
permissions: "0644"
package_update: true
packages:
- qemu-guest-agent
- curl
- wget
- gnupg
- ca-certificates
- apt-transport-https
- chrony
- socat
- conntrack
- ipset
- ethtool
runcmd:
# Enable qemu-guest-agent
- systemctl enable qemu-guest-agent
- systemctl start qemu-guest-agent
# Load kernel modules
- modprobe overlay
- modprobe br_netfilter
- modprobe nf_tables
# Apply sysctl settings
- sysctl --system
# Disable UFW (GDC requirement)
- systemctl stop ufw || true
- systemctl disable ufw || true
# Enable and start chrony for NTP (GDC requirement)
- systemctl enable chrony
- systemctl start chrony
# Restart SSH to apply configuration
- systemctl restart ssh
クラスタ構築
VMの作成が完了したら、クラスタを構築していきます。
管理ワークステーションの準備
まず管理ワークステーションにて、Google Cloud認証を行います。
# Google Cloud認証
gcloud auth login
gcloud auth application-default login
ログインできたらデフォルトで利用するプロジェクトを設定しておきます。
gcloud projects list
# プロジェクト設定
export CLOUD_PROJECT_ID=<your-project-id>
gcloud config set project $CLOUD_PROJECT_ID
bmctlのダウンロード
クラスタ構築に利用するbmctlコマンドをダウンロードします。
手順は以下公式ドキュメント通りです。
mkdir ~/baremetal && cd ~/baremetal
export PATH="$HOME/baremetal:$PATH"
# bmctlのバージョンを指定
export BMCTL_VERSION=1.34.0-gke.566
# ダウンロード
gcloud storage cp gs://anthos-baremetal-release/bmctl/${BMCTL_VERSION}/linux-amd64/bmctl .
chmod +x ./bmctl
bmctl version
上記コマンド実行後、
以下のようにバージョンが表示されればOKです。
bmctl version: 1.34.0-gke.566, git commit: 544e9f784aaf86427d5f51e84878dc7bc33f880a, build date: 2025-12-05 11:15:57 PST , metadata image digest: sha256:ff741325230ff5c40a3fa9ad2c935c6c85d9f2c2bfd4f7c3cfb2379ecfc038ad
SSH鍵の準備
管理ワークステーションからクラスタノードへのSSH接続用の鍵を作成して配布します。
# 鍵を生成
ssh-keygen -t rsa -b 4096 -f ~/.ssh/gdc-key -N ""
# 全クラスタノードに公開鍵をコピー
ssh-copy-id -i ~/.ssh/gdc-key.pub ubuntu@192.168.10.210
ssh-copy-id -i ~/.ssh/gdc-key.pub ubuntu@192.168.10.211
ssh-copy-id -i ~/.ssh/gdc-key.pub ubuntu@192.168.10.212
構成ファイルの生成と編集
クラスタ作成のための設定ファイルを作成します。
以下のコマンドで設定ファイルを作成します。
また必要に応じて以下のオプションでGoogle Cloud上のリソースを合わせて設定します。
- --enable-apis:必要なAPIを自動的に有効化
- --create-service-accounts:必要なサービス アカウントを自動的に作成
bmctl create config -c edge-ha \
--enable-apis \
--create-service-accounts \
--project-id=$CLOUD_PROJECT_ID
以下のような出力が表示されればOKです、
APIの有効化とサービスアカウントの作成が自動で行われていることがわかります。
[2026-01-02 05:29:49+0900] Enabling APIs for GCP project <your-project-id>:
[2026-01-02 05:29:49+0900] Enabling the following APIs for GCP project <your-project-id>:
[2026-01-02 05:29:49+0900] - anthos.googleapis.com
[2026-01-02 05:29:49+0900] - anthosaudit.googleapis.com
[2026-01-02 05:29:49+0900] - anthosgke.googleapis.com
[2026-01-02 05:29:49+0900] - cloudresourcemanager.googleapis.com
[2026-01-02 05:29:49+0900] - compute.googleapis.com
[2026-01-02 05:29:49+0900] - connectgateway.googleapis.com
[2026-01-02 05:29:49+0900] - container.googleapis.com
[2026-01-02 05:29:49+0900] - gkeconnect.googleapis.com
[2026-01-02 05:29:49+0900] - gkehub.googleapis.com
[2026-01-02 05:29:49+0900] - gkeonprem.googleapis.com
[2026-01-02 05:29:49+0900] - iam.googleapis.com
[2026-01-02 05:29:49+0900] - kubernetesmetadata.googleapis.com
[2026-01-02 05:29:49+0900] - logging.googleapis.com
[2026-01-02 05:29:49+0900] - monitoring.googleapis.com
[2026-01-02 05:29:49+0900] - opsconfigmonitoring.googleapis.com
[2026-01-02 05:29:49+0900] - serviceusage.googleapis.com
[2026-01-02 05:29:49+0900] - stackdriver.googleapis.com
[2026-01-02 05:29:49+0900] - storage.googleapis.com
[2026-01-02 05:29:54+0900] Creating service accounts with keys for GCP project <your-project-id>
[2026-01-02 05:29:58+0900] Service account keys stored at directory bmctl-workspace/.sa-keys
[2026-01-02 05:29:58+0900] Created config: bmctl-workspace/edge-ha/edge-ha.yaml
また以下のようなディレクトリ構成が作成されます。
.
├── bmctl
└── bmctl-workspace
└── edge-ha
└── edge-ha.yaml
次に生成された設定ファイル(edge-ha.yaml)を編集します。
実際に利用した設定ファイルは以下の通りです。
これは公式ドキュメントの「高可用性エッジ プロファイル」の例を元に、
実環境に合わせて各種IPアドレスなど、コメントを入れている箇所を変更しています。
gcrKeyPath: bmctl-workspace/.sa-keys/<your-project-id>-anthos-baremetal-gcr.json
sshPrivateKeyPath: /home/<os-user-name>/.ssh/gdc-key # ノードへログインする際に利用するSSH鍵のファイルパスを指定
gkeConnectAgentServiceAccountKeyPath: bmctl-workspace/.sa-keys/<your-project-id>-anthos-baremetal-connect.json
gkeConnectRegisterServiceAccountKeyPath: bmctl-workspace/.sa-keys/<your-project-id>-anthos-baremetal-register.json
cloudOperationsServiceAccountKeyPath: bmctl-workspace/.sa-keys/<your-project-id>-anthos-baremetal-cloud-ops.json
---
apiVersion: v1
kind: Namespace
metadata:
name: cluster-edge-ha
---
apiVersion: baremetal.cluster.gke.io/v1
kind: Cluster
metadata:
name: edge-ha
namespace: cluster-edge-ha
spec:
type: standalone
profile: edge
anthosBareMetalVersion: 1.34.0-gke.566
gkeConnect:
projectID: <your-project-id>
# VMのIPを指定
controlPlane:
nodePoolSpec:
nodes:
- address: 192.168.10.210
- address: 192.168.10.211
- address: 192.168.10.212
# 物理ネットワークと競合しない範囲を指定
clusterNetwork:
pods:
cidrBlocks:
- 10.244.0.0/16
services:
cidrBlocks:
- 10.96.0.0/20
loadBalancer:
mode: bundled
ports:
controlPlaneLBPort: 443
vips:
# ノードと同じサブネット内であり、尚且つaddressPoolsの範囲外を指定
controlPlaneVIP: 192.168.10.220
# addressPoolsの範囲内を指定
ingressVIP: 192.168.10.230
# LoadBalancer型Serviceに割り当てられるIPの範囲を指定
addressPools:
- name: pool1
addresses:
- 192.168.10.230-192.168.10.254
clusterOperations:
projectID: <your-project-id>
location: asia-northeast1
storage:
lvpNodeMounts:
path: /mnt/localpv-disk
storageClassName: local-disks
lvpShare:
path: /mnt/localpv-share
storageClassName: local-shared
numPVUnderSharedPath: 5
# ノードアクセス設定(設定しない場合はrootユーザーとなる)
nodeAccess:
loginUser: ubuntu
nodeConfig:
podDensity:
maxPodsPerNode: 110
クラスタの作成
次に以下のコマンドでクラスタの作成を開始します。
結構時間がかかるので、お茶でも飲んで気長に待ちましょう。
(私の環境だと全体で1時間ぐらいかかりました)
bmctl create cluster -c edge-ha
クラスタ作成が始まると、以下のように進捗が表示されます。
もし作成中にエラーが出る場合はログファイル(create-cluster.log)を確認しましょう。
[2026-01-02 07:57:30+0900] Running command: bmctl create cluster -c edge-ha
Please check the logs at bmctl-workspace/edge-ha/log/create-cluster-20260102-075730/create-cluster.log
2026/01/02 07:57:39 well-defined vars that were never replaced: DISTRIBUTION,KUBE_RBAC_PROXY_IMAGE_TAG
[2026-01-02 07:57:39+0900] Creating bootstrap cluster... ⠇ 2026/01/02 07:59:40 well-defined vars that were never replaced: IPV6_DUALSTACK
[2026-01-02 07:57:39+0900] Creating bootstrap cluster... OK
[2026-01-02 08:00:30+0900] Installing dependency components... ⠧ W0102 08:01:17.062653 1570660 schema.go:148] unexpected field validation directive: validator, skipping validation
[2026-01-02 08:00:30+0900] Installing dependency components... ⠏ I0102 08:01:18.243517 1570660 warnings.go:110] "Warning: spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`."
[2026-01-02 08:00:30+0900] Installing dependency components... ⠏ I0102 08:01:23.293357 1570660 warnings.go:110] "Warning: metadata.name: this is used in Pod names and hostnames, which can result in surprising behavior; a DNS label is recommended: [must not contain dots]"
[2026-01-02 08:00:30+0900] Installing dependency components... ⠹ I0102 08:01:23.622053 1570660 warnings.go:110] "Warning: metadata.name: this is used in Pod names and hostnames, which can result in surprising behavior; a DNS label is recommended: [must not contain dots]"
[2026-01-02 08:00:30+0900] Installing dependency components... ⠦ I0102 08:01:23.982776 1570660 warnings.go:110] "Warning: metadata.name: this is used in Pod names and hostnames, which can result in surprising behavior; a DNS label is recommended: [must not contain dots]"
[2026-01-02 08:00:30+0900] Installing dependency components... ⠏ I0102 08:01:24.258648 1570660 warnings.go:110] "Warning: metadata.name: this is used in Pod names and hostnames, which can result in surprising behavior; a DNS label is recommended: [must not contain dots]"
[2026-01-02 08:00:30+0900] Installing dependency components... ⠙ I0102 08:01:24.524826 1570660 warnings.go:110] "Warning: spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`."
[2026-01-02 08:00:30+0900] Installing dependency components... ⠸ 2026/01/02 08:01:24 well-defined vars that were never replaced: DISTRIBUTION,KUBE_RBAC_PROXY_IMAGE_TAG
[2026-01-02 08:00:30+0900] Installing dependency components... ⠋ I0102 08:01:27.442661 1570660 warnings.go:110] "Warning: spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`."
[2026-01-02 08:00:30+0900] Installing dependency components... ⠇
なお実際の作成処理としては、
bmctlを実行したサーバー上にてKindでbootstrap cluster(クラスタ作成用のクラスタ)が作られ、その子が実施しています。
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f835bda2d96d gcr.io/anthos-baremetal-release/kindest/node:v0.24.0-gke.132-v1.32.9-gke.700 "/usr/local/bin/entr…" 2 minutes ago Up 2 minutes 127.0.0.1:43047->6443/tcp bmctl-control-plane
またbootstrap clusterの設定ファイルは「~/baremetal/bmctl-workspace/.kindkubeconfig」にあるので、実際に動いているpod達を見ることもできます。
もし作成処理にてエラーが発生して、ログファイルで原因が特定できない場合は、直接リソースを参照するのも良さそうです。
export KUBECONFIG=~/baremetal/bmctl-workspace/.kindkubeconfig
$ kubectl get node
NAME STATUS ROLES AGE VERSION
bmctl-control-plane Ready control-plane 10m v1.32.9-gke.700
$ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
capi-kubeadm-bootstrap-system capi-kubeadm-bootstrap-controller-manager-1.34.0-gke.566-d86xqv 2/2 Running 0 9m32s
capi-system capi-controller-manager-1.34.0-gke.566-9bcc4c448-rpn5h 2/2 Running 0 9m32s
cert-manager cert-manager-5d7f6fbcb8-8f4vm 1/1 Running 0 9m40s
cert-manager cert-manager-cainjector-6bcc886fd5-dn574 1/1 Running 0 9m40s
cert-manager cert-manager-webhook-f79db77f9-sbv4f 1/1 Running 0 9m40s
cluster-edge-ha bm-system-check-kernel-edge-ha-34jxc-k5d8j 0/1 Completed 0 2m39s
なお余談ですが最初は管理ワークステーションもVMで立てて、なおかつ最小構成(CPU:2コア、メモリ4GB)でやってみたんですが、クラスタの作成が永遠に終わりませんでした…。
(各種リソースがカツカツすぎて、podがエラーになったりしていた)
なので管理ワークステーションは、
マシンリソースに余裕があるものを使うことを強くおすすめします。
(クラスタ作成 途中経過…)
[2026-01-02 08:02:54+0900] Waiting for preflight check operator to show up... OK
[2026-01-02 08:04:07+0900] Waiting for preflight check job to finish... OK
[2026-01-02 08:07:27+0900] - Validation Category: machines and network
[2026-01-02 08:07:27+0900] - [PASSED] 192.168.10.211
[2026-01-02 08:07:27+0900] - [PASSED] 192.168.10.211-gcp
[2026-01-02 08:07:27+0900] - [PASSED] 192.168.10.212
[2026-01-02 08:07:27+0900] - [PASSED] 192.168.10.212-gcp
[2026-01-02 08:07:27+0900] - [PASSED] gcp
[2026-01-02 08:07:27+0900] - [PASSED] pod-cidr
[2026-01-02 08:07:27+0900] - [PASSED] 192.168.10.210
[2026-01-02 08:07:27+0900] - [PASSED] 192.168.10.210-gcp
[2026-01-02 08:07:27+0900] - [PASSED] node-network
[2026-01-02 08:07:27+0900] Flushing logs... OK
[2026-01-02 08:07:28+0900] Applying resources for new cluster
[2026-01-02 08:07:28+0900] Waiting for cluster kubeconfig to become ready OK
(クラスタ作成 途中経過……)
[2026-01-02 08:15:38+0900] Writing kubeconfig file
[2026-01-02 08:15:38+0900] The kubeconfig of the cluster being created is present at bmctl-workspace/edge-ha/edge-ha-kubeconfig
[2026-01-02 08:15:38+0900] Please restrict access to this file, as it contains the authentication credentials of your cluster.
Waiting for cluster to become ready: Requeue after 10s, Control Plane NodePool, System Services Deployment, GKE Connect Dependent System Services Deployment, GKE Connect⠋
以下のように「Deleting bootstrap cluster... OK」というログが出ていれば、
クラスタ作成は完了しています。
[2026-01-02 08:52:48+0900] Please run the following command to get the cluster nodes' status:
[2026-01-02 08:52:48+0900] kubectl --kubeconfig bmctl-workspace/edge-ha/edge-ha-kubeconfig get nodes
[2026-01-02 08:52:48+0900] Waiting for node pools to become ready OK
[2026-01-02 08:53:08+0900] Waiting for metrics to become ready in GCP OK
[2026-01-02 08:53:38+0900] Waiting for cluster API provider to install in the created admin cluster OK
[2026-01-02 08:53:49+0900] Moving admin cluster resources to the created admin cluster
[2026-01-02 08:53:56+0900] Flushing logs... OK
[2026-01-02 08:53:56+0900] Deleting bootstrap cluster... OK
また最終的に以下のディレクトリ構成になります。
.
├── bmctl
└── bmctl-workspace
├── config.json
├── config.toml
└── edge-ha
├── edge-ha-kubeconfig
├── edge-ha.yaml
└── log
├── create-cluster-20260102-075730
│ ├── 192.168.10.210
│ ├── 192.168.10.211
│ ├── 192.168.10.212
│ ├── bootstrap-cluster
│ ├── check-kernel
│ └── create-cluster.log
└── preflight-20260102-075730
試しに以下コマンドでノードの状態を確認してみます。
export KUBECONFIG=~/baremetal/bmctl-workspace/edge-ha/edge-ha-kubeconfig
kubectl get nodes
各ノードの STATUS が Ready になっていれば、利用可能な状態です。
作成時間を見た感じだと、1台ずつ作成/設定されているようです。
NAME STATUS ROLES AGE VERSION
gdc-node-1 Ready control-plane 22m v1.34.1-gke.2900
gdc-node-2 Ready control-plane 10m v1.34.1-gke.2900
gdc-node-3 Ready control-plane 38m v1.34.1-gke.2900
念の為podも確認してみますが、STATUSを見た限り特に問題なさそうです。
kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
anthos-identity-service ais-fbxfh 3/3 Running 0 6m32s
anthos-identity-service ais-mmzq5 3/3 Running 0 5m26s
anthos-identity-service ais-zhdmz 3/3 Running 0 5m59s
capi-kubeadm-bootstrap-system capi-kubeadm-bootstrap-controller-manager-1.34.0-gke.566-ddxbbh 2/2 Running 0 2m45s
capi-system capi-controller-manager-1.34.0-gke.566-9bcc4c448-wkkmt 2/2 Running 0 2m46s
cert-manager cert-manager-5d7f6fbcb8-szx4w 1/1 Running 0 20m
cert-manager cert-manager-cainjector-6bcc886fd5-tf6q2 1/1 Running 0 20m
cert-manager cert-manager-webhook-f79db77f9-bdwdx 1/1 Running 0 20m
cluster-edge-ha bm-system-network-health-check-edge-ha-5xj95-sfqzn 0/1 Completed 0 89s
cluster-edge-ha cluster-profile-deployer-1.34.0-gke.566-8c9sj 0/1 Completed 0 53s
管理コンソール ログイン
さて、クラスタの作成が完了した段階でGKEの管理コンソールにて作成したクラスタが見えるようになります。
ただし、まだログインしていない状態なので、クラスタの中身を見ることができません。

ログインの方法は何種類かありますが、
今回は設定が簡単そうなトークンでの認証を試してみます。
やることはクラスタロールを作成して、ロールを紐づけたサービスアカウントを作成、
そしてトークン用のシークレットを作成するだけです。
# ClusterRoleの作成
cat <<EOF > cloud-console-reader-role.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cloud-console-reader-role
rules:
- apiGroups: [""]
resources: ["nodes", "persistentvolumes", "pods"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
EOF
kubectl apply -f cloud-console-reader-role.yaml
# ServiceAccountの作成
KSA_NAME=cloud-console-reader-ksa
kubectl create serviceaccount ${KSA_NAME}
kubectl create clusterrolebinding cloud-console-reader-ksa-binding \
--clusterrole view --serviceaccount default:${KSA_NAME}
kubectl create clusterrolebinding cloud-console-reader-role-binding \
--clusterrole cloud-console-reader-role --serviceaccount default:${KSA_NAME}
# Tokenを作成
SECRET_NAME=cloud-console-reader-ksa-token
kubectl apply -f - << __EOF__
apiVersion: v1
kind: Secret
metadata:
name: "${SECRET_NAME}"
annotations:
kubernetes.io/service-account.name: "${KSA_NAME}"
type: kubernetes.io/service-account-token
__EOF__
# トークンを表示
kubectl get secret ${SECRET_NAME} -o jsonpath='{$.data.token}' | base64 --decode
作成したトークンをGoogle Cloudコンソールの「Kubernetes Engine」→「クラスタ」→対象クラスタの「ログイン」で入力します。

無事ログインできれば、クラスタの中身を確認できます。
-
クラスタ詳細

-
ノード一覧

-
ノード詳細

ロードバランサー構築
利用できるロードバランサーにはいくつか種類がありますが、
今回はバンドル型ロードバランサー(MetalLB)を利用しており、クラスタ作成時に自動的にデプロイされます。
実際に確認してみると、
クラスタ作成完了時点で、以下のロードバランサー関連のリソースが動作しています。
kubectl get pods -n kube-system | grep metallb
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system pod/metallb-controller-867db85799-tksqc 1/1 Running 0 33m
kube-system pod/metallb-speaker-jfs98 1/1 Running 0 33m
kube-system pod/metallb-speaker-m76qp 1/1 Running 0 24m
kube-system pod/metallb-speaker-sqhql 1/1 Running 0 33m
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-system daemonset.apps/metallb-speaker 3 3 3 3 3 baremetal.cluster.gke.io/lbnode=true,kubernetes.io/os=linux 33m
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
kube-system deployment.apps/metallb-controller 1/1 1 1 33m
NAMESPACE NAME DESIRED CURRENT READY AGE
kube-system replicaset.apps/metallb-controller-867db85799 1 1 1 33m
アプリデプロイ
サンプルアプリをデプロイしてLoadBalancerの動作を確認してみます。
cat <<'EOF' > nginx-sample.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-sample
labels:
app: nginx-sample
spec:
replicas: 3
selector:
matchLabels:
app: nginx-sample
template:
metadata:
labels:
app: nginx-sample
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-sample
spec:
type: LoadBalancer
selector:
app: nginx-sample
ports:
- port: 80
targetPort: 80
EOF
kubectl apply -f nginx-sample.yaml
デプロイ後、リソースの状態を確認します。
今回の場合、serviceのEXTERNAL-IPに192.168.10.231が割り振られていることがわかります。
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-sample 3/3 3 3 53s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 49m
service/nginx-sample LoadBalancer 10.96.2.205 192.168.10.231 80:32092/TCP 53s
アクセステスト:
# LoadBalancer経由でアクセス
curl http://192.168.10.231/
# IngressVIP経由でアクセス
curl http://192.168.10.230/
以下のようにnginxのウェルカムページが表示されれば成功です。
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
もちろんブラウザからも確認できます。

外部公開してみる
オンプレミスのGDCクラスタをインターネットに公開するには、Cloudflare Tunnelを使用します。
Cloudflare TunnelはCloudflareのエッジネットワーク経由でトラフィックをルーティングし、ファイアウォールに穴を開けることなくサービスを公開できます。
構成は以下の通りです。

cloudflared CLIツールのインストール
まずはcloudflaredのCLIツールをインストールします。
管理ワークステーションで実行します。
# GPGキーの追加
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
# リポジトリの追加
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
# インストール
sudo apt-get update && sudo apt-get install cloudflared
# バージョン確認
cloudflared --version
以下のようにバージョンが表示されればOKです。
cloudflared version 2025.11.1 (built 2025-11-07-16:59 UTC)
Tunnelの作成と設定
# Cloudflareにログイン
cloudflared tunnel login
# Tunnelを作成
cloudflared tunnel create gdc-tunnel
# DNSレコードを設定
cloudflared tunnel route dns gdc-tunnel gdc.your-domain.com
Kubernetesへのデプロイ
# Namespaceとクレデンシャルの作成
kubectl create namespace cloudflare-tunnel
kubectl create secret generic tunnel-credentials \
--from-file=credentials.json=$HOME/.cloudflared/<TUNNEL_ID>.json \
-n cloudflare-tunnel
# cloudflaredのデプロイ
cat <<'EOF' > cloudflared.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflared-config
namespace: cloudflare-tunnel
data:
config.yaml: |
tunnel: <TUNNEL_ID>
credentials-file: /etc/cloudflared/creds/credentials.json
metrics: 0.0.0.0:2000
no-autoupdate: true
ingress:
- hostname: gdc.your-domain.com
service: http://192.168.10.231:80
- service: http_status:404
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflared
namespace: cloudflare-tunnel
spec:
replicas: 3
selector:
matchLabels:
app: cloudflared
template:
metadata:
labels:
app: cloudflared
spec:
containers:
- name: cloudflared
image: cloudflare/cloudflared:latest
args:
- tunnel
- --config
- /etc/cloudflared/config/config.yaml
- run
volumeMounts:
- name: config
mountPath: /etc/cloudflared/config
readOnly: true
- name: creds
mountPath: /etc/cloudflared/creds
readOnly: true
volumes:
- name: config
configMap:
name: cloudflared-config
- name: creds
secret:
secretName: tunnel-credentials
EOF
kubectl apply -f cloudflared.yaml
デプロイ後、Podの状態を確認します。
kubectl get pods -n cloudflare-tunnel
NAME READY STATUS RESTARTS AGE
cloudflared-5fccc9cfb8-2lnn9 1/1 Running 0 30s
cloudflared-5fccc9cfb8-nl95r 1/1 Running 0 30s
cloudflared-5fccc9cfb8-s2n8t 1/1 Running 0 30s
ログを確認して、Tunnelが正常に接続されているか確認します。
kubectl logs -n cloudflare-tunnel -l app=cloudflared
以下のように"Registered tunnel connection"が表示されれば成功です。
2026-01-02T00:18:26Z INF Registered tunnel connection connIndex=0 connection=52f24c5f-1fea-4c91-80ee-c88621dd7b32 event=0 ip=198.41.200.23 location=nrt15 protocol=quic
2026-01-02T00:18:26Z INF Tunnel connection curve preferences: [X25519MLKEM768 CurveP256] connIndex=1 event=0 ip=198.41.192.227
2026-01-02T00:18:26Z INF Registered tunnel connection connIndex=1 connection=6a55ecfc-1bb3-44f3-a363-fca95476a5c6 event=0 ip=198.41.192.227 location=nrt09 protocol=quic
2026-01-02T00:18:27Z INF Tunnel connection curve preferences: [X25519MLKEM768 CurveP256] connIndex=2 event=0 ip=198.41.200.193
2026-01-02T00:18:27Z INF Registered tunnel connection connIndex=2 connection=0c9da0b4-42f4-445c-b63e-1e7224df266c event=0 ip=198.41.200.193 location=nrt07 protocol=quic
2026-01-02T00:18:28Z INF Tunnel connection curve preferences: [X25519MLKEM768 CurveP256] connIndex=3 event=0 ip=198.41.192.7
2026-01-02T00:18:28Z INF Registered tunnel connection connIndex=3 connection=e3451d10-45eb-4471-a653-cb03f0949616 event=0 ip=198.41.192.7 location=nrt01 protocol=quic
これでインターネットから設定したドメイン名でアクセスできるようになります。

ログとモニタリング
Cloud LoggingとCloud Monitoringはベアメタルシステムコンポーネントでデフォルトで有効になっています。
ただしデフォルトでは「システムコンポーネントのみ」が対象です。
以下はログエクスプローラの実際の画面ですが、システムコンポーネントのみ表示されることがわかります。
今回はアプリケーションのログも有効にしてみます。

アプリケーションロギングの有効化
アプリケーションレベルのロギングを有効にするには、Stackdriverオブジェクトを編集します。
kubectl --namespace kube-system patch stackdriver stackdriver --type=merge -p '
spec:
enableCloudLoggingForApplications: true
'
cat <<'EOF' > my-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: "monitoring-example"
namespace: "default"
labels:
app: "monitoring-example"
spec:
replicas: 1
selector:
matchLabels:
app: "monitoring-example"
template:
metadata:
labels:
app: "monitoring-example"
spec:
containers:
- image: gcr.io/google-samples/prometheus-dummy-exporter:latest
name: prometheus-example-exporter
imagePullPolicy: Always
command:
- /bin/sh
- -c
- ./prometheus-dummy-exporter --metric-name=example_monitoring_up --metric-value=1 --port=9090
resources:
requests:
cpu: 100m
EOF
kubectl apply -f my-app.yaml

ログフィルタリング
全てのアプリケーションログをCloud Loggingに送信するとコストが増加する懸念がありますが、
そう言った場合は必要なログのみを送信するようフィルタリングを設定できます。
kubectl --namespace kube-system edit stackdriver stackdriver
spec:
enableCloudLoggingForApplications: true
appLogFilter:
keepLogRules:
- namespaces:
- default
contentRegexes:
- ".*(ERROR|WARN).*"
ruleName: keep-important-logs
dropLogRules:
- podLabelSelectors:
- disableGCPLogging=yes
ruleName: drop-excluded-logs
kubectl run pod1 \
--image gcr.io/cloud-marketplace-containers/google/debian10:latest \
--restart Never --command -- \
/bin/sh -c "while true; do echo 'ERROR is 404\\nINFO is not 404' && sleep 1; done"
実際に管理コンソールをみるとフィルタリングしたログ(ERROR|WARN)のみ集約されており、INFOのログは存在していません。

Artifact Registryを利用する
プライベートなArtifact RegistryからコンテナイメージをPullできるように設定します。
Artifact Registryの準備
export PROJECT_ID=${PROJECT_ID}
export REGION=asia-northeast1
# Artifact Registry APIを有効化
gcloud services enable artifactregistry.googleapis.com
# リポジトリを作成
gcloud artifacts repositories create gdc-images \
--repository-format=docker \
--location=$REGION \
--description="GDC private container images"
# サービスアカウント作成
gcloud iam service-accounts create gdc-registry-reader \
--display-name="GDC Registry Reader"
# 権限付与
gcloud artifacts repositories add-iam-policy-binding gdc-images \
--location=$REGION \
--member="serviceAccount:gdc-registry-reader@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/artifactregistry.reader"
# サービスアカウントキーを作成
gcloud iam service-accounts keys create ~/baremetal/ar-key.json \
--iam-account=gdc-registry-reader@${PROJECT_ID}.iam.gserviceaccount.com
imagePullSecretsの設定
# Secretの作成
kubectl create secret docker-registry ar-pull-secret \
--docker-server=${REGION}-docker.pkg.dev \
--docker-username=_json_key \
--docker-password="$(cat ~/baremetal/ar-key.json)" \
-n default
# default ServiceAccountに追加
kubectl patch serviceaccount default -n default \
-p '{"imagePullSecrets": [{"name": "ar-pull-secret"}]}'
動作確認
# テスト用イメージをプッシュ
docker pull nginx:latest
docker tag nginx:latest ${REGION}-docker.pkg.dev/${PROJECT_ID}/gdc-images/nginx:latest
gcloud auth configure-docker ${REGION}-docker.pkg.dev
docker push ${REGION}-docker.pkg.dev/${PROJECT_ID}/gdc-images/nginx:latest
# プライベートレジストリからPodをデプロイ
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: ar-test-pod
spec:
containers:
- name: nginx
image: ${REGION}-docker.pkg.dev/${PROJECT_ID}/gdc-images/nginx:latest
ports:
- containerPort: 80
EOF
Podのステータスやログを確認すると、
ステータスがRunningでSuccessfully pulled image が表示されており、プライベートレジストリからイメージをPullできています。
$ kubectl get pod ar-test-pod
NAME READY STATUS RESTARTS AGE
ar-test-pod 1/1 Running 0 26s
$ kubectl describe pod ar-test-pod | grep -A 5 "Events"
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 39s default-scheduler Successfully assigned default/ar-test-pod to gdc-node-1
Normal Pulling 39s kubelet Pulling image "asia-northeast1-docker.pkg.dev/<your-project-id>/gdc-images/nginx:latest"
Normal Pulled 36s kubelet Successfully pulled image "asia-northeast1-docker.pkg.dev/<your-project-id>/gdc-images/nginx:latest" in 3.063s (3.063s including waiting). Image size: 59797235 bytes.
さいごに
以上、Google Distributed Cloudで始めるお家GKEでした。
今回実際に構築してみて、bmctlによるクラスタ作成は約1時間程度で完了したので、思ったよりもスムーズに構築することができました。
ただし、ハードウェア要件はそれなりに高いため、自宅環境で運用するにはある程度のリソースが必要です。
またエッジプロファイルを使えば最小構成でも動作しますが、快適に運用するには推奨構成に近いスペックを用意することをおすすめします。
費用面では、vCPU単位の従量課金が発生します。
本記事の構成で約半日動かして750円程度でしたので、検証用途であれば十分現実的な範囲かと思います。
また新規アカウントであれば$300分の無料クレジットも活用できますので、ぜひ試してみてください。
それでは、皆様のおうちKubernetes環境構築が円滑に進むことを願っています!






