コネヒト開発者ブログ

コネヒト開発者ブログ

マネージドインスタンスドレインを活用して ECS インスタンス入れ替えを terraform apply 一発で終わるようにした話

プラットフォームグループでインフラエンジニアをしている @laughk です。

少し前の話になりますが、マネージドインスタンスドレインを活用して terraform apply コマンドのみで ECS インスタンスの入れ替えを自動で行えるようにしました。その際、どういった実装をしたのかをまとめます。

動機

コネヒトでは Amazon ECS を活用しており、主要な ECS クラスタのデータプレーンは Fargate を利用していますがそうでないクラスタもあります。

EC2 をデータプレーンとして利用している場合、定期的な ECS インスタンスの入れ替えが必要になります。ですが稼働中の ECS タスクをドレインしつつ入れ替えしていくのは難しい作業ではありませんが手間と労力がかかります。

なんとかしてこの手間を楽にしたい、できれば普段利用している terraform で解決できるとうれしいと思い調査を始めました。

調査・実装

実装方法については最初は検索ではなかなかそれらしき事例は見つけられず、そもそも何をどう実装すればいいかもなかなか掴めずにいましたが Perplexy に聞いてみたところ簡易的なサンプル実装を示されて一気に実装方針が見えてきました。

perplexyに聞いている様子

示されたサンプル実装だけではやりたいことは実現できなかったものの、その内容から大まかには次の二つを terraform 管理できれば ECS インスタンス入れ替えの実現ができそうであることがわかりました。

  1. オートスケーリンググループ(ASG)と起動テンプレートを管理し、インスタンスリフレッシュの設定をする
  2. ECS クラスタに Capacity Provider を設定し、ASG を Capacity Provider に紐付ける

EC2 をデータプレーンとする ECS クラスタの Capacity Provider は、簡単にいうならば ECS タスクが必要とするリソース量に応じてオートスケーリングの ECS インスタンスを増減させることが可能なものです。この Capacity Provider ではマネージドインスタンスドレインという機能が利用でき、デフォルトで有効となっています。 1 これを利用することで ECS タスクをドレインしつつ ECS インスタンスの入れ替えを行うことができます。

ここまでの情報をもとに terraform で ECS インスタンスの入れ替えのほとんどを自動化する実装を進めることができました。

管理するべき AWS リソースとサンプルコード

実際に実装したコードの一部を引用しつつ、管理するべき AWS リソースとそのコードを紹介します。 ファイル構成は次の通りです。

|-- dev/
|   `-- main.tf
|-- prd/
|   `-- main.tf
`-- module/
    |-- main.tf
    `-- variables.tf

dev, prd と環境ごとにディレクトリを分けていますがこれらは基本的に module を呼び出しているだけです。 実際に ECS インスタンスの入れ替えを行うための実装は module に閉じ込めています。

module/main.tf の内容は次のとおりです。

resource "aws_ecs_cluster" "my_cluster" {
  name = var.ecs_cluster_name
}

resource "aws_ecs_capacity_provider" "my_capacity_provider" {
  name = var.ecs_cluster_capacity_provider_name

  auto_scaling_group_provider {
    auto_scaling_group_arn         = aws_autoscaling_group.my_asg.arn
    managed_termination_protection = "ENABLED"
    managed_scaling {
      status                    = "ENABLED"
      target_capacity           = 100
      minimum_scaling_step_size = 1
      maximum_scaling_step_size = 10000
    }
  }

}

resource "aws_ecs_cluster_capacity_providers" "my_cluster_capacity_provider" {
  cluster_name = aws_ecs_cluster.my_cluster.name

  capacity_providers = [
    aws_ecs_capacity_provider.my_capacity_provider.name
  ]
}

resource "aws_launch_template" "my_lt" {
  name          = var.lt_name
  image_id      = var.lt_image_id
  instance_type = var.lt_instance_type
  ebs_optimized = var.lt_ebs_optimized
  key_name      = var.lt_key_name

  # ECS エージェントとDockerデーモンがECSクラスターに参加するように
  # ユーザーデータを設定します
  user_data = base64encode(<<-EOF
                #!/bin/bash
                echo ECS_CLUSTER=${aws_ecs_cluster.my_cluster.name} >> /etc/ecs/ecs.config
              EOF
  )

  lifecycle {
    create_before_destroy = true
  }

  block_device_mappings {
    device_name = "/dev/xvda"

    ebs {
      delete_on_termination = "true"
      encrypted             = "false"
      iops                  = var.lt_ebs_volume_iops
      volume_size           = 30
      volume_type           = var.lt_ebs_volume_type
      snapshot_id           = var.lt_ebs_snapshot_id
    }
  }

  iam_instance_profile {
    name = var.lt_iam_instance_profile
  }

  monitoring {
    enabled = true
  }

  network_interfaces {
    associate_public_ip_address = "false"
    device_index                = 0
    ipv4_address_count          = 0
    ipv4_addresses              = []
    ipv4_prefix_count           = 0
    ipv4_prefixes               = []
    ipv6_address_count          = 0
    ipv6_addresses              = []
    ipv6_prefix_count           = 0
    ipv6_prefixes               = []
    network_card_index          = 0
    security_groups             = var.lt_security_group_id
  }

}

resource "aws_autoscaling_group" "my_asg" {
  name                      = var.asg_name
  min_size                  = var.asg_min_size
  max_size                  = var.asg_max_size
  desired_capacity          = var.asg_desired_capacity
  vpc_zone_identifier       = var.asg_vpc_zone_identifier
  health_check_type         = "EC2"
  health_check_grace_period = var.asg_health_check_grace_period
  force_delete              = true
  enabled_metrics           = var.asg_enabled_metrics
  protect_from_scale_in     = var.asg_protect_from_scale_in

  launch_template {
    id = aws_launch_template.my_lt.id

    # "$Latest" では Launch Template に更新があっても instance_refresh(インスタンス入れ替え)が発動しないので、直接バージョンを指定する
    # 反対に発動してほしくない場合は、"$Latest" を指定する
    # see. https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group
    version = var.asg_ir_enabled ? aws_launch_template.my_lt.latest_version : "$Latest"
  }

  lifecycle {
    create_before_destroy = true
  }

  instance_refresh {
    strategy = "Rolling"
    preferences {
      min_healthy_percentage       = 100
      max_healthy_percentage       = 110
      instance_warmup              = 300
      scale_in_protected_instances = "Refresh"
      checkpoint_delay             = var.asg_ir_checkpoint_delay
    }
    triggers = ["launch_template"]
  }

  tag {
    key                 = "Name"
    value               = var.asg_name
    propagate_at_launch = true
  }

  tag {
    key                 = "AmazonECSManaged"
    value               = true
    propagate_at_launch = true
  }

}

resource "aws_autoscaling_lifecycle_hook" "my_asg_ec2_terminating_lifecycle_hook" {
  name                   = var.asg_ec2_terminating_lifecycle_hook_name
  autoscaling_group_name = aws_autoscaling_group.my_asg.name
  default_result         = var.asg_ec2_terminating_lifecycle_hook_default_result
  heartbeat_timeout      = var.asg_ec2_terminating_lifecycle_hook_heartbeat_timeout
  lifecycle_transition   = "autoscaling:EC2_INSTANCE_TERMINATING"
}

ポイントは次の通りです。

  • Capcity Provider, ASG の本体と ASG で利用する Launch Template はもちろん、ECS クラスタに Capacity Provider を紐付けるためのリソース ( aws_ecs_cluster_capacity_providers ) を忘れずに実装
  • ECS クラスタそのものはクラスタ名さえわかれば良いので、管理せずに variable で直接渡せるようにしてしまっても問題なし
  • 運用上問題ないところは割り切ってデフォルト値や決め打ちで設定
  • ASG の launch_template の version は "$Latest" ではなく直接バージョンを指定しないとインスタンスの入れ替えが発動しないので注意。ここではその仕様を利用して ECS インスタンスの入れ替えを実施したくないクラスタもまとめて扱えるようにしている

この module は dev, prd の main.tf で以下のように呼び出します。(locals の内容は割愛)

data "aws_ssm_parameter" "latest_ami_id" {
  name = "/aws/service/ecs/optimized-ami/amazon-linux-2023/recommended/image_id"
}

data "aws_ami" "latest_ami" {
  most_recent = true
  filter {
    name   = "image-id"
    values = [data.aws_ssm_parameter.latest_ami_id.value]
  }
}


module "app1" {
  source = "../module"

  ecs_cluster_name                   = "app1"
  ecs_cluster_capacity_provider_name = "app1-capacity-provider"

  asg_name                = "EC2ContainerService-app1-EcsInstanceAsg"
  asg_min_size            = 0
  asg_vpc_zone_identifier = local.vpc_zone_identifier

  lt_name              = "ec2-container-service-app1"
  lt_ebs_snapshot_id   = [for bdm in data.aws_ami.latest_ami.block_device_mappings : bdm.ebs.snapshot_id].0
  lt_image_id          = data.aws_ssm_parameter.latest_ami_id.value
  lt_key_name          = local.ssh_key_name
  lt_security_group_id = local.security_group_id

}


module "app1_batch" {
  source = "../module"

  ecs_cluster_name                   = "app1-batch-cluster"
  ecs_cluster_capacity_provider_name = "app1-batch"

  asg_name                      = "EC2ContainerService-app1-batch-EcsInstanceAsg"
  asg_min_size                  = 2
  asg_max_size                  = 2
  asg_vpc_zone_identifier       = local.vpc_zone_identifier
  asg_health_check_grace_period = 300
  asg_ir_enabled                = false

  lt_name              = "ec2-container-service-app1-batch"
  lt_image_id          = data.aws_ssm_parameter.latest_ami_id.value
  lt_ebs_snapshot_id   = [for bdm in data.aws_ami.latest_ami.block_device_mappings : bdm.ebs.snapshot_id].0
  lt_security_group_id = local.security_group_id
  lt_key_name          = local.ssh_key_name
}

ポイントは次の通りです。

  • コネヒトでは ECS インスタンスをカスタマイズせずに利用しているため、ParameterStore から最新の AMI ID を参照して利用 2
  • モジュールの variable.tf である程度デフォルト値を定めているので、呼び出すときは環境ごとの差異を加味しつつも最小限のパラメータで呼び出せるようにしている

このように実装すると、次のように terraform apply を実行するだけで AMI ID に変更がある場合に ECS インスタンスの入れ替えが始まります。

## dev 環境での例

$ cd dev
$ terraform apply

その際は ASG のインスタンスリフレッシュが発動し、マネージドインスタンスドレインによって自動でドレインしながら指定した AMI ID の ECS インスタンスに入れ替わります。 ただし module 呼び出しの際に asg_ir_enabledfalse にしている場合はインスタンスリフレッシュは発動しません。

ハマりどころや Tips

ECS インスタンス入れ替え時の Terminate 発動までの時間が長い場合の対処

一番最初に module を実装した際、terraform apply コマンドのみで ECS インスタンスの入れ替えはドレインを含め自動化できたものの、退役する古いインスタンスの terminate が発動するまでに1時間近くかかる問題に遭遇しました。

この問題は試行錯誤をした結果、 aws_autoscaling_lifecycle_hookheartbeat_timeout を短めに設定することで解決しています。 module の variables.tf に次のように設定し、デフォルト値を300秒(5分)にしています。

variable "asg_ec2_terminating_lifecycle_hook_heartbeat_timeout" {
  type    = number
  default = 300
}

ECS サービスではないスタンドアロンなタスクが動く環境では使えない

この記事で紹介している方法では ECS サービスに紐づくタスクに対してのみ利用できます。

ECS Schedule Task などで利用されるようなスタンドアロンな ECS タスクに対しては、ECS タスクの終了を待つような動作はされずに問答無用で ECS インスタンスが terminate されて入れ替わります。(実際に検証を行いましたが、確かにそのような動作をしました)

そのため、スタンドアロンなタスクが動く ECS インスタンスに関しては ASG の Launch Template の更新までを terraform で行うようにし、ECS インスタンスの入れ替えは手動で行うようにしています。( module 呼び出しの際に asg_ir_enabledfalse にしています)

終わりに

terraform apply で ECS インスタンスの入れ替えを自動で行う方法について紹介しました。

ECS インスタンス入れ替えの自動化は以前は AutoScaling のライフライクルフックを拾う lambda を用意するなど、大掛かりな仕組みを実装する必要がある印象でした。ですが少なくとも最近では、マネージドインスタンスドレインのおかげで terraform で最低限のリソースを用意するだけで実現可能になりました。

コネヒトではデータプレーンが EC2 の ECS クラスタは一つあたりの規模は大きくないものの、小さいものがたくさんある状況なので今回の自動化によって大幅な作業効率化ができました。 今回の事例が ECS on EC2 の運用に関わっている方の参考になれば幸いです。

参考記事

もともとこの記事で紹介した事例は Perplexy から得たヒントをもとに試行錯誤を繰り返して実装した関係で感覚的に理解していたことも多く、記事の執筆にあたっては言葉の整理が必要でした。その際、以下の記事も参考にさせていただきました。


  1. https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/enable-managed-instance-draining.html
  2. モジュールとしては AMI ID は文字列で渡せば良いだけなので、自前で ECS インスタンスを用意している場合はその AMI ID を直接指定すればよいです

ecschedule用のYAMLファイル専用リポジトリを解体して、各アプリケーションのリポジトリへ移管した話

プラットフォームグループでインフラエンジニアをしている @laughk です。 今回は、当社の ECS Scheduled Task の管理リポジトリを解体し、各アプリケーションのリポジトリへ移管した話を紹介します。

背景

コネヒトでは、ECS Scheduled Task の管理を一元管理するリポジトリがありました。このリポジトリでは ecschedule というツールを使って EventBridge のルールを YAML で管理、あわせて ECS のタスク定義もJSONで管理し、GitHub Actions 経由で EventBridge ルールの適用を ecschedule apply で行うというものでした。ecschedule については、以下の記事で導入の話を書いています。

github.com

tech.connehito.com

このリポジトリでは、3つのプロダクトの EventBridge のルールが一元管理されていました。

これは導入当初は各プロダクトのデプロイツールで ecs-deploy を使っていた関係で ECS サービスやタスク定義などのリソースをコード管理するという習慣自体が無かったことに由来します。ECS 関連のリソースの管理される場所も定まっておらず、ecschedule で必要とされる定期実行バッチの EventBridge のルールを管理する YAML ファイルを含め適切な管理場所も無かったため、独立したリポジトリを用意した経緯があります。

なお、定期実行バッチで利用する ECS タスク定義の JSON も同様の状況でしたが、後述するように ecspresso の導入によって Web アプリケーションのデプロイとまとめて管理されるようになりました。

なぜ一元管理しているリポジトリを解体したのか

ecschedule で管理していたリポジトリを解体した理由は、主に以下の2点です。

  • ECS 関連のデプロイ・リソースの管理状況が変わった
  • ecschedule の運用が安定し、プロダクトのリポジトリから独立している状況が煩わしくなった

結果として、 ecschedule で管理していたリポジトリを解体し、各プロダクトのリポジトリに ecschedule 用の YAML ファイルの管理と CI/CD などの自動化された仕組みを移管しました。

ECS関連のデプロイ・リソースの管理状況が変わった

ecs-deploy から ecspresso に移行したことで、タスク定義やデプロイ周りのコード化、デプロイの CI/CD 化が進みました。 特にそれまで Web アプリケーション用の ECS タスク定義と定期実行バッチ用の ECS タスク定義が別々に管理されていたのが、ecspresso によってデプロイも含めて共通化されたことが大きいです。

ecspresso 導入後に Web アプリケーションと定期実行バッチ用の ECS タスク定義更新の共通化については、以下のスライドにまとめているので興味のある方はご覧ください。

www.docswell.com

結果、定期実行バッチの EventBridge のルールだけが別のリポジトリに分かれている状況がかえって手間になりました。

ecschedule の運用の安定し、プロダクトのリポジトリから独立している状況が煩わしくなった

ecschedule 導入当初はツールに扱いなれていないメンバーも多く、ちょっとした記載方法のミスや設定漏れが発生するケースもあり、インフラエンジニアが中央集権的に管理する意義はありました。

しかし、問題が発生するごとにチェックスクリプトを用意して CI/CD でチェックする対応を繰り返していくうちにトラブルが発生するケースはなくなり、結果としてインフラエンジニアが介入する必要はほとんどなくなりました。

むしろ定期実行バッチの追加・変更・削除については関連する機能を実装した開発メンバー・チームのほうが状況を適切に把握しているケースがほとんどのため、インフラエンジニアが管理しているリポジトリに対して別で Pull Request を出す運用がそもそも実態に見合っていないものになっている状況でした。

解体から移管の概要

ecschedule 用の YAML ファイルを一元管理していたリポジトリの解体と各アプリケーションのリポジトリへの移管の概要は下図のイメージです。リポジトリのディレクトリごとに分かれていたものを、対応するアプリケーションのリポジトリに移管しました。

解体から移管の際に意識・工夫したこと

リポジトリの解体から設定ファイルの移管の際に意識・工夫したことは以下の通りです。

  • git log は git filter-repo を使って git log を保持した状態で移管
  • コードだけ移管するのではなく CI/CD による自動化もセット。人やチームに移管するのではなく、ツール・仕組みに移管する
  • デプロイで扱う CLI ツールは aqua で管理

git log は git filter-repo を使って保持した状態で移管

ecschedule 向けの設定ファイルを単純に移管することは簡単です。 しかし、それなりの時間運用していたものであるため、過去の経緯はできる限り追跡可能にしておきたいです。

そこで git filter-repo を使って、過去のコミット履歴を保持したままリポジトリを移管しました。 1 git 本体には含まれていないため、追加で brew や scoop でインストールする必要があります。 2

github.com

cluster1/prd-event-rules.yamlcluster1/dev-event-rules.yaml というファイルを batches というリポジトリからリポジトリ service1 の scheduled_batch_rules/ 配下に git log を保った状態で移動する例を示します。

CLIオペレーションとしては、おおよそ次の流れになります。一度移行元で scheduled_batch_rules/*.yaml だけの構成を作った後で、移行先リポジトリに強制的にマージするイメージです。

## ローカルにクローンしている batches リポジトリに移動
$ cd /path/to/batches

## 移行したいファイル以外、すべてをgit log含めて削除する
$ git filter-repo --path cluster1/dev-events-rules.yaml --path cluster1/prd-events-rules.yaml --force

## cluster1/ を scheduled_batch_rules/ に git log を保ちつつリネーム
$ git filter-repo --path-rename cluster1:scheduled_batch_rules --force

## 移行先のクローンしてあるリポジトリに移動
$ cd /path/to/cluster1

## 移行元のリポジトリをリモートに追加
$ git remote add batches /path/to/batches

## 移行元のリポジトリのリモートブランチを取得
$ git fetch batches

## 移行元のリポジトリのブランチを移行先のリポジトリにマージ
$ git merge batches/main --allow-unrelated-histories

注意点

  • /path/to は適宜自分の環境のものに読み替えてください
  • git fileter-repo は一度実行すると rebase などでも元に戻すことはできなくなるため、移行元となるリポジトリは移行用に別途クローンしておくことをおすすめします。
  • 移行元を移行先で取り込む際の git merge--allow-unrelated-histories オプションがないとエラーとなりマージできません。

コードだけ移管するのではなく CI/CD による自動化もセット

単純に設定ファイルの管理を別リポジトリに移すだけではこれまで利用してきた CI/CD による自動化された仕組みも失われてしまいます。 そうなると当然ながら CI/CD も含めて移管することになりますが、これまでの定期実行バッチの EventBridge のルールを管理するためだけのリポジトリからアプリケーションの開発が行われているリポジトリへの移管となるため、既存の CI/CD の影響を加味しつつ行いました。

移管した仕組みは次の通りです

  1. Pull Request 作成時に行うジョブ
    • ecschedule diff (差分チェック)
    • ecschedule 用の YAML の最低限のチェックツールの実行
  2. main ブランチマージの際に行うジョブ/tag push ( GitHub Release 作成)の際に行うジョブ 3
    • ecschedule apply (dev 環境への適用)
    • ecschedule apply (prd 環境への適用)

コネヒトでは全社的に GitHub Flow が採用されているためこのような流れになっています。

ecschedule の適用については既に ecspresso によるデプロイ(ECSサービス更新とバッチ用のECSタスク定義更新)が同様の流れで行われるようにしていたため、基本的にはデプロイが成功した後にジョブが走るようにしています。

それぞれについてもう少し詳しく説明していきます。

Pull Request 作成時に行うジョブ

まずは ecschedule diff によるYAMLと実際の EventBrige の EventBridge のルールの差分チェックを行うシンプルなジョブです。 GitHub Actions の YAML は次の通りです。

name: diff-check
on:
  push:
    paths:
      - "scheduled_batch_rules/*"
      - ".github/workflows/scheduled-batch-yaml-check.yaml"

permissions:
  id-token: write
  contents: read

jobs:
  diff-check:
    strategy:
      matrix:
        target:
          # diff check の対象となるファイルパスを指定
          - scheduled_batch_rules/dev-events-rules.yaml
          - scheduled_batch_rules/prd-events-rules.yaml
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: setup aqua
        uses: aquaproj/aqua-installer@v3.0.1
        with:
          aqua_version: v2.36.0

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::123456789012:role/assume-role-for-ecs
          aws-region: ap-northeast-1
          role-session-name: GitHubActions-${{ github.run_id }}

      - name: check diff
        run: |
          ecschedule diff -conf ${{ matrix.target }} -all

工夫しているポイントとしては次のものがあります

  • トリガーの push.paths に ecschedule で利用する設定ファイルとGitHub Actions の YAML ファイルを指定し、定期実行バッチを変更しない場合は CI/CD ジョブが走らないようにする
  • matrix を使って dev 環境と prd 環境の差分チェックを並列して行う

次は、ecschedule 用の YAML の最低限のチェックツールの実行を行うジョブです。

check_rules_yaml.sh という独自に用意したシェルスクリプトを使って、YAML の構文チェックを行っています。内容は本エントリの趣旨から外れてしまうため詳細は割愛しますが、 EventBridge のルール名の重複チェックと overrides.*.command に余分なスペースが入っていいかのチェックを行います。

name: scheduled batch yaml check
on:
  push:
    paths:
      - "scheduled_batch_rules/*"
      - ".github/workflows/scheduled-batch-yaml-check.yaml"

jobs:
  scheduled_batch_yaml_check:
    strategy:
      matrix:
        target:
          # syntax check の対象となるファイルパスを指定
          - scheduled_batch_rules/dev-events-rules.yaml
          - scheduled_batch_rules/prd-events-rules.yaml

    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v4
        with:
          path: service1

      - name: Check out platform-toolbox
        uses: actions/checkout@v4
        with:
          repository: Connehito/platform-toolbox
          token: ${{ secrets.MACHINE_USER_GITHUB_ACCESS_TOKEN }}
          sparse-checkout: ecs_scheduled_task
          path: platform-toolbox

      - name: Run Syntax Check
        run:
          /bin/bash platform-toolbox/ecs_scheduled_task/check_rules_yaml.sh service1/${{ matrix.target }}

工夫しているポイントは次のとおりです。

  • 独自のチェックスクリプトはインフラツール管理用のリポジトリに移管し、CI/CD 上でマルチリポジトリクローンで対応
  • ecschedule diff 同様に matrix を使って dev 環境と prd 環境の構文チェックを並列して行う

特に、前者のチェックスクリプト check_rules_yaml.sh の扱いは複数のリポジトリで共有して利用できてかつ多重管理になることを避けたかった背景があります。

独自に作成したスクリプトなので最悪コピペで ecschedule の設定ファイル同様に移管することもできますが、それでは管理が散り散りになり今後の更新・修正が煩雑になります。当初は Draft Releaseデプロイフローを作成してみました - コネヒト開発者ブログ でも紹介されているカスタムアクションを作って共有化することも検討しましたが、その場合シェルスクリプトでチェックアウトしたリポジトリのファイル内容を読み込むのに難があったため、今回は actions/checkoutrepository オプションを使って別リポジトリのクローンを行うことで対処しました。4

Connehito/platform-toolbox というリポジトリは、元々コネヒトの主にインフラエンジニアが業務で利用する共通のスクリプトやツールを置いているプライベートリポジトリです。今回の check_rules_yaml.sh の置き場所としても適切であったため、このリポジトリに移管しました。

今回のシェルスクリプト check_rules_yaml.sh はコネヒト独自のものであるものの、CI/CD 上で利用するちょっとしたスクリプトがある場合でも同様に共通化することができます。

mainブランチマージ/tag push時に行う適用のジョブ

ecschedule apply が伴うジョブは次の通りです。

# ---- (中略) ----

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:

        # --- ( ecspresso によるデプロイジョブ ) ---

  scheduled_batch_rules:
    runs-on: ubuntu-latest
    needs: [ deploy ]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: setup aqua # aqua で管理しているパッケージのインストール
        uses: aquaproj/aqua-installer@v3.0.1
        with:
          aqua_version: v2.36.0

      # 以下、定期実行バッチルールの適用
      # dev環境向けのバッチルールが変更された場合のみに、適用する
      - uses: dorny/paths-filter@v3
        id: scheduled-batch-rules-changes
        with:
          filters: |
            src:
              - 'scheduled_batch_rules/dev-events-rules.yaml'

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      - name: ecschedule apply to dev
        if: steps.scheduled-batch-rules-changes.outputs.src == 'true'
        run: |
          ecschedule apply -conf scheduled_batch_rules/dev-events-rules.yaml -all

ecschedule apply については ecspresso によるデプロイが成功した後にシンプルに ecschedule の適用ジョブが実行されるように追加しています。この時、 main ブランチマージの時のみ dorny/paths-filter で定期実行バッチ用の EventBridge のルールファイルを指定し、定期実行バッチを変更しない場合は極力 CI/CD ジョブが走らないようにしています。

本当は tag push の際も同様にしたいところですが paths-filter の仕様上 main ブランチマージ直後に作成された tag の場合、正しく差分を検知できません。そのため、本番環境への適用のジョブでは paths-filter を使わず、冪等であることを加味して常に ecschedule apply するようにしています。

デプロイで扱うCLIツールは aqua で管理

ecschedule の設定ファイルの移管は CI/CD の自動化も含めて行いましたが、 ecschedule そのもののも aqua を使って管理するようにしました。

github.com

aqua は Go 製の宣言型 CLI パッケージマネージャで、リポジトリトップに aqua.yaml というファイルを置くことで、リポジトリ内で利用するCLIツールとそれらのバージョン管理ができます。 aqua を導入した背景としては、主に次の課題を解決するためです。

  1. 各個人で ecschedule, ecspresso などを brew, scoop, mise などの管理に任せていると、いざローカルで利用する必要がある際にバージョンが合わずに想定外の動作をするケースがある
  2. ローカルで利用する CLI ツールのバージョンと GitHub Actions で利用する CLI ツールのバージョンをそろえたい
  3. CLI ツールの継続的なバージョンアップ

これらの課題は一度 CI/CD でワークフローが出来上がってしまえば、その後あまり意識されることが無くなってしまうものであります。 しかし、長期的な視点で考えるといずれも放置されるような状況になると「ある日突然 CI/CD が動かなくなる」という事態にもなりかねません。 そのため、ecschedule 導入のタイミングで同時に仕組みでつぶしておきたい課題でありました。

aqua を導入することで、リポジトリ内で利用する CLI ツールのバージョン管理を一元化し、 GitHub Actions での利用も含めてバージョンを一元管理できるようになりました。また、 renovate にも対応しているため、継続的なアップデートも人の手によるものではなく仕組みで行えるようにできます。

バッチ専用リポジトリを解体してどうなっているか

リポジトリの解体から移管が完了してからしばらく経ちますが、当初の目論見通りの改善ができたように感じています。

具体的には、バッチ追加の Pull Request がアプリケーション実装と EventBridge のルール追加・変更を一つのリポジトリで一気通貫して行えるようになり、インフラエンジニアによる形骸化した Pull Request レビューも必要なくなりました。

一見些細な事ではありますが、開発者が普段コミットしているリポジトリで完結するようになったことで、複数のリポジトリに合わせる余計な環境構築の必要はなくなり、別々に Pull Request を出してそれぞれのデプロイのタイミングを伺う必要も無くなりました。より必要な開発に集中できる環境を用意することに寄与できたように思えます。

ecschedule のような「直接アプリケーションには関わらない、けれどもシステムの運用上アプリケーションの開発者が直接触れたほうがよいインフラリソース管理」において、コネヒトでは今回のようなアプリケーションリポジトリに統合するという形に落ち着きました。もしも同様にアプリケーションと距離の近いインフラリソースの管理に課題を感じているケースの参考になれば幸いです。


  1. git log を保持したまま別のリポジトリにファイルを移す方法として git filter-branch を使う事例もあります。しかし git filter-branch は git 2.46.1 時点で公式でも非推奨となっており、git filter-repo を使うことが推奨されています。参考: Git - git-filter-branch Documentation
  2. https://github.com/newren/git-filter-repo/blob/main/INSTALL.md#installation-via-package-manager
  3. コネヒトでは一般的なステージング環境を dev 、本番環境を prd と呼んでいます。
  4. actions/checkout: Action for checking out a repo - Checkout multiple repos (private): https://github.com/actions/checkout?tab=readme-ov-file#checkout-multiple-repos-private

PyCon JP 2024で登壇してきました!

みなさんこんにちは。MLエンジニアのたかぱい(@takapy0210)です。

先日開催されたPyCon JP 2024 のDay1で、「低コストで実現する社内文書RAG機能を搭載したAIチャットボット開発」というタイトルで登壇してきました。

本日はその発表資料と当日の様子をお届けしようと思います。

PyCon JPとは

Pythonや、Pythonを使ったソフトウェアについて情報交換、交流をするためのカンファレンスです。 今回は久しぶりにオンサイトのみの開催でした。

  • 概要:日本最大級のPythonユーザーカンファレンス
  • 日時:2024年09月27日(金)~ 2024年09月29日(日)
  • 会場:TOC有明コンベンションホール

トーク内容

Slackを活用したAIチャットボットの構築と、そのチャットボットに社内文書に基づいた回答を生成させる機能(RAG)を低コストで実装した方法についてお話しました。
RAGの精度向上などの話は控えめに、0→1でRAGを実装し肌感を掴みたい方向けの内容になっています。

前半では、Slack Appで実現するシンプルなAIチャットボットの開発方法について説明し、後半では、AIチャットボットに社内文書に基づいた回答を生成させる仕組み(RAG)の紹介や、RAGを実現する上でのポイントや低コストで実現するTipsについて紹介しました。

登壇の様子

カンファレンスの感想

今回は2021年・2022年に引き続き、3回目の登壇でした。
2021年・2022年ともにオンライン/オフラインのハイブリット開催であり、完全オフラインでのPyCon参加は初めてだったのですが、思った以上に多くの人が来場されており、コミュニティの熱量の高さがうかがえました。

また、タイムテーブルを見ても分かる通りさまざまなトピックのトークがあり、かつ英語でのトークも多く、Pythonが幅広い用途・地域で用いられているんだなと、改めて実感しました。

参加者の地域

また、今回は参加者一人一人にNFCタグ付きの名札も配られており、参加者同士がコミュニケーションをしやすい工夫も感じられました。

最後に

PyCon JP 2024運営スタッフの皆様、当日コメントや質問をしてくださった皆様、Speakerの皆様、ありがとうございました & お疲れ様でした!

コネヒトのデータや機械学習周りの仕事についてもう少し知りたいな、と言う方がいましたら、以下のページにまとめているので、良ければ見てみてください!
カジュアル面談もお気軽にお声掛けいただければと思います!

tech.connehito.com

コネヒトはiOSDC Japan 2024に協賛いたします!

こんにちは、iOS/Androidアプリエンジニアの@katsutomuです!

本日は、iOSアプリ開発者の祭典iOSDC Japan 2024に協賛するお知らせです。

コネヒトはiOSDC Japan 2024に協賛いたします!

iOSDC Japan 2024に、シルバースポンサーとして協賛いたします。

iosdc.jp

スポンサーするにあたって、コネヒトは「人の生活になくてはならないものをつくる」というミッションを掲げているので、技術コミュニティについても同様に、サポートして一緒に盛り上げていくことができたら、と思っております。

イベント概要

  • 開催 2024年8月22日(木)~ 8月24日(土)
  • 場所 早稲田大学理工学部 西早稲田キャンパス + ニコニコ生放送
  • 対象 iOS関連技術およびすべてのソフトウェア技術者
  • タイムテーブル https://fortee.jp/iosdc-japan-2024/timetable
  • 主催 iOSDC Japan 2024 実行委員会 (実行委員長 長谷川智希)
  • 共催 早稲田大学 理工学術院
  • 後援 早稲田大学グローバル科学知融合研究所

iOSDCトークンはこちら!

#mamari-swiftui

これはiOSDCチャレンジで使うトークンです!

今年もオンラインだけではなく、オフラインでも開催される予定です。 技術者同士のつながりや知識の共有が広がることにワクワクしています。皆さん、楽しみましょう!

3年ぶりのTech Visionアップデートに込めた想い

こんにちは。CTOの永井(shnagai)です。

今日は、約3年ぶりにアップデートしたConnehito Tech Visionについて、主に戦略面のアップデート内容とそこに込めた想いを交えつつ書いていきます。

tech-vision.connehito.com

Connehito Tech Visionとは

Connehito Tech Visionは「Beyond a Tech Company」というビジョンを掲げた、コネヒトにおけるテクノロジーへの「態度」を表明した羅針盤です。

コネヒトのエンジニア組織及び技術領域において、何を大事に何に投資していくかといった未来の構想や方向性をまとめたものになります。

Tech Visionのスコープ

環境変化に適応するために2つのフェーズに分けることでスコープを明確にしています。

今回Phase2に突入したため、Phase1の成果を振り返りつつ、新たな戦略を追加しました。

Phase1

  • 2021年〜2023年

Phase2

  • 2023年〜2025年

戦略のアップデート

ここからはアップデートを行ったそれぞれの戦略について、具体の内容とその背景について説明していきます。

Phase2においては、元々5つあった戦略のうち2つを完了と位置づけ、「Run with Tech」という新しい戦略を含めた4つの戦略を制定しました。

各内容のアップデートにおいて、現場のエンジニア主導で、これまでの振り返りとPhase2で目指すべき方向性を踏まえて議論を行いました。

Phase2の戦略

スーパーモブ

内容 - クロスファンクショナルチームを組成し、開発基盤の強化や開発体験の向上を図る

目的 - 人が増えても入れ替わっても開発の質と速度が落ちないようにするため

Phase1において、通常タスクの中でPHPのバージョンアップをする仕組みが整ったり、本番データの開発環境への同期を行ったりと、普段のプロダクト開発チームの枠を飛び越えたエンジニアの交流と技術面での成長の促進に繋がる取り組みが活性化しました。

Phase2においてもこの取り組みを継続していくことで、エンジニアの成長を促進し、組織としての技術力向上を目指しています。

Run with Tech【New】

内容 - 世の中のテクノロジーの成長と共にコネヒトも成長し、新たな価値を生み出しステークホルダーを喜ばせる

目的 - 新しいテクノロジーを導入し、ユーザー体験の革新、競争力の強化、業務効率の向上、生産性と提供価値の最大化を図り、「DevとBiz」の垣根を越えて次世代の働き方を体現するため

2023年から、生成AIをはじめとしたテクノロジーの活用を推進する「Run with Tech」というプロジェクトがスタートしました。短期的には、テクノロジーを活用した業務効率の向上を目指しており、すでに100時間以上の業務時間削減の成果も出てきています。

実行プロセスの中でBizメンバーの可能性を広げることと、余白を生み出した先に新たな価値創造をしステークホルダーを喜ばせることをゴールに目指します。

Failable System

内容 - 失敗が出来る(失敗しても問題ない、失敗が評価される)システムと組織をつくる

目的 - 大きな成果が期待できるチャレンジを後押しするため

不確実性の高い現代の市場環境において、失敗を恐れずにチャレンジを続けることが違いを生み出す上で重要だと考えています。

この戦略を掲げることで、失敗がしやすいシステムや仕組みの構築に投資するという意思表示としています。 加えて、評価制度においてもチャレンジをしっかりと評価することで、エンジニアが積極的にチャレンジを続けて競争力の源泉になりうる環境を作り出していきます。

1to1 AI

内容 - 一人一人に最適化された情報をデータや機械学習を用いて提供する

目的 - 単一の情報をブロードキャストするのではなく、それぞれの家族に寄り添った情報を提供すること、および大量の非構造化データを活用するため

Phase1において、プロダクトへのレコメンドの複数導入や検索システムの内製化にトライしてきました。

マス向けの情報提供では届けられない価値をユーザーに提供するために、特定の技術には固執せずにこれからも様々な可能性を探りながら、トライを続けていきたいと思っており、ママリというメインプロダクトにおいては一番の投資分野として位置づけています。

Phase1で完了した戦略

3人に1人

内容 - 社内のエンジニア比率を33%以上にする

目的 - 会社としてやりたい事業を全て実現するため - 技術の力で、プロダクトだけに留まらず社内の課題も解決するため

Phase1制定時はエンジニア数が少なく、各所でエンジニアの不足を感じていたため、エンジニアの比率を上げる取り組みを行いました。

結果として、優秀なエンジニアを多く採用することができ、Run with Techに代表されるようなプロダクト開発に閉じない新たな取り組みを行えるようになったので、この戦略自体はPhase1で完了と位置づけしました。

Let's Go

内容 - バックエンドのシステムにGoを積極的に導入する

目的 - Go言語の採用により、技術的な選択肢を増やすため

Phase1制定時は、ほぼすべてのプロダクトでPHPを採用していましたが、技術的な選択肢を増やすためにGo言語の採用を進めました。

結果として、検索APIやメインシステムへのGo言語の一部導入を通じ、Go言語を書けるエンジニアが増え、コネヒトの技術スタックの選択肢の1つになったので、この戦略自体はPhase1で完了と位置づけしました。

これから

Phase1の振り返りを通じて、各戦略確実に制定時よりも前に進んでおり、Tech Visionという羅針盤を掲げる効果性を感じることができたと考えています。

ここで紹介した戦略はエンジニア中心に取り組むものが多いですが、このTech Visionは全社員が一丸となって取り組むものと位置づけており、アップデートのプロセスにおいても、経営メンバー全員が読み込みやFBを行って今回のアップデートに至りました。

このTech Visionを持続的にアップデートして、実行していくことがコネヒトの技術への投資スタンスを示すものだと考えており、全社一丸で実行力をもってビジョンの達成に向かっていきたいと思っています。

hrmos.co

ママリで使用している Aurora MySQL のアップグレード(v2 から v3)を行った EOL 対応プロジェクトの進め方を振り返る

こんにちは。コネヒトでは Aurora EOL 対応芸人となっているささしゅう(@sasashuuu)です。本日は、コネヒトを代表するサービス「ママリ」のデータベースを Aurora MySQL v2 から v3 へアップグレードした際のプロジェクトの全容をお伝えしたいと思います。

はじめに

まず、ママリというサービスと Aurora の構成について触れておきます。 ママリは、子育て Q&A アプリおよび情報サイトを通じてママ向けのサービスを提供しています。詳細や各種サービスの利用については、弊社ホームページをご覧ください。

connehito.com

現在のママリの主要な AP サーバーとデータベースの構成は以下の通りです(※具体的なサーバーの台数については図では省略)。

AP サーバー構成

用途 利用サービス サーバー台数
Q&A アプリ用 AP サーバー(Native アプリ API 向け) ECS Fargate 4 ~ 30タスク
(サービスは1つ)
Q&A アプリ用 AP サーバー(Web アプリ向け) ECS Fargate 2 ~ 4タスク
(サービスは1つ)
情報サイト用 AP サーバー(Web アプリ向け) ECS Fargate 4 ~ 20タスク
(サービスは1つ)

データベース構成

用途 利用サービス サーバー台数
ママリ共用 RDB Aurora MySQL v3 2インスタンス
(リーダー・ライターそれぞれ db.r6g.4xlarge)

※Aurora に関してはレプリカの Auto Scaling により必要時にスケールイン/アウトの設定あり。

※アップグレード前は db.r4.4xlarge が 3 台の構成(アップグレードの機会に見直しを実施)。

プロジェクトの進め方の全体像

プロジェクトの進め方は以下のフローで行いました。本記事では1つ1つのトピックの詳細な解説は省略しますが、どういったフローでアップグレードに向けた作業をおこなっていたのかをざっくりとイメージしていただけると幸いです。

プロジェクトの構成

プロジェクトのメンバーは基本的にインフラエンジニア1名とアプリケーションエンジニア(サーバーサイド)1名の体制で臨みました。作業が増えるフェーズではエンジニアを増員し(最大5名程度)、手分けして作業を行いました。主に CI・Dev 環境でのテストや動作確認、不具合修正対応のフェーズで特に人手が必要でした。アサインと業務分担については、インフラエンジニアとアプリケーションエンジニアの作業領域を分けつつ、必要に応じて互いに協力し合いました。下記は先ほどのフローに担当領域を示した図です。青色がインフラエンジニア、赤色がアプリケーションエンジニア、共に重なっている箇所は両方が担当しました。

良かった点

EOL 対応を進める上で特に良かった点をピックアップしていきます。

MySQL Shell アップグレードチェッカユーティリティの活用

MySQL Shell の アップグレードチェッカユーティリティ という機能があります。これは MySQL5.7 から 8.0 へのアップグレードに伴う互換性に関するエラーや問題を検知してくれるツールです。Aurora というマネージドサービスを使用している都合上一部正常にチェックできない項目もありますが、基本的な互換性はありますので十分に活用できる印象です。以下は今回アップグレード対象だった Aurora v2(MySQL5.7) へ実行した結果の例を一部記載したものです(※一部マスク処理あり)。

接続

mysqlsh -u xxx -h xxx -P xxx -p --ssl-ca=xxx

アップグレードチェッカーの実行

 MySQL  xxx:xxx ssl  JS > util.checkForServerUpgrade()
The MySQL server at xxx:xxx, version 5.7.12-log - MySQL Community
Server (GPL), will now be checked for compatibility issues for upgrade to MySQL
8.0.32...

中略

1) Usage of db objects with names conflicting with new reserved keywords
  Warning: The following objects have names that conflict with new reserved
    keywords. Ensure queries sent by your applications use `quotes` when
    referring to them or they will result in errors.
  More information:
    https://dev.mysql.com/doc/refman/en/keywords.html

  xxx.xxx.rank - Column name
  xxx.xxx.rank - Column name
  xxx.xxx.rank - Column name

4) Usage of utf8mb3 charset
  Warning: The following objects use the utf8mb3 character set. It is
    recommended to convert them to use utf8mb4 instead, for improved Unicode
    support.
  More information:
    https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-utf8mb3.html

  xxx - schema's default character set: utf8
  xxx.xxx.xxx - column's default character set: utf8
  xxx.xxx.xxx - column's default character set: utf8
  xxx.xxx.xxx - column's default character set: utf8

以下略

上記の例では、MySQL8.0 で予約キーワードとなる rank カラムを使用している箇所や、MySQL8.0 で非推奨となる utf8mb3 を使用している箇所などが検知されました。

CI の活用

ママリの開発では GitHub Actions による CI/CD を構築しています。このワークフロー上では PHPUnit による自動テストが実行され、アプリケーションのデプロイ前に問題があればワークフローで異常を検知できます。Dev や Prd 環境のアップグレード前に、このワークフロー上で使用している MySQL のイメージを MySQL5.7 から 8.0 へ変更しテストすることによって、アップグレードにおける不具合を効率よく検知することができました。以下はワークフローの設定ファイルの例です(※一部マスク処理あり)。

name: Pull request
on:
  pull_request
env:
  AWS_ROLE_ARN: xxx
permissions:
  id-token: write
  contents: read
jobs:
  php:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0 # 5.7から変更

中略

      - name: Run phpunit
        env:
          xxx: xxx
          xxx: xxx
          xxx: xxx
          xxx: xxx
        run: vendor/composer/bin/phpunit -d memory_limit=512M

以下略

CLI ベースでの手順構築

Aurora のアップグレード方法には様々な手法がありますが、弊社では AWS CLI を使用したオペレーションを採用しました。CLI ベースだと移植性が高く、順序立てた手順の構築がしやすいというメリットが大きいと感じたためです。個人的な見解になりますが、各アップグレード方法の内容について比較したものは以下となります。

アップグレード方法の比較

メリット デメリット
コンソール GUI による直感的な操作が可能 移植性が低い(リソースのリンクやキャプチャを手順に埋め込んでいる場合、他のデータベースのアップグレード作業でも使いまわしづらい)
AWS CLI ・移植性が高い(他のデータベースのアップグレード作業でもパラメータを変えて使いまわしやすい)
・IaC に比べ順序立てた手順が作りやすい
コマンド実行時のコピペミス等によるリスクがある
IaC ・(コードの)バージョン管理やレビューのしやすさがある
・fix したコードを使用するためオペミス等のリスクが減る
順序立てた手順を実行する観点で扱いが難しい(例として段階的なバージョンアップグレードが必要であったり、アップグレードとインスタンスクラスの変更を同時に行えない問題がある)

CLI を用いたアップグレードについて実際の使用例を一部ご紹介します。弊社の場合、Notion であらかじめ手順書を作成し、作業時にコマンドをコピーアンドペーストで実行していくという方法を取っていました。

RDS Blue/Green Deployments の利用

今回のアップグレードでは Amazon RDS Blue/Green Deployments を利用しました。Green 環境の構築および Blue 環境とのレプリケーションからスイッチオーバーによる切り替えを提供してくれる機能です。以下の手順でアップグレードを行いました。

  1. Green 環境の作成
  2. Green 環境のアップグレード
  3. Green 環境への DDL 実行
  4. スイッチオーバー

良かった点は、事前に必要な作業を Green 環境で済ませておくことによって、サービスの計画停止の時間を短縮できることでした。特に今回はエンジンのアップグレードに加え、アップグレードに伴う DDL の実行(※後述)も必要であったこともあり、計画停止の前段階でネックとなる作業を終わらせることができました。ただし、Green 環境への DDL 実行は基本的に推奨されていない(サポートの方へ確認済み)ことから、実施する場合はリスクを承知の上で行う必要があります。

チームや専門領域を跨いだエンジニア間の定期的なコミュニケーション

1〜2週間に1回程度、インフラエンジニア・アプリケーションエンジニアが同期を取り連携していました。互いの進捗や状況を共有することで、発生している問題や課題が把握できたので、プロジェクト全体で見た際の人的リソース調整などが上手く行えていたように感じます。また、インフラエンジニアのみでは把握しづらい開発組織全体への影響を考慮した舵取りをよりしやすくなったのも良いポイントだったと思います。

プロジェクト内で共通のカンバンを使用する

所属チームや取り扱うサービスにより issue やタスクカード等の置き場所が異なる弊社ですが、基本的に EOL 対応のプロジェクトでは共通のものを使用するようにしました。 プロジェクト全体の状況が一元的に管理しやすくなるのでオススメです。

弊社の場合、Notion のデータベース等を活用していました。 下記は実際に使用していたものの一例です。

タスク管理
発生した問題等の管理

反省点・課題

続いて EOL 対応を進める上での反省点や課題をピックアップしていきます。

通常のアプリケーション開発業務との並行運用

Dev 環境アップグレードから Prd 環境アップグレードまでにバージョンの乖離期間があり、その期間に通常のアプリケーション開発に支障をきたすことがありました。Prd 環境がまだ MySQL5.7 であるもCIの環境を 8.0 に変えたことにより、CI をパスしたとしても Prd 環境での動作の担保ができない状態になっていました。Prd リリース前にシステムの異変を検知するという CI の目的を踏まえると、Prd 環境と同じ 5.7 で CI が通れば Prd リリースして良いことになります。ただタイミング悪く乖離期間にリリースが重なってしまったものがありました。5.7 ではなく 8.0 を使用した CI でテストを通した結果、テストが落ちてしまうということがありました。これに関しては旧バージョンと新バージョンの CI を構築し、どちらでもテストおよびリリースが可能な状態にする対策を取りました。また、次回以降の EOL 対応では対象データベースに関わる開発チームから1名以上をアサインしてもらい、EOL 対応と通常のアプリケーション開発業務をよりうまく調整しながら進めていけるようにする対策を考えました。

計画停止作業の影響範囲の認知負荷

今回のアップグレード作業では、夜間に計画停止を行いました。影響範囲が広く、認知負荷も高い状態でした。影響のあるサービスにはメンテナンス画面を出したり、監視システムを無効にしたりする対応が必要で、ステークホルダーに対して利用停止を告知・依頼する手配も必要でした。この点においては今後認知負荷を軽減していけるように情報を集約していく必要があると感じました。

バイナリログレプリケーションを使用していた場合の Amazon RDS Blue/Green Deployments の利用

ママリのデータベースは to C 向けのサービスでの利用がメインですが、分析用に RDS for MySQL へのバイナリログレプリケーションも行っていました。

Amazon RDS Blue/Green Deployments では仕様上バイナリログが引き継がれないという問題がありました。下記のように Blue/Green 環境のスイッチオーバー後は、Blue 環境で使用していたバイナリログは引き継がれないため、スイッチオーバー後はそのままレプリケーションの再開が行えないというものです。

スイッチオーバー前の状態

スイッチオーバー後の状態

そのため以下の手順でオペレーションを行う必要がありました。

  1. スイッチオーバー前に Blue 環境で目印となるクエリを実行
  2. Blue 環境と分析 DB のレプリケーションを停止
  3. Blue/Green 環境のスイッチオーバー
  4. Green 環境にて目印のクエリが含まれたログファイルを dump
  5. 目印のクエリのファイル名・ポジションをもとに Green 環境と分析 DB のレプリケーションを再開

このように、RDS for MySQL とのバイナリログレプリケーションを構築している場合、特殊なオペレーションが発生するため注意が必要です。

想定外のスロークエリの発生

SELECT COUNT(*) のクエリが遅くなる事例はいくつかあるため事前にチューニングを行っていましたが、Prd アップグレード後に別種のスロークエリが発生しました。以下のような構文のクエリでした(※一部マスク処理あり)。

SELECT
    `xxx`. `xxx`,
    `xxx`. `xxx`,
    `xxx`. `xxx`,
    ...中略
FROM
    `xxx`.`xxx` AS `xxx`
        LEFT JOIN
    `xxx`.`xxx` AS `xxx` ON (`xxx`.`xxx` = `xxx`.`xxx`)
WHERE
    `xxx`.`xxx` >= xxx
        AND (NOT (`xxx`.`xxx` = xxx)
        AND NOT (`xxx`.`xxx` = (xxx)))
        AND `xxx`.`xxx` = xxx
        AND `xxx`.`xxx` = 'xxx'
        AND `xxx`.`is_hoge` = '0'
ORDER BY `xxx`.`xxx` DESC
LIMIT xxx;

is_hoge(仮名) という boolean のカラムを使用していましたが、比較演算子を等価から不等価に変更することでパフォーマンスが向上しました。

修正前

`xxx`.`is_hoge` = '0'

修正後

`xxx`.`is_hoge` != '1'

変更前のクエリは実行計画で Using temporary, Using filesort が発生していたのですが、比較演算子の変更によりこれらは解消されました。 なぜこのスロークエリが発生したのか、また等価比較の変更でなぜ Using temporary, Using filesort が解消され、パフォーマンスが向上したのかは不明です。 とはいえ事前検証をもう少し入念にしておけば Prd 環境のアップグレード後に慌てる必要はなかったため反省点でした。

非推奨文字セット・照合順序変更の DDL の対応コスト

MySQL8.0 から utf8mb3 が非推奨になります。デフォルトの文字セットが utf8mb4 になり、照合順序が utf8mb4_0900_as_ci となる仕様変更もあります。utf8mb3 を使用している箇所があったため、アップグレードに伴いデフォルトの文字セットおよび照合順序を変更する DDL を実行しました。DDL の実行には数時間(おおよそ3時間程度)かかりましたが、事前に Green 環境で実施したため、運用中のデータベースや計画停止作業に影響はありませんでした。ただし、事前対応の準備コスト(DDL 実行時間を見積もった上で逆算し、 Green 環境の構築と DDL 実行を行う等)があるため、場合によっては他の方法を検討する必要がありそうです。

おわりに

色々と大変なことはありましたが、Aurora MySQL v2 の EOL 対応を無事に終えることができました。個人的には弊社の場合だと v1 から v2 へのアップグレードよりもネックとなるポイントが多く、難易度が高かったように感じました。今回はコネヒトにおける事例を紹介しましたが、これから v2 EOL 対応を控える方々の参考になれば幸いです。頑張ってください!それではさようなら!

生成AIなどのテクノロジーの力でビジネス成長を目指す『Run with Tech』の活動紹介

こんにちは。CTO室プラットフォームグループに所属している @yosshiです。

今回、私が参加している「Run with Tech(以下、ランテク)」というチームの活動内容についてご紹介させていただきます。

目次

ランテクについて

コネヒトでは、Tech Visionの中でBeyond a Tech Companyというビジョンを掲げています。

ランテクは、このビジョンを実現するために2023年12月に設立されたチームで、生成AIをはじめとするテクノロジーの力を活用し既存の課題解決や新たなビジネス創出を目指した活動をしています。

具体的には、生成AIをはじめとしたテクノロジーの活用により社員の時間の余白を作ること、またテクノロジーに関する基礎知識の向上・スキルアップを図ることを目指しています。

現在はCTO・エンジニアなど5名のメンバーを中心に運営しています。

なぜやるのか?

こういった社内のテクノロジー活用を推進する場合、業務改善・BPRの側面が強くなりがちなのですが、目標としてはもう少し視点を前に、既存の改善だけではなく「新たなビジネス創出と成長、ステークホルダーへの価値提供」という部分を目指しています。

生成AIの誕生により、今後BizとDevの境界は今後どんどん曖昧になっていくと思うので、コネヒトではBiz/Devの垣根を超えて、それぞれの立場から必要なテクノロジーを活用する「アフターテックカンパニー*1」を目指していきたいと考えており、ランテクの活動はその状態を実現するために非常に重要な取り組みだと捉えています。

活動事例

ランテクでは2023年12月の活動スタートから、小さいながらも少しずつ成果が出てきたので、その中の活動内容・実績についていくつかご紹介したいと思います。

事例一覧

事例①:全社向け生成AI教育
事例②:生成AI利用時のプロンプト事例の共有
事例③:経営層向けの生成AI活用課題
事例④:記事タイトル生成時の生成AI活用(削減時間:10.5時間/月)
事例⑤:インスタグラム 記事情報取得の効率化(削減時間:3.75時間/月)

事例①:全社向け生成AI教育

まず、生成AIを身近なものに感じてもらうため、コネヒト全社員向けに生成AIに関する基礎教育を行いました。

生成AIとは何か?の基本的な説明から、実際に生成AI(ChatGPT)に触れてもらうところまでを一つのコンテンツとして提供しました。

スライドの一部抜粋

アンケート結果

また、教育終了後にアンケートも実施しました。

このアンケートはランテクの活動が始まってから定期的に実施しており、教育以降で生成AIに関する期待感が高まったこと・実際に利用する人が増えたことを実感しました。

<業務において生成AIを使う頻度は?>

<生成AIを利用することにより業務価値を上げられるという自信はありますか?>

※2023/12〜24/3の社内アンケートの実績。回答数:12月51名、1月46名、3月48名、

事例②:生成AI利用時のプロンプト事例の共有

生成AIの基礎教育だけではなく、プロンプトエンジニアリングのノウハウを共有しています。

これによって、実際に生成AIを使う際に、少しずつより精度の高い回答を得られるようになってきているように感じます。

共有したプロンプトの例


※ 2024年3月時点で有効とされているものを紹介しています。

事例③:経営層向けの生成AI活用課題

経営メンバーに対して生成AIに関する課題を出して、実際に取り組んでもらいました。

社内全体で取り組むにあたって、ボトムアップももちろん必要ですが、経営メンバーが生成AIを武器として使いこなし、その様子を見せてもらうのも大事だと思います。

具体的には、事業部を統括する立場や経営の立場から考える課題など、以下のような課題に取り組んでもらいました。

主な課題はこちら

技術課題①:マイGPTsを作ってみよう

技術課題②:管掌領域における生成AI活用案だし

戦略課題①:生成AI時代における会社の「事業」を考える

戦略課題②:生成AI時代における会社の「組織」を考える

また、取り組んでもらった課題については、後日全社員に向けて共有してもらいました。

共有会の様子はこちら

課題に取り組んでみて 経営メンバーの感想

発表会に出席した社員の感想

また経営メンバーの発表会を受けて社員から多くの感想が寄せられたので一部ご紹介します!

  • 生成AIはもちろん、経営がどう組織を考えているのか?をキャッチできるとても良い会だったなと思いました!ランテクの皆さん、経営の皆さん運営・準備ありがとうございました〜!
  • きれいに議論されたアウトプットではなく、個々がさまざまな粒度で考えた話を聞けたので生成AIに対してさらにワクワク感が高まりました!あとはちょうどどんなビジネスマンになりたいのか・・を最近もんもんと考えているのでヒントになりそうなありがたい会でした!
  • 生成AI活用による生産性や効率アップなど価値メリットに視線が集まる一方で、それが一般化した未来を考えたとき、家族をテーマに扱うコネヒトが社会に対してどのようなサービスを届けるべきか、あるいはどのような組織であるべきかを、あらためて思考したいなと思いました。

また、早速 取締役 Captainの@itoshoさんがこの課題で取り組んだ内容をブログに書いてくれました。

興味ある方はこちらもぜひご覧ください!

tech.connehito.com

事例④:記事タイトル生成時の生成AI活用

弊社のWEBサービスであるママリでは、記事編集時にタイトリングをする必要があります。

その際、メンバーによっては適切なタイトリングに悩むケースがありました。

そこで、編集経験や知見に関わらず、誰でも良いタイトリングができるように生成AIを活用することにしました。

生成AIの利用にあたり、ツールは弊社内で作成しているSlack上で動作する自作の生成AI Botで対応しています。

弊社のSlack Botについて詳しく知りたい方は、こちらの記事をご覧ください。

tech.connehito.com

tech.connehito.com

質問文

回答

プロンプトの工夫点として、過去に実際に反応の良かった記事タイトルを参考例としてChatGPTに与えているのですが、それによって生成AIが考えてくれるタイトル候補が、普段ママリがつけているものに近いものになったように感じます。

事例⑤:インスタグラム 記事情報の取得

これまでの流れでは、生成AIの事例が多くなってしまいましたが、ランテクでは生成AIに絞らず、状況に応じて別の技術も使用しています。

例えばこちらは、GAS(Google Apps Script)とインスタグラムのグラフAPIを利用して、Bizメンバーの業務を改善した事例となります。

弊社ではSNS運用やSNS投稿の記事化に力を入れているので、さまざまなアカウント情報を取得する機会があります。

これまではアカウントごとに直接インスタグラムの画面を見ていましたが、インスタグラムのグラフAPIとGASを利用することにより、特定ユーザーの投稿情報を自動取得してスプレッドシート上に転記する、という仕組みを作りました。

<取得した記事情報>

最後に

これまでに記載した通り、ランテクでは様々なテクノロジー活用に関する取り組みを実施しています。

コネヒトでは、新入社員が活躍しやすい環境を整えるため、3ヶ月間のオンボーディング期間を設けていますが、最近その教育プログラムの一環として、生成AIに関するトレーニングを追加しました。

Biz/Devに関わらず、社員全員がコネヒトに所属することで当たり前のように生成AIやテクノロジーを活用することができる状況を作り出し、最終的には全社員でビジネスの成長・ステークホルダーへの価値の提供を目指していきたいと思っています。

※このブログはChatGPTに壁打ちをしながら執筆しました。また、OGP画像もCanvaの生成AI機能を利用して作成しています。

*1:コネヒトのTech Visionの中で掲げている目標。テクノロジーを中心に据えつつもBizやDev、開発やセールスといったラベルを曖昧にしたテックカンパニーのことを指している