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
220
モノレポの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
Claude 3.5 で Haiku
hmatsu47
PRO
0
8
HeatWave on AWS の PrivateLink インバウンドレプリケーションで Aurora フェイルオーバーに追従する
hmatsu47
PRO
0
8
大吉祥寺.pm の LT で ChatGPT の力を借りて Next.js App Router ベースの投句箱を作って、 Lambda Web Adapter を使って公開した話
hmatsu47
PRO
0
8
ある日突然 DB の性能が 1/2(サイズのインスタンス相当)になった話
hmatsu47
PRO
0
30
pgvectorscale と pgai の話(ざっくり)
hmatsu47
PRO
0
49
pgvector 0.7.0 の新機能と、これから来る(かもしれない)pgvectorscale
hmatsu47
PRO
0
35
大人の社会科見学 ~ NTT 技術史料館に行ってみよう!
hmatsu47
PRO
0
420
pgvector 0.6.0 以降の進化についてざっくり取り上げてみる
hmatsu47
PRO
0
64
Cloudflare Workes からMySQL 系 DB への接続事情(2024/4 現在)
hmatsu47
PRO
0
130
Other Decks in Technology
See All in Technology
Oracle Cloud Infrastructureデータベース・クラウド:各バージョンのサポート期間
oracle4engineer
PRO
28
13k
Amplify Gen2 Deep Dive / バックエンドの型をいかにしてフロントエンドへ伝えるか #TSKaigi #TSKaigiKansai #AWSAmplifyJP
tacck
PRO
0
390
複雑なState管理からの脱却
sansantech
PRO
1
150
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
0
130
オープンソースAIとは何か? --「オープンソースAIの定義 v1.0」詳細解説
shujisado
9
1.1k
Lambdaと地方とコミュニティ
miu_crescent
2
370
SREが投資するAIOps ~ペアーズにおけるLLM for Developerへの取り組み~
takumiogawa
1
380
B2B SaaSから見た最近のC#/.NETの進化
sansantech
PRO
0
860
Amazon CloudWatch Network Monitor のススメ
yuki_ink
1
210
Exadata Database Service on Dedicated Infrastructure(ExaDB-D) UI スクリーン・キャプチャ集
oracle4engineer
PRO
2
3.2k
rootlessコンテナのすゝめ - 研究室サーバーでもできる安全なコンテナ管理
kitsuya0828
3
390
SREによる隣接領域への越境とその先の信頼性
shonansurvivors
2
520
Featured
See All Featured
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
131
33k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
4
370
A Tale of Four Properties
chriscoyier
156
23k
Bash Introduction
62gerente
608
210k
How to train your dragon (web standard)
notwaldorf
88
5.7k
Six Lessons from altMBA
skipperchong
27
3.5k
The Power of CSS Pseudo Elements
geoffreycrofte
73
5.3k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
26
2.1k
Music & Morning Musume
bryan
46
6.2k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.2k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
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