GitHub Actionsからさくらのクラウド オブジェクトストレージへアクセスする
2月1日よりオープンβとなった新しいさくらのクラウド オブジェクトストレージサービスにGitHub Actionsからアクセスしてみるサンプルです。
オブジェクトストレージは2021年3月31日までオープンベータ期間となります。お試しいただいてフィードバックいただけると嬉しいです! オブジェクトストレージ(β)の利用についてはこちらのニュースを確認してください。
この新しいオブジェクトストレージの特徴の一つとして、Amazon S3と高い互換性を持つAPIの提供があげられ、AWS CLIを通してオブジェクトの操作が可能です。マニュアルはこちらから
ということで、GitHub ActionsからAWS CLIを使ってオブジェクトストレージにアクセスを試してみました。
アクセストークンについて
オブジェクトストレージ利用開始時に、 アクセスキーID と シークレットアクセスキー が表示されます。こちらのアクセスキーは全てのバケットを操作できてしまうので、パーミッションメニューからバケットごとにアクセス件が設定なアクセスキーを生成するのがお勧めです。
(画像はマニュアルより)
アクセスキーを作成したら、GitHub Actionsを設定するrepositoryのSecretsに登録します。
Workflow の作成
GitHub ActionsでAWSへのアクセスを行う際には、
configure-aws-credentials
を使うことで設定をシンプルにすることができますが、このactionの中で aws sts get-caller-identity
相当のAPI呼び出しを行うため、オブジェクトストレージのアクセスキーではエラーになってしまい、今回は使うことができません。
なので、環境変数でアクセスキーを渡すようにしてワークフローを作ったところ動きました。
name: release on: push: branches: - main jobs: renew-cert: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: sync to object storage env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: eu-west-1 run: | aws --endpoint-url=https://s3.isk01.sakurastorage.jp s3 --only-show-errors --delete sync public_html/ s3://my-website-data/public_html/
aws
コマンドに --endpoint-url=https://s3.isk01.sakurastorage.jp
を渡せばオブジェクトストレージにアクセス可能です。
どうぞお試しあれ~
2021/03/03 追記
GitHub Actionsで
<botocore.awsrequest.AWSRequest object at 0x7f22f2d7d630>
のようなエラーが出る際は環境変数 AWS_REGION
を指定するか、awsコマンドに --region
オプションの追加をするとなおります。
参考
dehydrated とさくらのクラウドのDNS機能をつかってワイルドカード・マルチドメイン証明書取得
dehydrated
はshell scriptでできた letsencrypt/acme のクライアントです。certbotやlegoなど様々なツールがある中で、わりと長いこと使ってますが、安定してワイルドカード・マルチドメイン対応の証明書の取得・更新ができています。
dehydratedとさくらのクラウドのDNS機能と先週末つくったクライアント sacloudns
を使うことで、ほぼ自動的に証明書の取得と更新が行えるので、その紹介です。
こちらのツールは、リリースページからのダウンロードもしくはMacなら homebrew
でインストールが可能です。
% brew install kazeburo/tap/sacloudns
dehydrated の準備
dehydrated はGitHubからcloneしてきます
$ git clone https://github.com/dehydrated-io/dehydrated.git $ cd dehydrated
そして設定ファイルを用意します。
まず、 config
ファイルを作成
CERTDIR="${BASEDIR}/certs" ACCOUNTDIR="${BASEDIR}/accounts" HOOK="${BASEDIR}/hook.sh" HOOK_CHAIN="no" CHALLENGETYPE="dns-01" KEY_ALGO="prime256v1"
configのサンプルは docs/example
以下にあります。KEY_ALGOにprime256v1を指定することで楕円曲線暗号であるECDSAを使った証明書を作成できます。デフォルトはRSAになります
つぎに証明書を作成するドメイン一覧を記した domains.txt
を作ります
# スペース区切りで複数のドメインを記せます。ワイルドカードも使えますが行頭にはかけません example.com *.example.com example.jp *.example.jp > certalias chocon.me *.chocon.me
この記事で紹介する方法で証明書を作成する場合、こちらのドメインはすべてさくらのクラウドのDNS機能で管理されている必要があります。試してはないですが、Let's Encryptではマルチドメイン証明書のSAN(Subject Alternative Name)に100個までFQDNを追加することができるようです。
そして最後に、dns-01
で利用するDNSレコードの作成などを行う hook.sh
を用意します。
こちらはAmazon Route 53のクライアント、 cli53
を使う以下コードを参考にして作りました
https://github.com/whereisaaron/dehydrated-route53-hook-script/blob/master/hook.sh
#!/bin/bash set -e # This hook script is written based on dehydrated-route53-hook-script # https://github.com/whereisaaron/dehydrated-route53-hook-script deploy_challenge() { local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}" local ZONE=$(find_zone "${DOMAIN}") if [[ -n "$ZONE" ]]; then echo "Creating challenge record for ${DOMAIN} in zone ${ZONE}" sacloudns radd --wait --zone ${ZONE} --name _acme-challenge.${DOMAIN}. --ttl 60 --type TXT --data ${TOKEN_VALUE} else echo "Could not find zone for ${DOMAIN}" exit 1 fi } clean_challenge() { local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}" local ZONE=$(find_zone "${DOMAIN}") if [[ -n "$ZONE" ]]; then echo "Deleting challenge record for ${DOMAIN} from zone ${ZONE}" sacloudns rdelete --zone ${ZONE} --name _acme-challenge.${DOMAIN}. --type TXT --data ${TOKEN_VALUE} else echo "Could not find zone for ${DOMAIN}" exit 1 fi } deploy_cert() { local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}" # NOP } unchanged_cert() { local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" # NOP } function invalid_challenge { local DOMAIN="${1}" RESPONSE="${2}" local HOSTNAME="$(hostname)" (>&2 echo "Failed to issue SSL cert for ${DOMAIN}: ${RESPONSE}") } function get_base_name() { local HOSTNAME="${1}" if [[ "$HOSTNAME" == *"."* ]]; then HOSTNAME="${HOSTNAME#*.}" echo "$HOSTNAME" return 0 else echo "" return 1 fi } function find_zone() { local DOMAIN="${1}" local ZONELIST=$(sacloudns list | jq -r '.DNS[].DNSZone'|xargs echo -n) local TESTDOMAIN="${DOMAIN}" while [[ -n "$TESTDOMAIN" ]]; do for zone in $ZONELIST; do if [[ "$zone" == "$TESTDOMAIN" ]]; then echo "$zone" return 0 fi done TESTDOMAIN=$(get_base_name "$TESTDOMAIN") done return 1 } # # This hook is called at the end of a dehydrated command and can be used # to do some final (cleanup or other) tasks. # exit_hook() { : } HANDLER="$1"; shift if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|invalid_challenge|request_failure|exit_hook)$ ]]; then "$HANDLER" "$@" fi
さくらのクラウドのAPIキーを環境変数を設定
さくらのクラウドのコントロールパネルからAPIキーを作成し、SAKURACLOUD_ACCESS_TOKEN
と SAKURACLOUD_ACCESS_TOKEN_SECRET
の環境変数に設定します。
export SAKURACLOUD_ACCESS_TOKEN=xxx export SAKURACLOUD_ACCESS_TOKEN_SECRET=yyy
sacloudns は作業ディレクトリに、.env
ファイルを作成し、
SAKURACLOUD_ACCESS_TOKEN=xxx SAKURACLOUD_ACCESS_TOKEN_SECRET=yyy
のように記すと、コマンド実行時に自動で読み込むこともできます。
dehydrated の実行
準備ができたのでdehydratedを実行
# 初回はアカウントの作成が必要 $ ./dehydrated --register --accept-terms -f config $ ./dehydrated -c -f config
これで、DNSレコードの作成と検証が行われたのち、証明書が certs
ディレクトリ以下に作成されます。
% ls -l certs/chocon.me cert-1612763291.csr cert-1612763291.pem cert.csr -> cert-1612763291.csr cert.pem -> cert-1612763291.pem chain-1612763291.pem chain.pem -> chain-1612763291.pem fullchain-1612763291.pem fullchain.pem -> fullchain-1612763291. privkey-1612763291.pem privkey.pem -> privkey-1612763291.pem
証明書は nginx や Apache などのWebサーバや各クラウドにアップロードして使うこともできます。
おまけ: さくらのクラウド エンハンスドロードバランサーへの証明書アップロード
取得した証明書をさくらのクラウドのエンハンスドロードバランサーに設定してみます。
エンハンスドロードバランサーとは大規模なHTTP/HTTPSサービスに最適な高性能・高機能なロードバランサアプライアンスです。詳しくはこちら
こちらも参考になります。
エンハンスドロードバランサーの構築が済んでいるといるとして、以下のようにすると証明書が登録できます。
$ cat template.jq { "ProxyLB": { "PrimaryCert": { "ServerCertificate": $ServerCertificate, "IntermediateCertificate": $IntermediateCertificate, "PrivateKey": $PrivateKey }, "AdditionalCerts": [] } } $ jq -n \ -f template.jq --rawfile ServerCertificate certs/chocon.me/cert.pem \ --rawfile IntermediateCertificate certs/chocon.me/chain.pem \ --rawfile PrivateKey certs/chocon.me/privkey.pem \ | \ curl -d @- -X PUT \ -H "Content-Type: application/json" \ --user $SAKURACLOUD_ACCESS_TOKEN:$SAKURACLOUD_ACCESS_TOKEN_SECRET \ https://secure.sakura.ad.jp/cloud/zone/is1a/api/cloud/1.1/commonserviceitem/${リソース番号}/proxylb/sslcertificate
jqのテンプレート機能と、rawfileオプションとても便利ですね!
なお、この方法で証明書を登録した場合は、let's encryptでの証明書取得・更新機能は停止しておく必要があります。
エンハンスドロードバランサーのlet's encryptを利用した証明書取得では、1つのロードバランサーあたり、1個のドメインの証明書しか作れませんが、この方法だとそれよりも多くの証明書を登録できるので、たくさんのドメインを利用される場合は便利かもしれません。
最後に、ここまでの方法を一つのシェルスクリプトにまとめるなど自動化しておくのがいいでしょう。
何かのお役に立てれば幸いです。
さくらインターネット株式会社に入社しました
あっという間に1週間経ってしまいましたが、2021年1月18日より、さくらインターネットにてお仕事をしております。
先月から長めの冬休みにさせていただいておりましたが、本日さくらインターネットに入社させて頂きました。公私ともに長くお世話になっているこの会社でいろんなサービス支えていきます。
— スタンドの時間です! (@kazeburo) 2021年1月18日
1日目からリモートなので写真はイメージです pic.twitter.com/LqyOAoA5dA
前職中は本当にたくさんの皆様にお世話になりました。ありがとうございます。挨拶もほとんど出ておらず申し訳ありません。
さくらインターネットは20年ほどユーザとして使っており、またmixiやメルカリでもサーバを利用しており、公私共に馴染みのある会社です。
2018年の北海道胆振東部地震の際には対応はもちろん、情報がまとまってない中で直接お話を聞く機会をいただいたり、逐次DMなどで情報をいただくなど大変お世話になったのは強く印象に残っています。そのほか、ISUCON9でも問題の出題を一緒にやらせていただいたり、サイボウズ様、jig.jp様との共催で行われた小学生向けのIoTプログラム教室などにも息子とともに参加させていただきました。このためかさくらインターネットに転職をすることを息子に伝えると、ニンマリと喜んでおりました。
これまでのキャリアではWebサービスのパフォーマンス改善、運用などに携わっていたわけですが、さくらインターネットは少し異なるレイヤーへの挑戦になります。今までの経験を生かしつつ、新しいチャレンジも行い、さくらインターネットのお客様とそのお客様にとってよりよいサービスを提供していけるようやっていきます。
もし、さくらインターネットの利用に関わらずWebサービスのパフォーマンスや運用での悩みなどがありましたら、力になれることがあるかと思いますのでtwitterなどで相談くださいませ。
COVID-19の緊急事態宣言が出ているなか、初日からリモートとなかなか難しい船出になっておりますが、素晴らしい同僚の力を借りつつ、一つ一つやりはじめております。今後ともよろしくお願いします。
GitHub Actionsからさくらクラウドのコンテナレジストリ(Lab)にイメージをpushする
こちらのリポジトリが完成形です。
ここで紹介しているコンテナレジストリはLabプロダクトになります。「Labプロダクト」では、新機能の動作検証およびフィードバック収集を目的として、さくらのクラウドにおける開発中サービスを実験的に提供しておりますので、ご注意ください。
マニュアルはこちら
「Labプロダクトとは」も合わせてご確認ください。
コンテナレジトストリの作成
さくらのクラウドのメニューからコンテナレジトストリを選び、「追加」ボタンを押します。
公開・非公開は目的に応じて選択してください。
コンテナレジストリができたら、push/pullするためのユーザを作成します。
権限も適したものを選びましょう。今回はGitHub Actionsからpushを行うので、「Push & Pull」がよさそうです。
このアカウントはコンテナレジストリにのみ使われるアカウントになります。
GitHubリポジトリにシークレット登録
コンテナイメージを作成するリポジトリの Settings -> Repository secrets にて、先ほど作成した id とパスワードを SAKURACR_USER、SAKURACR_PASSWORDとして登録しました。
workflow YAMLの作成
リポジトリの .github/workflow/
以下にコンテナイメージの作成とコンテナレジストリ(Lab)にpushするためのYAMLファイルを作成します。
さくらのコンテナレジストリ(Lab)はDocker開発元が提供するDocker Hub互換のレジストリサービスであり、プロトコルにも互換性があります。GitHub Container RegistryやAWSのコンテナレジストリにpushする際に利用できる docker/login-action
と docker/build-push-action
の Actionを特にカスタマイズすることなく使えます。
http-dump-requestでは次のようにファイルを作成しました。
name: release on: # gitのtagが作られた時 push: tags: - "v[0-9]+.[0-9]+.[0-9]+" jobs: build_and_push: runs-on: ubuntu-latest env: IMAGE_NAME: http-dump-request REGISTRY: nomadscafe.sakuracr.jp steps: - name: checkout uses: actions/checkout@v2 # tagを入手 - name: Set tag to environment variable id: set-tag run: echo ::set-output name=version::${GITHUB_REF#refs/*/} - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Login to Sakura Container Registry uses: docker/login-action@v1 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.SAKURACR_USER }} password: ${{ secrets.SAKURACR_PASSWORD }} - name: Build and push uses: docker/build-push-action@v2 with: context: . push: true tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.set-tag.outputs.version }}
このファイルをコミットしgit tagを作成すると、狙い通りイメージの作成とさくらのコンテナレジトストリ(Lab)へのpushができました。
2021年1月現在、コンテナレジストリ(Lab)にはイメージの一覧機能がないので、今回つくったコンテナは以下のコマンドでレジストリからpullして実行ができることで動作を確認しました。
$ docker run -p 3000:3000 nomadscafe.sakuracr.jp/http-dump-request
おまけ
http-dump-request について
似たようなモノはたくさんありそうですが、検証の際に使える http request をそのままレスポンスとして返す Go のサーバです。
こんな感じで表示できます。上記で紹介しておりますが、 nomadscafe.sakuracr.jp/http-dump-request
から docker pull
して利用できます。
2020年に買って良かったもの
ほぼ1年間家で仕事をすることになり、自宅の机まわりの環境をこれまで以上に整えてきた1年だったので「#買って良かった2020」というお題で書いてみる。
ヘッドホンとマグカップ
奥さんから誕生日にもらったソニーの WH-H910N。色はアッシュグリーンです。ありがとう。
ワイヤレスで軽くてつけ心地がよく、ノイズキャンセルもあってとても気に入っています。
夏は冷たい飲み物、冬は暖かい飲み物を飲むために買ったKEYUKAのマグカップ。偶然にもヘッドホンと同じ色味でいい感じです。
また、Apple Musicのサブスクリプションも加入しました。
仕事しながら聞いてますが、「おしりたんてい」の「ププっとふむっと解決ダンス」だったり鬼滅の刃の曲が最もよくかかっているような気がします。
椅子
家で長く座って作業ができる椅子がない、ということで在宅勤務になってまず購入したのが椅子。
オフィスチェアの多くは黒い色ですが、割と大きい黒い物体が家にあると、家の中が暗くなりそうということで、オカムラのシルフィーのホワイトフレーム、座面はベージュを選びました。
届くまでに1ヶ月以上かかり、その間は折り畳み椅子でなんとかなんとか過ごしてましたが、背面フレームのデザインもよく満足。
椅子と言えば、ISUCON10個人スポンサーでいただいたアウトドアチェアもよい。
子供たちがよく座っています。941さんありがとう。
ちなみに机は場所的な問題で、ニトリの小さめの机
キーボード
今はBAROCCO MD770 RGB BTを使ってます。真ん中にMagic Trackpadをおくスタイル。左右分離型だいぶ慣れてきましたが、電話をしながら片手で打てないという弱点があります。
Magic Trackpadをそのままおくと低すぎて使いにくいので、百均で買った耐震マットを二枚重にして下にくっつけてます。
普段はFILCOの左右分離型のウレタンリストレストをキーボードの前において使っています。
冬対策
窓の近くに机を置いているので、寒いということで、こちらのパネルヒーターを買ってみた。
足の裏はもちろん、ブランケットを上にかければ簡易こたつになりとても暖かい。奥さんも小さいホットカーペットを買って机の下で使っています。
Apple Watch
Series 6を初購入。
Apple Watch Hermèsの赤色のレザーストラップにしました。1時間に1度立ち上がったり、20秒手を洗っているだけで褒めてくれるので自己肯定感がある。運動も気にし。。。
スタンドはEINBANDさんのAppleWatch木製スタンド。充電器のところはぴったりとハマったりではないので剥がせるタイプの両面テープで固定している。
最近はRaspberry Piを買ったので、homebridgeいれてApple WatchのホームアプリやSiriからLチカして喜んでいます。
来年は何買うかな~
ISUCON10予選問題で EC2 c6g.2xlarge vs c5.2xlarge をやってみる
少し前にISUCON10予選問題のスコアアップに取り組んでいたので、その成果を使い、EC2 Graviton2 インスタンスとIntel CPUのインスタンスでスコアを比較してみます。
ISUCON10予選問題チャレンジをしていた時のスレはこちら
ISUCON10予選のベンチマーク、Macbook Pro上で、23000まででるようになった。Xeon Gold 6148 x2上だと37000。
— kazeburo. (@kazeburo) 2020年10月28日
ここではISUCON10予選時のように複数台用意するのではなく、1台のサーバでGoのアプリケーションとMySQLを動作させ、同じサーバ上からベンチマークを実行しています。
準備
初期データを作る際のdockerコンテナでarm64対応していないものがあるため、Intel CPUのインスタンスで作成してscpする方法を取りました。
まず、 c5.2xlarge
のインスタンスを起動して準備していきます
# 依存のインストール sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common python3-pip git curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # Dockerをいれる sudo add-apt-repository \ "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io sudo gpasswd -a $USER docker # 一度logout & login # docker-compose を pip でインストール sudo pip3 install -U docker-compose # Goのインストール curl -fsSL https://golang.org/dl/go1.15.6.linux-$(dpkg --print-architecture).tar.gz | sudo tar -C /usr/local -xzf - export PATH=$PATH:/usr/local/go/bin echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee /etc/profile.d/go.sh # 初期データ作成で使うコマンドのインストール go get github.com/orisano/wayt export PATH=$PATH:$HOME/go/bin echo 'export PATH=PATH=$PATH:$HOME/go/bin' | tee -a ~/.bashrc # ISUCON10 予選問題をgit cloneして初期データ作成 git clone https://github.com/isucon/isucon10-qualify.git cd isucon10-qualify/initial-data pip3 install -r requirements.txt make
初期データ作成時に一度止まってしまいましたが、Ctrl-Cしてもう一度実行したらうまく動きました。
MySQLをインストールして、ユーザ作成をします。簡単なパスワードになるので気をつけましょう。
sudo apt install mysql-server sudo mysql > CREATE USER 'isucon'@'%' IDENTIFIED BY 'isucon'; > GRANT ALL ON *.* TO `isucon`@`%`; > CREATE DATABASE isuumo;
ここまでできたら c6g.2xlarge
側の準備をします。
c6g.2xlarge
でインスタンスを起動し、
sudo apt-get update sudo apt-get install -y mysql-server git make export PATH=$PATH:/usr/local/go/bin echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee /etc/profile.d/go.sh sudo mysql > CREATE USER 'isucon'@'%' IDENTIFIED BY 'isucon'; > GRANT ALL ON *.* TO `isucon`@`%`; > CREATE DATABASE isuumo;
このあと、Intel CPUのインスタンスから、isucon10-qualify
ディレクトリを scpで Graviton2 のインスタンスにコピーすれば準備完了です。
初期状態でのスコア比較
まず、Intel CPUである c5.2xlarge での初期スコア
isuumoアプリを make
して起動
cd isucon10-qualify/webapp/go make ./isuumo
別ターミナルを開き、ベンチマークを実行します。
cd isucon10-qualify/bench make ./bench
初期スコア
初期状態ではタイムアウトも多くますが、最終的に以下のスコアになりました。
c5.2xlarge
での初期スコア
2020/12/23 13:26:06 bench.go:102: 最終的な負荷レベル: 8 {"pass":true,"score":1335,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":34}],"reason":"OK","language":"go"}
c6g.2xlarge でも同じようにターミナルを2つ開いてアプリケーションとベンチマークを実行します。
c6g.2xlarge
での初期スコア
2020/12/23 14:18:14 bench.go:102: 最終的な負荷レベル: 9 {"pass":true,"score":1552,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":48}],"reason":"OK","language":"go"}
初期状態では、c6g.2xlarge
の方が 16%
ほどスコアがよいという結果になりました。
ベンチマーク時の負荷の様子を top
コマンドでみると次のようになっておりました。
c5.2xlarge
top - 14:15:39 up 10 min, 3 users, load average: 7.18, 2.64, 0.96 Tasks: 146 total, 2 running, 144 sleeping, 0 stopped, 0 zombie %Cpu(s): 85.6 us, 3.2 sy, 0.0 ni, 10.3 id, 0.5 wa, 0.0 hi, 0.4 si, 0.0 st MiB Mem : 15549.2 total, 12448.3 free, 929.9 used, 2171.0 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 14411.4 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 777 mysql 20 0 2989500 506064 37208 S 638.9 3.2 9:38.23 mysqld 1331 ubuntu 20 0 1526204 34796 7164 S 44.9 0.2 0:36.05 isuumo 1486 ubuntu 20 0 1521292 139536 7692 S 30.0 0.9 0:07.15 bench
c6g.2xlarge
top - 14:18:03 up 16 min, 3 users, load average: 5.98, 2.22, 1.13 Tasks: 189 total, 2 running, 187 sleeping, 0 stopped, 0 zombie %Cpu(s): 92.9 us, 4.0 sy, 0.0 ni, 1.7 id, 0.3 wa, 0.0 hi, 1.1 si, 0.0 st MiB Mem : 15709.3 total, 11525.9 free, 880.3 used, 3303.1 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 14661.9 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3516 mysql 20 0 3049328 490156 34860 S 707.0 3.0 11:29.75 /usr/sbin/mysqld 4896 ubuntu 20 0 1598624 40468 6208 S 39.5 0.3 0:46.90 ./isuumo 5193 ubuntu 20 0 1661656 153140 6860 S 33.9 1.0 0:13.45 ./bench
明らかにMySQLがボトルネックですが、c6g.2xlarge の方がidleが少なくCPUをよく使えているようにみえます。
感想戦ブランチでのスコア比較
次に、私のブランチへ切り替えてスコア比較をしてみます。
ブランチはこちら
効果があまりなかったものもありますが、主な変更点は次のようになります。
- nazotte検索に空間インデックスを利用
- 検索用テーブルと、データ用テーブルを分離してMySQLの負荷を削減
- 検索用インデックスの追加。
- 検索時にrangeクエリにならないようrangeIDカラムを作成しtriggerにてデータ投入
- 検索時にSQL_CALC_FOUND_ROWSを利用。結果をキャッシュしておき、同条件での検索時はSQL_CALC_FOUND_ROWSをつけずにキャッシュを利用する
- chairとestateは全てインメモリキャッシュ。検索時は
SELECT id
をする - 検索結果やchairとestateをキャッシュする際は、JSON化したものをキャッシュする
- bot判定を最適化
- fiber (fasthttp) へのフレームワーク変更
- MySQLに秘伝のタレを入れる
いれたMySQLに秘伝のタレ
disable-log-bin innodb_doublewrite = 0 innodb_flush_log_at_trx_commit = 0 innodb_flush_method = O_DIRECT innodb_adaptive_hash_index = 0
感想戦ブランチでのスコア
c5.2xlarge
での感想戦ブランチスコア
$ ./bench 2020/12/23 14:34:27 bench.go:78: === initialize === 2020/12/23 14:34:32 bench.go:90: === verify === 2020/12/23 14:34:32 bench.go:100: === validation === 2020/12/23 14:34:34 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:39 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:39 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:35:32 bench.go:102: 最終的な負荷レベル: 11 {"pass":true,"score":41462,"messages":[],"reason":"OK","language":"go"}
初期状態では負荷レベルが最大の11までには届きませんでしたが、ここでは7秒ほどで最大に達し、最終スコアは4万を超えてきました。これはMacbook Pro上でやっていたときより若干良い結果
c6g.2xlarge
での感想戦ブランチスコア
ubuntu@ip-10-0-11-141:~/isucon10-qualify/bench$ ./bench 2020/12/23 14:36:20 bench.go:78: === initialize === 2020/12/23 14:36:30 bench.go:90: === verify === 2020/12/23 14:36:31 bench.go:100: === validation === 2020/12/23 14:36:33 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:34 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:35 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:37:31 bench.go:102: 最終的な負荷レベル: 11 {"pass":true,"score":39460,"messages":[],"reason":"OK","language":"go"}
Intel CPUに比べると5%ほど低いスコアです。何回か実行してみましたが、4万に届くことはありませんでした。
ベンチマーク実行中の負荷をtopコマンドでみると次のようになっていました。
c5.2xlarge
top - 14:45:24 up 39 min, 3 users, load average: 3.68, 2.66, 2.15 Tasks: 147 total, 2 running, 145 sleeping, 0 stopped, 0 zombie %Cpu(s): 57.6 us, 4.3 sy, 0.0 ni, 36.8 id, 0.0 wa, 0.0 hi, 1.3 si, 0.0 st MiB Mem : 15549.2 total, 10447.3 free, 2096.7 used, 3005.3 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 13346.3 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3879 ubuntu 20 0 1663700 163836 7724 S 277.3 1.0 1:00.39 bench 1691 mysql 20 0 3335408 651500 37268 S 189.7 4.1 22:25.35 mysqld 3650 ubuntu 20 0 2572288 1.0g 15200 R 45.0 6.7 2:13.37 isuumo
c6g.2xlarge
top - 14:40:43 up 39 min, 3 users, load average: 1.44, 1.86, 1.69 Tasks: 185 total, 1 running, 184 sleeping, 0 stopped, 0 zombie %Cpu(s): 58.2 us, 3.6 sy, 0.0 ni, 37.0 id, 0.0 wa, 0.0 hi, 1.2 si, 0.0 st MiB Mem : 15709.3 total, 9707.4 free, 2011.2 used, 3990.7 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 13669.2 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 7423 ubuntu 20 0 1810080 205848 6788 S 268.8 1.3 0:59.13 ./bench 5417 mysql 20 0 3724652 652440 35008 S 203.0 4.1 18:10.73 /usr/sbin/mysqld 7352 ubuntu 20 0 2499408 986528 14492 S 38.9 6.1 0:50.62 ./isuumo
感想戦ブランチでは全体のCPU使用率が下がっています。大きな差ではありませんが、Intel CPUの方がMySQLのCPU使用率が小さくベンチマークとisuumoアプリのCPU使用率が大きいようにみえます。
感想など
tasksetコマンドを使い、benchコマンドが使うCPUを1個に絞った結果も合わせたグラフが次ようになります。
Graviton2はIntel CPUのインスタンスに比べてシングルスレッドのパフォーマンスが低めというベンチマーク結果もありますが、
この結果からも初期状態ではMySQLでのマルチスレッド性能が、感想戦ブランチではベンチマーカーのシングルスレッド性能がスコアに影響しているのかなと考えております。
感想戦ブランチではGraviton2のインスタンスの方がスコアが低いという結果になってしまいましたが、インスタンスのコストはGraviton2の方が20%ほど低く、スコアをコストで割った数値ではGraviton2の方が高くなるでしょう。
armなインスタンスの利用ではミドルウェアのパッケージやdockerのイメージが用意されていないことがあるなど、いくつか困難にぶつかることはありますが、パフォーマンスがよくコストメリットも大きいのでやはり使っていくといいんじゃないかと思いますね~
Re: 結局、Go言語をやめる理由はなかった件
すみません、ISUCONのアレに火がついてしまったので..。 Advent Calendarとはとくに関係がありません。
こちらの記事をみて、気になってしまったので interpolateParams
の追加とMySQLチューニングをしてベンチマークを回してみました。
ベンチマークの環境が公開されているのは素晴らしいですね。
8vCPU/16GB Memのc5a.2xlargeなEC2のインスタンスを起動して、以下の手順にしたがってdockerとdocker-composeをインストールしました。OSはUbuntu 20.04を使いました。
上記のbenchmarkのrepositoryをgit cloneし、compose build && compose upします。
$ git clone https://github.com/okdyy75/bench-docker.git $ sudo docker-compose build $ sudo docker-compose up -d
まず何も変更しない状態でのスコア結果は、
$ sudo docker-compose run --rm python sh -c 'cd python; pip install -r ./requirements.txt && time python bench.py' 平均秒数:9.670429 real 1m 36.73s user 0m 6.25s sys 0m 2.79s
Go言語
$ sudo docker-compose run --rm golang sh -c 'cd go; go build . && time ./go' 平均秒数:11.70815524 (0x0,0x0) real 1m 57.08s user 0m 3.84s sys 0m 10.83s
たしかに、Go言語の結果の方が少し悪くなっています。
ここで元記事にてmethaneさんから指摘のあった interpolateParams
の設定をいれます。
diff --git a/www/go/bench.go b/www/go/bench.go index 5c56714..a4b86d0 100644 --- a/www/go/bench.go +++ b/www/go/bench.go @@ -37,7 +37,7 @@ func getDB() *sql.DB { } dsn := fmt.Sprintf( - "%s%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true", + "%s%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true&interpolateParams=true", user, password, host, port, name) db, err := sql.Open("mysql", dsn)
そうすると、結果は
平均秒数:10.540287 real 1m 45.40s user 0m 2.99s sys 0m 6.26s
となり、pythonに追いつかないまでも、改善はしています。
ところで、ベンチマークを何回か動かしながら気になったのは、スコアが振れが大きく、iowaitがそれなりでているとところです。(Maki-Daisukeさんはioの影響を抑えるためNVMe SSDのついたインスタンスを使われておりました)
$ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 0 1 0 11964048 140004 3405756 0 0 0 7872 8115 13468 1 2 95 2 0 0 0 0 11964048 140004 3405932 0 0 0 7656 6939 11857 1 2 93 4 0 0 1 0 11956236 140004 3414672 0 0 0 16284 7068 12074 1 1 94 4 0 0 1 0 11956236 140004 3414672 0 0 0 7768 8375 14349 1 2 94 3 0 1 0 0 11956236 140004 3414672 0 0 0 7464 7463 12275 2 1 96 2 0 0 1 0 11955480 140004 3415720 0 0 0 8780 7971 12701 2 1 97 0 0 0 1 0 11955480 140004 3415720 0 0 0 7712 8561 13845 1 2 97 0 0 0 1 0 11955480 140004 3415720 0 0 0 7780 7966 13032 1 1 96 1 0 0 1 0 11954472 140004 3416496 0 0 0 8788 7051 12944 2 1 90 7 0 0 1 0 11964536 140004 3405576 0 0 0 9592 9176 16006 2 1 92 4 0 2 0 0 11964284 140004 3405832 0 0 0 8220 7737 13731 1 1 92 5 0 1 0 0 11964252 140004 3405928 0 0 0 8056 8078 12893 1 1 97 0 0 0 1 0 11955416 140004 3414756 0 0 0 16132 6565 11829 1 1 91 7 0 2 0 0 11955416 140004 3414756 0 0 0 7056 7333 12482 1 1 94 3 0
また、CPUも全然使えてません。
これはMySQL側がボトルネックになっている可能性が高いとみて、対ISUCON用秘伝のタレを投入して、再度実行してみました。
my.cnf を作成。4つのパラメータを追加
$ cat docker/mysql/my.cnf [mysqld] innodb_doublewrite = 0 innodb_flush_log_at_trx_commit = 2 innodb_flush_method = O_DIRECT_NO_FSYNC innodb_adaptive_hash_index = 0
compose.ymlに追記して、mysqlを再起動
$ git diff diff --git a/docker-compose.yml b/docker-compose.yml index 94b3dc7..e39de9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,7 @@ services: volumes: - "./.data/mysql:/var/lib/mysql" - "./log/mysql:/var/log/mysql" + - "./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf" ports: - "3306:3306" # php-fpm ################################################ $ sudo docker-compose restart mysql Restarting bench-docker_mysql_1 ... done
結果は
平均秒数:1.518821 real 0m 15.21s user 0m 6.12s sys 0m 1.69s
Go言語(interpolateParams=false)
平均秒数:1.770360 real 0m 17.70s user 0m 3.04s sys 0m 7.85s
Go言語(interpolateParams=true)
平均秒数:1.196632 real 0m 11.96s user 0m 2.26s sys 0m 4.95s
interpolateParamsが有効のGo言語が最速になりました。最初の10倍近い高速化ですね。
MySQLが高速化したことで順位が入れ替わるところはもう少し深堀してもいいかと思いますが、Pythonにしたってかなり速くなってますので、これくらいなら正直どの言語でやってもいいのではないかと思います。仲良くしましょう
Go言語を実行中のvmstatは以下のようになりました。
vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 0 12075152 140028 3363636 0 0 10 453 441 22 1 1 98 1 0 0 0 0 12075152 140028 3363636 0 0 0 0 139 257 0 0 100 0 0 0 1 0 12015176 140028 3387952 0 0 0 25772 1950 2773 5 1 93 1 0 1 0 0 11968016 140028 3391408 0 0 0 12732 31447 60372 9 7 84 1 0 1 0 0 11967732 140028 3391376 0 0 0 4660 42484 86361 7 8 85 0 0 1 0 0 11967748 140028 3391360 0 0 0 14592 35933 73675 6 6 87 1 0 1 0 0 11967256 140028 3391228 0 0 0 6964 38245 77879 7 7 86 0 0 2 0 0 11966928 140028 3391416 0 0 0 13028 36928 75262 6 7 87 0 0 2 0 0 11966816 140028 3391440 0 0 0 7352 37506 76638 7 7 86 0 0 1 0 0 11968984 140028 3391440 0 0 0 14120 32657 71858 8 7 85 0 0 1 0 0 11969976 140028 3391468 0 0 0 15564 33490 69433 7 7 86 1 0 2 0 0 11969976 140028 3391468 0 0 0 3392 40593 82689 7 8 85 0 0 1 1 0 11971740 140028 3391440 0 0 0 15568 35473 72960 6 7 86 1 0 2 0 0 11973520 140028 3391476 0 0 0 5420 36698 76628 8 7 85 0 0 1 0 0 11973344 140028 3391476 0 0 0 13000 37315 77527 6 7 87 0 0 1 0 0 11973392 140028 3391536 0 0 0 8284 34950 71476 6 8 86 0 0 2 0 0 11973408 140028 3391536 0 0 0 13840 38778 79834 6 7 86 1 0 1 0 0 11974132 140028 3391544 0 0 0 8692 37283 74883 7 7 86 0 0 3 0 0 11974132 140028 3391544 0 0 0 10444 37224 77928 6 8 86 0 0 2 0 0 11973108 140028 3391608 0 0 0 15688 34599 71052 6 7 86 1 0 0 0 0 11980788 140028 3391728 0 0 0 6868 33311 72294 7 8 84 0 0 0 0 0 12069692 140028 3363668 0 0 0 2588 2979 4393 1 1 98 0 0
iowaitが小さくなり、user, systemが以前より高くあがるようになりました。
topだと
top - 07:43:15 up 2:16, 2 users, load average: 0.43, 0.11, 0.05 Tasks: 169 total, 1 running, 168 sleeping, 0 stopped, 0 zombie %Cpu(s): 6.2 us, 3.9 sy, 0.0 ni, 85.9 id, 0.4 wa, 0.0 hi, 3.6 si, 0.0 st MiB Mem : 15827.2 total, 11689.1 free, 689.4 used, 3448.7 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 14833.8 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 44962 systemd+ 20 0 1377796 216128 20612 S 77.4 1.3 1:29.73 mysqld 47046 root 20 0 710064 10828 3836 S 60.5 0.1 0:06.25 go
ようになり、1vCPU 使い切りそうなぐらいまできてました。
ちなみに、MySQLのデータディレクトリをみんな大好きtmpfsにおくと、
Go言語(interpolateParams=true)
平均秒数:1.106469 real 0m 11.06s user 0m 2.45s sys 0m 5.07s
平均秒数:1.394543 real 0m 13.97s user 0m 6.15s sys 0m 1.70s
もう少しだけ速くなります。
まとめ
ミドルウェア含めたベンチマークを行う時は、ミドルウェアやOSの設定なども気を配るのがいいですね。広い視点を保つためもにもISUCONは役に立つ。