サポート外のリソースを含んだ Helm release を無理やり削除した
久々に helmfile sync を行った結果、PodSecurityPolicy リソースなんか知らないという理由で Upgrade に失敗した。
$ helmfile dep && helmfile sync ... Error: UPGRADE FAILED: resource mapping not found for name: "descheduler" namespace: "" from "": no matches for kind "PodSecurityPolicy" in version "policy/v1beta1" ensure CRDs are installed first
「PSP? なにそれ CRD か何か?」と言われる有様。そういえば、k8s クラスタを最近アップグレードしたときに不要な PSP を消していた気がする。
一旦手動で helm uninstall をしようとするも、同じようなエラーが出て uninstall できない。
$ helm uninstall descheduler -n kube-system --debug uninstall.go:97: [debug] uninstall: Deleting descheduler uninstall.go:119: [debug] uninstall: Failed to delete release: [unable to build kubernetes objects for delete: resource mapping not found for name: "descheduler" namespace: "" from "": no matches for kind "PodSecurityPolicy" in version "policy/v1beta1" ensure CRDs are installed first] Error: failed to delete release: descheduler helm.go:84: [debug] failed to delete release: descheduler helm.sh/helm/v3/pkg/action.(*Uninstall).Run helm.sh/helm/v3/pkg/action/uninstall.go:120 main.newUninstallCmd.func2 helm.sh/helm/v3/cmd/helm/uninstall.go:60 github.com/spf13/cobra.(*Command).execute github.com/spf13/cobra@v1.6.1/command.go:916 github.com/spf13/cobra.(*Command).ExecuteC github.com/spf13/cobra@v1.6.1/command.go:1044 github.com/spf13/cobra.(*Command).Execute github.com/spf13/cobra@v1.6.1/command.go:968 main.main helm.sh/helm/v3/cmd/helm/helm.go:83 runtime.main runtime/proc.go:250 runtime.goexit runtime/asm_arm64.s:1172
どうやら、以前この Chart を install したときには PSP があったので、今回 uninstall するにあったっても PSP を消そうとするが、API の対応がないので落ちているらしい。
ここで、helm はインストールしたときの情報を secrets に保存している。この内容をデコードすると、インストール時の YAML がそのまま格納されている。
$ kubectl describe secret -n kube-system sh.helm.release.v1.descheduler.v15 Name: sh.helm.release.v1.descheduler.v15 Namespace: kube-system Labels: modifiedAt=1687875686 name=descheduler owner=helm status=uninstalling version=15 Annotations: <none> Type: helm.sh/release.v1 Data ==== release: 13804 bytes $ kubectl get secret -n kube-system sh.helm.release.v1.descheduler.v15 -o json | jq .data.release -r | base64 -d | base64 -d | gunzip | jq { "name": "descheduler", "info": { "first_deployed": "2021-05-25T22:28:45.6228274+09:00", ... }, ... "manifest": "---\n# Source: descheduler/templates/podsecuritypolicy.yaml\napiVersion: policy/v1beta1\nkind: PodSecurityPolicy\nmetadata:\n name: descheduler\n annotations:\n seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'\n seccomp.security.alpha.kubernetes.io/defaultProfileName: 'runtime/de...(続く)" ... }
この manifest を使って uninstall しているのでは?と考えて、manifest から PodSecurityPolicy の部分を削って secrets の値を変更してみたところ、ビンゴだった。
$ gzip new_release.json --stdout | base64 -w 0 | base64 -w 0 | pbcopy $ kubectl edit secret -n kube-system sh.helm.release.v1.descheduler.v15 (コピーした内容を release に貼り付ける)
これで無事 PSP を除いた uninstall を行うことができた。
$ helm uninstall descheduler -n kube-system --debug uninstall.go:97: [debug] uninstall: Deleting descheduler client.go:478: [debug] Starting delete for "descheduler" CronJob client.go:478: [debug] Starting delete for "descheduler" ClusterRoleBinding client.go:478: [debug] Starting delete for "descheduler" ClusterRole client.go:478: [debug] Starting delete for "descheduler" ConfigMap client.go:478: [debug] Starting delete for "descheduler" ServiceAccount uninstall.go:150: [debug] purge requested for descheduler release "descheduler" uninstalled
全く同じ内容がドキュメントにあることにその後気づいた。事故らなくてよかったですね…。
今回の事象は Upgrade 時に何も考えずに非互換なリソースを消して済ましたのが原因であり、教訓としては以下になる。
- 非互換なリソースの変更を伴う Cluster upgrade は、そのリソースを触る前に Helm 配下にないか確認する
- 配下にあった場合は Helm を使ってそのリソースをなんとかするべき
curl の trace / trace-ascii が便利だった
curl で通信の内容を見ながら詳細なデバッグをしたいとき、 tcpdump や Wireshark でパケットキャプチャを行うのが定番だと思う。ただ、通信が多い環境だと実際にデバッグしたい通信を絞り込むのが面倒だし、TLS だと暗号化されていてさらに面倒。
curl のオプションを見ると通信内容のトレースを記録できる --trace
や --trace-ascii
があり、使ってみると便利だった。
例
$ curl https://www.google.com/ [--trace-time] --trace[-ascii] trace-result.txt
これで curl が行った通信の内容が trace-result.txt に記録される。-
にすると stdout に出力される。
また、--trace-time
をつけると trace に時刻が付与され、--trace
を --trace-ascii
にすると Hex は保存されず ascii の内容のみ確認することができる。
trace の内容は以下のような感じ(一部省略している)。
== Info: Trying 2404:6800:4004:80b::2004:443... == Info: Connected to www.google.com (2404:6800:4004:80b::2004) port 443 (#0) == Info: ALPN, offering h2 == Info: ALPN, offering http/1.1 == Info: successfully set certificate verify locations: == Info: CAfile: /etc/ssl/certs/ca-certificates.crt == Info: CApath: /etc/ssl/certs => Send SSL data, 5 bytes (0x5) 0000: 16 03 01 02 00 ..... == Info: TLSv1.3 (OUT), TLS handshake, Client hello (1): => Send SSL data, 512 bytes (0x200) 0000: 01 00 01 fc 03 03 ca 7f 00 66 6c 46 6f a9 2c 8f .......^?.flFo.,. 0010: 97 83 1a b8 c7 80 50 6d 0c c8 f8 73 bc 49 90 ab ......Pm...s.I.. 0020: ec 8b bc be 4f 0d 20 a4 f1 17 e2 8c 0d 6f 61 25 ....O. ......oa% 0030: 54 d8 13 27 b6 5d e2 0d 92 de 42 9f 93 2c ce c5 T..'.]....B..,.. 0040: 50 bd 3c 75 b9 bd 86 00 3e 13 02 13 03 13 01 c0 P.<u....>....... 0050: 2c c0 30 00 9f cc a9 cc a8 cc aa c0 2b c0 2f 00 ,.0.........+./. 0060: 9e c0 24 c0 28 00 6b c0 23 c0 27 00 67 c0 0a c0 ..$.(.k.#.'.g... 0070: 14 00 39 c0 09 c0 13 00 33 00 9d 00 9c 00 3d 00 ..9.....3.....=. 0080: 3c 00 35 00 2f 00 ff 01 00 01 75 00 00 00 13 00 <.5./.....u..... 0090: 11 00 00 0e 77 77 77 2e 67 6f 6f 67 6c 65 2e 63 ....www.google.c 00a0: 6f 6d 00 0b 00 04 03 00 01 02 00 0a 00 0c 00 0a om.............. ... <= Recv SSL data, 5 bytes (0x5) 0000: 16 03 03 00 7a ....z == Info: TLSv1.3 (IN), TLS handshake, Server hello (2): <= Recv SSL data, 122 bytes (0x7a) 0000: 02 00 00 76 03 03 1d 3c 9b 52 99 6f e5 05 b7 9f ...v...<.R.o.... 0010: 2b 35 94 0e 11 bd 4f 3a de e3 b8 1d d4 16 a4 45 +5....O:.......E 0020: c4 91 13 b0 18 ef 20 a4 f1 17 e2 8c 0d 6f 61 25 ...... ......oa% ...
TLS で暗号化された内容も、元の HTTP が平文で見れるので便利。
<= Recv header, 13 bytes (0xd) 0000: 48 54 54 50 2f 32 20 32 30 30 20 0d 0a HTTP/2 200 .. <= Recv header, 37 bytes (0x25) 0000: 64 61 74 65 3a 20 54 68 75 2c 20 31 35 20 4a 75 date: Thu, 15 Ju 0010: 6e 20 32 30 32 33 20 30 32 3a 33 37 3a 34 33 20 n 2023 02:37:43 0020: 47 4d 54 0d 0a GMT.. <= Recv header, 13 bytes (0xd) 0000: 65 78 70 69 72 65 73 3a 20 2d 31 0d 0a expires: -1.. <= Recv header, 35 bytes (0x23) 0000: 63 61 63 68 65 2d 63 6f 6e 74 72 6f 6c 3a 20 70 cache-control: p 0010: 72 69 76 61 74 65 2c 20 6d 61 78 2d 61 67 65 3d rivate, max-age= 0020: 30 0d 0a 0.. <= Recv header, 45 bytes (0x2d) 0000: 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 20 74 65 content-type: te 0010: 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 xt/html; charset 0020: 3d 49 53 4f 2d 38 38 35 39 2d 31 0d 0a =ISO-8859-1.. <= Recv header, 245 bytes (0xf5) 0000: 63 6f 6e 74 65 6e 74 2d 73 65 63 75 72 69 74 79 content-security 0010: 2d 70 6f 6c 69 63 79 2d 72 65 70 6f 72 74 2d 6f -policy-report-o ...
eBPF の tc を使ってパケットを触ってみる開発入門資料
eBPF の tc (classifier
) を使ってパケットを触ってみる話をしたので、資料を貼ります。
eBPF は一度慣れるとかなり便利な仕組みで、見るべき場所が分かっていればそれなりに気軽に使うことができる。ただ、ドキュメントがだいぶ整備されてきたとはいえまだまだ最初の一歩の敷居は高いと思う。
この資料は、eBPF 開発の最初の一歩を踏み出したい人向けに作った。 eBPF を書いてデバッグし組み込むには、一体何を行う必要があってどこを見たらいいか?をハマりポイントとともに重点的に書いてみた。
Renovate で Flux 配下の Helm Chart をアップデートする
この記事は KMC アドベントカレンダー 14 日目の記事です。
前回の記事は id:crashrt さんでした。自分も百舌谷さん好きです!
背景
現在、Kubernetes クラスタに入れる Manifest を Flux (Fluxcd) を使って管理している。
Flux には Helm を管理できる機能があって、HelmRelease という Flux の CRD で Chart のバージョンや values を指定したらインストールしてくれる。
クラスタを運用するにあたっては積極的に Chart のバージョンを上げていきたいものの、いちいち Upstream repository を確認しにいくのはめんどくさい。
調べてみると依存パッケージを自動更新してくれることで有名な Renovate が Helm の更新に対応しているということで、導入してみた*1。
Renovate と Flux
Renovate は Flux にしっかり対応していて、HelmRelease ファイルがあれば自動的にバージョン更新の面倒を見てくれる。
ただ、更新を確認するためには HelmRelease とそのオブジェクトが参照している HelmRepository (Helm chart に対応するレポジトリの情報) の紐付けを正しく行う必要があるそうで、
- HelmRelease / HelmRepository ともに namespace が明示的に設定されている必要がある
- HelmRelease / HelmRepository ともに名前が一意じゃないと多分動かない
という制約がある。
ここで、現在クラスタでは Manifest の構成に Kustomize をいろいろ活用しており、
- namepsace は上位の Kustomization で上書きするようにしている
- HelmReelase / HelmRepository の共通パラメータは Component にまとめており、各アプリの Kustomzation でそれを上書きして
namePrefix
でリソース名を変更している
ため、 各 Kustomization 内の HelmRelease / HelmRepository には namespace の記述はなく、また(`namePrefix` が適用される前なので)リソース名も全て同じになっている。
そのため、 Renovate の Flux 機能をそのまま使うことはできなかった。
自前でパースする
Renovate は "Managers" でパースを行って、 パッケージ情報を "Datasource" として抽出し、その Datasource に対して更新のチェックを行う仕組みになっている。
これまでに書いていた話は Flux Manager がパースを行って Helm Datasource を HelmRelease から抽出するものだった。もし Flux Manager がうまく使えなかったとしても、他の Manager を使って Helm Datasource が抽出できれば Renovate はそれに対して更新チェックを行ってくれる。
そこで Regex Manager を使って正規表現で自前でパースするようにした。
具体的には、以下のような HelmRelease / HelmRepository が書かれたファイルに対して
--- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: HelmRepository metadata: name: repo spec: url: https://helm.cilium.io/ --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: release spec: chart: spec: chart: cilium version: 1.12.3 values: ipam: mode: kubernetes
以下のような正規表現を設定して Helm Datasource を抽出する*2。
{ ... "regexManagers": [ { "fileMatch": ["^k8s/apps/.*/helm-overlay.yaml$"], "matchStringsStrategy": "combination", "matchStrings": [ " url: \"?(?<registryUrl>\\S+)\"?\\s*", " chart: \"?(?<depName>[a-z0-9-]+)\"?\\s*", " version: \"?(?<currentValue>\\S+)\"?\\s*" ], "datasourceTemplate": "helm" } ], ... }
設定して数分待つと、ちゃんと Renovate が Helm を認識してくれてアップデートの PR を作ってくれた。
その他
これで Chart の更新が楽になったが、他にも Renovate を運用する上で細々としたポイントがあった。
StabilityDays
新しいバージョンが出てから一週間くらいは様子見したいので、StabilityDays を設定した。
確かに最新のバージョンが Pending になって Push されないようになった一方、1週間経過した(がすでに新しいバージョンが Publish されている)古いバージョンが Stable なものとして Push されるようになった。自分はリリースされてから1週間たっても新しいバージョンがない場合に Stable とみなしたいのであり、古いバージョンが Push されるのは意図した挙動ではない*3。
軽減策として "internalChecksFilter": "none"
を設定して StabilityDays に関わらず常に新しいバージョンを Push させることで古いバージョンが Suggest されることはなくなった。
StabilityDays を有効にすると GitHub PR に StabilityCheck という Status Check が追加されるようになる。最新バージョンが StabilityDays 分経過するまでこの Status が通らないので、 (Automerge を特に設定しない場合は)定期的に PR を見てステータスが緑色であれば確認してマージ、というフローを行うようになった。
StabilityDays が経過する前に新しいバージョンが出た場合は、その新しいバージョンが Push されるのでまたしばらく StabilityCheck が通らないことになる。Grafana のような高頻度で新バージョンがリリースされる Chart の場合、一生マージできなくなるという課題もある*4。
Automerge
Bitnami の Chart などはアプリケーションのバージョンとは関係なく Base image が更新されるだけで新しい Chart がリリースされることがある。そのような些細な変更をいちいち確認するのはめんどくさいので、patch バージョンの変更であれば自動的にマージ(Automerge)するように設定した。
{ ... "packageRules": [ { "matchUpdateTypes": ["patch"], "matchDatasources": ["helm"], "matchCurrentVersion": "!/^0/", "automerge": true } ], ... }
今のところ Renovate のマージを使っているので時間がかかるが、基本的にはうまくマージされている。 遅い問題で困ったら GitHub の Automerge を検討してもいいかもしれない。
うるさい
無限にアップデートがきてダルいという Renovate あるあるな問題もある。 Schedule を設定したら静かになると思いきや、既存の PR には Schedule に関わらず更新を送り続けるので Push 通知がうるさかった。
これに関しては、 "updateNotScheduled": false
を設定することで既存ブランチの更新も Schedule 内におさめるようにすることができた。
ただ、そうすると
月曜に 1.2.1 が出る → 土曜日に更新される → (月曜に 1.2.2 が出るがブランチは更新されない) → 火曜に 8日間たって stabilityDays を通過する → 意図せず 1.2.1 がマージされる
といったロジックで古いバージョンがマージされてしまう可能性がある。その対策として、automergeSchedule
を schedule
の直後に設定するようにして Automerge 発動を PR の更新直後に限定することにした。
Recap
最終的な Config は↓のようになった。将来的には minor も自動マージの対象とするかも。
{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base", ":disableDependencyDashboard" ], "timezone": "Asia/Tokyo", // バージョンが出てから8日間は Unstable とみなして Status check を pending にさせる "stabilityDays": 8, // Fluxcd は色々 flag をつけているので手動でアップデートしたい "flux": { "enabled": false }, "regexManagers": [ { "fileMatch": ["^k8s/apps/.*/helm-overlay.yaml$"], "matchStringsStrategy": "combination", "matchStrings": [ " url: \"?(?<registryUrl>\\S+)\"?\\s*", " chart: \"?(?<depName>[a-z0-9-]+)\"?\\s*", " version: \"?(?<currentValue>\\S+)\"?\\s*" ], "datasourceTemplate": "helm" } ], "packageRules": [ { "matchUpdateTypes": ["patch"], "matchDatasources": ["helm"], "matchCurrentVersion": "!/^0/", "automerge": true } ], // StabilityDays に関わらず、最新の package 情報を PR にする。 Automerge で古いバージョンが StabilityDays を満たしてマージされるのを防ぐため "internalChecksFilter": "none", "schedule": ["before 8am on saturday"], "automergeSchedule": ["after 7am and before 11am on saturday"], // Rebase やバージョン更新も Schedule 内に行わせる "updateNotScheduled": false, "prHourlyLimit": 0, }
Renovate と Helm の組み合わせ、なかなか便利なので使ってみてください!
*1:Dependabot は非対応だった https://github.com/dependabot/dependabot-core/issues/1744
*2:これができたのは HelmRepository と HelmRelease を同じファイルに記述するようにしていたため。別のファイルに書いて場合はどうすればいいのか知らない
*3:これはドキュメントに記載された挙動なので仕方ない
*4:実際になっている
dex と oauth2-proxy と ingress-nginx で OIDC ID token を取得して kubernetes-dashboad にログインする
Kubernetes では kubernetes-dashboard という各種リソースを見るためのダッシュボードが提供されている。
ブラウザ上で色々確認できるので便利なのだが、ログイン時は下のように自身の token か kubeconfig ファイルをアップロードする必要がある。
Kubernetes へのログインで Certificate や Token を使っている場合はそのままアップロードすればいいものの、 OIDC を設定したクラスタで OIDC 経由でログインしている場合は ID token を取得する必要があり、めんどくさい。
そこで、
- oauth2-proxy で kubernetes 向けの OIDC ID token を dex から取得
- ingress-nginx で ID token を Authorization header に Bearer token として付与し、 kubernetes-dashboard に送信
することでシームレスに見れるようにする。
dex
kubernetes が使っている client と同じ client id を使うので dex で新しく client を発行する必要はないが、 Callback URL を新たに登録する必要がある。
staticClients: - id: kubernetes-client redirectURIs: - ... - 'https://{k8s dashboard domain}/oauth2/callback' ...
oauth2-proxy
oauth2-proxy は dex から id token を取得し、 ingress-nginx に Authorization Header として渡す役割を果たす。以下のような環境変数を設定すると良い。
- name: OAUTH2_PROXY_PROVIDER value: oidc - name: OAUTH2_PROXY_OIDC_ISSUER_URL value: {OIDC issuer url} - name: OAUTH2_PROXY_COOKIE_DOMAINS value: {k8s dashboard domain} - name: OAUTH2_PROXY_WHITELIST_DOMAINS value: {k8s dashboard domain} # Re-validate OAuth token per 1 hour - name: OAUTH2_PROXY_COOKIE_REFRESH value: 9m # id token の期限より短くする - name: OAUTH2_PROXY_SCOPE value: "openid profile groups offline_access email" # offline_access は refresh token の取得に使う。 email は oauth2-proxy の動作に必須 - name: OAUTH2_PROXY_SET_AUTHORIZATION_HEADER value: "true"
ポイントは OAUTH2_PROXY_SET_AUTHORIZATION_HEADER
で、これを true にすることで id token を Authorization Header として ingress-nginx に渡すことができる。
ingress-nginx
ingress-nginx は oauth2-proxy にリダイレクト(& reverse proxy)して Authorization Header を取得し、それを付けた上で kubernetes-dashboard に reverse proxy する。
構成としては、oauth2-proxy に reverse proxy するための認証がない Ingress と、kubernetes-dashboard に reverse proxy するための auth_request 付き Ingress を用意することになる。
oauth2-proxy 用 Ingress
oauth2-proxy の認証まわりの endpoint である /oauth2
配下を oauth2-proxy の service にわたす。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" spec: rules: - host: {k8s dashboard host} http: paths: - path: /oauth2/ pathType: Prefix backend: service: name: kubernetes-dashboard-oauth2-proxy port: number: 80
kubernetes-dashboard 用 Ingress
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/auth-response-headers: "Authorization" nginx.ingress.kubernetes.io/auth-url: "https://{k8s dashboard host}/oauth2/auth" nginx.ingress.kubernetes.io/auth-signin: "https://{k8s dashboard host}/oauth2/start?rd=$scheme://$host$escaped_request_uri" spec: rules: - host: kubernetes-dashboard.kmc.gr.jp http: paths: - path: / pathType: Prefix backend: service: name: kubernetes-dashboard port: number: 443
ポイントは nginx.ingress.kubernetes.io/auth-response-headers: "Authorization"
を設定することで、これで oauth2-proxy から来た Authorization Header を Request Header に設定して kubernetes-dashboard にリクエストを送ってくれる。
これで、 kuberentes-dashboard へのリクエストに OIDC で認証された kubernetes において valid な Authorization Header が乗るようになり、ユーザーの権限で Kubenetes dashboard が閲覧・編集できるようになった*1。
*1:最初の Token を求める画面は出なくなる
Filebeat の Nginx module でアクセス先パスを正規化する
今回の ISUCON では Filebeat を使って Nginx のログを Elasticsearch に放り込み、 Kibana ダッシュボードで表示していた。
なかなか役に立ってよかったものの、 /user/:userID/home
のようなパスだと userID に様々な値が入ってしまい、総計では時間がかかっていても上位としてダッシュボードに出てこないという課題があった。
/user/4372904890324/home
のようなパスに対しては /user/:userID/home
といった正規化したフィールドを作ってから Elasticsearch に保存するようにしたい。
Ingest pipeline をいじる
Filebeat の nginx module ではパース処理は基本的に Elasticsearch の Ingest pipeline で行っており、 URL の情報は Ingest pipeline 側で初めてパースして見れるようになる。そのため、 URL に関した処理を行いたい場合は Ingest pipeline 内の URL パース処理の後に追加する必要がある(ずっと Filebeat 側に Processor を追加しようとして本番中も感想戦でもハマっていた…)。
Filebeat 関係の Ingest Pipeline は Filebeat が直接 Elasticsearch に登録するようになっており(filebeat setup --pipelines
コマンドで可能)、その設定は /usr/share/filebeat/module/nginx/access/ingest/pipeline.yml
にある。
このファイルには適用する Ingest pipeline の Processor (Filebeat の Processor とはまた別!)の設定が並んでおり、その末尾に正規化関係の処理を追加すれば良い。
... processors: ... - set: copy_from: url.path field: url_path_normalized - gsub: field: url_path_normalized pattern: "^/user/[a-zA-Z0-9-_]+/gacha/index$" replacement: "/user/:userID/gacha/index" ... (以降正規化したいパスを記載していく)
編集したら filebeat setup --pipelines
コマンドで Elasticsearch に Ingest pipeline を再登録する。登録された様子は Kibana の Management でも確認できる。
これで正規化後のパスが url_path_normalized
フィールドに追加され、 /user/
配下の API に時間がかかっていることが一目で分かるようになった。
めでたし。
ISUCON12 本選に出場した (:old_noto_innocent: Team)
ISUCON 12 本選に出場した。結果は 167492 点で 7 位(結果修正後)。そこそこかな?
アプリケーション周りは utgw の記事を見ると分かりやすいです
予選の記事は↓。ブログ賞受賞とのことで、個人スポンサーグッズをもらえるらしい。ありがとうございます!ありがとうございます!
やったこと
- いつものサーバー周り
Elastic Stack 関係いろいろ
- nginx のパスをいい感じに aggreate して表示しようとしたが、競技中は無理だったので諦めた
- (nginx のログをパースする) ingest pipeline が Elasticseach で処理されるのに気づいておらず、ひたすら Filebeat 側の処理を追加していた
- Filebeat が Elasticsearch に送らなくなったので全てを再構築した
- 結局 Filebeat 周りの設定ミスだったりファイルをうまくつかんでいなかったりが原因だった
- 感想戦では MySQL 周りをブラッシュアップ nonylene.hatenablog.jp
- nginx のパスをいい感じに aggreate して表示しようとしたが、競技中は無理だったので諦めた
バグ探しやレビュー
- nginx のパス aggregate を諦めた後はサーバー側でやることがなくなったのでアプリを見ていた
- utgw の記事で言及されている通り、確率的に落ちる不具合が終盤で発生していた
- はじめは uid が int32 の範囲を超えたときに起きるベンチマーカー or アプリケーションのバグかと疑っていたが、結論としては DB を分割したことに起因して invalid な session が指定された時の挙動が 403 -> 401 に 2/3 の確率で変化したことが原因だった
- Cloud Trace を眺めることで、エラーとなっているリクエストがだいたいどこまで動いているのかを把握できたことで、競技中のバグ発見につながったと思う。入れててよかったトレーシング
- そして爆速修正してくれたチームメイトに感謝
感想
今回は5台あったのでどうなることやらと思ったが、結果的にはいつもの構成とそこまで変わらなかった。考慮する変数が多いのはサーバー担当としてはやりがいがあった。
予選でうまくいった Elastic Stack は、新しく nginx のパスの aggregate を行おうとして失敗したり、 MySQL のクエリが大きすぎてうまくハンドリングできないなど、いくつか課題があった。試行時間も割りと使ってしまったし、その時間があれば上位に行けたかもしれない。次回に向けてブラッシュアップしていきたい。
最近はほとんどアプリを見れていなかったけど、今回は割りとコードを読んでレビューしたり挙動確認したりできた。チームの最終スコアにもつながったのでよかった。
ただ様々な賞に一つも引っかからなかったのはくやしい!次回も頑張りましょう。
今年も ISUCON 楽しかったです。やはり本選は予選で得た反省や知見をすぐ試せるといった点でもいいですね。個人的にも興味のある新しい技術を触るいい機会になりました。チームメイトや運営のみなさん、ありがとうございました!