目次
- はじめに
- 1. 事前準備
- 2. GCP プロジェクトと課金
- 3. Next.js を Cloud Run にデプロイ
- 4. 独自ドメインを接続する(いちばん詰まった所)
- 5. 費用の安全策
- つまずきポイントのまとめ
- DNSで詰まったときの切り分け
- おわりに
はじめに
お名前.comで取得した独自ドメインを使って、GCP の Cloud Run で自分のWebページ(Next.js 製の自己紹介サイト)を公開するまでの手順をまとめます。
実際にやってみると、ネームサーバーの設定、所有権の確認、DNS の反映待ちでかなり詰まりました。手順だけでなく、つまずいた点と原因も残します。
※AIで作成した記事のため不正確な情報が含まれている可能性があります。
本記事のドメイン・プロジェクトID・アカウントIDなどは、すべて架空の値(example.com、YOUR_PROJECT_ID など)に置き換えています。
作ったもの
ブラウザ → example.com (お名前.comで取得・DNSも同社)
→ Google のフロントエンド (HTTPS証明書は自動発行)
→ Cloud Run (東京) → Next.js のコンテナ
| 項目 | 内容 |
|---|---|
| ドメイン | お名前.com |
| ホスティング | Cloud Run(東京リージョン asia-northeast1) |
| アプリ | Next.js 16 / TypeScript / Tailwind CSS |
| 環境 | WSL2 (Ubuntu) |
| 費用の目安 | ドメイン代のみ。Cloud Run は最小インスタンス0で、無料枠に収まる想定 |
全体の流れ
- ローカル環境と
gcloudの準備 - GCPプロジェクト・課金・予算アラート
- Next.js を Cloud Run にデプロイ
- 独自ドメインを接続(ネームサーバー → 所有権の確認 → ドメインマッピング → A/AAAA レコード → 証明書待ち)
- 費用の安全策
静的なページだけなら、Firebase Hosting の方が安く簡単です。今回は GCP を学ぶ目的で Cloud Run を選びました。
1. 事前準備
Node.js
Next.js 16 は Node 20.9 以上が必要です。古い場合は nvm で更新します。
nvm install node
nvm alias default node
node -v
libatomic.so.1: cannot open shared object file (WSL / Ubuntu)
最新の Node は libatomic1 に依存します。入っていないと node 自体が起動しません。sudo のパスワード入力が要るので、通常のターミナルで実行します。
sudo apt-get update && sudo apt-get install -y libatomic1
gcloud CLI(sudo 不要の入れ方)
ホームディレクトリに展開する方法です。
curl -fsSL -o /tmp/gcloud.tar.gz \
https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
tar -xzf /tmp/gcloud.tar.gz -C ~
~/google-cloud-sdk/install.sh --quiet --usage-reporting=false \
--path-update=true --command-completion=true --rc-path=$HOME/.bashrc
gcloud auth login --no-launch-browser # 表示されたURLをブラウザで開いて認証
gcloud components install beta --quiet # ドメインマッピングで使う
gcloud beta ... は、コンポーネントが未導入だと対話確認で止まります。--quiet を付けても通らないので、先に上のコマンドで入れておきます。
2. GCP プロジェクトと課金
課金が有効か確認する
gcloud billing accounts list # OPEN が True か
gcloud billing projects describe YOUR_PROJECT_ID # billingEnabled: true か
私の場合、請求先アカウントが OPEN: False(無料トライアル終了)で、Cloud Run が使えませんでした。コンソールで有料アカウントに登録し直しました。Cloud Run / Cloud Build / Artifact Registry は課金が有効でないと使えません。
プロジェクトを作る(コンソール)
- 表示名は後から変更できますが、プロジェクトIDは作成後に変更できません。
- 作成直後は
gcloud projects listに出ないことがあります。IDが分かっていればgcloud projects describe YOUR_PROJECT_IDで直接確認できます。
既定値の設定と API の有効化
gcloud config set project YOUR_PROJECT_ID
gcloud config set run/region asia-northeast1
gcloud services enable \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com
予算アラートを先に作る
gcloud services enable billingbudgets.googleapis.com --project YOUR_PROJECT_ID
gcloud billing budgets create \
--billing-account=XXXXXX-XXXXXX-XXXXXX \
--display-name="homepage monthly" \
--budget-amount=5000JPY \
--filter-projects=projects/YOUR_PROJECT_ID \
--threshold-rule=percent=0.5 \
--threshold-rule=percent=0.9 \
--threshold-rule=percent=1.0
予算アラートは「通知」だけで、超過してもサービスは止まりません。 課金を抑えたいなら、後述の Cloud Run のスケール上限も併用します。
通知メールの宛先は、コンソールの「お支払い → 予算とアラート → 編集 → アクション」で確認できます。
3. Next.js を Cloud Run にデプロイ
standalone 出力にする
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone", // 本番に必要な最小限のファイルだけを出力する
};
export default nextConfig;
Dockerfile
Cloud Run はポート 8080 で待ち受けます。
FROM node:22-slim AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:22-slim AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-slim AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV HOSTNAME=0.0.0.0
ENV PORT=8080
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER node
EXPOSE 8080
CMD ["node", "server.js"]
COPY --from=builder /app/public ./public があるので、public/ ディレクトリが無いとビルドが失敗します。中身を全部消すと、空のディレクトリは Git に入らず消えてしまうため、public/.gitkeep を置いて残しておきます。
デプロイ
gcloud run deploy my-homepage \
--source . \
--region asia-northeast1 \
--allow-unauthenticated \
--min-instances 0 \
--max-instances 2
-
--source .で、Cloud Build が Dockerfile からビルドしてくれます。 -
--allow-unauthenticatedで、誰でもアクセスできる公開サイトになります。 -
--max-instances 2は費用の安全策です(後述)。
完了すると、https://my-homepage-PROJECT_NUMBER.asia-northeast1.run.app のような URL が表示されます。まずはここで表示を確認します。
初回デプロイが 403 で失敗する
ERROR: ... PROJECT_NUMBER-compute@developer.gserviceaccount.com does not have
storage.objects.get access to the Google Cloud Storage object ...
組織配下のプロジェクトでは、Cloud Build が使う既定のサービスアカウントにビルド権限がないことがあります。このプロジェクト限定で、ビルド用のロールを付与します。
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
--role="roles/cloudbuild.builds.builder" \
--condition=None
コンソールの IAM から「Cloud Build サービス アカウント」ロールを追加しても同じです。付与したら、もう一度デプロイします。
4. 独自ドメインを接続する(いちばん詰まった所)
流れは次のとおりです。
(1) ネームサーバーを DNSレコード設定用に変更 … お名前.com
(2) TXTレコードで所有権を証明 … お名前.com + Search Console
(3) ドメインマッピングを作成 … gcloud
(4) A / AAAA レコードを登録 … お名前.com
(5) 証明書の発行を待つ
4-1. ネームサーバーを変更する(これを忘れると全部ダメ)
お名前.comの「DNSレコード設定」は、ドメインのネームサーバーが次の4つのときだけ有効になります。
01.dnsv.jp
02.dnsv.jp
03.dnsv.jp
04.dnsv.jp
「ネームサーバーの設定」→「他のネームサーバーを利用」から設定して保存します。
ここで最初にハマりました。 DNSレコード設定の画面にTXTを登録して「有効」と表示されているのに、インターネット上のDNSには出てきませんでした。
原因は、ネームサーバーが初期値(dns1.onamae.com / dns2.onamae.com)のままだったことです。
DNSレコード設定の確認画面に出る「ネームサーバー変更: しない」は、「ドメインのネームサーバーを変えない」という意味です。ネームサーバーの変更は別の画面で行う必要があります。
現在のネームサーバーは、Google の DNS over HTTPS で確認できます(dig が無い環境でも使えます)。
curl -s "https://dns.google/resolve?name=example.com&type=NS" | tr ',' '\n' | grep '"data"'
01.dnsv.jp. 〜 04.dnsv.jp. が出れば切り替え済みです。
確認画面の料金に注意してください。 私の場合、設定の確認画面に「ドメインプロテクション(1,078円)+ サービス維持調整費(275円)」が自動で追加されていました。DNSの設定には不要なオプションです。確認画面で料金欄が0円になっていることを確認し、チェックを外してから「設定する」を押します。
4-2. ドメインの所有権を確認する
ドメインマッピングを作る前に、Googleにドメインの所有者であることを証明する必要があります。確認していないと、次のエラーになります。
The provided domain does not appear to be verified for the current account.
gcloud domains verify example.com # Search Console の画面が開く
- Search Console で「ドメイン」プロパティに
example.comを入力します。 - 表示された TXT レコードの値(
google-site-verification=xxxxxxxx...)をコピーします。 - お名前.comのDNSレコード設定に、次のTXTレコードを追加します。
| ホスト名 | TYPE | TTL | VALUE |
|---|---|---|---|
| (空欄) | TXT | 3600 | google-site-verification=xxxxxxxx... |
- DNSに出たことを確認してから、Search Console の「確認」を押します。
curl -s "https://dns.google/resolve?name=example.com&type=TXT" | tr ',' '\n' | grep '"data"'
- 完了すると、次のコマンドでドメインが表示されます。
gcloud domains list-user-verified
-
確認ボタンを連打しないこと。 DNSに出る前に押しても失敗するだけで、画面が消えて分かりにくくなりました。消えたときは、
gcloud domains verifyをもう一度実行するか、Search Console で同じドメインを入れ直すとTXTの値が再表示されます(値は同じです)。 - 認証は、
gcloudにログインしているのと同じGoogleアカウントで行います。別アカウントだとgcloudから見えません。 - TXTレコードは消さずに残します。
4-3. ドメインマッピングを作成する
gcloud beta run domain-mappings create \
--service my-homepage \
--domain example.com \
--region asia-northeast1
成功すると、登録すべきA レコード4件、AAAA レコード4件が表示されます。
NAME RECORD TYPE CONTENTS
my-homepage A 216.239.32.21
my-homepage A 216.239.34.21
my-homepage A 216.239.36.21
my-homepage A 216.239.38.21
my-homepage AAAA 2001:4860:4802:32::15
my-homepage AAAA 2001:4860:4802:34::15
my-homepage AAAA 2001:4860:4802:36::15
my-homepage AAAA 2001:4860:4802:38::15
(値は環境により変わることがあるので、必ず自分の出力を使ってください。)
4-4. A / AAAA レコードを登録する
お名前.comのDNSレコード設定で、ホスト名は空欄、TTLは3600のまま、表示された8件を登録します。
- TXT は「このドメインの持ち主は私」という所有権の証明です。
- A / AAAA は「このドメインへのアクセスを、このIPアドレスに送る」という接続先の指定です。Aが IPv4、AAAA が IPv6 です。
- HTTPS証明書は、ドメインが Cloud Run を指していることを Google が確認して発行します。A / AAAA が無いと発行されません。
4-5. 証明書の発行を待つ
gcloud beta run domain-mappings describe \
--domain example.com --region asia-northeast1 \
--format='yaml(status.conditions)'
| 状態 | 意味 |
|---|---|
CertificatePending |
A / AAAA が向くのを待っている |
Ready: True と CertificateProvisioned: True
|
証明書の発行済み |
最後に、実際にアクセスして確認します。
curl -sI https://example.com
待つしかない場面が2つあります。
-
A / AAAA を登録したのに引けない: 以前に問い合わせたときの「レコードなし」の答えが、DNSに否定キャッシュされているためです。応答に含まれる SOA レコードの最後の数字(例:
300)が保持秒数です。数分〜1時間で解消します。 -
CertificateProvisioned: Trueなのに HTTPS が繋がらない:SSL_ERROR_SYSCALLで TLS の途中で切断されることがあります。IPアドレスを直接指定しても同じなら、手元のDNSではなく Google 側の反映待ちです。設定を直す必要はなく、数分〜1時間ほどで開通しました。
5. 費用の安全策
予算アラートは通知だけなので、次の対策も入れています。
Cloud Run のスケール上限
先ほどの --max-instances 2 です。アクセスの急増や攻撃があっても、インスタンスが増え続けて請求が膨らむことを防げます。--min-instances 0 なので、アクセスがないときは費用がかかりません。
古いコンテナイメージの自動削除
gcloud run deploy --source は、デプロイのたびにイメージを Artifact Registry に溜めます。クリーンアップポリシーで自動削除します。
[
{
"name": "delete-old-images",
"action": { "type": "Delete" },
"condition": { "tagState": "any", "olderThan": "14d" }
},
{
"name": "keep-recent-3",
"action": { "type": "Keep" },
"mostRecentVersions": { "keepCount": 3 }
},
{
"name": "keep-latest-tag",
"action": { "type": "Keep" },
"condition": { "tagState": "tagged", "tagPrefixes": ["latest"] }
}
]
gcloud artifacts repositories set-cleanup-policies cloud-run-source-deploy \
--project YOUR_PROJECT_ID --location asia-northeast1 \
--policy=cleanup-policy.json --no-dry-run
14日を超えたものを削除し、最新3つと latest タグは残します。削除されたイメージのリビジョンにはロールバックできなくなる点だけ、注意が必要です。
つまずきポイントのまとめ
| # | 症状 | 原因 | 対処 |
|---|---|---|---|
| 1 |
node が起動しない |
libatomic1 が無い(WSL) |
sudo apt-get install -y libatomic1 |
| 2 | Cloud Run が使えない | 課金が無効 | 請求先アカウントを有効にして、プロジェクトにリンク |
| 3 | デプロイで 403(storage.objects.get) |
組織配下で、既定のサービスアカウントに権限がない |
roles/cloudbuild.builds.builder を付与 |
| 4 |
public/ を消したらビルド失敗 |
Dockerfile が public/ をコピーする。空ディレクトリは Git に入らない |
public/.gitkeep を置く |
| 5 | domain does not appear to be verified |
所有権の確認が未完了 | TXT レコードで確認する |
| 6 | TXT が DNS に出ない | ネームサーバーが初期値のまま |
01〜04.dnsv.jp に変更する |
| 7 | 確認画面で料金が発生 | ドメインプロテクションが自動で選択されていた | チェックを外して0円を確認してから設定 |
| 8 | Search Console の確認が失敗・画面が消えた | DNS に出る前に連打した | TXT が引けるのを確認し、確認ボタンは1回だけ |
| 9 | A / AAAA が引けない | 否定キャッシュ | 数分〜1時間待つ |
| 10 | 証明書は発行済みなのに HTTPS が繋がらない | Google 側の反映待ち | 待つ(設定の修正は不要) |
| 11 |
dig / nslookup が無い |
WSL に未導入 |
curl "https://dns.google/resolve?name=…&type=…" で代用 |
DNSで詰まったときの切り分け
まず「どのネームサーバーが答えているか」を見ます。お名前.comの画面にレコードが出ていても、ネームサーバーが違えば無効です。
| 見るもの | type= |
判断 |
|---|---|---|
| ネームサーバー | NS |
01〜04.dnsv.jp か |
| 所有権の TXT | TXT |
google-site-verification=… が出ているか |
| 接続先 |
A / AAAA
|
8件が出ているか(SOAだけなら否定キャッシュ) |
curl -s "https://dns.google/resolve?name=example.com&type=NS" | tr ',' '\n' | grep '"data"'
おわりに
作業の大半は「ドメインの設定(ネームサーバー・所有権・DNS)」で、アプリのデプロイそのものは gcloud run deploy 1コマンドで済みました。詰まりどころは、
- ネームサーバーを変えないとDNSレコード設定が効かない
- DNSやHTTPSの反映は、設定が正しければ待つしかない場面がある
- レジストラの確認画面で、有料オプションが自動で選ばれていることがある
の3点に集約されると思います。同じ構成を試す方の参考になれば幸いです。