LoginSignup
2

More than 3 years have passed since last update.

SourcegraphをGitHub Actions+CloudFormationで構築・管理する

Last updated at Posted at 2019-12-10

こちらは、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時なので、それ以外の時間帯はインスタンスを停止させ、なるべく低コストに運用できると良さそうです

ということで、アーキテクチャは以下のような形になりました🍠

アーキテクチャ.png

CloudFormationのデプロイとEC2インスタンスの起動および停止はGitHub Actionsを用いて行います
本記事の執筆時点で、指定時間にインスタンスを立ち上げるにはCloudWatch EventsからLambdaを実行する必要があるようですが、
GitHub Actionsには所定のワークフローを定期実行する機能があるので、こいつでAWS CLIを呼び出すようにすれば手軽にインスタンスの起動・停止が実現できそうです

できたもの

会社で実際に使っているものは社内ネットワークでの利用を前提とした構成になっているので、
インターネットアクセスが可能な形に修正したものを以下リポジトリに公開しました:octocat:

構築手順

まず、リポジトリをフォークします🍴

image.png

フォーク直後だとGitHub Actionsが有効にならないようなので、Actionsタブから有効化しておきます(スクショ撮り忘れた...)

Settings -> Secrets -> Add a new secret で、以下の値を設定します

image.png

キー 説明
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 デプロイ先のサブネットID
AVAILABILITY_ZONE配下のサブネットを指定すること
SECURITY_GROUP_ID_LIST セキュリティグループID(複数指定時はカンマ区切り)
TCP443と2633ポートを開放したものを指定

シークレットが指定できたら、なんでもよいのでmasterブランチにプッシュします
とりあえずなのでREADME.mdをいじります

image.png

適当に文面を編集の上、コミットします

この記事を書いていて思い至ったのですが、これだと横展開がしづらいので、タグ打ったらデプロイ...みたいな感じにした方がよいですね
あるいはワークフローの手動実行ができるとよいのですが...

image.png

masterブランチが更新されると、CloudFormation上にRESOURCE_NAME で指定したスタックが生成されます

deploy.yml
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 }}
deploy.bash
#!/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"

image.png

image.png

インスタンスができたら、パブリックIPを確認し、接続してみます

image.png

Sourcegraphはソースコード検索用途のため、httpでは運用したくありません
幸いなことに、デフォルトで管理画面向けに自己署名のSSL証明書を発行する仕様となっているため、こいつを使ってhttps通信をおこなうこととします

https://インスタンスのパブリックIP/に接続すると、以下メッセージが表示されます
接続できない場合は、セキュリティグループでTCP443ポートと2633ポートが空いていることを確認してください

image.png

https通信にあたり、証明書が不審のため接続を渋られている状況です
Google Chromeであれば保護されていない通信 -> 証明書(無効)
-> 詳細な情報
と開いていくと、指紋の項にフィンガープリントが表示されています

image.png

要はこの証明書が信頼できるものか判断できればいい...ということですが、
本リポジトリでは、インスタンス構築時に生成した証明書のフィンガープリントをCloudwatchLogsに吐き出すようにしています

Cloudwatch -> ロググループ -> /var/log/messages -> インスタンスIDを開いたのち、certでログをフィルタすると表示されますので、
値が一致することを確認し、問題なければ証明書の信頼設定を行います

image.png

Google Chrome上で表示されている証明書アイコンをデスクトップにドロップしてダウンロードの後、
ダブルクリックするとキーチェーンアクセスが表示されます

image.png

今回追加した証明書を右クリック -> 情報を見る -> 信頼 -> この証明書を使用するとき常に信頼を選び、
ウィンドウを閉じる際にログインユーザのパスワードを入力すると、証明書が信頼されているものとして指定されます

image.png

再度先ほどのURLにアクセスすると、詳細設定を表示を押した後の画面で、接続先へのアクセスが可能になっています

image.png

いい感じですね!

image.png

これ以降の設定は、先ほどのさくらのナレッジ様の構築手順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にインスタンスの自動停止がおこなわれます🍜

start_instance.yml
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 }}
instance.bash
#!/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など任意のものに変更してください

image.png

削除

デフォルトだとEC2インスタンスにt2.medium を指定しているため、放っておくと課金されてしまいます
Sourcegraphを削除したい場合は、CloudFormationからスタックの削除を行ってください👋

image.png

おわりに

個人的にはスケジュールタスクはだいぶ強力な機能に感じます...
Dockerコンテナを任意の時刻に実行できる訳ですから、AWSをはじめとした各種クラウドサービスに対してちょっとしたバッチ処理をやりたい時にはうってつけの機能だなと思いました

ちなみに、GitHub Actionsよりも、EC2起動時のUserDataの設定が本記事の中で一番大変だったのですが、本旨から逸れるため割愛します💔

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2