type holyshared = Engineer<mixed>

技術的なことなど色々

Terraformのバージョンを上げた

terraformを1.12系にあげて、providerも一緒に最新まであげる作業をやっていた。    まずterraformのファイルのバージョン指定を変えて、Terraform Cloudの設定でバージョンを~> 1.12.0に変える。

Terraform Cloudの設定でバージョンを変えないと、古いTerraformで実行しようとするので必須。

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 6.47"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "~> 6.47"
    }
  }
  required_version = "~> 1.12.0"
}

また、google-betaで使用していたリソースが、googleで利用できるようになっていたので、providerをコマンドで変える。

terraform state replace-provider \
  registry.terraform.io/hashicorp/google-beta \
  registry.terraform.io/hashicorp/google

これでローカルで terraform plan を実行して、差分にクリティカルなものが出なかったので、このままgit commitしてpushし、Terraform Cloudの結果を確認した。

内容に問題なかったので、Applyして実際にアプリケーションの動作確認して問題なかったので作業は完了した。

Apollo Serverをv4からv5にする

ApolloServer v5がリリースされたのでアップグレードして見ました。
expressのmiddlewareとしてして組み込んでいたのでそれの対応だけで済みました。

今回express用のmiddlewareは別のパッケージになったみたいなので別途インストールします。
expressもついでにv5にしたかったので、express v5用のパッケージをインストールします。

yarn add @apollo/server @as-integrations/express5 express

後はmiddlewareを指定しているところを新しいパッケージに置き換えます。

import { createServer } from "http";
import { expressMiddleware, type ExpressContextFunctionArgument } from "@as-integrations/express5";

const app = express();
const httpServer = createServer(app);

const graphqlServer = ApolloServer({});
await graphqlServer.start();

app.use(
  "/graphql",
  cors({
    origin: [
      "http://localhost:3000",
    ],
    credentials: true,
  }),
  express.json(),
  expressMiddleware(graphqlServer, {
    context: async (
      args: ExpressContextFunctionArgument,
    ): Promise<{}> => {
      return {}
    }
  });
);

app.listen(process.env.PORT || 3000);

これだけでバージョンアップ自体は完了でした。
パッケージの参照を変えるだけで、v5系に移行できたので対応自体はすぐに終わりました。

build-push-actionでsecretを扱う

npmのプライベートなパッケージをインストールするのに環境変数を利用している際、DockerのコンテナをNPMのアクセストークンが漏れないようにビルドする必要が出たりします。

例えば.yarnrc.ymlで環境変数を参照している場合などです。

npmAuthToken: "${NPM_AUTH_TOKEN}"

この場合、Dockerコンテナをビルドする場合、環境変数をSecretとしマウントします。

docker buildx build -f packages/api/Dockerfile \
  --secret id=npm_auth_token,env=NPM_AUTH_TOKEN \
  -t api:latest .

Dockerファイルでは--mount=type=secretを利用して参照します。
これでプライベートなパッケージもインストールでき、NPMのアクセストークンも漏れないはずです。

FROM --platform=x86_64 node:22.17.0-bullseye-slim AS builder

COPY package.json yarn.lock tsconfig.json .yarnrc.yml /app/

WORKDIR /app

# id,envはdockerコマンドで指定する。
RUN --mount=type=secret,id=npm_auth_token,env=NPM_AUTH_TOKEN yarn workspaces focus -A --production

ここまでできたらGithub Actionsでbuild-push-actionを利用して、コンテナをpushできるようになります。
秘匿情報はbuild-push-actionのsecret-envsをid=環境変数名形式で指定すれば利用できます。

- uses: docker/build-push-action@v5
  env:
    NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
  with:
    push: true
    file: "packages/api/Dockerfile"
    provenance: false
    tags: |
      ${{ secrets.REPOSITORY_URL }}:latest
    secret-envs: |
      "npm_auth_token=NPM_AUTH_TOKEN"

無事にGithub Actionsでコンテナがビルドできれば問題ありません。

Cloudflareでクライアント証明書を利用する

パスフレーズ付きの秘密鍵を作成する。 鍵はECC(Elliptic Curve Cryptography)鍵を使用します。(RSAでもいい)

openssl ecparam -name prime256v1 -genkey | \
openssl ec -aes256 -out client.private.key

ECC鍵を使用して、CloudflareにアップロードするためのCSR(証明書署名要求)を作成する。 証明書を区別しやすくする為にサブジェクトを適宜していしておくと良い。

openssl req -new -key client.private.key -out client.csr \
  -subj "/C=JP/CN=example/emailAddress=client@example.com"

CSRファイルができたらクライアント署名書の画面で、CSRを入力する。

CSRの入力画面

証明書が表示されるので、コピーしてPKCS12形式でファイル保存する。

openssl pkcs12 -export \
  -inkey client.private.key \
  -in client.csr \
  -out client.p12

後は利用したいデバイスにインストールして、保護されているサイトにアクセスできれば完了。 Macの場合は、キーチェーンの証明書にアクセスして、常に信頼などの変更しておくと良い。

Could Buildを第2世代のリポジトリに対応させた

新しいアプリケーションに置き換える対応をしている時についでにCloud Buildのリポジトリを第2世代に置き換えた。

第2世代のリポジトリはTerraformで作成してあるので、Cloud Buildの設定を変えるだけで対応自体は済んだが制限があってビルド自体が失敗してしまう。

原因は Concurrent Build CPUs (Regional Default Pool) の上限値が0だったのが問題だったよう。

failed precondition: due to quota restrictions, Cloud Build cannot run builds in this region, please contact Google Cloud Support at https://cloud.google.com/support-hub

リージョンはasia-northeast1にしていたので、割り当てアラートとシステムの上限の項目(ロケーションなど)表示を確認すると、下記の様になっていたので間違いなさそう。

build_origin : default
region : asia-northeast1

Cloud Build

制限の変更を依頼してからすぐに変更完了の通知が来たので試してみると正常に実行できた。

運用しているアプリの変更

運用しているアプリの変更をちょっとずつ進めていて、半分ぐらい完了した。   単一のアプリケーションを複数の小さいアプリに分割をしていて、段階的に進めているので全ての完了が見えてきた。  

アプリ自体をAPIサーバー、投稿管理アプリ、投稿の表示アプリに分割していて、APIサーバー、投稿管理アプリが終わった。
APIサーバーはそのまま既存のアプリから移動するだけでよくそんなに時間は掛からなかった。  

投稿管理アプリの方はReactRouter v7に置き換えるので、手探り状態で進めたが、既存のReact Componentを流用しつつ、比較的短時間で終わったように思う。  

複数のアプリケーションに分割したことで、Cloud Buildのビルドが短時間で終わるようになったし、個別のアプリケーションを単体でデプロイできるようになったのでやって良かった。