🎅

CloudRunからAWS環境にプライベートな経路でアクセス

2024/12/12に公開

はじめに

こんにちは。AI Shiftの開発チームの大長です。

この記事は AI Shift Advent Calender 2024 の12日目の記事になります。

私は今年の6月にAI Shiftにジョインし、インフラ担当としてプロダクトが稼働するGKE基盤を含むクラウド関連、Arog CDなどCI/CD基盤の運用を行っています。

最近、Cloud RunからAWS環境のAPIサーバにセキュアにアクセスしたいという要望があり、インフラ構成を検討していました。

当初、Google Cloud環境とAWS環境をVPN接続後、Cloud RunからDirect VPC Egress経由でVPCに接続し、VPN経由でAWS環境のAPIサーバに直接接続する構成を検討していました。

ただ、要件として相互通信の必要はなかったため、構成を再検討したところ、ハイブリッド接続ネットワークエンドポイントグループ(ハイブリッド接続NEG)を利用することで、ロードバランサーのバックエンドにAWS環境のサーバのプライベートIPアドレスを指定できることを知りました。

今回の記事では、ハイブリッド接続NEGを利用して、Cloud RunからAWS環境のAPIサーバにプライベートでアクセスする環境の構築手順をご紹介します!

ハイブリッド接続ネットワークエンドポイントグループ(NEG)とは

ハイブリッド接続NEGは、オンプレミス環境、他社クラウドなどGoogle Cloud以外の環境をエンドポイントとして定義できるネットワークエンドポイント(NEG)の1つです。

https://cloud.google.com/load-balancing/docs/negs/hybrid-neg-concepts?hl=ja

ハイブリッド接続NEGを利用することで、プライベートなIPアドレスをエンドポイントとして設定することができるため、パブリックなエンドポイントを別途用意する必要はありません。ロードバランサーのバックエンドにCloud VPN、Cloud Interconnectなどのプライベートに接続した環境のIPアドレスを直接指定することができ、ロードバランサー経由でアクセスすることができます。

https://medium.com/google-cloud-jp/hybrid-load-balancing-27e77a4ec62

ハイブリッド接続NEGのユースケースとしては、下記のケースがあります。

  • ロードバランサー経由でオンプレミス環境、他社クラウド環境で稼働するアプリケーションにトラフィックを転送することで、既存アプリケーションを有効活用しつつ、Google Cloud環境と併用したハイブリッドなインフラ構成を実現できる
  • ロードバランサー経由でオンプレミス環境、他社クラウド環境で稼働するアプリケーションにトラフィックを転送することで、既存リソースにGoogle Cloudの最新のネットワークセキュリティサービス製品を適用できる
  • Google Cloud環境、オンプレミス環境、他社クラウド環境にトラフィック分割を行うことで、オンプレミス環境からGoogle Cloud環境に段階的にインフラを移行できる

構築手順

本記事で構築する環境は下記になります。

ファイルの構成

今回作成したファイル群、ディレクトリ構成は下記になります。

$ tree .
.
├── misc
│   ├── tst-vpn-connect-ec2-user.key
│   └── tst-vpn-connect-ec2-user.key.pub
├── src
│   └── curl-golang
│       ├── Dockerfile
│       ├── go.mod
│       └── main.go
└── terraform
    ├── aws_ec2.tf
    ├── aws_vpc.tf
    ├── aws_vpn.tf
    ├── gcp_cloud_run.tf
    ├── gcp_compute_engine.tf
    ├── gcp_load_balancer.tf
    ├── gcp_vpc.tf
    ├── gcp_vpn.tf
    ├── locals.tf
    ├── provider.tf
    ├── terraform.tf
    ├── terraform.tfstate
    ├── terraform.tfstate.backup
    └── variables.tf

5 directories, 19 files

Terraformを実行すると、下記のリソースが作成されます。

  • AWS
    • Amazon VPC、パブリックサブネット、プライベートサブネット、インターネットゲートウェイ
    • Site-to-Site VPN接続、カスタマーゲートウェイ、仮想プライベートゲートウェイ
    • Amazon EC2、キーペア、セキュリティグループ、IAMロール
  • Google Cloud
    • VPCネットワーク、サブネット(インスタンス用サブネット、プロキシ専用サブネット)
    • Cloud VPN、Cloud Router、ピアVPNゲートウェイ、VPNトンネル
    • 内部ロードバランサー、ハイブリッド接続NEG
    • Compute Engine、Cloud Run、サービスアカウント

Terraformの詳細は下記のリポジトリをご確認下さい。実行する際は locals.tf を適切な値に書き換えて下さい。

https://github.com/daicho-takuma/sample-cloudrun2vpn-tf/tree/main

事前準備

Terraformを実行すると上記の全てのリソースが作成されます。ただ、TerraformでCloud Run + Artifact Registryを同時に作成しようとするとエラーが発生するため、Artifact Registryの作成は事前にgcloud CLIで行い、Terraformではデータソースで読み込む形にしています。

それでは、Cloud Runで実行する簡易的なプログラム(環境変数 TARGET_IP に対してHTTPリクエストを行うプログラム)をgcloud CLIを利用して、Artifact Regisotryにアップロードします。

gcloud CLIの利用方法について公式ドキュメントをご確認下さい。
https://cloud.google.com/sdk/gcloud?hl=ja

下記のコマンドでArtifact Regisotryにリポジトリを作成し、イメージをアップロードします。

$ GCP_PROJECT_ID='XXXXXX'
$ GCP_REGION='asia-northeast1'
$ GCP_AR_REPOSITORY_ID='tst-vpn-connect-sample-app-repo'

$ cd src/curl-golang/ 
$ go mod init curl-golang
# Artifact Registry にリポジトリ作成
$ gcloud artifacts repositories create ${GCP_AR_REPOSITORY_ID} \
--project=${GCP_PROJECT_ID} --location=${GCP_REGION} --repository-format=docker

# Cloud Build を利用して、Artifact Registry にイメージをアップロードする
$ gcloud builds submit --project=${GCP_PROJECT_ID} \
--tag ${GCP_REGION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GCP_AR_REPOSITORY_ID}/curl-golang

環境構築

事前準備が完了したので、Terraformを実行して、実際に検証環境を構築します。

$ cd ../../terraform/
$ terraform init
$ terraform plan
$ terraform apply

検証環境の構築自体はこれで完了です。主要な部分について軽く解説をさせていただきます。

Cloud VPN / Site-to-Site VPNの作成

最初に、Google Cloud環境にCloud VPN(HA-VPN)、AWS環境にSite-to-Site VPNを作成して、IPsec VPN接続を行いました。Terraformを実行すると一括でリソースが作成されますが、下記の順番でリソースが作成されています。

  1. GCP) Cloud VPN(HA-VPN)/Cloud Routerを作成
  2. AWS) カスタマーゲートウェイ/仮想プライベートゲートウェイ/Site-to-Site VPN接続を作成
  3. GCP) ピアVPNゲートウェイ/VPNトンネル/BGPセッションを作成

Cloud VPNを作成後に自動的に割り当てられたグローバルIPを利用してカスタマーゲートウェイを作成する、Site-to-Site VPN接続で作成したIP情報を利用してトンネルを作成するなど、手動で作成する場合、作成順序が重要になります。

コマンドラインインターフェース(gcloud/AWS CLI)で構築を行う場合、下記のページをご確認下さい。
https://cloud.google.com/network-connectivity/docs/vpn/tutorials/create-ha-vpn-connections-google-cloud-aws?hl=ja

Cloud VPN(HA-VPN)とSite-to-Site VPNでVPN接続を行う場合、VPNトンネルは4本作成されます。Terraform実行後、VPN経由でAWS環境で稼働するインスタンスのプライベートIPに接続できるか確認を実施します。

<Google Cloud>
ブラウザから管理コンソールにログインし、CloudShellを起動。IAP(Identity-Aware Proxy)経由でインスタンスにログイン後、AWS側のインスタンス ( 10.0.10.10 ) へのPING疎通できることを確認します。

XXXXX@cloudshell:~ $ gcloud compute ssh tst-vpn-connect-gcp-vm-01 --zone asia-northeast1-a --tunnel-through-iap
XXXXX@tst-vpn-connect-gcp-vm-01:~$ ping 10.0.10.10
PING 10.0.10.10 (10.0.10.10) 56(84) bytes of data.
64 bytes from 10.0.10.10: icmp_seq=1 ttl=253 time=3.63 ms
64 bytes from 10.0.10.10: icmp_seq=2 ttl=253 time=2.91 ms
64 bytes from 10.0.10.10: icmp_seq=3 ttl=253 time=2.77 ms
64 bytes from 10.0.10.10: icmp_seq=4 ttl=253 time=2.62 ms
64 bytes from 10.0.10.10: icmp_seq=5 ttl=253 time=3.56 ms
^C
--- 10.0.10.10 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 2.619/3.098/3.633/0.417 ms

<AWS>
ブラウザから管理コンソールにログインし、セッションマネージャーで接続後、Google Cloud側のインスタンス ( 10.10.10.10 ) へのPING疎通できることを確認します。

sh-4.2$ ping 10.10.10.10
PING 10.10.10.10 (10.10.10.10) 56(84) bytes of data.
64 bytes from 10.10.10.10: icmp_seq=1 ttl=63 time=3.46 ms
64 bytes from 10.10.10.10: icmp_seq=2 ttl=63 time=2.65 ms
64 bytes from 10.10.10.10: icmp_seq=3 ttl=63 time=2.77 ms
64 bytes from 10.10.10.10: icmp_seq=4 ttl=63 time=2.96 ms
64 bytes from 10.10.10.10: icmp_seq=5 ttl=63 time=2.91 ms
^C
--- 10.10.10.10 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 2.652/2.955/3.467/0.278 ms

内部ロードバランサー / ハイブリッド接続NEGの作成

次に、内部アプリケーションロードバランサー、ハイブリッド接続NEGを作成しました。内部ロードバランサーを作成後、バックエンドにハイブリッド接続NEGを作成しています。

ハイブリッド接続NEGは、 IP:PORT の形で指定できるため、AWS環境で稼働するインスタンスのプライベートIPであるc10.0.10.10:80 を指定しています。

https://github.com/daicho-takuma/sample-cloudrun2vpn-tf/blob/main/terraform/gcp_load_balancer.tf#L73-L88

これにより、内部ロードバランサーにアクセスした際、AWS環境で稼働するインスタンスのレスポンスが返却されます。先ほどと同様、ブラウザからCloudShellを起動し、IAP(Identity-Aware Proxy)経由でインスタンスにログイン後、内部アプリケーションロードバランサーに割り当てられたIPアドレスに80ポートでアクセスできることを確認します。

XXXXX@cloudshell:~ $ gcloud compute ssh tst-vpn-connect-gcp-vm-01 --zone asia-northeast1-a --tunnel-through-iap
XXXXX@tst-vpn-connect-gcp-vm-01:~$ curl 10.10.20.3
Hello World from tst-vpn-connect-aws-public-vm-01

AWS環境のインスタンスに80ポートでアクセスした際にレスポンスが返却されるのは、EC2作成時にユーザデータでApacheのインストールを実施しています。
https://github.com/daicho-takuma/sample-cloudrun2vpn-tf/blob/main/terraform/aws_ec2.tf#L29-L36

※ Apacheをインストールしただけですと no healthy upstream が返却されてしまうため、ドキュメントルートに適当なファイル index.html を配置して下さい。

Cloud Run / Direct VPC Egressの作成

最後に、Cloud Run、Direct VPC Egressを作成しました。Cloud RunからDirect VPC Egress経由でVPC内部の内部ロードバランサーにアクセスできるようにしています。

TerraformでDirect VPC Egressを設定している箇所は下記になります。とても簡単ですね。
https://github.com/daicho-takuma/sample-cloudrun2vpn-tf/blob/main/terraform/gcp_cloud_run.tf#L22-L28

Cloud RunからVPC内のリソースにアクセスする場合、以前であればサーバーレスVPCアクセスコネクタがありました。ただ、2024年04月にDirect VPC Egressの一般提供が開始したため、現在であればパフォーマンス性能の高いDirect VPC Egressを利用するのが推奨になります。
https://cloud.google.com/blog/ja/products/serverless/direct-vpc-egress-for-cloud-run-is-now-ga

Direct VPC EgressとサーバーレスVPCアクセスコネクタの違いについては、下記の記事がわかりやすいためご確認下さい。
https://blog.g-gen.co.jp/entry/cloudrun-direct-vpc-egress

動作確認

動作確認として、Cloud Runにアクセスし、Cloud Runから内部ロードバランサー経由でAWS環境で稼働するインスタンスにアクセスできることを確認します。今回、Cloud Runの認証を「認証が必要」にしているため、下記の通りIDトークンをリクエストヘッダーに付与してアクセスしています。

$ curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" https://tst-vpn-connect-sample-run-app-XXXXXXXXXXX.asia-northeast1.run.app
{"status_code": 200, "body": "Hello World from tst-vpn-connect-aws-public-vm-01\n"}

ちなみに、ご存じの方も多いかもしれませんが、内部ロードバランサー経由でAWS環境で稼働するインスタンスにアクセスした場合、プロキシ専用サブネットからのアクセスになるため、FW等でIP制限を行う際はプロキシ専用サブネットのネットワークCIDRを許可する必要があります。下記がAWS環境で稼働するインスタンスのアクセスログの一部になります。

[root@ip-10-0-10-10 ~]# tail -f /var/log/httpd/access_log
10.10.30.4 - - [10/Dec/2024:20:37:20 +0000] "GET / HTTP/1.1" 200 50 "-" "Envoy/HC"
10.10.30.3 - - [10/Dec/2024:20:37:22 +0000] "GET / HTTP/1.1" 200 50 "-" "Go-http-client/1.1"
10.10.30.2 - - [10/Dec/2024:20:37:25 +0000] "GET / HTTP/1.1" 200 50 "-" "Envoy/HC"
10.10.30.3 - - [10/Dec/2024:20:37:25 +0000] "GET / HTTP/1.1" 200 50 "-" "Go-http-client/1.1"
10.10.30.4 - - [10/Dec/2024:20:37:25 +0000] "GET / HTTP/1.1" 200 50 "-" "Envoy/HC"

クリーンアップ

下記のコマンドを実行し、Terraformで作成したリソースを全て削除します。

$ terraform destroy

上記コマンドでリソースの削除を確認後、下記のコマンドを実行して、Artifact Registryを削除します。

$ GCP_PROJECT_ID='XXXXXX'
$ GCP_REGION='asia-northeast1'
$ GCP_AR_REPOSITORY_ID='tst-vpn-connect-sample-app-repo'

# Artifact Registryのリポジトリ削除
$ gcloud artifacts repositories list --project=${GCP_PROJECT_ID}
$ gcloud artifacts repositories delete ${GCP_AR_REPOSITORY_ID} \
--project=${GCP_PROJECT_ID} --location=${GCP_REGION}
$ gcloud artifacts repositories list --project=${GCP_PROJECT_ID}

<罠1: Artifact Registry削除後にterraform destoryを実行すると下記のエラーが発生する>

...
│ Error: Requested image was not found.
│
│   with data.google_artifact_registry_docker_image.sample,
│   on gcp_cloud_run.tf line 66, in data "google_artifact_registry_docker_image" "sample":66: data "google_artifact_registry_docker_image" "sample" {

Terraformからデータソースで読み込んでいる都合上、Artifact Registryを先に削除すると、terraform destryができなくなります。先にリポジトリを削除してしまった場合、データソースの記述を削除しましょう。

<罠2: サブネット削除時に下記のエラーが発生し、削除ができない>

│ Error: Error when reading or editing Subnetwork: googleapi: Error 400: The subnetwork resource 'projects/XXXXXX/regions/asia-northeast1/subnetworks/tst-vpn-connect-gcp-subnet-ane1' is already being used by 'projects/XXXXXX/regions/asia-northeast1/addresses/serverless-ipv4-1111111111111111111', resourceInUseByAnotherResource

Cloud RunがIPアドレスを開放していないのが原因です。1〜2時間経過後に、再度、リソースの削除を試して下さい。
https://cloud.google.com/run/docs/configuring/vpc-direct-vpc?hl=ja#delete-subnet

注: Cloud Run リソースを削除または移動した後、Cloud Run が IP を解放するまで 1~2 時間待ってからサブネットを削除してください。

まとめ

今回、内部ロードバランサー、ハイブリッド接続NEGを経由して、Cloud RunからVPN接続したAWS環境で稼働するインスタンスにプライベートな経路でアクセスすることができました。ハイブリッド接続NEGを活用することで、既存の資産を活かしつつ、ハイブリッドなインフラを簡単に構築することができるので、今後も機会があれば活用していきたいです。

おわりに

ここまで読んでいただきありがとうございました。今回は全く生成AIが関係ない記事でしたが、AI ShiftはAI最前線の会社なので、来年はAIっぽい記事を書けるように精進します。

AI Shiftの開発チームでは、AIチームと連携してAI/LLMを活用したプロダクト開発を通し、日々ユーザのみなさまにより素晴らしい価値・体験を届けるべく開発に取り組んでいます。

AI Shiftではエンジニアの採用に力を入れています!この分野に少しでも興味を持っていただけましたら、カジュアル面談でお話しませんか?(オンライン・19時以降の面談も可能です!)
【面談フォームはこちら】

https://hrmos.co/pages/cyberagent-group/jobs/1826557091831955459

AI Shift Tech Blog

Discussion