🥳

BEENOS社内でISUCONハンズオンを開催したレポート

2022/08/09に公開

BEENOSの三上です。
本日は、先日弊社内で開催したISUCONハンズオンについてレポートさせてください。

なお、「ISUCON」は、LINE株式会社の商標または登録商標です。
本記事はLINE株式会社に商標利用の許諾を頂いて記載しております。

ISUCON?

Iikanjini Speed Up Contestを略し、ISUCONと呼びます。
詳しい説明は他記事に譲りますが、1チーム最大3名で参加し、与えられたインスタンス3台の範囲でWebサービスをチューニングする大会です。

なお本記事を書いている三上はISUCON11, ISUCON12に参加していますが、いずれも70~80位ぐらいで予選敗退です、すいません 🙏
一方でISUCONを通じて学べることはWebアプリケーションを開発・運用するのに非常に有用な知識が散りばめられていると思っており、それに参加する、あるいは社内で行ったりハンズオンをするというのはとっても良いことだと考えています。

社内での取り組み(未来含む)

BEENOSでは以前、夏・冬にそれぞれエンジニア合宿を行っていたのですが、コロナの影響もあり最近は開催出来ておりませんでした。
6月辺りはコロナも落ち着いていたので、これを再開しようという動きがあり、今年は社内ISUCON合宿をやろう!という機運があります。
(実際に開催されるかどうかは今後のコロナ感染者動向次第... 🤔)

ですが社内ISUCONをやるといっても、ISUCON参加経験者が少なく、その状態でぶっつけ本番で開催しても開催側も参加者も面白くないでしょう。
一旦ISUCONがどういうものか?どの様にしてチューニングを行っていくのか?を予習出来るハンズオンを開催しようというのが今回の目論見です。

この辺りを長く書いてもしょうがないので、以降はハンズオンのために用意した具体的な資料等を記載します。

ハンズオンのやり方、用意したもの

今回のハンズオンでは1台のインスタンスでgo製アプリ、nginx、DB、ベンチマーカーの全ての役割をこなしてもらいます。
本来ベンチマーカーは分離すべきですし、ISUCONの開催スタイルとも異なりますが、あくまでチューニングの初歩に入門してもらうということに重点を置いています。

インフラ構築用のTerraformとちょっとしたscript

Terraform周りは本職ではない & 一時的なリソースなのでざっと書いています。
もし粗があってもご容赦ください。

なお、VPN等ネットワークリソースも作成していましたが、記事掲載時に短くするために記載も端折っております。
そのままコピペした場合はデフォルトVPCで起きるものと思います、流用する方は必要に応じて追記してください。
(長いのでaccordionにて。)

Terraform
provider.tf
provider "aws" {
  region  = "ap-northeast-1"
  profile = "mad-playground"
}
config.tf
terraform {
  backend "s3" {
    bucket  = "terraform"
    key     = "isucon-hands-on/terraform.tfstate"
    region  = "ap-northeast-1"
    profile = "**AWS PROFILE**"
    encrypt = true
  }
}
ec2.tf
module "aws_isucon_sg" {
  source = "terraform-aws-modules/security-group/aws"

  name        = "isucon-hands-on"
  description = "Security group for ISUCON hands-on instances"

  ingress_cidr_blocks = [
    # 接続を許可するIPを記載
  ]
  ingress_rules = [
    "ssh-tcp",
    "http-80-tcp",
    "https-443-tcp"
  ]
  egress_rules  = [
    // 全開放でも良いけど一応制限、外部からツールなど取得するので、80, 443を開けておく。
    "http-80-tcp",
    "https-443-tcp",
  ]
}

resource "aws_key_pair" "isucon-instance-key" {
  key_name   = "isucon-instance-key"
  # インスタンスの鍵。いい感じにしてください。
  public_key = file("keys/id_rsa.pub")
}

resource "aws_instance" "isucon11q" {
  for_each = toset(local.participants)

  # isucon11-qualify c.f.: https://github.com/matsuu/aws-isucon
  ami           = "ami-0796be4f4814fc3d5"
  # t系インスタンスだとバーストで処理能力に波が出来てしまうので、c系インスタンスで用意
  instance_type = "c5.large"
  key_name      = aws_key_pair.isucon-instance-key.key_name

  subnet_id              = aws_subnet.public.id
  vpc_security_group_ids = [module.aws_isucon_sg.security_group_id]

  tags = {
    Name = "ISUCON11q_${each.value}"
    # 後で行う鍵登録のオペレーションをやりやすくするために、githubユーザー名をタグとして付与
    ghName = "${each.value}"
  }

  root_block_device {
    volume_type = "gp2"
    volume_size = 30
  }
}

output "isucon11q_public_ip" {
  description = "Instance public IP"
  value       = [for instance in aws_instance.isucon11q : instance.public_ip]
}
locals.tf
locals {
  app_name = "isucon-hands-on"

  participants = [
    // 参加者のGitHubアカウントを列挙する、これをループしてEC2インスタンスを構築する
    "aki-webii",
  ]
}

構築したインスタンスの情報(IP)取得、またインスタンスへの鍵登録はなんとなく zx を使用しました。
jsは普段書き慣れているので、これでscriptが書けるのはなかなか体験が良かったです。

1. インスタンスリスト(IP含む)の取得

参加者の方々にインスタンスのIPアドレスを共有するために、AWSから情報を取得して console.table() してるものです。
jq でやっても全然良さそうですね。

getInstanceList.mjs
#!/usr/bin/env zx

$.verbose = false;

const awsProfileName = '**AWS PROFILE NAME**'

const describeInstanceResult =
  await $`aws ec2 describe-instances --filters "Name=tag:Name,Values=ISUCON11q_*" --profile ${awsProfileName}`;
const instanceGroups = JSON.parse(describeInstanceResult.stdout).Reservations;

const filteredInstances = instanceGroups
  .filter((elm) =>
    elm.Instances[0]?.Tags.some(
      (tag) => tag.Key === "Name" && /^ISUCON11q_.*/.test(tag.Value)
    )
  )
  .map((elm) => elm.Instances[0]);

console.table(
  filteredInstances.map((elm) => ({
    participantName: elm.Tags.find((tag) => tag.Key === "ghName")?.Value,
    publicIp: elm.PublicIpAddress,
  }))
);

2. 各インスタンスに参加者の公開鍵を登録する

参加者ごとに割り当てたインスタンスの isucon ユーザーに公開鍵を登録していきます。
(サポートのときに入れるように自分の鍵も登録しています。)
鍵はGitHubから取得しています。 e.g.: https://github.com/aki-webii.keys

registerKeys.mjs
#!/usr/bin/env zx

$.verbose = false;

const awsProfileName = '**AWS PROFILE NAME**'
const adminUserGhUserName = 'aki-webii'
const masterSshKeyPath = '**INSTANCE KEY PATH**'
const targetUserName = 'isucon'
const targetUserAuthorizedKeyPath = '/home/isucon/.ssh/authorized_keys'

const describeInstanceResult =
  await $`aws ec2 describe-instances --filters "Name=tag:Name,Values=ISUCON11q_*" --profile ${awsProfileName}`;
const instanceGroups = JSON.parse(describeInstanceResult.stdout).Reservations;

const filteredInstances = instanceGroups
  .filter((elm) =>
    elm.Instances[0]?.Tags.some(
      (tag) => tag.Key === "Name" && /^ISUCON11q_.*/.test(tag.Value)
    )
  )
  .map((elm) => elm.Instances[0]);

await Promise.all(filteredInstances.map(async (instance) => {
  const ghUserName = instance.Tags.find((tag) => tag.Key === "ghName")?.Value;

  try {
    await $`ssh -i ${masterSshKeyPath} ubuntu@${instance.PublicIpAddress} 'curl https://github.com/{${adminUserGhUserName},${ghUserName}}.keys | sudo tee -a ${targetUserAuthorizedKeyPath} && sudo chown ${targetUserName}:${targetUserName} ${targetUserAuthorizedKeyPath} && sudo chmod 600 ${targetUserAuthorizedKeyPath}'`
  } catch (err) {
    console.error(err);
  }
}));

ハンズオンのマニュアル

当日はssh接続確認の初動だけ確認した後は、3〜4人のグループに分かれて、グループごとにハンズオン手順を進めてもらいました。

ハンズオンの題材は、ISUCON11予選としました。
AWS上に参加者分のインスタンスを構築して行いましたが、その際matsuuさんの AMI を活用させて頂きました、感謝致します。

ハンズオン内で取り組んで貰おうしたのは下記3点です。

  • pt-query-digest を使ったslow queryの分析、インデックスの付与
  • alp でのアクセスログ解析のために正規表現を用意
  • データ登録エンドポイントの速度改善のためのbulk insert

実際に用意したマニュアルの一部を抜粋し、別記事として公開しました。
もしハンズオン開催の方が読んでらっしゃる場合は参考になれば幸甚です 🙏
BEENOS社内ISUCONハンズオンマニュアル

振り返り、まとめ

開催後のアンケートでは、下記の様なポジティブな意向、感想を参加頂いた皆さんから頂きました。(原文ママ)

  • 自分の作成して運用しているクエリがまだ早くできるかもしれないのでそこを改善できればしていきたいです。
  • ISUCONについて腰が重かったのでこういった会で半ば強制的に一歩を踏み出せたのはとてもよかったです!
  • 普段とは異なる、フルCUI環境での原因調査や対応を経験できて楽しかったです!
  • 家でもやろうと思いました(自戒)

因みに今後開催されるだろう社内ISUCONでは、皆さん トップ を狙っているようですね。

何位を目指しますか? - トップ: 45.5%

一方で今回のハンズオン会の反省点としては下記のような点を感じています。

  • 題材となるISUCON11予選問題自体の説明が薄かった(質問があったので、当日にISUCON11予選マニュアルとアプリケーション説明動画を共有して補足)
  • VS Codeのリモートでの編集機能の選択肢を提供するなど、vim等に慣れていない向けの配慮をした方が良かった
  • ハンズオン会2時間に対して分量が多く、最後まで手順が行えなかった

今後もブラッシュアップしながら社内でのISUCONハンズオンを引き続き実施していきたい所存です、興味のある人が集まってくれるようならISUCON部として活動していくことも考えられますね。
引き続きよろしくお願い致します、わいわい。

Wanted!!

BEENOSグループでは一緒に働いて頂けるエンジニアを強く求めております!
少しでも気になった方は、社内の様子や大事にしていることなどをThe BEENOSにて発信しておりますので、是非ご覧ください。

https://beenos.com/blog/

とても気になった方はこちらで求人も公開しておりますので、お気軽にご応募ください!
「自分が該当する職種がないな...?」と思った方はオープンポジションとしてご応募頂けると大変嬉しく思います 🙌

世界で戦えるサービスを創っていきたい方、是非是非ご連絡ください!よろしくお願い致します!!

世界で戦えるサービスを創っていく

BEENOS Tech Blog

Discussion