デジタル庁のABRジオコーダーとCloud Runで、ジオコーディングAPIを作ってみた
はじめに
こんにちは!CareNetのYTです。
入力形式やソースの異なる住所データの紐付けを行いたいとき、住所文字列の正規化を行うことが不可欠です。本記事では、Google CloudのCloud Run Serviceとデジタル庁が提供するアドレス・ベース・レジストリ ジオコーダーを活用して、住所文字列の正規化が可能なジオコーディングAPIを構築する方法をご紹介します。
デジタル庁のアドレス・ベース・レジストリ ジオコーダーについて
リクエストされた住所文字列を、デジタル庁のアドレス・ベース・レジストリ(ABR: Address Base Registry)と照合することで、正規化された住所文字列や緯度経度等の情報を返却するジオコーダーです。詳細は以下の公式ページやGitHubをご覧ください。
ジオコーディングAPIのインフラ選定
インフラが満たすべき条件
ジオコーディングAPIのインフラを選定するにあたり、以下の5つの条件を考慮する必要がありました。
- ABRジオコーダーがRESTサーバとして利用可能な形で実装されており、コンテナベースで動作し、HTTPリクエストを受け付ける必要があること
- ビルド時に巨大なデータセットをダウンロードする必要があり、イメージサイズが10GBを超えること
- 直近での利用想定は少なく、ゼロインスタンスとしたいが、将来的に用途が増えてリクエストの増加も考えられること
- コールドスタートが最小限で、ゼロインスタンスからでも素早く処理できること
- チームとしてはAWSとGoogle Cloudのマルチクラウドで運用しているため、AWSまたはGoogle Cloudであれば、どちらのインフラでも構わないが、住所データがGoogle Cloud上にあり、ジオコーディングAPIを呼び出すクライアントもGoogle Cloud内に実装予定であること
Cloud Run Serviceを選定した理由
Google CloudのCloud Run Serviceは、主に以下のような特徴が知られるインフラです。
・フルマネージドでインフラ管理が不要である
・コンテナをそのままデプロイすることができるため、開発が簡単である
・サーバレスであり、トラフィックに応じた自動スケールが可能で、使用した分のみ従量課金される
調査と検証の結果、Google CloudのCloud Run Serviceは、以下のように上記の1〜5の条件を満たしていると考えました。
- コンテナ化されたアプリケーションをデプロイでき、HTTPリクエストでのトリガーがデフォルトでサポートされていること
- イメージを配置するArtifact Registryの上限は5TBであり、Cloud Run Serviceは、今回の大規模イメージでもデプロイ可能であったこと
- ゼロインスタンスをサポートしており、リクエストがない場合は、Artifact Registryのストレージ料金のみで済むこと。また、将来的にトラフィックが増加した場合は自動スケーリングするため、運用の柔軟性も確保できること
- デプロイ後にはビルド済みのイメージを直接利用する仕組みであり、今回の用途においてコールドスタート時間が許容範囲内だったこと
- ジオコーディングAPIを、呼び出すクライアントとGoogle Cloudの同プロジェクト内に実装することで、低遅延でセキュアな接続を行いやすいだろうと考えたこと
AWSのApp Runnerも検討しましたが、以下のリンクのように、ストレージ容量が小さく、大規模イメージをデプロイできないため、上記の条件2を満たしませんでした。
開発環境準備
Dockerコマンドが実行できる環境であれば、どこでも開発可能です。
今回は、MacにDocker Desktopをインストールして実行しました。
コンテナ開発
以下のようなディレクトリ構成で各ファイルを作成していきます。
ディレクトリ構成
sample_repository
├── Dockerfile
├── docker-compose.yaml
└── cloudbuild.yaml
Dockerfileの例
FROM node:22.9.0-slim
ENV APP_HOME /root
WORKDIR $APP_HOME
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
locales \
jq \
git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen \
&& locale-gen
ENV LANG=ja_JP.UTF-8
RUN npm cache clean -f
RUN npm i -g @digital-go-jp/abr-geocoder
# アドレス・ベース・レジストリからデータセットをダウンロード
RUN sh -c "abrg download -d $APP_HOME/.abr-geocoder"
ENTRYPOINT ["abrg", "serve", "-d", "/root/.abr-geocoder"]
なお、アドレス・ベース・レジストリからデータセットをダウンロードするステップにおいて、全国地方公共団体コードを指定すると、地域を限定したジオコーディングAPIとすることができます。指定せずに日本全国のデータをダウンロードする場合はビルドに数時間かかるため、ローカルでテストする際も特定地域のみとしておくことで、時間を短縮できます。以下は、千代田区のみのデータをダウンロードする例です。
RUN sh -c "abrg download -c 131016 -d $APP_HOME/.abr-geocoder"
docker-compose.yamlの例
ローカルでの動作確認時に用いるdocker-compose.yamlの例は以下です。
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: geocoder
ports:
- "3000:3000"
Cloud Run Serviceにデプロイする際に用いるcloudbuild.yamlの記載については、後述の項目で説明しています。
ローカルでの動作確認
以下のコマンドを実行し、ローカル上でコンテナを起動します。
docker compose up --build
上述の通り、全国のジオコーディングAPIとする場合はビルドに数時間かかるため、ローカルテスト時には、地域を限定しておくことをおすすめします。
コンテナの起動に成功したら、例えば以下のコマンドを実行することで、動作を確認できます。
curl 'http://0.0.0.0:3000/geocode?address=%E6%9D%B1%E4%BA%AC%E9%83%BD%E5%8D%83%E4%BB%A3%E7%94%B0%E5%8C%BA%E5%AF%8C%E5%A3%AB%E8%A6%8B%E7%94%BA'
※addressとして指定しているのは「東京都千代田区富士見町」をエンコードした文字列です
すると、以下のようなレスポンスが返ってきます。
[{"query":{"input":"東京都千代田区富士見町"},"result":{"output":"東京都千代田区富士見町","other":"町","score":1,"match_level":"machiaza","coordinate_level":"city","lat":35.694003,"lon":139.753634,"lg_code":"131016","machiaza_id":"0004000","rsdt_addr_flg":-1,"blk_id":null,"rsdt_id":null,"rsdt2_id":null,"prc_id":null,"pref":"東京都","county":null,"city":"千代田区","ward":null,"oaza_cho":"富士見","chome":null,"koaza":null,"blk_num":null,"rsdt_num":null,"rsdt_num2":null,"prc_num1":null,"prc_num2":null,"prc_num3":null}}]
レスポンスの詳細については、以下の公式の仕様書をご参照ください。
Cloud Run Serviceへのデプロイ
Cloud Run Serviceへのデプロイは、Cloud Buildを利用することで自動化できます。その設定の各ステップは以下の通りです。
Artifact Registryにリポジトリの作成
Google Cloudのコンソールから、Artifact Registry > リポジトリの作成へと進みます。
名前の欄に任意のリポジトリ名を入力し、形式は「Docker」が選ばれていることを確認します。今回はマルチリージョンである必要はないため、ロケーションタイプは「リージョン・asia-northeast1(東京)」を選択します。今回のケースでは、その他の設定はデフォルトのままで特に問題ありません。
詳しくは以下をご参照ください。
なお、次項で利用するARTIFACT_REPOSITORYの環境変数の値は、Artifact Registryのコンソールから作成したリポジトリへと進み、以下の赤丸からコピーした値が入ることを想定しています。
cloudbuild.yamlの作成
Cloud BuildによりCloud Run Serviceにデプロイする際、構成ファイルとして用いるcloudbuild.yamlの例は以下です。ビルドに時間がかかるため、タイムアウトを長めに設定しています。また、ジオコーディングAPIのREST APIサーバーのポートのデフォルトは3000であるため、Cloud Run Serviceのポートを3000に指定することもポイントです。
steps:
# コンテナイメージのビルド
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '${_ARTIFACT_REPOSITORY}/${_DOCKER_IMAGE_NAME}', '.']
# Artifact Registryへのビルド済みイメージのプッシュ
- name: 'gcr.io/cloud-builders/docker'
args: ['push', '${_ARTIFACT_REPOSITORY}/${_DOCKER_IMAGE_NAME}']
# Cloud Run Serviceへのデプロイ
# https://cloud.google.com/sdk/gcloud/reference/run/deploy
- name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'deploy'
- '${_RUN_SERVICE_NAME}'
- '--image'
- '${_ARTIFACT_REPOSITORY}/${_DOCKER_IMAGE_NAME}'
- '--region'
- '${_REGION_NAME}'
- '--platform'
- 'managed'
- '--no-allow-unauthenticated'
- '--concurrency'
- '10'
- '--max-instances'
- '100'
- '--timeout'
- '3600'
- '--memory'
- '16Gi'
- '--cpu'
- '4'
- '--port'
- '3000'
- '--service-account'
- '${_RUN_SERVICE_ACCOUNT}'
- '--set-env-vars'
- 'APP_ENV=${_APP_ENV}'
# Cloud Run Serviceに対して特定のサービスアカウントのみアクセスできるように設定
# https://cloud.google.com/sdk/gcloud/reference/run/services/add-iam-policy-binding
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'gcloud'
args:
- 'run'
- 'services'
- 'add-iam-policy-binding'
- '${_RUN_SERVICE_NAME}'
- '--region'
- '${_REGION_NAME}'
- '--member'
- 'serviceAccount:${_CLIENT_SERVICE_ACCOUNT}'
- '--role'
- 'roles/run.invoker'
timeout: 4h
options:
logging: CLOUD_LOGGING_ONLY
substitutions:
_APP_ENV: prod
_ARTIFACT_REPOSITORY: {Artifact Registryのリポジトリ名}
_CLIENT_SERVICE_ACCOUNT: {ジオコーディングAPIにアクセスするクライアントのサービスアカウント}
_DOCKER_IMAGE_NAME: {イメージ名(任意)}
_REGION_NAME: asia-northeast1
_RUN_SERVICE_ACCOUNT: {ジオコーディングAPIのサービスアカウント}
_RUN_SERVICE_NAME: abrg-api
cloudbuild.yamlの記載法の詳細は以下をご参照ください。
また、今回は認証が必要なCloud Run Serviceとし、アクセスできるクライアントのサービスアカウント名を指定する形としましたが、「Cloud Run Serviceへのデプロイ」のステップで、--no-allow-unauthenticatedの設定を--allow-unauthenticatedとし、「Cloud Run Serviceに対して特定のサービスアカウントのみアクセスできるように設定」のステップを削除すると、認証を行わずにアクセスできるAPIとなりますが、インターネット上から誰でもアクセスできる状態となるため、注意が必要です。詳細は以下をご参照ください。
Cloud Build用のサービスアカウントの作成
Google Cloudのコンソールから、IAMと管理 > サービスアカウント へと進み、サービスアカウントを作成します。最小権限のみのカスタムロールを作ってこのサービスアカウントに付与することがベストプラクティスですが、今回は作業の簡略化のため、以下の事前定義ロールを付与しました。
・サービス アカウント ユーザー
・Cloud Build サービス アカウント
・Cloud Run 管理者
Cloud Buildの設定
Google Cloudのコンソールから、Cloud Build > トリガー > トリガーを作成 へと進みます。
名前には任意のトリガー名を入力します。
「リポジトリ」において、今回のソースコードが置かれているGitHubリポジトリと接続します。ブランチを指定し、「イベント」で「ブランチにpushする」を選択することで、該当ブランチに最新コードがプッシュされた時点でCloud Buildがトリガーされるようになります。Cloud Build構成ファイルの場所で、上記で作成したcloudbuild.yamlが読み込まれるよう指定します。
必要な場合は、「ビルドを実行する前に承認を必要とする」にチェックを入れてください。「サービスアカウント」の項では、上記で作成したサービスアカウントを選択し、「作成」を押下します。
Cloud Run Serviceへのデプロイ
ソースコードを上記で指定したリポジトリ・ブランチへプッシュすると、上記のCloud Buildがトリガーされます。今回のケースでは、コンテナイメージのサイズが大きいため、私が実行した際は、Cloud Run Serviceへのデプロイが完了するまで3時間強かかりました。
Cloud Run Serviceの動作確認
デプロイに成功したら、Cloud Runのコンソールから作成したCloud Run Serviceへとアクセスします。
画面上部にURL欄がありますが、こちらがCloud Run ServiceのURLとなります。上記のcloudbuild.yamlにおいて、CLIENT_SERVICE_ACCOUNTの環境変数で指定したサービスアカウントが設定されたクライアントからこちらのURLにリクエストすることで、ローカルで動作確認した際と同様のリスポンスを受け取ることができます。
cloudbuild.yamlにおいて、認証不要な公開APIとして設定していた場合は、ローカルから以下のコマンドを実行することで動作確認が可能です。
curl '{Cloud Run ServiceのURL}/geocode?address=%E6%9D%B1%E4%BA%AC%E9%83%BD%E5%8D%83%E4%BB%A3%E7%94%B0%E5%8C%BA%E5%AF%8C%E5%A3%AB%E8%A6%8B%E7%94%BA'
※addressとして指定しているのは「東京都千代田区富士見町」をエンコードした文字列です
まとめ
Cloud Run Serviceとデジタル庁が提供するアドレス・ベース・レジストリ ジオコーダーを活用して、住所文字列の正規化が可能で、リクエスト時以外には課金されないジオコーディングAPIを手軽に構築することができます。
We are hiring!
株式会社ケアネットでは一緒に働く仲間を募集しています。詳しくは以下をご覧ください。
Discussion