Cloud Runとワークフロー
GCPの管理下の環境に、コンテナを乗せて動かすことができるサーバーレス環境です。
AWSのFargateと似たような立ち位置っぽいです。
https://cloud.google.com/run?hl=ja
Cloud Runの実行は、HTTPアクセスで行います。
これだけだと使い勝手が悪いので、ワークフローと組み合わせます。
https://cloud.google.com/workflows?hl=ja
これにより、GCPのコンソール画面からCloud Runが実行できるようになります。
今回はこれらを使って、Word Pressのテーマリリースをサーバレスで実行できるようにします。
なお、一連の作業には下記のチュートリアルが参考になります。
https://cloud.google.com/run/docs/triggering/using-workflows?hl=ja
事前準備
スキーム
リリースするテーマは、GitHubで管理します。
これをCloud Runで取得し、WPサーバへ配置します。
Cloud Runの実行準備
Cloud Runについてはこちら参考にセットアップしました。
https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service
GitHubトークンの取得・保管
今回はコマンドでGithubから資材をクローンするため、トークンを作成しておきます。
GitHubにログインし、「Settings」→「Developer settings」→「Personal access tokens」へ遷移します。
下記URLからも飛べます。
https://github.com/settings/tokens
「Generate new token」を押下し、
トークンの説明、有効期限、スコープを設定して「Generate token」を押下します。
スコープついて、今回はリポジトリをクローンするだけなので、「repo」だけチェックしておけば十分です。
有効期限が切れるとコンテナが実行できなくなるため、気をつけましょう。
作成されたトークンは後でGCPへアップロードするため、コピーしておきます。
ローカルでテストしたい場合には、ファイルとして保存しておきます。
生成したトークンを、Scret Managerにアップロードします。
https://console.cloud.google.com/security/secret-manager
「シークレットを作成」を選択し、先ほどコピーしたトークン値を設定して作成します。
これで、GitHubのトークンをGCPで利用できるようになりました。
Cloud Runのソースコード
Cloud Runにデプロイするソースコードはこれだけです。
- Dockerfile
- requirements.txt
- app.py
Dockerfile
コンテナのインフラ設定が書かれているファイルです。
# Python image to use.
FROM python:3.10-alpine
# Set the working directory to /app
WORKDIR /app
# copy the requirements file used for dependencies
COPY requirements.txt .
# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# Install Git
RUN apk update \
&& apk add git
# Copy the rest of the working directory contents into the container at /app
COPY . .
# Enviroment Variables
ENV GITHUB_TOKEN_PATH /etc/token/github_token
ENV GITHUB_REPO_OWNER tktkjuntech
ENV GITHUB_REPO_NAME wordpress-themes
ENV WP_THEME_NAME original-theme
ENV WP_HOST XXXXXXXXXX
ENV WP_PORT 10022
ENV SFTP_USER autohacks
ENV SFTP_KEY_PATH /etc/ssh/id_rsa
ENV REMOTE_WORK_DIR /home/user/wordpress/public_html/wp-content/themes
# Run app.py when the container launches
ENTRYPOINT ["python", "app.py"]
リリーススクリプトをPythonで作成するため、Pythonのイメージをベースに作成しています。
コンテナ内でGitを利用するため、RUNコマンドでGitをインストールするよう記述。
Alpine Linuxのイメージをベースに作成しているため、yum
やapt-get
が使えないのですが、
これに気づかず苦労しました。。
結果、apk
でインストールするようにしています。
requirements.txt
pythonで使うライブラリを記載しています。
要するにpip install
するやつです。
Flask==2.2.2
GitPython==3.1.27
paramiko==2.10.4
app.py
リリーススクリプトです。
import os
import git
import paramiko
import shutil
from flask import Flask
app = Flask(__name__)
@app.route("/")
def release_wp_theme():
try:
print("Get New Artifacts ...")
with open(os.environ.get("GITHUB_TOKEN_PATH"), 'r', encoding='utf-8') as f:
github_token = f.read().splitlines()[0]
repo_owner = os.environ.get("GITHUB_REPO_OWNER")
repo_name = os.environ.get("GITHUB_REPO_NAME")
artifacts_path = './artifacts'
theme_name = os.environ.get("WP_THEME_NAME")
# Init
os.system(f"rm -rf {artifacts_path}")
os.system(f"rm -rf ./{theme_name}.zip")
#
git_clone_url = f'https://{github_token}@github.com/{repo_owner}/{repo_name}.git'
cloned_repo = git.Repo.clone_from(git_clone_url,artifacts_path)
#
# ZIP作成
print("Archive Artifacts ...")
archive_target_path = f"{artifacts_path}/{theme_name}/"
shutil.make_archive(theme_name, 'zip', root_dir=archive_target_path)
#
# SSH接続
print("Create New Connection ...")
sftp_host = os.environ.get("WP_HOST")
sftp_port = os.environ.get("WP_PORT")
sftp_user = os.environ.get("SFTP_USER")
sftp_key_file = os.environ.get("SFTP_KEY_PATH")
rsa_key = paramiko.RSAKey.from_private_key_file(sftp_key_file)
#
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(sftp_host, port=sftp_port, username=sftp_user, pkey=rsa_key)
sftp_con = client.open_sftp()
#
# Zipファイル送信
print("Send New Artifacts ...")
remote_work_dir = os.environ.get("REMOTE_WORK_DIR")
remote_theme_dir = f"{remote_work_dir}/{theme_name}"
remote_archive_path = f"{remote_work_dir}/{theme_name}.zip"
sftp_con.put(f"./{theme_name}.zip",remote_archive_path)
#
# 既存のディレクトリ削除
print("Remove Old Artifacts ...")
client.exec_command(f"rm -rf {remote_theme_dir}")
# ファイル配布
print("Distribute New Artifacts ...")
stdin, stdout, stderr = client.exec_command(f"unzip {remote_archive_path} -d {remote_theme_dir}")
cmd_result = ''
for line in stdout:
cmd_result += line
print(cmd_result)
# ZIP削除
print("Remove Archive File ...")
sftp_con.remove(remote_archive_path)
# Connection Close
client.close()
print("Complete!")
#
return "Success!"
except Exception as e:
return str(e)
if __name__ == '__main__':
server_port = os.environ.get('PORT', '8080')
app.run(debug=False, port=server_port, host='0.0.0.0')
Cloud Runで動かすスクリプトということで、
HTTPアクセスを受けて実行する実行する方式となっています。
GitHubからリリースする資材をリリースする資材を取得したのち、
SFTPでWPサーバへ配布します。
サーバには公開鍵認証で接続するため、秘密鍵をGitHubトークンと同じ要領でSecret Managerへ保管しておきます。
デプロイからCloud Run実行まで
リリーススクリプトが存在するディレクトリで、gcloud run deploy
を実行します。
後々テスト実行したいため、Allow unauthenticated invocations...
はy
にしておきます。
$ gcloud run deploy --source .
Service name (release-wp-theme):
This command is equivalent to running `gcloud builds submit --tag [IMAGE] .` and `gcloud run deploy release-wp-theme --image [IMAGE]`
Allow unauthenticated invocations to [release-wp-theme] (y/N)? y
完了するとService URL:
にデプロイ先のURLが表示されるので、ブラウザでアクセスしてみます。
すると、下記エラーが画面に返却されます。
指定されたディレクトリにファイルがないよ、と怒られています。
ここからは、コンソール画面で設定を続けます。
https://console.cloud.google.com/run
GCPのコンソール画面に行くと、先ほどデプロイしたサービスが表示されています。
これを選択し、「新しいリビジョンの編集とデプロイ」を選択します。
編集画面をスクロールしていくと、「シークレット」という項目があります。
ここで、Secret Managerに保管してあるGitHubトークンとWPサーバ接続用の秘密鍵をボリュームとしてマウントします。
マウントパスは、Dockerfile内でENVに記述しているものと合わせます。
これでデプロイし、再度コンテナのURLへアクセスすると、
無事「Success!」が返ってきて、リリースすることができました。
ワークフローへの接続
ここからは、デプロイしたCloud Runサービスをワークフローで実行できるようにします。
現状だとインターネット上から誰でもリリースできるようになっているため、
まずはCloud Runでアクセス制限を行います。
デプロイしたサービスを選択し、トリガーから設定を下記へ変更します。
保存後、再度コンテナのURLへアクセスすると、
403エラーが返ってくるようになりました。
ここから、ワークフローでCloud Runを実行できるようにします。
まずは下記を参考に、サービスアカウントを作成します。
https://cloud.google.com/run/docs/triggering/using-workflows?hl=ja
サービスアカウントが作成できたら、ワークフローを作成します。
サービスアカウントには、先ほど作成したものを指定します。
# This is a sample workflow to test or replace with your source code.
#
# This workflow passes the current day of the week to the Wikipedia API and
# returns a list of related Wikipedia articles.
# The current day of the week (in GMT) is retrieved from a Cloud Function
# unless you input your own search term (for example, {"searchTerm": "Monday"}).
main:
params: [input]
steps:
- exeCroudRun:
call: http.get
args:
url: https://XXXXX.a.run.app
auth:
type: OIDC
result: output
- returnOutput:
return: ${output}
先ほど手動アクセスしたURLへアクセスするだけの、簡単なフローとなっています。
これをデプロイし、実行してみます。
無事、ステータス200で「Success!」が返ってきました。