Terraformを使って、CloudRunからPublicIPを持つCloudSQLに対して、DB接続を行う
記事の内容
GCPのCloudSQLに対して、Public IPを付与し、CloudRunからDB接続を行う方法を解説します(Terraformを利用します)
記事を読むと得られるもの
- PublicIPをもったCloudSQLのセットアップの仕方
- CloudRunのセットアップの仕方
- CloudRunとCloudSQLの接続方法
- TerraformでCloudRunとCloudSQLを作成する方法
対象読者
- GCPでDBを利用した構成を作りたい人
- CloudRunを使いたい人
- CloudSQLにPublicIPを付与して運用したい人
- Terraformで上記構成を作りたい人
記事の長さ
5分で読めます
CloudSQLをセットアップ
まずはPublicIPを付与したCloudSQLを作成します。
以下のTerraformのコードをapplyする形で、MySQLをセットアップします。
resource "google_sql_database" "database" {
project = var.project_id
name = "database"
instance = google_sql_database_instance.instance.name
}
resource "google_sql_database_instance" "instance" {
project = var.project_id
name = "instance-1"
region = "asia-northeast1"
database_version = "MYSQL_8_0"
settings {
tier = "db-f1-micro"
}
deletion_protection = "true"
}
resource "google_sql_user" "users" {
project = var.project_id
name = "test-user"
instance = google_sql_database_instance.instance.name
password = "password_1234"
}
上記のApplyが無事に成功すると、Public IP Addressを持ったCloudSQLが作成されます。
同時に、
- id: test-user
- password: password_1234
のユーザーも作成されますので、このid,passwordを使って、MySQLに接続を行います。
試しに接続してみる
上記で作成したMySQLに対して、実験で接続してみます。
Autorized networksに自分のグローバルIPを登録する
CloudSQLは、Authorized networks
で許可されているIPアドレスの範囲からのみ、アクセスを受け付けます。デフォルト設定の場合、すべての接続がDenyされているので、現在使っているWifiのグローバルIPアドレスを登録します。
この画像の例では、123.123.123.123
のIPアドレスからの接続のみ許可しています。
現在使っているIPアドレスの調べ方
$ curl httpbin.org/ip
{
"origin": "123.123.123.123"
}
上記コマンドを実行すると、現在のローカルPCが使用しているグローバルIPを調べることができます。
ローカルから接続する
Authorized networksに、現在使用しているグローバルIPを設定できたので、ローカルPCからCloud SQLに接続することができるようになりました。以下のコマンドで、接続を試みます。
$ mysql -utest-user -p -h 11.111.111.12
Enter password: <password_1234を入力>
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 902
Server version: 8.0.31-google (Google)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [database]> use database;
Database changed
MySQL [database]> CREATE TABLE users (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT);
Query OK, 0 rows affected (0.154 sec)
MySQL [database]> INSERT INTO users VALUES ();
Query OK, 1 row affected (0.023 sec)
MySQL [database]> INSERT INTO users VALUES ();
Query OK, 1 row affected (0.012 sec)
MySQL [database]> SELECT * FROM users;
+----+
| id |
+----+
| 1 |
| 2 |
+----+
2 rows in set (0.009 sec)
無事に接続できることを確認できました。
※接続テストの際に利用できるように、usersテーブルとダミーデータも一緒に作成しておきました。
これで、CloudSQLの設定は完了です。
CloudRunを作成する
CloudRun上で動作するDockerImageを作成する
CloudRunはDockerImageを動作させるサービスなので、MySQLに接続して、その結果を返すsimpleなDocker Imageを用意します。
index.js
const express = require('express');
const mysql = require('promise-mysql');
const app = express();
const port = 8080;
const createUnixSocketPool = async () => {
return mysql.createPool({
user: "test-user",
password: "password_1234",
database: "database",
socketPath: "/cloudsql/<CloudSQLの接続名>",
});
};
let pool;
(async () => {
pool = await createUnixSocketPool();
})();
app.get('/', async (req, res) => {
try {
const connection = await pool.getConnection();
const results = await connection.query('SELECT * FROM users');
connection.release();
res.send(results);
} catch (error) {
res.status(500).send(error.message);
}
});
app.listen(port, () => {
console.log(`App running on http://localhost:${port}`);
});
package.json
{
"name": "mysql-nodejs-app",
"version": "1.0.0",
"description": "A Node.js app connecting to MySQL using Unix sockets",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.17.1",
"promise-mysql": "^5.0.3"
}
}
Dockerfile
# nodeのイメージを取得
FROM node:14
# アプリケーションディレクトリを作成
WORKDIR /usr/src/app
# アプリケーションの依存関係をインストール
COPY package*.json ./
RUN npm install
# アプリケーションのソースをバンドル
COPY . .
# ポート8080で実行
EXPOSE 8080
CMD [ "node", "index.js" ]
※CloudSQLの接続名の部分は、<GCP Project ID>/<リージョン名>/<CloudSQL名>
に変更してください。(CloudSQLのコンソールのConnection name
の欄でも確認ができます。)
上記3ファイルを共通のディレクトリに格納し、そのディレクトリで以下コマンドを実行して、DockerImageをGCRに格納します(Project ID部分は、自分のGCPのProject IDに変更してください)
$ gcloud builds submit --tag gcr.io/<Project ID>/test
CloudRun用にCloudSQLに接続できるServiceAccountを作成する
PublicIPが付与されたCloudSQLを全ネットワークに対して公開してしまうと、誰からでもアクセスできるようになってしまうため危険です。
そのため、CloudSQLを一般公開はせずに、Cloud Runにroles/cloudsql.client
を持ったServiceAccountを付与することで、CloudRunからの接続を許可します。
resource "google_service_account" "new_account" {
project = var.project_id
account_id = "test1234"
display_name = "Test Service Account"
}
resource "google_project_iam_member" "project" {
project = var.project_id
role = "roles/cloudsql.client"
member = "serviceAccount:${google_service_account.new_account.email}"
}
この、Terraformを実行することで、CloudSQLをClientとして利用するServiceAccountの作成ができます。
CloudRunを起動する
- CloudRun用に作成したDockerImage
- CloudRun用に作成したServiceAccount
を用いて、CloudRunを起動します。
resource "google_cloud_run_service" "default" {
project = var.project_id
name = "test-sql"
location = "asia-northeast1"
template {
spec {
service_account_name = google_service_account.new_account.email
containers {
image = "gcr.io/<Project ID>/test"
}
}
metadata {
annotations = {
"run.googleapis.com/cloudsql-instances" = "<Project ID>:asia-northeast1:instance-1"
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
resource "google_cloud_run_service_iam_member" "public" {
project = var.project_id
service = google_cloud_run_service.default.name
location = "asia-northeast1"
role = "roles/run.invoker"
member = "allUsers"
}
上記がTerraformでCloudRunを設定したコードになります。
google_cloud_run_service_iam_member
でallUsers
を指定することで、インターネットを通して、すべてのユーザーがこのCloudRunで動作するアプリケーションにアクセスできるようになります。
また、annotation部分に、CloudSQLとの接続の作成する記載を書くことで、cloudsqlproxy経由でのCloudSQLへの接続を許可します。
ブラウザから確認する
ここまでの構築で、CloudSQLとCloud Runの実装が完了しました。最後に、CloudRunが動作するサーバーにブラウザ経由でアクセスして、正常にCloudSQLと接続できていることを確認します。
$ open $(gcloud run services describe test-sql --format 'value(status.url)' --region asia-northeast1 --platform managed)
このコマンドで、CloudRunのデフォルトURLをブラウザで開くことができます。
無事、CloudSQLの内容が表示されています。
ソースコード
TerraformとCloudrun上で動作するアプリケーションのソースコードを以下のGithubの公開リポジトリに置いておきました。
note
勉強法やキャリア構築法など、エンジニアに役立つ記事をnoteで配信しています。
Discussion