CUEBiC TEC BLOG

キュービックTECチームの技術ネタを投稿しております。

EC2上で動かしていたDatadog監視をECSクラスター上に構築してみました

概要

こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。
前回の記事で弊社のモニタリング運用をDatadogに集約したことを紹介しました。OpenSearch時代に活用していた集約サーバーのFluentdの設定ファイルをDatadogに向き先を変えて、さらにDatadogエージェントをインストールして、集約サーバーから弊社メディアへの外形監視とDBのパフォーマンスモニタリングを実施しています。

アーキテクチャ

アーキテクチャ

課題

ただ単なる集約サーバーだったものにDatadogエージェントを導入して、外形監視も担うとなるとサーバー運用が必要となり、負荷が生じてきました。
そこでEC2ではなく、ECSに移行してDatadogモニタリングクラスターを構築してみることにしました。

アーキテクチャ

アーキテクチャ

FargateのアーキテクチャはFluentdのECSサービスとDatadogのECSサービスを組み合わせたクラスタ構成になります。

Fluentdコンテナ構築

図では省略していますが、FluentdのECSに転送する際にメディアサーバー群とFargateの間にNLBを置いています。これによってIPアドレス単位で集約先を指定したものをNLBのドメイン単位で宛先を指定することが可能になります。

# fluentd設定ファイル(メディアサーバー側)
<source>
  @type tail
  path /var/log/nginx/access.log
  format ltsv
  time_format %d/%b/%Y:%H:%M:%S %z
  types size:integer,status:integer,upstream_response_time:float,time:time,response_time:float
  tag nginx
</source>
<match nginx>
  @type forward
  <server>
#    host XXX.XXX.XXX.XXX  #IPアドレス
    host XXXXXXXXXXXXXXX.elb.ap-northeast-1.amazonaws.com
    port XXXXXXX
  </server>
</match>
# fluentd設定ファイル(fluentd ECS側)
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<match nginx>
  @type copy
  <store>
    @type datadog
    @id nginx-access
    api_key "#{ENV['ddapikey']}"  #fluentd設定ファイル内の環境変数設定

    include_tag_key true
    tag_key @log_name

    dd_source 'nginx'
    service   'access-log'
  </store>
</match>

幸いなことにFluentdのDockerイメージは公式がDocker Hubに用意していました。後は先のfluentd設定ファイルをコンテナにコピーするDockerfileを書いてECSにデプロイします。

# Fluentdコンテナ
FROM fluent/fluentd:v1.15.3-debian-1.0
USER root

RUN buildDeps="sudo make gcc g++ libc-dev" \
    && apt-get update && apt-get upgrade -y \
    && apt-get install -y --no-install-recommends $buildDeps \
    && sudo gem install fluent-plugin-datadog \
    && sudo gem sources --clear-all \
    && SUDO_FORCE_REMOVE=yes \
       apt-get  purge -y --auto-remove \
               -o APT::AutoRemove::RecommendsImportant=false \
               $buildDeps \
    && rm -rf /var/lib/apt/lists/* \
    && rm -rf /tmp/* /var/tmp/* /usr/lib/ruby/gems/*/cache/*.gem

COPY  fluentd/etc/fluent.conf /fluentd/etc/

USER fluent

ちなみにFluentdの設定ファイルにDatadogのAPIキーを埋め込む場合、ECSタスク定義から環境変数を設定しますが、認証情報系は直打ちせずにSecrets Manager*1やSystems Managerのパラメータストア*2から呼び出すようにしておいたほうが安全です。

"secrets": [
    {
        "name": "ddapikey",
        "valueFrom": "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:parameter/datadog/apikey"
    }
]

Datadogエージェントコンテナ構築

Datadogエージェントも公式がコンテナイメージを配布しているので、必要な環境変数をセットしてECSサービスに組み込みました。

# Datadogエージェントコンテナ
FROM public.ecr.aws/datadog/agent:latest

USER dd-agent
RUN mkdir /etc/datadog-agent/secrets

# HTTPチェック設定ファイル配置
COPY etc/datadog-agent/conf.d/http_check.d/conf.yaml /etc/datadog-agent/conf.d/http_check.d/conf.yaml

# DB Application Performance Monitoring設定ファイル&DBパスワード情報配置(db_password情報は暗号化)
COPY etc/datadog-agent/conf.d/mysql.d/conf.yaml /etc/datadog-agent/conf.d/mysql.d/conf.yaml
COPY etc/datadog-agent/secrets/db_password /etc/datadog-agent/secrets/db_password

http_check.d/conf.yamlの中に監視したいメディアの情報を記載しています。

# コーポレートサイト
  - name: cuebic website
    url: https://cuebic.co.jp/
    timeout: 5
    tags:
        - "env:prd"

実は当初DatadogエージェントのコンテナはApp Runner*3を使って実装してみようと思いました。App RunnerならECRにコンテナイメージをセットして指定するだけで自動デプロイの実装までマネージドにやってくれるので、新規メディアへの外形監視作業も先述のHTTPチェックの設定ファイルを更新し、ECRにプッシュするだけで自動的に監視されて運用が楽になるからだと考えました。
ところがApp RunnerではDatadogエージェントのようなコンテナからのプッシュ型通信とは相性が悪くうまくいかないことが判明しました。このあたりの詳細はこちらのブログを御覧ください。 zenn.dev

妥協案としては自前でGitHub Actions☓ECSの自動デプロイ基盤を実装することで解決しました。ECSへデプロイしてくれるGitHub Actionsワークフローファイルは公式が用意してくれているのでそちらを活用します。*4

GitHub Actions☓ECS自動デプロイアーキテクチャ

デプロイアーキテクチャ

これでHTTPチェック設定ファイルに新規メディア情報を記載してGitHubリポジトリにプッシュすることでGitHub Actionsが起動し、リポジトリ内のDockerfileをビルド、プッシュしてECRに格納されます。その後ECRのイメージを基にFargateへデプロイすることでECSによるDatadogモニタリングクラスタ基盤が構築できます。

所感

EC2上で動かしていたDatadog監視をECSへ移行できました。ガッツリとECSクラスタをイチから構築するのは今回が初めてで、セキュリティ設定ミスやネットワーク設定の不備など不慣れから生じるミスが多かったですが、最終的に実現したいことができてよかったと思います。App Runnerを使った挑戦は残念ながらできませんでしたが、違う機会で再挑戦してみようと思います。

デブサミ2023の登壇資料の構成をデータ分析で作成してみた_その2

キュービックでテックリードをやっている尾﨑です。Developers Summit 2023にCTO加藤と以下のテーマで登壇します。

2023年2月10日(金)11:50 : データウェアハウス構築時のアンチパターンを克服したサクセスストーリー

  • 登壇にあたって、単にやったことを発表したのでは面白くない
  • 何か今まで学んだデータサイエンスを活かしつつ登壇資料を作れないだろうか?

ということで前回は、登壇資料の構成をデータ分析でやったら面白いのでは?という仮説の基、 プロセス1〜3を紹介しました。

今回は続きの4から見ていきます。

1.企画のスコープを決める
2.検討/導入/運用/改善の4工程のイベントを洗い出す
3.工程ごとのデータの散らばりを観測する
4.データを可視化する★
5.企画で取り上げるべきイベントを可視化する
4.データを可視化する

前回は「3.工程ごとのデータの散らばりを観測する」でデータの散らばりを散布図で観測するところまで実施しました。

このままでは分かりにくいので、もう少し見やすくします。

導入工程にフォーカスすると、 「要件、人、インフラ、プロセス」に散らばりが見られました。 これはサービス選定時に重視していたポイントと一致します💡

運用工程にフォーカスすると、 「コード、設計、欠陥、ビルド」に散らばりが見られました。 これは既存の運用で認識していた負債や課題と言えるでしょう💡

改善工程にフォーカスすると、 「アーキテクチャー、サービス」に散らばりが見られました。 これは導入時に重視したポイントと、既存の運用課題を鑑みて改善を行った点が考えられます💡

5.企画で取り上げるべきイベントを可視化する

今度は企画の4象限の③④に該当するデータを4.とマージして 工程別に取り上げるべきデータを可視化します。

結果としてはこのようになりました。

これで登壇で取り上げる各工程のテーマが判明しました。 いかがでしたか?今回は登壇資料の構成を分析でやってみました。 当日はこちらで分析した内容をもとに作成した内容を発表します。

最後にアーキテクチャーをちょっとだけお見せします

面白そう!と思ったらエントリーしてもらえると嬉しいです。 対象のエンジニアはデータエンジニア、データサイエンティスト、バックエンドエンジニア、データベースアドミニストレータ、Webアプリケーションエンジニアとなります。是非エントリーお願いします。

2023年2月10日(金)11:50 : データウェアハウス構築時のアンチパターンを克服したサクセスストーリー


speakerdeck.com


尾﨑 勇太(おざき ゆうた)

プログラミング未経験から金融系のSierを3年経て、教育関係のWEBベンチャーを1年、品質管理を3年、キュービック(現在)と業界歴9年目。 結婚・子育てで開発を離れていたが、自社開発をしたいという思いが募り、キュービックに参画。 残される側の立場を複数社で経験し、技術負債の返済と再構築を実施してきたためマイナスからゼロ。ゼロからイチが得意。 現在はDWHの基盤構築をしながら新規プロダクトのデータ分析などを中心に実施。

デブサミ2023の登壇資料の構成をデータ分析で作成してみた_その1

キュービックでテックリードをやっている尾﨑です。Developers Summit 2023にCTO加藤と以下のテーマで登壇します。

2023年2月10日(金)11:50 : データウェアハウス構築時のアンチパターンを克服したサクセスストーリー

  • 登壇にあたって、単にやったことを発表したのでは面白くない
  • 何か今まで学んだデータサイエンスを活かしつつ登壇資料を作れないだろうか?

ということで今回は、登壇資料の構成をデータ分析でやったら面白いのでは?という仮説の基、 構成を考えたプロセスをご紹介します。

以下の流れで実施しました。

1.企画のスコープを決める
2.検討/導入/運用/改善の4工程のイベントを洗い出す
3.工程ごとのデータの散らばりを観測する
4.データを可視化する
5.企画で取り上げるべきイベントを可視化する
1.企画のスコープを決める

以前、採用施策を企画した際に活用させていただいたマケフリさんの4象限を使用。

makefri.jp

ここで、私なりに仮説を立てました。

結果、スコープはいかに限定して進めました。

③正解がないが、自分が体験したこと
④正解があり、自分が実践していること(あれば)
2.検討/導入/運用/改善の4工程のイベントを洗い出す

イメージのように以下の項目をそれぞれ洗い出してマッピングしました。

フロー
問題となった種別
問題の原因となった種別
企画の4象限
工程の4象限

ちなみに問題となった種別と問題の原因となった種別に関しては、技術負債の洗い出しマニュアルを 部署内で作成して運用を開始したので、技術負債の13の種類を応用しました。

3.工程ごとのデータの散らばりを観測する

データが洗い出せたので、工程ごとにデータの散らばり具合をみていきます。

工程は以下の不確実性とコストを4象限で表すことにします。

ここに、2で洗い出した結果を重み付けして散布図で表すと以下のようになりました。

※母集団とのずれを小さくするためにコストと不確実性の重みの平均を軸で表現しています。

導入/運用フェーズにデータのばらつきが見られますね。 既存システムの運用課題に対しての導入検討と導入フェーズでの課題が見えてきました。 さぁここからどのように分析していくのか。 続きは、その2でお伝えしたいと思います。

面白そう!と思ったらエントリーしてもらえると嬉しいです。 対象のエンジニアはデータエンジニア、データサイエンティスト、バックエンドエンジニア、データベースアドミニストレータ、Webアプリケーションエンジニアとなります。是非エントリーお願いします。

2023年2月10日(金)11:50 : データウェアハウス構築時のアンチパターンを克服したサクセスストーリー


speakerdeck.com


尾﨑 勇太(おざき ゆうた)

プログラミング未経験から金融系のSierを3年経て、教育関係のWEBベンチャーを1年、品質管理を3年、キュービック(現在)と業界歴9年目。 結婚・子育てで開発を離れていたが、自社開発をしたいという思いが募り、キュービックに参画。 残される側の立場を複数社で経験し、技術負債の返済と再構築を実施してきたためマイナスからゼロ。ゼロからイチが得意。 現在はDWHの基盤構築をしながら新規プロダクトのデータ分析などを中心に実施。

OpenSearchの古いインデックスを定期的に削除できるようにした

概要

前回メディアのログをOpenSearchに集約させたことをお話しました。

cuebic.hatenablog.com

このとき課題としてストレージに上限があり、溜まり続けると新しいログが転送できないという問題がありました。
OpenSearchAPIを叩くことで削除することが可能ですが、手動でAPIリクエストを送るのは面倒です。

curl  -XDELETE https://endpoint.com/<index>

今回EventBridge + Lambdaで自動で古いインデックスを削除できるようにしましたので、紹介いたします。
(シェルスクリプトのままLambdaに実装もできなくはないですが、他のエンジニアにも読めるように見よう見まねでPythonで書いてみました)

アーキテクチャ構成図

アーキテクチャ

かなりシンプルなアーキテクチャ構成図ですが使っているものは実際にこれだけです。
EventBridgeが毎日深夜1時にトリガーをかけてLambdaを実行させます。

Lambdaソースコード

import requests
import json
import os
import boto3
from datetime import date,timedelta
import datetime
import calendar
from requests.exceptions import Timeout

def lambda_handler(event, context): 
    
    #パラメーターストアからOpenSearch 認証情報を取得
    ssm = boto3.client('ssm','ap-northeast-1')
    OpenSearch_user     = ssm.get_parameter(
        Name='/opensearch/cuebic-media-log/user',
        WithDecryption=True
    )["Parameter"]["Value"]
    
    OpenSearch_password = ssm.get_parameter(
        Name='/opensearch/cuebic-media-log/pass',
        WithDecryption=True
    )["Parameter"]["Value"]
 
    # 過去日付情報取得
    ut = datetime.datetime.now()
    ago_30 = ut-timedelta(days=30)
    ago_90 = ut-timedelta(days=90)
    
    # 対象URLリスト
    index_list = [ ~~~~~~ ]

    # タイムアウト値設定(コネクションタイムアウト3.0秒、リードタイムアウト7.5秒)
    connect_timeout = 3.0
    read_timeout    = 7.5
    try:
        for index in index_list:
            #30日過ぎたログをクローズさせる
            response = requests.post('https://endpoint.com/'+index+ago_30.strftime('%Y%m%d')+'/_close', auth=(OpenSearch_user, OpenSearch_password), timeout=(connect_timeout, read_timeout))
            #90日すぎたログを削除する
            response = requests.delete('https://endpoint.com/'+index+ago_90.strftime('%Y%m%d'), auth=(OpenSearch_user, OpenSearch_password), timeout=(connect_timeout, read_timeout))

        
    except Timeout:
        print('Timeout')
        return

    except:
        print('Unkown exception')
        return
    
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps({
            "message": response.text
        })
    }

ソースコード解説

LambdaがOpenSearchへアクセスする際に使用する認証情報はSystems Managerのパラメーターストアを利用しています。

Systems Manager パラメーターストア
Secrets Managerと違ってパラメーターのローテーション機能はありませんが、無料で使えるので今回はこちらを採用しました。

    ssm = boto3.client('ssm','ap-northeast-1')
    OpenSearch_user     = ssm.get_parameter(
        Name='/opensearch/cuebic-media-log/user',
        WithDecryption=True
    )["Parameter"]["Value"]

各種ログのインデックス名には日付を付けていますので、インデックス名の日付が30日以上前のものはクローズにし画面上から検索できないようにする。90日以上前のものはOpenSearchから完全に削除するようにしています。

基本的にインデックス名はログ名+yyyymmddでしたので、ログ名部分を配列変数にしてfor構文でAPIを実行するようにしています。

課題

今の所うまくいっているので問題にはなっていませんが、メッセージ出力が最後に実行された responseに対する結果のみを出力しますのでもし実行に失敗した場合のログ調査ができない問題があります。

    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps({
            "message": response.text #リストの最後に格納されているインデックス削除の結果しか出力されない
        })
    }

ログを配列に格納すれば解決できそうな気がしますので、Pythonに詳しい人に相談してみようと思います。

所感

OpenSearchに溜まり続けるインデックスの削除をEventBridge + Lambdaで自動化してみました。OpenSearchの料金は事前に確保したEBSのストレージサイズに応じてコストが上がるので、事前確保分を抑えて定期的に削除する運用にすれば良い改善になるのではないかなと思います。

おまけ

curlAPIを実行する方法をどうやってPythonで実行すればいいのか最初わからなかったですが、こちらのコンバーターサイトを使えばcurlコマンドの内容をGoやPythonに変換してくれるのでプログラミングの苦手な私には重宝しました。みなさんも使ってみてください。

curlconverter.com

Terraformのdynamicブロックを使って動的にリソースパラメーターを分けてみた

概要

こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。
以前弊社でのTerraformの取り組みについて紹介しました。

cuebic.hatenablog.com

この中で環境によってリソース数の差分が生じる、例えば本番環境なら冗長化のためNAT Gatewayを3台稼働させるが、検証環境ならコスト最適化のため1台のみ稼働させると言ったケースではfor_eachを使って動的にリソース数を管理する方法について紹介しました。
ではAWS CodePipelineで本番環境のみにApprovalステージを追加したいというような要望のときはどうすればよろしいでしょうか🤔

本番環境ではデプロイ直前に承認作業がある

CodePipelineのステージはパラメーターの一つで複数行に渡り記述されるのでfor_eachで分岐するとなるとうまくいきません。

# CodePipeline作成Terraform(一部パラメーター省略)
resource "aws_codepipeline" "example-pipeline" {
  artifact_store {
    location = var.s3-location
    type     = "S3"
  }
  name     = "pipeline-name"

  # ソースステージ
  stage {
    name = "Source"
    action {
      category = "Source"

      configuration = {
        Branch               = "main"
        Owner                = "master"
        PollForSourceChanges = "false"
        Repo                 = "pipeline-repo"
      }

      name             = "Source"
      namespace        = "SourceVariables"
      output_artifacts = ["SourceArtifact"]
      owner            = "ThirdParty"
      provider         = "GitHub"
      region           = "ap-northeast-1"
      run_order        = "1"
      version          = "1"
    }
  }

  # ビルドステージ
  stage {
    name = "Build"

    action {
      category = "Build"

      configuration = {
        ProjectName = "build-project"
      }

      input_artifacts  = ["SourceArtifact"]
      name             = "Build"
      output_artifacts = ["BuildArtifact"]
      owner            = "AWS"
      provider         = "CodeBuild"
      region           = "ap-northeast-1"
      run_order        = "1"
      version          = "1"
    }
  }

  # 承認ステージ(本番環境のみ作成)
  stage {
      name = "Confirm"
      action {
        category  = "Approval"
        name      = "RequestApprove"
        owner     = "AWS"
        provider  = "Manual"
        region    = "ap-northeast-1"
        run_order = "1"
        version   = "1"
      }
  }

  # デプロイステージ
  stage {
    name = "Deploy"

    action {
      category = "Deploy"


      input_artifacts = ["BuildArtifact"]
      name            = "Deploy"
      owner           = "AWS"
      provider        = "ECS"
      region          = "ap-northeast-1"
      run_order       = "1"
      version         = "1"
    }
  }
}

承認ステージ部分が本番環境だけに必要なステージです。ここだけを環境ごとに分割したいという状況は一般的にあると思います。
この問題を解決してくれるのがdynamicブロックとよばれるものです。今回このdynamicブロックを用いて環境毎に異なるリソースパラメータの動的作成について紹介します。

dynamicブロックについて

www.terraform.io

dynamicはブロック単位のループ処理を実装したいときに使います。
例えばセキュリティグループの作成でdynamicブロックを使ったとします。

# セキュリティグループ作成
locals {
  ingress_web = [
  # [description, from_port, to_port, protocol, cidr_blocks]
    ["HTTPS from VPC", 443, 443, "tcp", "10.0.0.0/8"],
    ["HTTP from VPC", 80, 80, "tcp", "192.168.0.0/16"],
  ]
}

resource "aws_security_group" "web" {
  name        = "allow-web"
  vpc_id      = aws_vpc.main.id

  dynamic "ingress" {
    for_each = local.ingress_web
    content {
      description = ingress.value[0]
      from_port   = ingress.value[1]
      to_port     = ingress.value[2]
      protocol    = ingress.value[3]
      cidr_blocks = ingress.value[4]
    }
  }

  egress = [
    {
      from_port        = 0
      to_port          = 0
      protocol         = "-1"
      cidr_blocks      = ["0.0.0.0/0"]
    }
  ]

  tags = {
    Name = "allow-web"
  }
}

ここではingressルールでHTTP通信とHTTPS通信の許可設定を付与する箇所でdynamicブロックを使っています。ingressのパラメータはポートが異なっても設定項目は同じなので設定値をingress_webという変数でまとめればingressの記述回数が一回で済みます。
これを応用してdynamicでループするかどうかをTrue or Falseで分岐するようにすれば本番環境のみに承認ステージを追加することができます。

dynamicブロックによる分岐設定

先程のTerraformファイルを以下のように修正します。

# CodePipeline作成Terraform(一部パラメーター省略)
resource "aws_codepipeline" "example-pipeline" {
  artifact_store {
    location = var.s3-location
    type     = "S3"
  }
  name     = "pipeline-name"

  # ソースステージ
  stage {
    name = "Source"
    action {
      category = "Source"

      configuration = {
        Branch               = "main"
        Owner                = "master"
        PollForSourceChanges = "false"
        Repo                 = "pipeline-repo"
      }

      name             = "Source"
      namespace        = "SourceVariables"
      output_artifacts = ["SourceArtifact"]
      owner            = "ThirdParty"
      provider         = "GitHub"
      region           = "ap-northeast-1"
      run_order        = "1"
      version          = "1"
    }
  }

  # ビルドステージ
  stage {
    name = "Build"

    action {
      category = "Build"

      configuration = {
        ProjectName = "build-project"
      }

      input_artifacts  = ["SourceArtifact"]
      name             = "Build"
      output_artifacts = ["BuildArtifact"]
      owner            = "AWS"
      provider         = "CodeBuild"
      region           = "ap-northeast-1"
      run_order        = "1"
      version          = "1"
    }
  }

  # dyanmicを追加、論理型変数でTrue時のみリソースを作成する
  dynamic "stage" {
    for_each = var.production_approval ? [1] : []
    content {
      name = "Confirm"
      action {
        category  = "Approval"
        name      = "RequestApprove"
        owner     = "AWS"
        provider  = "Manual"
        region    = "ap-northeast-1"
        run_order = "1"
        version   = "1"
      }
    }
  }

  # デプロイステージ
  stage {
    name = "Deploy"

    action {
      category = "Deploy"


      input_artifacts = ["BuildArtifact"]
      name            = "Deploy"
      owner           = "AWS"
      provider        = "ECS"
      region          = "ap-northeast-1"
      run_order       = "1"
      version         = "1"
    }
  }
}

dynamicブロックを追加し、production_approvalという変数がTrueの時リソースを一回作成するように設定します。
production_approvalはTrue/Falseの論理型変数なので、variables.tfには以下のように記載します。

# variables.tf
variable "production_approval" {
  type        = bool
  default     = false
  description = "本番環境のみにApprovalステージを設定するためのif分岐"
}

本番環境のterraform apply実行先のCodePipelineモジュールファイルにtrueを加えます。

module "cba-codepipeline" {
  source              = "../../modules/codepipeline

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   
  # trueとすることでfor_eachで承認ステージを作成する
  production_approval = true
}

これによって本番環境では承認ステージ付きのCodePipelineが作成され、検証環境では承認ステージなしのCodePipelineを作成できます。
(注:production_approvalのデフォルト値をfalseにしているので検証環境に変数をセット不要です)

所感

dynamicブロックを使ってCodePipelineの承認ステージの作成可否を環境ごとに分岐できるようにしました。
Moduleで汎用化する際、パラメータ単位でリソースを作り分けたいというケースもあると思います。そうしたケースではdynamicブロックを使って環境別の条件別分岐を実装すると良いと思います。
みなさんもぜひ試してみてください。

参考文献

future-architect.github.io

stackoverflow.com

kleinblog.net

関連記事

Terraformに関連する記事はこちらもご覧ください。

cuebic.hatenablog.com

herp.careers

スイッチURLの管理からサヨウナラ、AWS Extend Switch Rolesを使ってみた

概要

こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。
以前弊社で運用しているAWSマルチアカウントについて記事にしました。

cuebic.hatenablog.com

この時、複数のアカウントへのスイッチURLを管理しようとすると結構管理が面倒になるときがあります。ロール履歴では最大5つまでのスイッチ履歴が保存されるので5個までのAWSアカウントでしたら問題はありませんが、それ以上増えるとなるとスイッチ先のAWSアカウントとIAMロールARNを指定する必要があります。

スイッチURLをドキュメントに一覧化してスイッチしたいアカウントのURLを参照するという方法もありますが、毎回ドキュメントページまで行くのが個人的にはおっくうだと思っています。何か方法はないかなと思い調べてみましたが、AWS Extend Switch RolesというGoogle Chromeアプリを使うことでスイッチURLを簡単にできることがわかりましたので、AWS Extend Switch Rolesを使ったスイッチURL管理について紹介いたします。

AWS Extend Switch Roles

chrome.google.com

上記のChrome ウェブストアからAWS Extend Switch Rolesをインストールします。
インストール後、アドオンに追加されていますのでConfiguration画面へ移行します。

Configuration画面では、~/.aws/configファイルで記述するするprofileブロックと同様にスイッチ先のアカウントIDやスイッチIAMロール名、移行先のデフォルトリージョンなどを指定します。

全部モザイクですとわかりにくいのでサンプルコードを置いておきます。

[SwitchA]
aws_account_id = 012345678910
role_name = SwitchARole
region = ap-northeast-1
color = 6f1603

[SwitchB]
aws_account_id = 123456789100
role_name = SwitchBRole
region = ap-northeast-1
color = 220259

[SwitchC]
aws_account_id = 234567891001
role_name = SwitchCRole
region = ap-northeast-1
color = f7efee

[SwitchD]
aws_account_id = 345678910012
role_name = SwitchDRole
region = ap-northeast-1
color = d29665

color部分はカラーコードを記載すれば色の変更ができますので、画面下から好みの色のカラーコードを生成してペーストしてください。

AWSコンソール画面でExtend Switch Rolesのアイコンをクリックするとスイッチロールリストが表示されます。

所感

AWS Extend Switch Rolesを使ったスイッチURLの切替方法について紹介しました。最初は一つのAWSアカウントからプロダクトを始めても事業が成長すると複数のAWSアカウントを利用するようになると思います。
そのときにこのアカウントのスイッチURLってなんだっけ?切り替えの煩わしさを少しでも減らせれば開発効率も上がると思います。Chromeアプリ版を紹介しましたが、Firefoxアドオン版もありますのでFirefoxユーザーも気になりましたらぜひチェックしてみてください。

addons.mozilla.org

参考文献

https://dev.classmethod.jp/articles/try-aws-extend-switch-roles-v2-pre-release/
https://dev.classmethod.jp/articles/introduction-aws-extend-switch-role/

ターミナルで完結!便利なGit運用 Tipsを紹介してみた

概要

こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。
皆さんはGit開発する際、ターミナルでgitコマンド実行後GitHubのサイトを訪れてPRしマージされますでしょうか。そしてマージした後またローカルに戻ってgit pullでローカルブランチも最新にしていますか🤔
せっかくターミナルで操作しているのに途中でブラウザ操作が入るのは個人的には面倒だと感じています。git pushした後の操作も全てターミナルで完結すればブラウザ切り替えする手間も減らせて煩わしさが減ると思いませんか。
ローカルのリポジトリもバラバラにgit clone するとあのリポジトリどこだっけ?となり探すのがこれまた面倒になります。
今回GitHub上の操作をターミナルで実現できるGitHub CLIコマンドの紹介とghq、pecoを使った快適なリポジトリ管理を紹介していきます。

GitHub CLIについて

cli.github.com

GitHub CLIは2020年にバージョン1系がリリースされ、昨年8月にバージョン2系がリリースされたGitHub操作をターミナル上で実現するためのOSSになります。
GitHub CLIをインストールすることで前述のgit push以降のGitHub操作をターミナルから操作できるようになりシームレスなGit運用ができるようになります。バージョン2では拡張コマンド機能が追加され、よりカスタマイズされた操作ができるようになりました。(今回拡張コマンドを使うことはないので詳細な說明は割愛いたします)

インストール

概要はこのくらいにして早速インストールしていきます。MacでしたらHomebrewからインストールできます。

brew install gh

その他にも多くのパッケージマネージャ経由でインストールできますので、GitHubのREADMEページをご覧ください。

github.com

初期設定

インストールできましたらGitHub CLIの認証を行います。 SSHGitHubに接続されていましたらローカルのSSH秘密鍵GitHub CLIに認証させます。HTTPで接続されていましたらトークン値を貼り付けましょう。

gh auth login
$ gh auth login
? What account do you want to log into? GitHub.com
? What is your preferred protocol for Git operations? SSH
? Upload your SSH public key to your GitHub account? Skip
? How would you like to authenticate GitHub CLI? Paste an authentication token
Tip: you can generate a Personal Access Token here https://github.com/settings/tokens
The minimum required scopes are 'repo', 'read:org'.
? Paste your authentication token:
~~~~~~省略~~~~~~

認証が通りましたらGitHubアカウントとしてGitHub CLIが使えるようになります。

$ gh auth status
github.com
  ✓ Logged in to github.com as XXXXXXXXXX (/Users/XXXXXXXXXX/.config/gh/hosts.yml)
  ✓ Git operations for github.com configured to use ssh protocol.
  ✓ Token: *******************

Goインストール

このあと紹介するghqもpecoもGo製のOSSなので事前にGoをインストールする必要があります。MacでしたらHomebrewでインストールできますので事前にインストールします。

brew install go

ghqについて

github.com
ghqとはリポジトリ管理を楽にするコマンドラインツールでGo製のOSSです。GitHubリポジトリをローカルにクローンする場合、git cloneを実行したディレクトリ直下にローカルリポジトリが作成されます。
もし適当なディレクトリにクローンして管理するとどこにローカルリポジトリがあるのか探すのが大変ですし、ローカルにリポジトリをクローンし過ぎるとあっという間にローカルリポジトリの数は膨れ上がります。ghqとこのあと紹介するpecoを組み合わせることで、目的のリポジトリに瞬時に移動できます。

インストール

brew install ghq

pecoについて

github.com
pecoも先述のとおりGo製のOSSです。インタラクティブgrepツールでghqと組み合わせれば強力なリポジトリ管理ツールになります。

インストール

brew install peco

初期設定

2つをインストールできましたらまずは使用感について試しておきましょう。
ghq get <リモートリポジトリURL>GitHubリポジトリをローカルにクローンすることができます。クローン先はデフォルトでは$HOME/ghqですが、git configでクローン先を変更できます。

git config --global ghq.root '~/go/src' #~/go/srcディレクトリ直下にリポジトリを取得

取得したリポジトリghq listで一覧化できます。

$ ghq list
github.com/aws/aws-health-tools
github.com/cuebic/XXXXXXXXXXXXXXXX
github.com/iann0036/former2

続いてpecoも試しておきましょう。pecoはパイプで前のコマンドの結果を渡すことで真価を発揮します。

Executed `ps -ef | peco`, then the query `root` was typed. This shows all lines containing the word root GitHub READMEから引用

エイリアスコマンド作成

ghqとpecoの動作確認が完了しましたらこの2つを組み合わせたワンライナーコマンドを作成し、シェルの読み込みファイルに記載します。

# .zshrc

# ghq+peco
alias ghcd='cd $(ghq root)/$(ghq list | peco)'

記載しましたらsourceコマンドで読み込みます。

source ~/.zshrc

実行結果

$ ghcd

QUERY>
github.com/cuebic/XXXXXXXXXXXXXXXX
github.com/cuebic/XXXXXXXXXXXXXXXX

ターミナル操作

必要な準備は完了しました。これでどのように変わるでしょうか。
試しに新しいOSSリポジトリをローカルに落としたい場合は、git cloneからghq getに変わります。

$ ghq get git@github.com:github/gitignore.git
     clone ssh://git@github.com/github/gitignore.git -> /Users/ghq/github.com/github/gitignore
       git clone --recursive ssh://git@github.com/github/gitignore.git /Users/ghq/github.com/github/gitignore
Cloning into '/Users/ghq/github.com/github/gitignore'...
remote: Enumerating objects: 9730, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 9730 (delta 0), reused 4 (delta 0), pack-reused 9724
Receiving objects: 100% (9730/9730), 2.30 MiB | 2.16 MiB/s, done.
Resolving deltas: 100% (5288/5288), done.

ghcdですぐにローカルにクローンしたリポジトリに移動できます。

$ ghcd
QUERY>                                                  
github.com/github/gitignore

そして、ローカルリポジトリの編集が完了し、リモートリポジトリにコミットしてマージする場合もターミナル内で全て完結できます。

# リモートリポジトリへのaddからpush作業
$ git add .

$ git commit -m "fix"
[feature/add-gitops-article 37cb79b] fix
 2 files changed, 55 insertions(+), 6 deletions(-)

$ git push origin feature/add-gitops-article
Enumerating objects: 14, done.
Counting objects: 100% (14/14), done.
Delta compression using up to 8 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (8/8), 61.15 KiB | 15.29 MiB/s, done.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * [new branch]      feature/add-gitops-article -> feature/add-gitops-article

 # mainブランチへのPRからマージ作業
$ gh pr create

Creating pull request for feature/add-gitops-article into main in cuebic/XXXXXXXXXXXX

? Title fix
? Body <Received>
? "What's next? Submit"
https://github.com/cuebic/XXXXXXXXXXXX/pull/19

$ gh pr status

Relevant pull requests in cuebic/XXXXXXXXXXXX

Current branch
  #19  fix [feature/add-gitops-article]

Created by you
  #19  fix [feature/add-gitops-article]

Requesting a code review from you
  You have no pull requests to review

$ gh pr merge
? What merge method would you like to use? Create a merge commit
? Delete the branch locally and on GitHub? Yes #マージ後にローカルブランチを削除するか選択
? "What's next? Submit"
✓ Merged pull request #20 (feature/add gitops article)
remote: Enumerating objects: 2, done.
remote: Counting objects: 100% (2/2), done.
remote: Compressing objects: 100% (2/2), done.
Unpacking objects: 100% (2/2), 1.24 KiB | 633.00 KiB/s, done.
remote: Total 2 (delta 0), reused 0 (delta 0), pack-reused 0
From ssh://github.com/cuebic/XXXXXXXXXXXX
 * branch            main       -> FETCH_HEAD
   43e72c6..016f5bd  main       -> origin/main
Updating 43e72c6..016f5bd
Fast-forward
 entry/2022/05/17/122008.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 95 insertions(+), 6 deletions(-)
✓ Deleted branch feature/add-gitops-article and switched to branch main #ローカルのブランチを削除

所感

ご覧のようにgit addからGitHubリポジトリのマージまで全てターミナル上で完結しました。今までは毎回ローカルでgit pushした後にGitHubのサイトを開いて、PRを作成しマージする必要がありました。ターミナルからその操作を全て完結すればストレスフルなGit開発ができると思いますのでみなさんもぜひ試してみてください。

参考文献

zenn.dev

qiita.com