Posts for: #2022/08

コワーキングのオンラインイベントに参加した

0時に寝て6時に起きた。

バッチ処理一覧と手動実行

2週間前からフロントエンドの画面作りを始めた。2つ作らないといけない画面があってそのうちの1つを作るのに1週間ちょっとかかった。最初に作る画面で次の順番で作業を進めた。

  1. web api のエンドポイントの整備
  2. ページング処理
  3. 検索フォームのコンポーネント作り
  4. v-data-table の slot 埋め込み
  5. モーダルダイアログと更新処理

やりたいところに関係する vuejs, next, vuetify の機能を調べたり、イベントの伝搬の仕組みを調べたりしながら作成した。一度理解したら簡単なので2つ目の画面は半日で完成して pr を出して、もうそのまま本番環境にデプロイした。1つ目の画面の方が要件が複雑で2つ目の方が簡単だったというのもあるけど、どちらもストーリーポイント5が割り当てられているチケットの作業工数は1週間強と4時間といったものになった。なんというか、ストーリーポイントは中長期でみれば、このような人間が成長して一定期間内に消化できるポイントが増えることを計測する狙いもあるけれど、短期でみたらまったくプロジェクトマネジメントには役に立たない。

最近フロントエンド開発者がチームに参加して、コードを読んだらだいぶひどいみたいなことを言ってた。開発リーダーもフロントエンドは基本的に動いたら OK とか答えてた。だから品質が悪い。

コワーキングのオンラインイベント

先日 カフーツさんのイベント のイベントに参加した。それがきっかけとなり、いとうさんが手掛けている Beyond the Coworking 〜移働の時代〜 という note のメンバーシップという有償コミュニティのようなものに入ってみた。記事を読むだけなら1,000円/月で、それ以上の付加価値サービス向けに2,000円/月という料金設定になっている。毎月 zoom でオンラインミーティングを行うというので参加してみた。いとうさんは少し前にコロナに感染して療養していたそうなので日程が急に決まったせいか、たまたま私しか参加者がいなかったので1on1みたいな感じで雑談した。コミュニティを運営するためのサービスとして何がいいかという話しをしたんだけど、たしかにこれとお勧めできるものがない。note も最近そういった機能を追加して sns になろうとしているように垣間見える。

  • note のコミュニティ機能とメンバーシップ
    • 試しに掲示板を使ってみているが、メンバーはほとんど書き込まない
      • いとうさんと私しか、ほとんど書き込みしていない
      • 但し、メンバーに質問していると掲示板をみてはいるという
    • 掲示板はストックのサービスだからリアルタイムに返信をもらうことをそもそも期待していない
    • リアルタイム性の高いサービスならチャットツールがよいのではないか?
      • slack, discord, ms teams など
    • 他のツールもどうか?
      • note の掲示板, notion, trello など

他にもコワーキングスペースをうまく運営するためにはコワーキングスペースマネージャーが必要だといとうさんは考えている。コミュニティマネージャーはコミュニティ形成を目的とするが、コワーキングスペースマネージャーは似て非なるものだという。あれもこれもできないといけないという話しをしてたら、基本的にスーパーマンを要求するポジションになるみたいw、とはいえ、求められる能力として3つをあげると次のような話しをされていた。

  • 利用者と話しができる
    • 利用者の居場所になるには、利用者の業界や業務をある程度は理解して話せないといけない
    • コワーキングだから協調のためにお互いの相互理解が必要になる
  • 人の紹介ができる
    • コワーキングだから協調のために利用者同士、または自身の人脈からマッチする人を紹介できないといけない
  • 仕事の斡旋ができる
    • ビジネスなので仕事を依頼したい人、仕事を受けたい人、仕事 (お金) がまわらないと継続できない

カフーツさんはうちのオフィスから一駅、自転車で10分の距離にオフィス兼コワーキングスペースがある。1人でお仕事をしていると相談相手がいないことの弊害 がある。身近に信頼できる相談相手がいることは重要だと思う。今後もビジネス寄りのコミュニティやコワーキングの在り方を学んでいこうと思う。

雰囲気だけで画面を作れた

0時に寝て6時に起きた。

slots で v-data-table のカラムを書き換える

昨日の続き。v-html で v-dta-table のカラム書き換えしてたら slots でやれと言われた続き。次のようなテンプレートのコードでカスタムカラムを配置するためのコンポーネントを作れば既存の vuejs の仕組みで保守もしやすそうに思う。

<my-wrapping-data-table>
  <template #[`item.data`]="{ item }">
    <my-custom-cell-layout :item="item" />
  </template>
</my-wrapping-data-table>

vuejs のテンプレートのこの構文はどういう評価をされるのかが理解できない。

<template #[`item.data`]="{ item }">

そしたら同僚がそれは次の構文のシンタックスシュガーだと教えてもらった。いずれにしても dsl 万歳って感じで私からは訳がわからない。雰囲気でテンプレート書いて動けばいいんだけど。

<template v-slot:`item.data`="row">
  <my-custom-cell-layout :item="row.item" />
</template>

その後、要素の更新処理のモーダルダイアログ画面も作って1週間以上に渡って開発していた画面を一通り作り終えた。vuejs のことわかってない素人でも雰囲気だけで動く画面は作れた (pr のときにほとんどレビューで指摘を受けなかったので大半は間違ってはないのだろう) 。簡単と言えば簡単ではある。ちなみに私が作ったものが初のページング可能な一覧画面になる。検索フォームもページングに連動してクエリを実行できるようにすべてフルスクラッチでコンポーネントを作った。

v-html は使わなくてもよい

0時に寝て7時に起きた。また日曜日は寝てた。

任意のカラムの書き換え

v-data-table の、あるセルが複雑なデータをもっていて、単純にその値を表示するのではなく、一定の構造化やレイアウトを調整した状態で表示したい。セル内の構造を書き換える方法を私は知らなかったので v-html という api を使って書き換えればよいのだと思った。しかし、これは間違いだった。間違いの訂正は翌日にやるとして仮に v-html を使うとしても xss の懸念があるのでスクリプトをエスケープしてあげないといけない。Sanitize v-html #6333 でも議論されていて vue3 はデフォルトでエスケープする仕組みが入るのかな?vue2 だと sanitize-html を使って次のようにラップすればいいと書いてあった。実際に動かしてみるとスクリプトを実行できたので v-html は危険だというのはわかった。

<div v-html="$sanitize(value)" />

この仕組みを作って pr でレビューしてもらっていたら、カラムの構造を書き換えたいだけなら slots を使えば普通にできると教えてもらった。また明日へ。

夏休み2回目

昨日は深夜も夕方もたくさん話して疲れたのか、ブログを書こうかと考えていたけど面倒になって休んでた。

eks クラスター障害のふりかえり

ストレッチ

今日の開脚幅は開始前155cmで、ストレッチ後161cmだった。開始前の数値が悪いけど、とくに調子が悪いというわけでもなかったので計測の仕方がよくなかったか、たまたま足の開き方が悪かったかといったところだろう。昨日、高級時計と投資の話しを聞いたのでトレーナーさんとお金があったら何に使う話しが盛り上がった。トレーナーさんも庶民的な方でとくにお金があっても贅沢をしたいというものでもないらしい。私も改めてお金があったら何がしたいかを考えてみてもお金を使ってやりたいことはとくにないなというのが率直な思いでもある。私には自分がやったことのないことをやってみたいという思いしかなく、そのためにお金を使ってきた側面も多々あるけれど、お金を直接的に使って何かをやるというよりも、生活できるだけのお金があれば、その時間に自分がやったことのないことに挑戦して人生を楽しむということぐらいしかやることはないんだなと、トレーナーさんと話していて考えたりしていた。

os 雑談

午後から昨日の障害のふりかえりといくつか調査をして、夕方におがわさんに声をかけたら話し相手になってくれると返ってきた。障害のふりかえりをした後に課題管理の雑談もしていて4時間弱ぐらい話してた。私自身、反省しないといけないとは思っていたのでおがわさんはちゃんと指摘をしてくれた。この歳になると私を叱ってくれる人はいない。たまに失敗したときにおがわさんに話して然るべき指摘をしてもらえるのは本当にありがたい。

kubernetes がどうやって動いているのかを理解して運用しているのか?

コンピューターがどうやって動いているのかを理解しているのか?

私が未熟で理解できていなかったからこんな障害を見過ごしていた。システム障害が発生しても人生は終わりじゃない。反省してから次につなげる。おがわさんと話していて、os の仕組みや機能についていくつか教えてもらった。

  • 制限対象の違い
    • ulimit はユーザーに対する制限
    • /proc ファイルシステムはシステムに対する制限
  • /proc/sys/kernel/pid_max は kernel のデフォルト値として 32768 が設定されているが、systemd を使っていれば値を変更している場合がある
    • 私の ubuntu マシンだと 4194304 が設定されていた
  • コンテナ内から /sys/fs/cgroup/memory/memory.usage_in_bytes をみると、コンテナが使っているメモリ量なのか?
    • cgroup の使い方次第で変わる、システムの値かもしれないしコンテナの値かもしれない
  • ゾンビプロセスに残っている情報は親プロセスが子プロセスの統計情報を取得するために必要なもの
    • どんな子プロセスも一時的にはゾンビプロセスになる
    • kernel の task_struct 構造体やその他の構造体、リンクしている情報などをすべて足すと1つのゾンビプロセスが10数 KiB 程度ではないか
      • 仮に 15 KiB として 32000 個のゾンビプロセスがあると約 469 MiB のメモリ量になるのでだいたい数字があう
  • ユーザー空間とカーネル空間の違いを理解できるようになった方がよい
    • システムコールの fork がエラーになるのはシステムがおかしいとすぐに気付けるはず
      • fork がエラーになる原因として考えられるのはメモリを確保できないか、pid_max に達したか、いずれにしてもエラーコードをみれば推測がつくのではないか
  • 環境にログインできるのであれば strace を使えば障害調査に役立つ
    • 私が普段使っていないツールなので障害のときにとっさに扱えるよう練習しておく

簡単な現象の組み合わせ障害

0時に寝て6時に起きた。

eks クラスター障害の原因判明

過去に2回発生していた eks クラスター障害 の原因がようやくわかった。テスト環境も本番環境は5日ごとに再現していて、datadog で k8s のダッシュボードでそれぞれの pod 単位のメモリ使用量をみると datadog-agent の pod がメモリリークしていることに気付いた。そこから当たりをつけて datadog-agent の issue を調べると次のバグに遭遇していた。

ゾンビプロセスが生成されて、それが os のプロセス数上限に達してしまい、それによってプロセス (スレッド) が生成できなくなって、その結果として aws/amazon-vpc-cni-k8saws-node という eks クラスターの管理アプリケーションが動かなくなって、それが動かないと k8s ノードのステータスが NotReady になってしまって、通常の pod のアプリケーションも動かなくなってしまうという現象が発生していた。datadog-agent のアップグレードは私が行ったものだし、その後の k8s ノードの監視や調査で気付きが足りなかったと反省した。

  • datadog-agent の新しいバージョンをテスト環境でもうしばらく検証してもよかった
  • datadog-agent をリソースリークの可能性を私の中の調査対象から外していた
    • 世の中で使われているものに致命的なバグが起きないだろうという先入観があった
  • プロセスを生成できない原因として考えられる背景を調査すべきだった
    • ulimit を確認してリソース制限はないようにみえた
    • プロセス数やゾンビプロセスを調べていなかった
    • kernel に /proc/sys/kernel/pid_max という上限設定があることを知らなかった
  • テスト環境と本番環境で5日程度で落ちるという周期性から気付くべきだった
    • たしかにテスト環境から1日遅れて本番環境で障害が発生していた
    • 周期性があることでリソースリークの可能性は高いとすぐに調査すべきだった
  • datadog で k8s のダッシュボードを調べるべきだった
    • すでに用意されているものがあったのでみようと思えばみえた
  • aws のインフラ要因ではないかと疑っていた
    • ごめんなさい

これは悔しい。自分の無能さや気付きの低さを実感した事件だった。私が注意深く観察していればもう1週間早く気付けた。そのせいで余分な障害と調査に時間を費やした。1つ1つは全く難しくない現象が巧妙に絡みあって隠蔽された結果としての状況に気付けなかった。注意して1つずつ観察して追跡していけばすぐに気付けた。本当に悔しい。

1つだけ言い訳をさせてもらうと、私は本番環境にアクセスできない。だからテスト環境と本番環境で発生している現象が同じかどうかを判断できず、調査を進める確証をもてなかった。

呑み

あまりに悔しかったのと調査してたら遅くなって晩ご飯食べる気力もなかったので気分転換に仲のよい焼き鳥屋さんに寄ってみた。あとから常連客のセブンイレブンの店長さんも来られて、私は初対面かなと思ってたんだけど先方は知っていると言ってたから以前にもカウンターでご一緒していたみたい。何気はなしに3人で2時前ぐらいまで雑談していた。

その店長さんがロレックスを購入しようと考えているという話しになって、資産または投資商品としてのロレックスの話しになった。たまたまヒカキンが1億円で買ったロレックスがいま2億円になっているといった話しがあったそうで、いまがバブルな状態らしいが、ロレックスをはじめとした高級時計の資産価値が上がっているらしい。私は腕時計を身につけないし高級時計もまったく興味はないが、投資商品の1つなんだというところに関心がもてた。

中小企業の社長の一般的な節税方法の1つに外車を買ったり売ったりするという話しがある。儲かったときに経費で外車を買って、赤字のときに外車を売って雑所得に変える。車は社用車として経費で落とせるから可能なことだが、高級時計はどうなのだろうか? 結論から言うと、普通の会社では高級時計は経費にできない。経費の原則は売上を上げるために必要な支出を経費とできる。普通の会社は高級時計で売上を上げることはできない。一方で経費として認められる職業もある。芸能人がそうだという。それは番組のために必要だという理屈で経費で落とせる。おそらくヒカキンも経費で高級時計を購入して、そのことを動画にしているのも仕事で必要だという言い訳作りの目的もあるのだと推測する。

vuetify のコンポーネント調査

0時に寝て7時に起きた。

vuetify で検索フォームのコンポーネント作成

ページング処理ができた。次に検索リクエストのための検索条件を扱うフォームを汎用コンポーネントで作ってみることにした。コンポーネントを生成するための検索条件のオブジェクトを外部から渡して、あとはよしなに grid に構成要素を配置する。v-date-picker のところは本当はもっと凝った作りをしないといけない。ここでは型で分岐してコンポーネントを配置する概念を表しているだけ。v-col は cols は1から12までの数字を受け取る。この数値を調整して v-spacer を入れることで行の位置調整もできるのがひと工夫しているところ。イベントハンドラーは click:search とか click:searchClearのように名前を付け替えて、外部から意図したイベントのみをフックできるように考慮している。

<template>
  <v-container>
    <v-row dense>
      <v-col v-for="item in conditions" :cols="item.cols" :key="item.label">
        <v-switch
          v-if="Boolean === item.type"
          v-model="item.value"
          :label="$t(item.label)"
          :clearable="true"
        />

        <v-text-field
          v-if="String === item.type"
          v-model="item.value"
          :label="$t(item.label)"
          :clearable="true"
        />

        <v-text-field
          v-if="Number === item.type"
          v-model="item.value"
          type="number"
          :label="$t(item.label)"
          :clearable="true"
        />

        <v-date-picker
          v-if="Date === item.type"
          v-model="item.value"
          :clearable="true"
        />

        <v-spacer v-if="null === item.type">
          <!-- use an empty block for grid layout -->
        </v-spacer>
      </v-col>
    </v-row>
    <v-row>
      <v-col cols="4">
        <v-btn
          v-text="$t('label.clearSearchCondition')"
          @click="_searchClear"
        />
      </v-col>
      <v-col cols="3">
        <v-btn color="primary" v-text="$t('label.search')" @click="_search" />
      </v-col>
    </v-row>
  </v-container>
</template>
<script lang="ts">
import { PropType, defineComponent } from '@vue/composition-api';
export interface SearchConditionItem<T = any> {
  type: PropType<T> | null;
  name: string;
  label?: string;
  col: number;
  value?: any;
  fromValue?: string | null;
  toValue?: string | null;
}

export default defineComponent({
  components: {},
  props: {
    conditions: {
      type: Array as PropType<SearchConditionItem[]>,
      default: () => [],
    },
  },
  setup(props, context) {
    return {};
  },
  methods: {
    _search(value: any) {
      this.$emit('click:search', value);
    },
    _searchClear(value: any) {
      for (const c of this.conditions) {
        c.value = null;
        c.fromValue = null;
        c.toValue = null;
      }
      this.$emit('click:searchClear', value);
    },
  },
});
</script>

呼び出し側ではこんな感じ。任意の conditions を渡し、検索ボタンをクリックしたときのイベントハンドラーを登録する。

<search-condition-form
  :conditions.sync="searchCondition"
  @click:search="search"
  v-on="$listeners"
/>

vuetify で初めてコンポーネントを作ってみた。雰囲気だけで実装している個人的な所感だけど、template, script, style を1つのファイルに同梱する考え方が私には馴染まない。1つのファイルに複数の構文が混在する認知負荷が気になるのと、1つのファイルに複数のコードを同梱しているメリットが私には感じられない。もしかしたら小さいシンプルなコンポーネントなら見通しがよいのかもしれない。しかし、業務での開発だと一定の複雑さをもつコンポーネントの方が大半だと思うので1ファイルが1画面におさまらない。どうせエディターを画面分割して複数画面でソースを読むのであれば、その画面のソースが1つのファイルでも別のファイルでも私にとってあまり大差ない。ファイル間の依存関係さえ適切に管理できればファイルは用途ごとに分割できた方が人間にとってわかりやすいのではないかとも思う。一方でフレームワーク側からみたら依存関係の解決はやや煩雑な処理になるので開発や依存管理がシンプルになってビルドが速くなるといったメリットがあったりするのかもしれない。どうなんだろう?

vuejs の template 調査

0時に寝て6時に起きた。

連日のサービスイン作業

引き続きサービスインの運用対応は大変そうでちゃんと検証していない修正を慌ててマージしようとしているからテスト環境まで壊れてて関係ない開発にも影響が出ていた。今日も別の施設のサービスインだったらしくて、ある機能がないとそのサービスインの切り替え作業ができないという話しだったそうで、当日に慌てて pr を作ってマージしてた。先週からわかっていた必要な機能を実装してなくて、週末は残業も休出もしてなくて、今日になって慌てて修正してマージしてた。昔の開発と比べてがんばっててできないのではなくて、いまの開発はがんばってないからできないという雰囲気になったなという印象。

vuejs の template と expression

あるフォームのコンポーネントを作ろうと思って interface を定義していてデフォルト値をテンプレート側に指定できるといいんじゃないかと考えた。というのは typescript の interface のメンバーは値を保持できないから。例えば、次のようなコードで :cols="item.col ?? 2" のように表現できたら嬉しいように思う。

<v-row dense v-for="item in conditions" :key="item.label">
  <v-col :cols="item.col ?? 2">
    {{ element }}
  </v-col>
</v-row>

余談だけど、?? は null 合体演算子という名前は知っていたけど、これを英語で何と呼ぶのか知らなかった。Nullish coalescing operator と言う。ググってみると vuejs の issue でもそこそこ議論されていて vue3 からサポートするとしながら、根強い要望があるのか? vue2 でも 2.7 でサポートしたらしい。こういうモダンな javascript の expression を ESNext syntax と呼んだりするみたい。それすらも知らなかった。

たまたまうちで使っているのは vue 2.6.14 なので vue 2.7 で動くのかどうか検証できないけど、いま使っている nuxtjs2 との依存関係があるのでそれ次第で vue 2.7 にアップグレードの可否が決まるらしい。全然フロントエンドの開発がわからないので、こういう基本的なところで引っかかると背景を調べるのに時間がかかる。

vuetify のイベントリスナーの調査

4時に寝て7時に起きた。日曜日にたくさん寝たせいか、昨日は眠れなかった。サービスインの運用対応はまだまだドタバタしていてデータの不整合に苦しんでいるみたい。大変そうだけど、なにもやることない。

vuetify の v-data-table のページング処理

昨日から vuetify のページング処理を調査している。コンポーネント的には2種類ある。

  • v-pagination: 汎用のページングコンポーネント
  • v-data-table: data table のコンポーネント (ページング機能がある)

既存のアプリケーションは nuxtjs で実装されているので vuetify や vue.js のサンプルコードをそのまま動かせるわけではない。丸1日、試行錯誤していてビューと値の束縛、イベントの伝搬などの振る舞いをだいたい理解できた。宣言的なフレームワークなので振る舞いを理解できれば開発量は少なく済む。但し、理解するまで振る舞いを理解するのに設定を試行錯誤で試して動かすのでデバッグは時間がかかる。一覧画面で使っている v-data-table のページング処理対応から始める。Server-side paginate and sort を参考にしながら v-data-table に加えた主な変更はこれら。

<v-data-table
  ...
  :server-items-length="serverItemsLength"
  :disable-pagination="disablePagination"
  :hide-default-footer="hideDefaultFooter"
  :options.sync="options"
  :footer-props="{
    itemsPerPageOptions: [10, 20, 50, 100],
  }"
  v-bind="$attrs"
  v-on="$listeners"
>

v-data-table をラップするコンポーネントでは次のようにイベントリスナーを登録する。

<my-nice-component
  ...
 :server-items-length="serverItemsLength"
 :disable-pagination="false"
 :hide-default-footer="false"
  @update:options="dataOptionsHandler"
>

  async dataOptionsHandler(options: DataOptions) {
    const page = options.page ?? 1;
    const limit = options.itemsPerPage;
    const offset = limit * (page - 1);
    // limit/offset を使った web api リクエスト
  }

vm.$listeners によると v-on="$listeners" のように書くと、ラップしているコンポーネントのイベントリスナーをよしなに伝搬してくれるみたい。これはこれで便利だけど、イベントリスナーの定義がどこにもでてこないのでコード検索ができなくなる。最近の宣言的なフレームワークの流行りと言えばそうだけど、知ってないとなんで動くのかわからないコードにはなる。

週明けのサービスイン

0時に寝て7時に起きた。

3つ目のサービスイン

またまた私は勘違いしていて明日だと思っていたら今日が3つ目のサービスインだった。約1ヶ月ぶりのサービスイン になる。もう3回目なので要領よく切り替え作業やるのかなと眺めてたけど、新たなトラブルもいくつかあって、これまで同様、ドタバタしているようにみえる。私は本番環境にアクセスできないので何かトラブルがあっても聞いた内容から類推で助言を述べるしかできず、とはいえ、何かあったら質問がくるかもしれないからハドルに入って成り行きを見守ってないといけない。とくに手伝うこともないのにメンバーの作業が完了するまで待ってないといけない。この切り替え作業や運用対応をやっていると要件定義やコードレビューなどは放置されるのでまた作業のスケジュールが先送りになる。私は別に困らないけど、しばらくだらだらした開発が続く。

夏バテ

だいぶ暑さはましになってきた感じはあるけど、なんか調子よくなくて寝てた。お腹空いたら外に出掛けるきっかけにもなるんだけど、暑さで食欲もあまりなくて珍しく1日中なにも食べなかった。