こちらは、GitHub Actions Advent Calendar 2019 11日目の記事になります!
はじめに
ハンズラボ株式会社のyktakaha4です🍲
普段は、7日目に投稿しているhomines22さんらと、某小売のポイントシステムや、ECサイトの開発・運用に携わっています
当初弊社のアドベントカレンダーに2枠投稿するつもりで記事を書いていたのですが、参加者が増えて枠を譲ることとしたため、こちらに引っ越しました
今年ももうすぐ終わりですね...🎅
色々ありましたが、業務で大変だったのはやはり消費増税対応(もとい軽減税率対応)でした
機能変更がないため普段見ないフリ🙈をしているあのレガシーコードから、注文や商品といった業務の根幹に関わる大事なこのテーブルまで、あちらこちらに軽減税率区分
や税率別合計金額
といった項目を追加する...という対応で、
修正自体は軽微なのに対して業務影響度合いが大きく、小売に関わるシステムをご担当の諸氏にも頭を抱えた方がいらっしゃったものと思います
私個人としても、外部ECサイトとのデータ連携や、注文締め、出荷、お客様へのメール送信などといった様々な箇所に対して改修を行いましたが、
複数システム・機能間のフロント、API、バッチからデータベースのカラムまで横断的に影響調査を行うのは、中々骨の折れる作業でした
最初はVSCodeやGitHubの検索機能、grepなどで状況に応じて適当に調べていましたが、
作業の途中からローカル環境にLivegrepを導入したところ、調査の効率がだいぶ上がり、特に想定外のリポジトリやコードに影響が及んでいた...というケースに事前に気づくことができるようになりました
Livegrepはとてもいいツールでしたが、検索対象のリポジトリやインデクシングを自分で管理する必要があったり、アカウントによるアクセス制御などの機能がついておらず複数人で利用するには不便な部分があったので、
税対応が完了して最近やっと人心地ついたところで、改めてチームで全文検索の仕組みを手軽に共用できるよう整備することとしました
どんなものを作るか
今回は、アカウント管理やリポジトリの自動クローリングといった機能がついた全文検索システムであるSourcegraphのOSS版を使うことにしました
こちらの記事に非常によくまとまっていますが、runするだけで即座に利用できる便利なDockerイメージが公開されているので、こちらをAWSで運用します🐳
社内向けの用途でスケーリングも必要なく、かつインスタンス停止時にデータを残したかったので、
DockerがインストールされたEC2をCloudFormationで作成しようと思います
また、弊社の標準的な勤務時間は10時から19時なので、それ以外の時間帯はインスタンスを停止させ、なるべく低コストに運用できると良さそうです
ということで、アーキテクチャは以下のような形になりました🍠
CloudFormationのデプロイとEC2インスタンスの起動および停止はGitHub Actionsを用いて行います
本記事の執筆時点で、指定時間にインスタンスを立ち上げるにはCloudWatch EventsからLambdaを実行する必要があるようですが、
GitHub Actionsには所定のワークフローを定期実行する機能があるので、こいつでAWS CLIを呼び出すようにすれば手軽にインスタンスの起動・停止が実現できそうです
できたもの
会社で実際に使っているものは社内ネットワークでの利用を前提とした構成になっているので、
インターネットアクセスが可能な形に修正したものを以下リポジトリに公開しました
構築手順
まず、リポジトリをフォークします🍴
フォーク直後だとGitHub Actionsが有効にならないようなので、Actions
タブから有効化しておきます(スクショ撮り忘れた...)
Settings -> Secrets -> Add a new secret
で、以下の値を設定します
キー | 説明 |
---|---|
AWS_ACCESS_KEY_ID | AWSのアクセスキーID |
AWS_SECRET_ACCESS_KEY | AWSのアクセスキー |
AWS_DEFAULT_REGION | デプロイ先のリージョンap-northeast-1 など |
RESOURCE_NAME | AWS上でのリソース名sourcegraph-ec2 など |
AVAILABILITY_ZONE | デプロイ先のアベイラビリティゾーンap-northeast-1c など |
SUBNET_ID | デプロイ先のサブネットIDAVAILABILITY_ZONE 配下のサブネットを指定すること |
SECURITY_GROUP_ID_LIST | セキュリティグループID(複数指定時はカンマ区切り) TCP443と2633ポートを開放したものを指定 |
シークレットが指定できたら、なんでもよいのでmaster
ブランチにプッシュします
とりあえずなのでREADME.mdをいじります
適当に文面を編集の上、コミットします
この記事を書いていて思い至ったのですが、これだと横展開がしづらいので、タグ打ったらデプロイ...みたいな感じにした方がよいですね
あるいはワークフローの手動実行ができるとよいのですが...
master
ブランチが更新されると、CloudFormation上にRESOURCE_NAME
で指定したスタックが生成されます
on:
push:
branches:
- master
name: Deploy
jobs:
deploy:
name: Deploy to AWS
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Setup Python 3.7
uses: actions/setup-python@v1
with:
python-version: "3.7"
architecture: "x64"
- name: Install awscli
run: pip install --upgrade pip awscli
- name: Deploy CloudFormation Stack
run: ./scripts/deploy.bash
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
RESOURCE_NAME: ${{ secrets.RESOURCE_NAME }}
AVAILABILITY_ZONE: ${{ secrets.AVAILABILITY_ZONE }}
SUBNET_ID: ${{ secrets.SUBNET_ID }}
SECURITY_GROUP_ID_LIST: ${{ secrets.SECURITY_GROUP_ID_LIST }}
#!/bin/bash -eu
base_dir="$(cd "$(dirname $0)/.."; pwd)"
aws cloudformation deploy \
--stack-name "$RESOURCE_NAME" \
--template-file "$base_dir/aws/cfn.yml" \
--capabilities CAPABILITY_NAMED_IAM \
--no-fail-on-empty-changeset \
--parameter-overrides \
ResourceName="$RESOURCE_NAME" \
AvailabilityZoneName="$AVAILABILITY_ZONE" \
SubnetId="$SUBNET_ID" \
SecurityGroupIdList="$SECURITY_GROUP_ID_LIST"
インスタンスができたら、パブリックIPを確認し、接続してみます
Sourcegraphはソースコード検索用途のため、httpでは運用したくありません
幸いなことに、デフォルトで管理画面向けに自己署名のSSL証明書を発行する仕様となっているため、こいつを使ってhttps通信をおこなうこととします
https://インスタンスのパブリックIP/
に接続すると、以下メッセージが表示されます
接続できない場合は、セキュリティグループでTCP443ポートと2633ポートが空いていることを確認してください
https通信にあたり、証明書が不審のため接続を渋られている状況です
Google Chromeであれば保護されていない通信 -> 証明書(無効) -> 詳細な情報
と開いていくと、指紋
の項にフィンガープリントが表示されています
要はこの証明書が信頼できるものか判断できればいい...ということですが、
本リポジトリでは、インスタンス構築時に生成した証明書のフィンガープリントをCloudwatchLogsに吐き出すようにしています
Cloudwatch -> ロググループ -> /var/log/messages -> インスタンスID
を開いたのち、cert
でログをフィルタすると表示されますので、
値が一致することを確認し、問題なければ証明書の信頼設定を行います
Google Chrome上で表示されている証明書アイコンをデスクトップにドロップしてダウンロードの後、
ダブルクリックするとキーチェーンアクセスが表示されます
今回追加した証明書を右クリック -> 情報を見る -> 信頼 -> この証明書を使用するとき
で常に信頼
を選び、
ウィンドウを閉じる際にログインユーザのパスワードを入力すると、証明書が信頼されているものとして指定されます
再度先ほどのURLにアクセスすると、詳細設定を表示
を押した後の画面で、接続先へのアクセスが可能になっています
いい感じですね!
これ以降の設定は、先ほどのさくらのナレッジ様の構築手順のSourcegraphの初期設定
の項を参考に行ってください!
{
// The externally accessible URL for Sourcegraph (i.e., what you type into your browser)
// This is required to be configured for Sourcegraph to work correctly.
"externalURL": "https://ec2-52-194-175-117.ap-northeast-1.compute.amazonaws.com",
"auth.providers": [
{
"type": "builtin",
"allowSignup": false
}
]
}
運用
平日の8:30にインスタンスの自動起動と、毎日19:30にインスタンスの自動停止がおこなわれます🍜
on:
schedule:
# 日本時間で月〜金の毎日9時30分に実行
- cron: 30 0 * * SUN-THU
name: Start Instance
jobs:
start:
name: Start Instance
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Setup Python 3.7
uses: actions/setup-python@v1
with:
python-version: "3.7"
architecture: "x64"
- name: Install awscli
run: pip install --upgrade pip awscli
- name: Start Instance
run: ./scripts/instance.bash start
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
RESOURCE_NAME: ${{ secrets.RESOURCE_NAME }}
#!/bin/bash -eu
cmd="$1"
case "$cmd" in
"start")
instance_state="stopped"
instance_operation="start-instances"
;;
"stop")
instance_state="running"
instance_operation="stop-instances"
;;
* )
echo "invalid command: $cmd"
exit 1
;;
esac
instance_id="$(
aws ec2 describe-instances --filters "Name=tag:Name,Values=$RESOURCE_NAME" "Name=tag:AutoStart,Values=enabled" |
jq -r ".Reservations[].Instances[] | [.InstanceId, .State.Name] | select(.[1]==\"$instance_state\")[0]" |
head -1
)"
if [ -n "$instance_id" ]
then
aws ec2 $instance_operation --instance-ids "$instance_id" > /dev/null
echo "succeed: resource-name=$RESOURCE_NAME, command=$cmd"
else
echo "unfound: resource-name=$RESOURCE_NAME, state=$instance_state"
fi
自動起動・停止を止めたい場合は、EC2のコンソールからインスタンスのタグ設定を開き、AutoStart
キーの値をdisabled
など任意のものに変更してください
削除
デフォルトだとEC2インスタンスにt2.medium
を指定しているため、放っておくと課金されてしまいます
Sourcegraphを削除したい場合は、CloudFormationからスタックの削除を行ってください👋
おわりに
個人的にはスケジュールタスクはだいぶ強力な機能に感じます...
Dockerコンテナを任意の時刻に実行できる訳ですから、AWSをはじめとした各種クラウドサービスに対してちょっとしたバッチ処理をやりたい時にはうってつけの機能だなと思いました
ちなみに、GitHub Actionsよりも、EC2起動時のUserDataの設定が本記事の中で一番大変だったのですが、本旨から逸れるため割愛します💔