先日、このblogをWordPressから静的サイトジェネレーターのHugoへ移行しました。
Hugoでブログを運営するにあたって如何に運用を楽にするかという問題があったので、問題解決&自分の勉強も兼ねてCIを構築することにしました。
概要
今回構築するCIのフローとしては大まかにはこんな感じになります。
- 自端末で記事を更新
- github上のローカルリポジトリへプッシュ
- Cloud Buildでビルドを開始
- ビルドで作成されたコンテンツをFirebase Hostingへデプロイ
前提条件
以下の設定ならびに環境が用意されていることを前提に書いています。
- Windows10 と WSL1 に gcloud CLIがインストール済み
- GCPにプロジェクトが作成済み&プロジェクトの課金設定が有効
- Firebase Hosting が利用可能な状態
- githubにコンテンツ管理用のプライベートリポジトリとテーマ管理用のプライベートリポジトリが作成済み
CI構築
Cloud Buildの有効化
こちらのページを参考にCloud Build API を有効にし、Cloud Shell でGoogle Cloud へログインします。
Githubのテーマ管理リポジトリの鍵登録
このblogの自作テーマを使用していて、Githubでコンテンツ管理用のリポジトリとは別のプライベートリポジトリで管理をしています。
なので、コンテンツ管理用のリポジトリのサブモジュールとしてテーマ管理リポジトリを追加しています。
CIを構成するにあたり、テーマ管理リポジトリからテーマを取得する必要があるため、テーマ管理リポジトリに公開鍵を登録します。
秘密鍵は暗号化してコンテンツと一緒にリポジトリに登録し、テーマを取得する際に利用します。
- 登録する鍵を作成(WSL作業)
$ mkdir /mnt/c/ssh $ cd /mnt/c/ssh $ ssh-keygen -t ed25519 Generating public/private ed25519 key pair. Enter file in which to save the key (/home/user/.ssh/id_ed25519): id_ed25519 Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in id_ed25519. Your public key has been saved in id_ed25519.pub. The key fingerprint is: ...(以下略)... $ ls -l /mnt/c/ssh -rwxrwxrwx 1 user user 411 May 1 21:55 id_ed25519 -rwxrwxrwx 1 user user 96 May 1 21:55 id_ed25519.pub
- Githubのリポジトリに公開鍵を登録
- githubのテーマを管理しているプライベートリポジトリを開く
- 「Settings > Deploy keys > Add deploy key」をクリック
- [Title]に任意の名前を入力し、[Key]に先ほど作成した「id_ed25519.pub」の中身をコピペで貼り付けて「Add key」をクリック
- known_hosts ファイルの作成
> ssh-keyscan -t rsa github.com > c:\ssh/known_hosts
暗号鍵を作成し秘密鍵とトークンを暗号化
- クラウド KMS のキーリングと暗号鍵を作成
キーリングを作成
> gcloud kms keyrings create [KEYRING-NAME] \ --location=global
暗号鍵を作成
> gcloud kms keys create [KEY-NAME] \ --location=global \ --keyring=[KEYRING-NAME] \ --purpose=encryption
- Cloud Build サービス アカウントに暗号鍵へのアクセス権を付与
公式の「暗号化されたリソースの使用」ページの「Cloud Build サービス アカウントに暗号鍵へのアクセス権を付与する」を参照し、暗号鍵を使用した復号化権限を付与します。
-
秘密鍵を暗号鍵を使って暗号化
> gcloud kms encrypt \ --plaintext-file=id_ed25519 \ --ciphertext-file=id_ed25519.enc \ --location=global \ --keyring=[KEYRING-NAME] \ --key=[KEY-NAME]
- 暗号化した秘密鍵とknown_hostsを格納
hugoのコンテンツフォルダに暗号化した秘密鍵とknown_hostsファイルを格納
> mkdir .ssh > mv c:\ssh/id_ed25519.enc .ssh > mv c:\ssh/known_hosts .ssh > tree hugo ├─.ssh │ ├─known_hosts │ └─id_ed25519.enc ├─archetypes ├─content ├─public ├─resources ├─static └─themes └─my-theme
- Firebase Hostingへデプロイするためのtokenを取得
※)ログイン画面が表示されるのでログインする> firebase login:ci Visit this URL on any device to log in: https://accounts.google.com/o/oauth2/auth?client_id= ...(略)... Waiting for authentication... + Success! Use this token to login on a CI server: 1/******************* ⇐ (token情報) Example: firebase deploy --token "$FIREBASE_TOKEN"
- トークンを暗号化する(WSL作業)
$ echo -n 1/******************* | gcloud kms encrypt \ --plaintext-file=- \ --ciphertext-file=- \ --location=global \ --keyring=[KEYRING-NAME] \ --key=[KEY-NAME] | base64 <暗号化されたトークン>
Cloud Buildで使用するコンテナ作成
Hugoのビルドを行うためのコンテナとFirebase Hostingへデプロイするためのコンテナを作成します。
コンテナイメージはGitHubの「GoogleCloudPlatform/cloud-builders-community」リポジトリで公開されているものを利用させてもらいます。
- コンテナイメージの取得
> git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
- Firebaseデプロイ用コンテナ作成
以下のコマンドを実行する前に「firebase」フォルダに入っている「firebase.bash」の改行コードが「LF」であることを確認してください。
改行コードが「CRLF」であった場合は「LF」に変換してからコマンドを実行するようにしてください。> cd cloud-builders-community/firebase > gcloud builds submit --config cloudbuild.yaml .
- Hugoビルド用コンテナ作成
Hugoのビルドを行うためのコンテナを作成します。
通常のHugoをビルドするだけであればクローンしてきたコンテナイメージをそのまま使用すれば動きますが、今回はHugo Extendedを使用するため、コンテナイメージを少し修正します。> cd ../hugo
Dockerfileを以下の内容で修正
FROM busybox AS build-env ENV HUGO_VERSION=0.54.0 RUN wget "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gz" RUN tar zxf "hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gz" FROM gcr.io/distroless/cc ENTRYPOINT ["/hugo"] COPY --from=build-env /hugo /
コンテナ作成
> gcloud builds submit --config cloudbuild.yaml .
トリガーの作成
Cloud Buildのページにアクセスし、CIの起動設定を行います。
- [トリガーの作成]をクリック
- ソースの選択で[GitHub]を選択し、[リポジトリ接続サービスの提供を目的として、Google が認証トークンを収集、保存することに同意します]にチェックを入れる
- GitHubの自分のリポジトリにGCPがアクセスしていいか問われるので許可する
- リポジトリを選択でhugoのコンテンツを管理しているリポジトリを選択し、[続行]をクリック
- トリガーの設定は以下の内容で設定
- トリガーのタイプ: ブランチ
- ブランチ(正規表現): master
- ビルド設定: Cloud Build 構成ファイル(yaml または json)
- Cloud Build 構成ファイルの場所: / cloudbuild.yaml
- Cloud Build 構成ファイルの場所: / cloudbuild.yaml
- トリガーのタイプ: ブランチ
- [保存]をクリック
諸々の設定
cloudbuild.yamlの作成
コンテンツ格納フォルダ配下に「cloudbuild.yam」を作成し、Cloud Buildで動かしたいコンテナと処理を記載します。
記載する内容は以下の通りです。
# Firebase Hostingへデプロイするためのtokenを復号
secrets:
- kmsKeyName: projects/[Project-Name]/locations/global/keyRings/[KEYRING-NAME]/cryptoKeys/[KEY-NAME]
secretEnv:
FIREBASE_TOKEN: <暗号化されたトークン>
# テーマを格納しているリポジトリにアクセスするための秘密鍵を復号
steps:
- id: "Decrypt the secret key"
name: 'gcr.io/cloud-builders/gcloud'
args:
- kms
- decrypt
- --ciphertext-file=.ssh/id_ed25519.enc
- --plaintext-file=/root/.ssh/id_ed25519
- --location=global
- --keyring=[KEYRING-NAME]
- --key=[KEY-NAME]
volumes:
- name: 'ssh'
path: /root/.ssh
# SSH認証を行うための設定
- id: "Settings for performing SSH authentication"
name: 'gcr.io/cloud-builders/git'
entrypoint: 'bash'
args:
- '-c'
- |
chmod 600 /root/.ssh/id_ed25519
cat <<EOF >/root/.ssh/config
HostName github.com
IdentityFile /root/.ssh/id_ed25519
StrictHostKeyChecking no
EOF
mv .ssh/known_hosts /root/.ssh/known_hosts
volumes:
- name: 'ssh'
path: /root/.ssh
# サブモジュールのHugoテーマを取得
- id: "git submodule update"
name: 'gcr.io/cloud-builders/git'
args:
- submodule
- update
- --init
- --recursive
- --remote
volumes:
- name: 'ssh'
path: /root/.ssh
# Hugoコマンドでビルド
- id: "Generate html with hugo"
name: 'gcr.io/${PROJECT_ID}/hugo'
args: ['']
# ビルドされたアイテムをFirebase Hostingへデプロイ
- id: "Deploy to Firebase Hosting"
name: 'gcr.io/${PROJECT_ID}/firebase'
args: ['deploy']
secretEnv: ['FIREBASE_TOKEN']
cloudbuild.yamの内容を少し解説します。
初めに、暗号化されたFirebase Hostingへデプロイするためのトークンを利用できるように復号化するための設定を記載します。
次に、テーマをGitHubから取得するための設定を記載します。
コンテンツと同じリポジトリでテーマファイルを管理している場合は特に意識する必要はないのですが、今回のケースのようにコンテンツとテーマで別々のリポジトリで管理し、テーマリポジトリをコンテンツリポジトのサブモジュールとして追加している場合は一工夫必要となります。
というのも、今回のケースでGitHubからテーマを取得するタスクを入れていない場合、Hugoビルド実行時にテーマが存在していない扱いになり、予め自端末等でビルドを行っていた内容がデプロイされることになります。
なので、テーマをGitHubのプライベートリポジトリから取得できるよう暗号化していた秘密鍵の復号タスクと、SSHの設定を行います。
残りのタスクはHugoのデプロイ処理と成果物をFirebase Hostingへデプロイを行っています。
gitmodulesの確認
コンテンツ管理フォルダ配下の「.gitmodules」を開き、url設定を確認します。
以下のようにurlの値がGitHubからSSHでクローンを行うときのアドレスになっていることを確認します。
HTTPSのアドレスが設定されている場合、SSHのアドレスに修正を行います。
[submodule "themes/[theme-name]"]
path = themes/[theme-name]
url = [email protected]:[user-name]/[theme-repository].git
gitignoreの設定
コンテンツ管理フォルダ配下の「.gitignore」を開き、以下の設定を追加します。
この設定は好みの話ではあると思いますが、なるべくビルドされたファイルのみデプロイしてほしいので、publicフォルダ配下はGitHubで管理対象外としています。
# hugo
public/*
CI実行
動かす前に「cloudbuild.yam」や「.gitmodules」、「.gitignore」、暗号化した秘密鍵等が既にリモートリポジトリに反映されている、またはプッシュ対象に含まれていることを確認します。
後は、Cloud Buildのトリガー作成時に指定したリポジトリのmasterブランチにプッシュを行うと、1分程度2の後内容が反映されます。
おわりに
割と細部を端折り気味に書きましたが思いの外長くなってしまいました。
多分、今回のパターンに全て当てはまることはそう多くないと思いますが、参考になれば幸いです。
あと、今回紹介した「cloudbuild.yam」やHugoコンテナの設定等はGitHubに近日中に上げようかと思っています。
2019/05/09 追記
- 「cloudbuild.yam」やHugoコンテナの設定をGitHubに公開しました。