Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
モノレポのGitHub(Enterprise)からCodePipelineを呼び出す小ネタ
Search
hmatsu47
PRO
February 23, 2022
Technology
0
250
モノレポのGitHub(Enterprise)からCodePipelineを呼び出す小ネタ
JAWS-UG 浜松 AWS 勉強会 2022#2 2022/2/25
hmatsu47
PRO
February 23, 2022
Tweet
Share
More Decks by hmatsu47
See All by hmatsu47
今年の FESTA で初当日スタッフ+登壇してきました
hmatsu47
PRO
0
6
攻略!Aurora DSQL の OCC(楽観的同時実行制御)
hmatsu47
PRO
0
4
PostgreSQL でもできる!GraphRAG
hmatsu47
PRO
0
3
Aurora DSQL のトランザクション(スナップショット分離と OCC)
hmatsu47
PRO
0
8
いろんなところに居る Amazon Q(Developer)を使い分けてみた
hmatsu47
PRO
0
25
「ゲームで体感!Aurora DSQL の OCC(楽観的同時実行制御)」の結果ログから Aurora DSQL の動作を考察する
hmatsu47
PRO
0
2
ゲームで体感!Aurora DSQL の OCC(楽観的同時実行制御)
hmatsu47
PRO
0
34
PostgreSQL+pgvector で GraphRAG に挑戦 & pgvectorscale 0.7.x アップデート
hmatsu47
PRO
0
48
LlamaIndex の Property Graph Index を PostgreSQL 上に構築してデータ構造を見てみる
hmatsu47
PRO
0
17
Other Decks in Technology
See All in Technology
All About Sansan – for New Global Engineers
sansan33
PRO
1
1.2k
AI時代、“平均値”ではいられない
uhyo
8
2.2k
AI-Readyを目指した非構造化データのメダリオンアーキテクチャ
r_miura
1
280
Node.js 2025: What's new and what's next
ruyadorno
0
1k
FinOps について (ちょっと) 本気出して考えてみた
skmkzyk
0
190
Introdução a Service Mesh usando o Istio
aeciopires
1
280
だいたい分かった気になる 『SREの知識地図』 / introduction-to-sre-knowledge-map-book
katsuhisa91
PRO
3
1.2k
[VPoE Global Summit] サービスレベル目標による信頼性への投資最適化
satos
0
210
「改善」ってこれでいいんだっけ?
ukigmo_hiro
0
400
Contract One Engineering Unit 紹介資料
sansan33
PRO
0
8.9k
JSConf JPのwebsiteをGatsbyからNext.jsに移行した話 - Next.jsの多言語静的サイトと課題
leko
2
180
Building a cloud native business on open source
lizrice
0
170
Featured
See All Featured
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
285
14k
Raft: Consensus for Rubyists
vanstee
140
7.2k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
34
2.3k
How STYLIGHT went responsive
nonsquared
100
5.8k
Testing 201, or: Great Expectations
jmmastey
45
7.7k
Optimizing for Happiness
mojombo
379
70k
A designer walks into a library…
pauljervisheath
209
24k
Agile that works and the tools we love
rasmusluckow
331
21k
How to Ace a Technical Interview
jacobian
280
24k
Six Lessons from altMBA
skipperchong
29
4k
Navigating Team Friction
lara
190
15k
Transcript
モノレポの GitHub (Enterprise) から CodePipeline を呼び出す小ネタ JAWS-UG 浜松 AWS 勉強会 2022#2 2022/2/25
まつひさ(hmatsu47)
自己紹介 松久裕保(@hmatsu47) • https://qiita.com/hmatsu47 • 現在のステータス: ◦ 名古屋で Web インフラのお守り係をしています
◦ Aurora MySQL v1(5.6)の EoL が発表されたのでアップ開始 ▪ https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aur ora.MySQL56.EOL.html • v1 → v3 移行を画策中 2
本日の小ネタ • こちら↓の Lambda 関数を正しく動くよう実装 ◦ GitHub モノレポを AWS CodePipeline
と統合して、プロジェクト固有の CI/CD パイプラインを実行する(Amazon Web Services ブログ) • Zenn で記事化済み ◦ https://zenn.dev/hmatsu47/articles/73c624fb5730dd • 参考にした記事 ◦ Backlogの課題にGitHubのコミットを連携する方法(ponsuke_tarou’s blog) 3
モノレポの課題 • 開発プロジェクト(プロダクト・サービス)別にビルド →デプロイするのに手間が掛かる ◦ 必要がなくても全プロジェクトをビルドパイプラインが走る • そこで提示されたのが前掲の記事 ◦ GitHub
モノレポを AWS CodePipeline と統合して、プロジェクト固有の CI/CD パイプラインを実行する(Amazon Web Services ブログ) ◦ ただし動作に問題がある 4
何が問題? • 対象ブランチの指定がない ◦ どこのブランチに push してもパイプラインが実行されてしまう • コードの変更以外の操作まで拾う ◦
誤動作の可能性がある • 複数フォルダにまたがる push でも、1 本のパイプライン しか実行されない 5
修正後は • 対象ブランチの指定が可能 • パイプライン実行対象のフォルダを指定可能 • 複数フォルダにまたがる push で複数のパイプラインを並 列呼び出し可能
• 例外的に全パイプラインを並列呼び出しするフォルダの 指定が可能 6
設定の流れ(詳細は前掲の Zenn 記事を参照) 1. Secrets Manager にシークレットを保存 2. Lambda 関数を作成
3. API Gateway を作成し、Lambda 関数を統合 4. IAM Role(Lambda 実行用)にポリシーを追加 5. GitHub (Enterprise) で Webhook を設定 6. CodePipeline を設定(変更) 7
1. Secrets Manager にシークレットを保存 • GitHub → (API Gateway →)Lambda
認証時に使用 ◦ パスワードジェネレータなどで生成 ◦ Secrets Manager で「その他のシークレットのタイプ」を選択 ▪ キー : GHE_SECRETS ▪ 値 : 生成したシークレットの値 ▪ 任意の名前を付けて保存 8
2. Lambda 関数を作成 • GitHub Webhook からのリクエストを受けて、対象の CodePipeline を呼び出す ◦
コードと↓の環境変数を登録 ▪ 呼び出すパイプライン名のサフィックス : job_name_suffix • 「【フォルダ名】+【サフィックス】」の CodePipeline を呼び出します ▪ シークレット名(先ほど保存したもの) : secrets_name ▪ パイプライン実行対象のブランチ名 :trigger_branch • 「refs/heads/【ブランチ名】」の形で指定 9
2. Lambda 関数を作成 import json import hmac, hashlib import boto3
import base64 import ast, re import os from botocore.exceptions import ClientError def lambda_handler(event, context): body = event['body'] if is_correct_signature(event['headers']['x-hub-signature'], body): print('認証成功') project_names = [] job_name_suffix = os.environ['job_name_suffix'] body_json = json.loads(body) ref = body_json['ref'] if ref == os.environ['trigger_branch'] and len(body_json['commits']) > 0: # 指定ブランチへのコミットの場合だけ処理 added_files = body_json['commits'][0]['added'] removed_files = body_json['commits'][0]['removed'] modified_files = body_json['commits'][0]['modified'] + added_files + removed_files print('added / removed / modified : {}'.format(modified_files)) # どのプロジェクトのビルドを行うかファイルパスから判断 includes = ['project1', 'project2', 'project3'] pipelines_count_max = len(includes) common = ['common'] 10
2. Lambda 関数を作成 for file_path in modified_files: pos = file_path.find('/')
if pos > 0: # パスにフォルダを含む→プロジェクト名を確認 project_name = file_path[:pos] if common.count(project_name) > 0: # 共有プロジェクト名であれば全て呼び出しパイプラインに含める project_names = includes break if project_names.count(project_name) == 0: # 対象プロジェクト初検出→呼び出しパイプラインに含める project_names.append(project_name) if len(project_names) == pipelines_count_max: # すべてのプロジェクトを検出→ループを抜ける break # 対象プロジェクトをビルドするパイプラインを呼び出す print('projects : {}'.format(project_names)) if len(project_names) > 0: for project_name in project_names: return_code = start_code_pipeline('{}{}'.format(project_name, job_name_suffix)) print(return_code) return { 'statusCode': 200, 'body': json.dumps('Modified project in repo: {}'.format(project_names)) } 11
2. Lambda 関数を作成 def get_secrets_manager_dict(secret_name: str) -> dict: """Secrets Managerからシークレットのセットを辞書型で取得する"""
secrets_dict = {} if not secret_name: print('シークレットの名前未設定') else: session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name='ap-northeast-1' ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) except ClientError as e: print('シークレット取得失敗:シークレットの名前={}'.format(secret_name)) print(e.response['Error']) else: if 'SecretString' in get_secret_value_response: secret = get_secret_value_response['SecretString'] else: secret = base64.b64decode(get_secret_value_response['SecretBinary']) secrets_dict = ast.literal_eval(secret) return secrets_dict 12
2. Lambda 関数を作成 def get_secrets_manager_key_value(secret_name: str, secret_key: str) -> str:
"""AWS Secrets Managerからシークレットキーの値を取得する.""" value = '' secrets_dict = get_secrets_manager_dict(secret_name) if secrets_dict: if secret_key in secrets_dict: # secrets_dictが設定されていてsecret_keyがキーとして存在する場合 value = secrets_dict[secret_key] else: print('シークレットキーの値取得失敗:シークレットの名前={}、シークレットキー={}'.format(secret_name, secret_key)) return value def is_correct_signature(signature: str, body: dict) -> bool: """GitHubから送られてきた情報をHMAC認証する.""" if signature and body: # GitHubのWebhookに設定したSecretをSecrets Managerから取得する secret = get_secrets_manager_key_value(os.environ['secrets_name'], 'GHE_SECRETS') if secret: secret_bytes = bytes(secret, 'utf-8') body_bytes = bytes(body, 'utf-8') # Secretから16進数ダイジェストを作成する signedBody = "sha1=" + hmac.new(secret_bytes, body_bytes, hashlib.sha1).hexdigest() return signature == signedBody else: return False 13
2. Lambda 関数を作成 def start_code_pipeline(pipelineName): client = codepipeline_client() response =
client.start_pipeline_execution(name=pipelineName) return True cpclient = None def codepipeline_client(): global cpclient if not cpclient: cpclient = boto3.client('codepipeline') return cpclient 14 • こちらで公開中 ◦ https://github.com/hmatsu47/github-monorepo-codepipeline
3. API Gateway を作成し、Lambda 関数を統合 • HTTP の API Gateway
を作成 ◦ 先ほど作成した Lambda 関数を統合 ◦ 任意の API 名を指定 ◦ ルートのメソッドは POST に限定 ◦ ステージ名「$default」のまま自動デプロイ指定で作成 15
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow",
"Action": [ "secretsmanager:GetResourcePolicy", "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret", "secretsmanager:ListSecretVersionIds" ], "Resource": "【シークレットのARN】" }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": [ "secretsmanager:GetRandomPassword", "secretsmanager:ListSecrets" ], "Resource": "*" } ] } 4. IAM Role(Lambda 実行用)にポリシーを追加 16
5. GitHub (Enterprise) で Webhook を設定 • ↓を指定して作成 ◦ Payload
URL :API Gateway の URL ▪ https://XXX.execute-api.ap-northeast-1.amazonaws.com/【リソースパス】 ◦ Content type :application/json ◦ Secret :生成したシークレット 17
6. CodePipeline を設定(変更) • Source ステージで↓のチェックを外す ◦ 検出オプションを変更する ▪ ソースコードの変更時にパイプラインを開始する
18
やってみた感想など • 意外と面倒 ◦ 情報があまり出回っていない ◦ 本当に大変なのは GitHub Webhooks 〜
Lambda よりも CodePipeline 側 ◦ GitHub Actions でやったほうが… • 事情で GitHub Actions が使えない場合の代替策 19