くりにっき

フルスタックキュアエンジニアです

地域.rbカレンダーでconnpass APIの個人利用申請をした

tl;dr;

connpass APIの仕様変更で 地域.rbカレンダー を閉じるつもりでしたが、個人利用申請を行ったので2024年5月23日(木)以降もカレンダーが更新されます

時系列

今年の2月にconnpass APIの無償提供終了が発表

3月頃にプランが発表されて、料金体系が個人向けじゃなさそうなので地域.rbカレンダーを閉じる(connpassの有料APIに対応しない)ことを決意

この時点では固定IPアドレスも費用かかりそうできっついなあということで閉じる方針は継続。

これを見てRubyKaigi後に機運が高まった。*1(実質KaigiEffect*2

申請した2日後くらいに許諾された!!!

詳しいログ

github.com

具体的にやったこと

2024年5月23日(木)以降もConnpass APIを使うには固定IPアドレスが必要なので、その対応を行いました。

地域.rbカレンダーのカレンダー生成のエンドポイントはCloud Functionsで動いているため*3Serverless VPC Access Connectorを利用してCloud Functionからインターネットに出る時にCloud NATを経由して送信元のIPアドレスを固定化しました。

変更前後の構成は下記のような感じです。

Before

After

この図だと端折っていますが、送信元のIPアドレスが本当に固定化されているかの動作確認としてVPC内にVMを立てたりもしています。(Cloud NATはVPCからインターネットに出る時の送信元を全て固定IPアドレスにするので、VPC内にVMを立てても当然同じIPアドレスになる)

Terraform

具体的な方針は下記が参考になりました。

dev.classmethod.jp

これを参考に実装したのが下記のTerraformになります。

# VPC
locals {
  subnet_cidr = "10.128.0.0/20" # 10.128.0.0 - 10.128.15.255

  # Requires /28 for Serverless VPC Access Connector
  # c.f. https://cloud.google.com/vpc/docs/serverless-vpc-access
  vpc_access_connector_cidr = "10.128.16.0/28" # 10.128.16.0 - 10.128.16.15
}

resource "google_compute_network" "main" {
  name                    = "main"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "main" {
  name          = google_compute_network.main.name
  network       = google_compute_network.main.self_link
  ip_cidr_range = local.subnet_cidr
  region        = "asia-northeast1"
}

resource "google_compute_router" "main" {
  name    = google_compute_network.main.name
  region  = google_compute_subnetwork.main.region
  network = google_compute_network.main.self_link
}

resource "google_vpc_access_connector" "main" {
  name          = "main"
  machine_type  = "f1-micro"
  ip_cidr_range = local.vpc_access_connector_cidr
  network       = google_compute_network.main.id

  # NOTE: requires 2+ instances
  min_instances = 2
  max_instances = 3
}

# Cloud NAT
resource "google_compute_router_nat" "main" {
  name   = google_compute_network.main.name
  router = google_compute_router.main.name
  region = google_compute_router.main.region

  nat_ip_allocate_option = "MANUAL_ONLY"
  nat_ips                = [google_compute_address.nat.self_link]

  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}

resource "google_compute_address" "nat" {
  name        = "nat"
  region      = google_compute_subnetwork.main.region
  description = "Static Address for Cloud NAT"
}

アプリケーション側(デプロイ時にFunctionをVPC Connectorに紐づける設定)は下記

github.com

ハマったポイント:Terraformでapplyする時だけなぜかエラーになる

具体的には下記のようなエラーです

│ Error: Error waiting to create Connector: Error waiting for Creating Connector: Error code 13, message: An internal error occurred: VPC Access connector failed to get healthy. Please check GCE quotas, logs and org policies and recreate.
│ 
│   with google_vpc_access_connector.main,
│   on vpc.tf line 27, in resource "google_vpc_access_connector" "main":
│   27: resource "google_vpc_access_connector" "main" {

設定自体は問題なさそうだし、なんなら全く同じ設定をコンソールから設定できたので本当に謎でした。( terraform apply でのみエラーになる)

Terraformで利用しているサービスアカウントに対して service-PROJECT_NUMBER@gcp-sa-vpcaccess.iam.gserviceaccount.com の「サービスアカウントユーザー( roles/iam.serviceAccountUser )」*4 のIAMロールが必要なのが原因でした。 *5

最後に

作った後で気づいたけど自分の用途だとオーバーテクノロジーだったかもしれないです。

似たようなことをやりたい人は参考にして下さい。

*1:このポストはRubyKaigi直前のものなんだけど機運が高まったのはRubyKaigi後

*2:https://togetter.com/li/162817

*3:icsを返すエンドポイントを自分のGoogle Calendarに登録してからGitHub Pagesで公開している

*4:https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountUser

*5:https://cloud.google.com/vpc/docs/configure-serverless-vpc-access?hl=ja#service_account_permissions