TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

941

こんにちは、あるいはこんばんは。だいたいサーバサイドのエンジニアの( @taclose )です☆ もうあと1か月で ISUCON13 ですね!お祭りですね! という事で、今日は Windows 上でISUCONの環境を構築して、是非練習してもらえたらなと記事を書きました! これを参考にしながら是非、みなさんもトライしてみてください! 読者ターゲット 前書き 手順1:Docker Composeを使えるようにする(Rancher Desktopのインストール) 手順2:WSL2(Ubuntu 22.04)を準備する 手順3:WSL2上でdockerのプロセス操作が出来るようにする IntegrationsにUbuntuが表示されない方 手順4:Ubuntuのセットアップ 手順5:ISUCON11の環境構築 ISUCON11をforkする ISUCON11のclone~起動まで ISUCON11を触ってみる localhostが解決出来ないって人! 最後に 参考記事等 読者ターゲット ISUCONをDocker使って練習したい! dockerとかなんとなくはわかるよ! 前書き 「WSL上でISUCON環境構築」「 Mac 上で~」という記事はちらほら見かけるのですが、WSL2上に環境構築となると罠がちらほらあるものです。 なので、自分がつまづいたポイントとかを挟みながら説明していこうと思います。 各手順は 何も準備されていない前提 で書いています。「それは準備出来てるぜ!」という項目は飛ばして進めちゃってくださいね。 尚、今回は ISUCON11の本番問題 をインストールする手順ですが、基本的にはgitから落とすproject次第で手順はほぼ変わりません。 手順1:Docker Composeを使えるようにする(Rancher Desktopのインストール) 以下の Rancher Desktop を Windows にインストールしてください。 rancherdesktop.io 特段詰まるポイントはないかと思いますが、 WindowsでRancher Desktopを使うには - とことんDevOps | 日本仮想化技術のDevOps技術情報メディア とかを参考にしながら進めてもらえればと思います。 手順2:WSL2( Ubuntu 22.04)を準備する PowerShell 上で以下のコマンドを実行する事で Ubuntu -22.04のWSLが準備されます。 # WSL2を標準とする > wsl --set-default-version 2 # Ubuntu-22.04をインストールする > wsl --install -d Ubuntu-22.04 # インストールが終わったらログインしてみる > wsl ~ -d Ubuntu 手順3:WSL2上でdockerのプロセス操作が出来るようにする 以下はRancher Desktopの設定画面です。WSLメニューのIntegrationsタブに、 Ubuntu があるはずなのでチェックを入れましょう。 WSL2上でdocker操作が出来るようにする Integrationsに Ubuntu が表示されない方 もし表示されない方は、インストールされているWSLがWSL2じゃない可能性が高いです。 WSL2をブログ記事の通りにインストールし直すか、 WSL1 から WSL2への移行 #WSL - Qiita これらを参考にしながらWSL2に移行してください。 手順4: Ubuntu のセットアップ ISUCON環境構築するにあたり、必要なライブラリがいくつかありますので、以下のコマンドを WSL2(Ubuntu)上 で実行してください。 # 時刻ずれてるとapt失敗する事があるので、念のため時刻調整 $ sudo hwclock --hctosys # apt最新化しておく $ sudo apt-get update -y # isuconのdocker buildやbenchmarkerのbuildに使ってます $ sudo apt install make # benchmarkerのbuildに使います $ sudo apt install -y golang-go # docker buildで必要となるもの $ sudo apt install gnupg2 pass $ sudo apt-get install -y libsecret-1-0 $ sudo apt-get install dbus-x11 手順5:ISUCON11の環境構築 いよいよですね!ISUCON11の環境構築や、当日のレギュレーションなどがまとめられた リポジトリ はこちらです。 github.com これを直接落としてきても良いのですが、実際に行った改修の管理とかするためにもforkする事をお勧めします。 ISUCON11をforkする 以下の画像のボタンを押下する事で自分の リポジトリ としてforkする事ができます。 自由にcommitできるので、練習が捗りそうですね! github からISUCON11をforkする ISUCON11のclone~起動まで 以下のコマンドをWSL2( Ubuntu )上の作業 ディレクト リで行ってください。 # forkしてない場合はこうですが、forkしてる場合は書き換えてくださいね。 # リポジトリをcloneしてくる $ git clone git@github.com:isucon/isucon11-final.git # docker-composeを使って環境を構築する # ※ちなみにMakefileを見てもらえれば、言語をGo以外にするコマンドとかわかります。 # 結構時間かかります。落としたい時はmake down。再起動したいなら make down; make upでOK $ cd isucon11-final/dev/ $ make up & # git clone行ったdirから見た移動です。 $ cd isucon11-final/benchmarker $ make ISUCON11を触ってみる http://localhost:8080 にアクセスしてもらうとブラウザ上で実行出来ている事が確認できます。 また、以下のコマンドでBenchmarkerを走らせられます。 # benchmarker走らせてみる $ ./isucon11-final/benchmarker/bin/benchmarker -target localhost:8080 localhost が解決出来ないって人! 試しに curl コマンドで試してもらって以下のような状況になる日とはWSLから Windows への通信が出来ていない事となります。 # Ubuntu上で以下が出ちゃう人 $ curl -X GET localhost:8080 curl: (7) Failed to connect to localhost port 8080 after 0 ms: Connection refused 原因としては色々ありますが、Rancher Desktopを使ってる方の場合、以下の設定を確認してください。 ここでこんな不具合の人少ないのかな... 的はずれな記事ばかりに行き当たってしまい苦労しましたOrz 私は過去に何かで通信うまくいかないので困惑した時に、これをチェックしてしまっていたのが原因だったようです。 最後に 環境構築終わりましたか!?ここからが始まりですからね! この環境なら AWS の利用料金とか気にせずに好きな時にISUCONの練習できるのがうれしいですよね! 自宅の Mac でも試してみたんですが、ほぼ同じ手順で環境構築問題なかったので皆さんも参考にしてみてください。 ではISUCON本番でお会いしましょう! 参考記事等 GitHub - isucon/isucon11-final: ISUCON11 本選 (ISUCHOLAR) WindowsでRancher Desktopを使うには - とことんDevOps | 日本仮想化技術のDevOps技術情報メディア WSL1 から WSL2への移行 #WSL - Qiita
はじめに こんにちは。フロントエンド開発課に所属している新卒1年目のm_you_sanと申します。 実務でtanstack tableを使う機会があり、便利に感じたので紹介させていただきます。 目次は以下の通りです。 はじめに tanstack tableとは 使い方 サンプルデータの用意 カラムの定義 テーブルオブジェクトの作成 テーブルの表示 チェックボックスの状態管理 行の削除 まとめ tanstack tableとは tanstack tableとはテーブルを容易に実装できるヘッドレスUIライブラリです。 ヘッドレスなので、UIとロジックを制御する部分を独立させることができ、柔軟にUIを制御することができます。 v7まではreact-tableという名前でしたが、メジャーアップデート後にtanstack tableになり、react以外にvue、solidなどの フレームワーク にも対応しています。 使い方 使い方について、サンプルコードを交えて解説します。 今回はtanstack tableとMUIを使用して、 チェックボックス 付きのテーブルを作成していきます。 tanstack tableを初めて使う初学者の方は、是非、手を動かしながら本記事を読んでいただければと思います。 インストール方法については、公式ドキュメントを参照してください。 MUI   tanstack table サンプルデータの用意 まず初めにテーブルに表示するサンプルデータを用意します。 今回は50個のデータを作成し、tasksの初期値とします。 その後、tasksをpropsとして子のCheckBoxTableに渡します。 App. tsx export type Task = { id: number ; name: string ; isDone: boolean ; } const App: React.FC = () => { const defaultData: Task [] = [ ... Array ( 50 ) .keys () ] .map (( i ) => ( { id: i + 1 , name: `タスク ${ i + 1 } ` , isDone: i % 2 === 0 ? true : false } )); const [ tasks , setTasks ] = useState ( defaultData ); return < CheckBoxTable data = { tasks } / > } カラムの定義 次にCheckBoxTableの中身を作成していきます。 初めにカラムを定義します。 ColumnDefに型情報を設定することで、tableやrowにも型情報が反映されます。 headerは列のヘッダーの表示内容となっており、 カスタム コンポーネント やテキストなどを使用して表示内容をカスタマイズすることができます。 今回はMUIと組み合わせて表示内容をカスタマイズしています。 また、headerにはtableを引数に設定して、tableの API を使用することができます。 cellは列内の各セルの表示内容です。 こちらもheaderと同様に表示内容をカスタマイズできます。 また、cellにはrowを引数に設定することができ、rowの API を使用することができます。 なお、 チェックボックス のハンドラーについてですが、table API とrow API で用意されているので、自分で作成する必要はありません。 CheckBoxTable. tsx type Props = { data: Task [] ; } export const CheckBoxTable: React.FC < Props > = ( { data } ) => { const columns: ColumnDef < Task > [] = [ { id: 'select' , header: ( { table } ) => ( < Checkbox //現在のページの全ての行が選択されているかどうか checked = { table.getIsAllRowsSelected () } //全ての行のチェックボックスを切り替えるために使用するハンドラーを返す onChange = { table.getToggleAllRowsSelectedHandler () } / > ), cell: ( { row } ) => ( < Checkbox //行が選択されているかどうか checked = { row.getIsSelected () }    //未実施の場合は非活性 disabled = { ! row.original.isDone } //チェックボックスを切り替えるために使用するハンドラーを返す onChange = { row.getToggleSelectedHandler () } / > ) } , { id: 'id' , header: 'タスクID' , accessorKey: 'id' } , { id: 'name' , header: 'タスク名' , accessorKey: 'name' } , { id: 'isDone' , header: 'タスクの実施状況' , cell: ( { row } ) => row.original.isDone? '実施済' : '未実施' } ] } テーブルオブジェクトの作成 次にテーブルオブジェクトを作成します。 テーブルオブジェクトの作成には、useReactTableを使用し、テーブルに表示するデータ、カラムを設定します。 getCoreRowModelはテーブルの行モデルを計算して返す関数となっていますが、あまり難しいことは考えずに、getCoreRowModel()を設定してあげれば良いと思います。 なお、data、columns、getCoreRowModelは必須オプションなので、どれか1つ欠けているとエラーが発生します。 CheckBoxTable. tsx const table = useReactTable < Task >( { data , columns , getCoreRowModel: getCoreRowModel () } ) テーブルの表示 次にテーブルを表示させます。 getHeaderGroupはテーブルのヘッダー情報、getCoreRowModelはテーブルの行の情報を返します。 getHeaderGroupで返されるヘッダー情報は、配列になっており、それらをmap関数で展開し、flexRender関数を使用して表示させています。 getCoreRowModelで返される行の情報についても、配列になっており、ヘッダーと同じ方法で表示させています。 CheckBoxTable. tsx return ( < Table > < TableHead > { table.getHeaderGroups () .map (( headerGroup ) => ( < TableRow key = { headerGroup.id } > { headerGroup.headers.map (( header ) => ( < TableCell key = { header.id } > { flexRender ( header.column.columnDef.header , header.getContext ()) } < /TableCell > )) } < /TableRow > )) } < /TableHead > < TableBody > { table.getCoreRowModel () .rows.map (( row ) => ( < TableRow key = { row.id } > { row.getVisibleCells () .map (( cell ) => ( < TableCell key = { cell.id } > { flexRender ( cell.column.columnDef.cell , cell.getContext ()) } < /TableCell > )) } < /TableRow > )) } < /TableBody > < /Table > ) 画面を確認すると、このようにテーブルが表示されているのが分かります。 なお、カラムを定義した際に、cellを使って表示内容を指定していない場合は、accessorKeyが抜けていると上手く表示されません。 { id: 'id' , header: 'タスクID' , // accessorKey: 'id' } , { id: 'name' , header: 'タスク名' , // accessorKey: 'name' } , チェックボックス の状態管理 テーブルの見た目は実装できたので、次に状態管理を追加します。 先程のテーブルオブジェクトにオプションを追加します。 CheckBoxTable. tsx const [ rowSelection , setRowSelection ] = useState ( {} ); //中略 const table = useReactTable < Task >( { data , columns , state: { rowSelection } , onRowSelectionChange: setRowSelection , getCoreRowModel: getCoreRowModel (), //実施済のタスクだけ選択されるように設定 enableRowSelection: ( row ) => row.original.isDone } ) 上記のコードでは、stateに状態管理する値を渡しており、onRowSelectionChangeで チェックボックス が切り替わったときにセッター関数を呼び出して、状態を更新しています。 状態管理しているrowSelectionはエディタなどで確認すると、RowSelectionTableStateとundefinedのユニオン型になっていることが分かります。 RowSelectionTableState型は公式ドキュメントを確認してみると、以下のようになっています。 export type RowSelectionState = Record < string , boolean > export type RowSelectionTableState = { rowSelection: RowSelectionState } つまり、 チェックボックス が切り替わったとき、rowSelectionはstring型のキーとboolean型の value を保持することが考えられます。 実際にrowSelectionの中身をコンソールで見ると、キーは選択されている各行のインデックスで、 value はtrueになっていることが分かります。 また、ここまで度々登場しているrow.originalについても、コンソールで中身を見ていきたいと思います。 row.originalには、tableオブジェクトに設定したデータの情報が入っているのが分かります。 今までの「cell: ({row}) => row.original.isDone? '実施済' : '未実施'」や「enableRowSelection: (row) => row.original.isDone」は、originalにあるisDoneを使って、true、falseを取得していたということです。 行の削除 最後に、選択した行を削除する機能を作成します。 今回作成する削除機能は、選択した行のidとタスクのidが一致するものをfilter関数で除くという簡易的なものになっています。 App. tsx const App: React.FC = () => { const defaultData: Task [] = [ ... Array ( 50 ) .keys () ] .map (( i ) => ( { id: i + 1 , name: `タスク ${ i + 1 } ` , isDone: i % 2 === 0 ? true : false } )); const [ tasks , setTasks ] = useState ( defaultData ); const handleDelete = ( ids: number [] ) => { const updateTasks = tasks.filter (( task ) => ! ids.includes ( task.id )); setTasks ( updateTasks ); } return < CheckBoxTable data = { tasks } onDelete = { handleDelete } / > } checkBoxTableにも変更を加えていきます。 まず、行が選択された状態でなければ、削除ボタンを押せないようにするため、isSelected がfalseの場合、非活性にしています。 そして、削除する際に選択されている行のidが必要なので、table.getSelectedRowModel()で選択されている行の情報を取得し、map関数で行のidを含む配列を作成しています。 作成した配列をonDeleteの引数にし、削除が完了したら、選択状態がリセットされるようにします。 CheckBoxTable. tsx type Props = { data: Task [] ; onDelete: ( ids: number [] ) => void ; } export const CheckBoxTable: React.FC < Props > = ( { data , onDelete } ) => { //中略 //行のどれか1つでも選択されていればtrueを返す const isSelected = table.getIsSomeRowsSelected (); //選択している行のidを配列にする const ids = table.getSelectedRowModel () .rows.map (( row ) => row.original.id ); const handleDelete = () => { onDelete ( ids ); //選択状態をリセット table.resetRowSelection (); } return ( <> < button // 何も選択されていなければ非活性 disabled = { ! isSelected } onClick = { handleDelete } > 削除 < /button > < Table > < TableHead > { table.getHeaderGroups () .map (( headerGroup ) => ( < TableRow key = { headerGroup.id } > { headerGroup.headers.map (( header ) => ( < TableCell key = { header.id } > { flexRender ( header.column.columnDef.header , header.getContext ()) } < /TableCell > )) } < /TableRow > )) } < /TableHead > < TableBody > { table.getCoreRowModel () .rows.map (( row ) => ( < TableRow key = { row.id } > { row.getVisibleCells () .map (( cell ) => ( < TableCell key = { cell.id } > { flexRender ( cell.column.columnDef.cell , cell.getContext ()) } < /TableCell > )) } < /TableRow > )) } < /TableBody > < /Table > < / > ) } 最後に画面で動作を確認してみます。 以下のように、動作していれば実装完了です。 上手く動作しない場合は、以下にコード全体を載せているので、そちらと見比べて、ご確認いただければと思います。 App. tsx export type Task = { id: number ; name: string ; isDone: boolean ; } const App: React.FC = () => { const defaultData: Task [] = [ ... Array ( 50 ) .keys () ] .map (( i ) => ( { id: i + 1 , name: `タスク ${ i + 1 } ` , isDone: i % 2 === 0 ? true : false } )); const [ tasks , setTasks ] = useState ( defaultData ); const handleDelete = ( ids: number [] ) => { const updateTasks = tasks.filter (( task ) => ! ids.includes ( task.id )); setTasks ( updateTasks ); } return < CheckBoxTable data = { tasks } onDelete = { handleDelete } / > } CheckBoxTable. tsx type Props = { data: Task [] ; onDelete: ( ids: number [] ) => void ; } export const CheckBoxTable: React.FC < Props > = ( { data , onDelete } ) => { const [ rowSelection , setRowSelection ] = useState ( {} ); const columns: ColumnDef < Task > [] = [ { id: 'select' , header: ( { table } ) => ( < Checkbox checked = { table.getIsAllRowsSelected () } onChange = { table.getToggleAllRowsSelectedHandler () } / > ), cell: ( { row } ) => ( < Checkbox checked = { row.getIsSelected () } disabled = { ! row.original.isDone } onChange = { row.getToggleSelectedHandler () } / > ) } , { id: 'id' , header: 'タスクID' , accessorKey: 'id' } , { id: 'name' , header: 'タスク名' , accessorKey: 'name' } , { id: 'isDone' , header: 'タスクの実施状況' , cell: ( { row } ) => row.original.isDone? '実施済' : '未実施' } ] const table = useReactTable < Task >( { data , columns , state: { rowSelection } , onRowSelectionChange: setRowSelection , getCoreRowModel: getCoreRowModel (), enableRowSelection: ( row ) => row.original.isDone } ) const isSelected = table.getIsSomeRowsSelected (); const ids = table.getSelectedRowModel () .rows.map (( row ) => row.original.id ); const handleDelete = () => { onDelete ( ids ); table.resetRowSelection (); } return ( <> < button disabled = { ! isSelected } onClick = { handleDelete } > 削除 < /button > < Table > < TableHead > { table.getHeaderGroups () .map (( headerGroup ) => ( < TableRow key = { headerGroup.id } > { headerGroup.headers.map (( header ) => ( < TableCell key = { header.id } > { flexRender ( header.column.columnDef.header , header.getContext ()) } < /TableCell > )) } < /TableRow > )) } < /TableHead > < TableBody > { table.getCoreRowModel () .rows.map (( row ) => ( < TableRow key = { row.id } > { row.getVisibleCells () .map (( cell ) => ( < TableCell key = { cell.id } > { flexRender ( cell.column.columnDef.cell , cell.getContext ()) } < /TableCell > )) } < /TableRow > )) } < /TableBody > < /Table > < / > ) } まとめ tanstack tableについて、サンプルコードを交えて紹介させていただきました。 今回実装した チェックボックス 以外に、ページネーションやソート機能も作成することができるので、詳しく知りたい方は、 公式ドキュメント を見ていただければと思います。 長くなってしまいましたが、本記事をお読みいただきありがとうございました。
はじめに メールディーラー開発課のyamamuuuです。 2023/10/08(日)に PHP Conference 2023が完全オフラインで開催されました。 PHP Conference Japan 2023 ラク スはシルバースポンサーとして協賛し、エンジニア4名が登壇した他、初のブース出展を行いました。 本ブログではイベントの参加レポートと、 ラク スからの登壇者本人によるレポートに加え、ブースやイベントの様子もお届けします。 もくじ はじめに もくじ 参加レポート 型安全なSQLテンプレートエンジンを構築する Webアプリケーションのパフォーマンス・チューニングの勘所 25分で理解する!Symfonyの魅力とその実践的活用法 PHP8.2から見る、2つの配列 RubyVM を PHP で実装する〜Hello World を出力するまで〜 普段のプロジェクト開発で当たり前すぎてあまり目立たないComposer良いところを褒めに褒めまくるLT readonly class で作る堅牢なアプリケーション スケーラブルサービス――疎結合に成長するシステムに不可欠な要素 ラクスからの登壇セッションのご紹介 PHP略語クイズ ノンフレームワークのレガシープロダクトを、Laravelに"載せる"実装戦略と、その後の世界 既存コードベースにもPHP_CodeSnifferを導入して楽したい! ユニットテスト環境整備~みんながテストを書ける環境へ~ クロージング ラクス初のブース出展 来場者配布物封入の儀 まとめ PHPerのためのコミュニティ PHPTechCafe 参加レポート 型安全な SQL テンプレートエンジンを構築する report by id:hirobex うさみけんた さん ( @tadsan ) による発表です fortee.jp SQLインジェクション 対策の必要性と、うさみさんが作られたテンプレートエンジン TetoSQL の実装についての トーク でした! TetoSQLは OSSとして公開 されているので 実際にコードを見てみると、より SQL テンプレートエンジンへの理解が深まるかも……!? Webアプリケーションのパフォーマンス・チューニングの勘所 report by id:hirobex 曽根 壮大さん ( @soudai1025 ) による発表です fortee.jp いざパフォーマンスを改善したい!と思っても、なにから手を付けていいのか、意外とわからないものです。 そんなとき、なにからはじめるべきなのか、どういう情報を取得すべきか、という話から始まり 実際の情報の見方や改善テクニックをお話しいいただきました。 25分で理解する! Symfony の魅力とその実践的活用法 report by id:Y-Kanoh ( @YKanoh65 ) 角田一平さん( @ippey_s )による発表です speakerdeck.com Simpleであることが特徴の Symfony についてまとめられた発表です。 Symfony は コンポーネント の集まりであり、ユーザが必要な コンポーネント を自分で選んで利用することができる仕組みになっています。また、依存を手軽に注入する仕組みも備わっています。 そのほかにも、充実した CLI ツールや、DataMapper型のORMであるDoctrineが採用されており、融通がきく作りになっているため、素早くアプリを作成し、 疎結合 を維持して拡張をすることができる フレームワーク であると言えます。 発表ではところどころに Symfony の特徴を端的にあらわすキーワードが入っており、スッと頭に入りやすい内容でした。 PHP8.2から見る、2つの配列 report by id:takaram meiheiさん ( @app1e_s ) による発表です。 speakerdeck.com PHP8.2 で新しくなった、配列の内部実装がテーマです。 PHP の配列は、他の言語で言う配列(リスト)と、一般にマップや 連想配列 と呼ばれるものが一緒になった少し特殊なデータ構造です。 その内部実装は、添字が0からの連番になっている場合(リスト)とそれ以外( 連想配列 )とで異なっています。 この2種類の内部実装が、どのようなデータ構造になっているのか、配列の初期化や要素の追加・削除を行ったときに PHP 内部でどんな処理が行われているかが解説されていました。 PHPカンファレンス にありながら、 C言語 を"完全に理解"できる(?)発表でした! RubyVM を PHP で実装する〜 Hello World を出力するまで〜 report by id:takaram めもりーさん ( @m3m0r7 ) による発表です。 speakerdeck.com 新たに Ruby を学ぶにあたって「普通に学ぶのは刺激が足りない。そうだ VM を作ろう!」という、なんともすごい学習方法を取った発表者のめもりーさんから、大きく分けて以下3点の解説がありました。 VM とは何か RubyVM のしくみ RubyVM を PHP でどう実装するか Ruby だけでなく PHP も VM (ZendVM) で動作していますが、その中で何が起こっているのかを考えたことのある人は少ないかもしれません。 普段なかなか知ることのできない、プログラムの処理系の中身を知ることのできる面白い発表でした。 最後にはデモもあり、実際に PHP で Ruby が動く様子を見ることができました。 まだ一部機能しか実装されていないとのことですが、 PHP で Ruby が動くというのはなんだか夢のある話ですね! 普段のプロジェクト開発で当たり前すぎてあまり目立たないComposer良いところを褒めに褒めまくるLT report by id:takaram yamiiさん ( @yamii_qq ) による発表です。 speakerdeck.com 多くの PHPer が普段から当たり前に使っている composer、当たり前すぎて気づかないが実はすごい!ということで、composer のいろんなすごいポイントを挙げてひたすら褒めるLTです。 npm / yarn / pnpm のような選択肢がある JavaScript と違って、 PHP のパッケージマネージャといえばほぼ composer 一択ということもあり、他と比較したり「ここがすごい」と考えたりする機会は意外と少ないものです。 しかし改めて目を向けてみると、こんなによく出来たツール・エコシステムを無料で使える *1 なんて!という気持ちになりますね! readonly class で作る堅牢なアプリケーション report by id:rks_hrkw 河瀨 翔吾さん( @shogogg )による発表です。 speakerdeck.com PHP8.2で追加されたreadonlyクラスについての発表です。 readonlyクラスは全てのプロパティがPHP8.1で追加されたreadonly propertyになるというものです。 readonlyクラスを導入することでオブジェクトをImmutableな状態にすることができます。それにより複雑さを減らし目の前のコードの読み書きに集中できるとのことです。 実際の活用事例やメリットだけでなくデメリットも話されていてreadonlyとは何たるかを知れる発表になっていました。 私もPHP8.2に上げてからはreadonlyをガンガン使っているので、この発表で学んだデメリットなども意識しながら今後も活用していきたいです。 スケーラブルサービス―― 疎結合 に成長するシステムに不可欠な要素 report by id:rks_hrkw 成瀬 允宣さん( @nrslib )による発表です。 speakerdeck.com アプリのスケーラブルをどう実現していくのかに加え、組織としてのスケーラブルについての内容もある、サービス全体のスケーラブルについての発表でした。 普段曖昧に理解しがちなスケールアップ等を具体的なイメージ共に1から説明されていたので、後の説明も頭に入りやすかったです。 1つずつ課題を解決しながら理想のスケーラブルシステムへと向かっていくような内容になっており、なぜイベントソーシングやメッセージブローカなどが存在するかなども理解しながら聞くことが出来ました。 いつかはこういったシステムも実務で触れてみたいですね~ リハーサル動画も上げてくださっているので是非ご覧になってみてください! www.youtube.com ラク スからの登壇セッションのご紹介 PHP 略語クイズ report by id:Y-Kanoh ( @YKanoh65 ) speakerdeck.com 私、Y-Kanohによる発表です。 LTは、イベントの最後にみんなで盛り上がるものということで、クイズ形式にPHPer関連の略語について紹介しました。 (カー○は関西だとまだ売ってますよ!!) ノン フレームワーク のレガシープロダクトを、Laravelに"載せる"実装戦略と、その後の世界 report by id:hirobex speakerdeck.com 私の発表です PHPerKaigi2023で発表させていただいたLTが好評だったので 今回レギュラー トーク 用に トーク 内容を増やして登壇しました! 20年以上開発がつづくレガシープロダクトをLaravelに載せたお話です。 既存コードベースにも PHP _CodeSnifferを導入して楽したい! reported by id:takaram 私、荒巻 ( @takaram71 ) の発表です。 www.docswell.com 私が担当するプロダクト・ 配配メール に PHP _CodeSniffer を導入した体験談をお話しました。 PHP _CodeSniffer は、 PHP コードのコーディングスタイルをチェックしてくれるツールです。たとえば以下のようなポイントをチェックしてくれます。 変数・クラス・メソッドなどの 命名 (スネークケースかキャメルケースかなど) 空白の使い方(インデント、 演算子 や括弧の前後など) 配列 リテラル は [] か array() か ただ既存のプロジェクトに実際に導入してみると、新規プロジェクトとは違った難しいポイントがあることがわかったため、それをどう解決したのかをお話しています。 このセッションを聞いたりスライドを見た方が、 PHP _CodeSniffer に興味を持っていただけていると嬉しいです!! ユニットテスト 環境整備~みんながテストを書ける環境へ~ report by id:rks_hrkw 私、堀川の発表になります。 speakerdeck.com 私の参加しているプロダクトで ユニットテスト の環境を導入・整備した体験談です。 困難も多かったですが最近はAI活用を筆頭に技術も進歩しているため、導入の難易度は下がっている印象です。 当然ですがプロダクトの息が長くなればなるほど導入には苦痛を伴うので、私たち以外にも ユニットテスト の環境が無いという方がいらっしゃればこの発表をきっかけに一歩踏み出していただければ幸いです。 クロージング report by id:Y-Kanoh ( @YKanoh65 ) クロージングでは、各地の PHP 系カンファレンスのイベント紹介がありました。 私も自分が実行委員長を務める PHPカンファレンス 関西の紹介をするため、登壇させていただきました! ラク ス初のブース出展 report by id:Y-Kanoh ( @YKanoh65 ) 今回は、 ラク スとしては初めての「ブース出展」を行いました!! 予想以上に広い会場で、他の企業の方もたくさんいる中での出展でしたが、たくさんの方に来ていただいて非常に盛り上がりました!! 今回、 ラク スのブースでは「 ラク ス認知度アンケート」と「 ラク ス PHP クイズ」を実施しました。 アンケートの結果は以下の通りです! みなさま、ご協力ありがとうございました!! アンケート結果 総回答数 220名 ラク スを知っていますか? 知っている 174名(79%)/ 知らない 46名(21%) PHPTechCafeを知っていますか? 知っている 82(42%) / 知らない 114(58%) アンケートに回答していただいた方には、以下の「Web × PHP TechCafe ステッカー」を、 クイズに回答いただいた方には「 ラク スオリジナルクリアファイル」を差し上げました!! PHPTechCafeステッカー 来場者配布物封入の儀 report by id:Y-Kanoh ( @YKanoh65 ) また、私は前日に東京へ移動し、来場者向けの配布物をトートバッグへ詰める「来場者配布物封入の儀」も行ってきました。 全部で1300個も作る必要があり、スタッフ含め30人程度の人数で数時間、配布物を袋に詰め続けました。 カンファレンスは勝手に生えてくるものではなく、誰かの努力で成り立っているため、手伝える部分は手伝い参加者も一緒になってイベントを維持していければと思います。 大勢で並んだ資料を詰めていく作業 詰め終わった袋の山 まとめ 今回のイベントは発表、参加、ブース出展と大忙しだったようですね。 ブースには多くの方々にご来場いただき大盛況でした! @YKanoh65 さんが実行委員を務める「 PHPカンファレンス 関西 2024」も楽しみですね! PHPerのためのコミュニティ PHPTechCafe ラク スでは PHP に特化したイベントを毎月開催しております。 その名も「PHPTechCafe」!! 次回は10/24(火)に『 PHPカンファレンス 2023を振り返る』 をテーマに開催します! まだまだ参加者を募集していますので、ぜひお気軽にご参加ください。 👉 PHPerのための「PHPカンファレンス2023を振り返る」PHP TechCafe 最後までお読みいただきありがとうございました! *1 : 確かに無料で使えますが、日頃お世話になっているのであれば寄付も考えたいですね😉
SRE課の飯野です。 去る2023/9/29(金)、『 SRE NEXT 2023 』が開催されました。 弊社SRE課からも6名が現地参加し、熱量あふれるたくさんのセッションを肌で体感してきました。 本ブログでは、SRE NEXT参加後にメンバーで実施した 感想戦 の内容をお届けします。 目次 SRE NEXTとは? 当日の様子 感想戦やってみよう 総括 SRE NEXTとは? SREに関わるトピックを扱う日本国内の大型カンファレンスです。 今年は約3年半ぶりにオフラインでも開催され(オンライン配信もありのハイブリッド方式)、東京・九段下の 九段会館 テラスにて行われました。 信頼性に関するプ ラク ティスに深い関心を持つエンジニアのためのカンファレンスです。 同じくコミュニティベースのSRE勉強会である「 SRE Lounge 」のメンバーが中心となり運営・開催されます。 SRE NEXT 2023は「Interactivity」「Diversity」「Empathy」という3つの価値観を掲げ、「双方向性のある意見交換の場にすること」「スタートアップから大企業まで、幅広い業種・領域・フェーズでのSRE Practice の実践を集約すること」「ビジネスサイド含めSRE以外の職責も含めて裾野を広げること」を意識して運営していき、より多様なSREの実践が普及することを目指します。 ( 公式サイト より) トータル1,000名を超える申込があり、オフライン参加の申込も220名ほどいらっしゃったようです。 大盛況なのが伺えますね。 当日の様子 タイムテーブル 基調講演がオープニングとエンディング枠でそれぞれ40分、それ以外のセッションは3トラック同時開催で各20分ずつ行われました。 また、ランチ休憩後にはオフライン会場限定でパネルディスカッションが開催されていました。 弊社のSRE課からは6名が参加しましたが、各人が興味のあるセッションをそれぞれ選択して拝聴しました。 もちろん重複したセッションもありますが、チームとしてのインプットの量がすさまじいですね! 閉会式のあとには懇親会が行われ、美味しいお酒と食事をいただきつつ、参加者の方々と交流することができました。 セッションを聴いたあの人やあの人とお話ができちゃうなんて! 感想戦 やってみよう さて、SRE NEXTに参加した熱が冷めやらぬうちに、後日さっそく 感想戦 を実施してみました。 実施するにあたって、事前に用意したフォーマットは下記です。 # 印象に残ったセッション ## タイトル ## 登壇者情報 ## スライド ## セッション概要 - セッションの内容を簡潔に ## 共有したい点、感想等 - どんな点に共感したか、疑問に思ったこと等 --- # 今後実施/挑戦したいこと - 参加してみてアクションを起こしたくなったものが何かあれば # 全体を通しての感想 - 率直な感想をご自由に それぞれが印象に残ったセッションを選択し、セッション概要と共有したい点/感想等を事前にまとめてもらいました。 以下、実際にまとめてもらった 感想戦 の内容を一部ご紹介します。 (※各感想の冒頭に記載しているSRE課メンバーの二つ名は課のみんなで考えました) カラオケでマイクを離さないのに唄わないUの感想 増え続ける公開アプリケーションへの悪意あるアクセス。多層防御を取り入れるSRE活動 セッション紹介ページ speakerdeck.com セッション概要 AWS パートナーもされていた方が現SREとして クラウド インフラでの具体的なセキュリティ対策を、レイヤーごとに細かくリストアップ 共有したい点、感想等 アプリ脳の自分からすると、具体的なセキュリティ対策のイメージがドッと増えた 他社のセキュリティ評価の観点を得られた セキュリティ評価上の ベンチマーク にしたいくらいの資料 今後実施/挑戦したいこと セキュリティに関して考えるときには上記で列挙されている観点は検討に入れていきたい 基調講演「 信頼性目標とシステムアーキテクチャー 」後半で話されていたレベルのことを主体的に話せるレベルまで解像度を上げたい 「 Runbookに何を書き、どのようにアラートを振り分けるか? 」にあったRunbookを「運用する」という観点でのアラート体制整備は取り入れるべき 全体を通しての感想 SREも突き詰めるとツラミ解決部隊なので、意外と共感できない部分とかもあり、概念自体の難解さと 不定 形さをあらためて認識した 面白い話ほど視座が高くて、今の自分には抽象度が高すぎた エンジニア経験が少ない自分には、具体性の高い 経験談 チックな話が理解しやすく、他社の現場目線を知ることができて非常に面白かった アイドルと設計をこよなく愛する課内の先生Iの感想 エンタープライズ 企業でのSRE立ち上げ挑戦の際に意識した事と気付き、現在地とこれから セッション紹介ページ speakerdeck.com セッション概要 イオングループ 子会社でのSREチーム創設と現在までの歩みを紹介(やってきたこと、意識したこと、しくじり事例) 共有したい点、感想等 目指すべきSREチームとしての方向性が我らと同じだと感じた その上で実績を積み上げているので、先人のマインドとしてとても参考になった やっていくにあたり ・組織に変化を起こすのは常に1人の行動から ・小さく始める  ・重要なところから  ・条件的にやりやすいところから ・満点は不可能 ・人が関わる以上、タイミングが必要な場合もある。準備をしながら機会を伺う  ・正論はときに人を傷つける 意識したこと ・意思決定者へ目指す方向を共有する ・説明よりも動くものを用意する  ・有益なものであれば乗ってくる ・コミュニケーション  ・オープンに話せる場を作っておく  ・同じ目標を持つ状況を作り出し、協力する   ・障害対応や有事の取り組みには、積極的に参加する   ・同じ目的・目標に向かって進む機会を増やす   ・短期的であればヒーロー的行動は効果的  ・結局は信頼関係が大事。相手の役に立つ事を積み上げる 今後実施/挑戦したいこと 成果物のデモ会の実施 不定 期のラジオをSRE課が主体となって開催 開発チームの領域でも、手を動かす部分を臆せず巻き取れるようになりたい 全体を通しての感想 上記のセッション以外にも参考になったセッションがたくさんあった 特に リクルートの近藤さんのセッション では開発チームを巻き込むSRE活動を紹介してくれていたので、真似できることはやりたい バカンス中もプールサイドでコーディングする課長Mの感想 勘に頼らず原因を見つけるためのオブザーバビリティ セッション紹介ページ speakerdeck.com セッション概要 オブザーバビリティの導入によってトラブルシュートの属人化を改善した事例を紹介 共有したい点、感想等 オブザーバビリティの基本をしっかりと解説しておりそもそもの概念が理解しやすい トラブルシュートでは特定メンバー頼りになりがちだが、個人の勘ではなくデータを元に対応可能な状態を目指すという考えはどこの会社でも今後必要になっていくものと考える 今後実施/挑戦したいこと コンテナ化が目下の目標だがオブザーバビリティ基盤の構築もしっかりと進めていきたい そもそも基盤があれば解決するものではなく、全エンジニアがオブザーバビリティの必要性を正しく認識する必要がある、そのための啓蒙活動は進めたい 全体を通しての感想 競合他社と比較して自社の立ち位置を客観視出来たのは良かった 現状を改善するための取り組みをしっかりと進めたいと思う 最近肩凝りに苦しむパパさんエンジニアIの感想 Warningアラートを放置しない!アラート駆動でログやメトリックを自動収集する仕組みによる恩恵 セッション紹介ページ speakerdeck.com セッション概要 本来アラートは「人間が即座にアクションを起こす」もの。 Warningは「これから重要な問題がおきそう」という扱いだが、 現実的に Slackにひっそりと流されてたりして、実際には「人間の行動が起きない」しかも「結構な数が出る」 → 結果、オオカミ少年と化し、役に立たなくなる。 そしてハインリッヒの法則に従い、一定の潜伏期間を経て大爆発する アラートの調査=人力=トイルが含まれる可能性が高い 「Warningアラートの調査はトイルになりがち」  アラート発生  →WebHook  →危ないかどうかの初期判断情報を集めるAPPを作ってみたら良かった! 共有したい点、感想等 「Warningはトイルです」と言われ、確かに!と思った。 ラク スと状況が似ているし、インフラ出身なので、「コレは使える!」とピンと来る感じがあった 今後実施/挑戦したいこと オブザーバビリティの導入 ページャのモダナイズ ChatOpsに寄せていきたい Runbookは脱 Excel をして GitHub に アラート時にAPPから引張りやすく そもそもRunbook自体を減らしていく活動(自動化) GitHub に寄せることでRunbookの管理状況を可視化 誰がどれだけメンテしている? 減っている?増えてしまっている? サービス毎に特性がある?お手本にすべきサービスは・・等 全体を通しての感想 開発の決済者(マネジメント層)もぜひ視聴してほしいイベントだった 他社と比較し、"検討" や "課題感" ではなくとりあえず "実践" してみないと、と感じた Google の山口さんも言っていたが、 SRE NEXT 2021で各社起案していたものが2023では実践レベルに 他社と比較すると、意思決定が遅い気がする 組織全体のマインドを変えないと、他社との差がより開いてしまう オブザーバビリティを導入したいという想いがより強くなった ビジネスの可視化は当然これの先にある、予測に基づいた設計をしていかないと 可観測性が低いことが一因となって意思決定のスピードも遅くなっているのでは k8s のアップデート戦略等、モダナイズの設計や方向性は他社とあまり外れておらず一安心 ビールと肉を愛する姉御エンジニアIの感想 開発者とともに作る Site Reliability Engineering セッション紹介ページ speakerdeck.com セッション概要 SREを開発者と”ともに”実現するための事例紹介 かつてはSREがやることはSREだけで決めていたが、それが本当に必要な作業だったのか?という疑問から行動を変えた 開発者からFBを得ることに注力し、以下に取り組む サーベイ の実施、取り組みの共有、チームに入る 上記は組織マネジメントを狙って実施したものだったが、結果話す場のデザインを行ったことによりふりかえりや挑戦/サポートの文化が定着した 共有したい点、感想等 開発チーム向けの サーベイ の実施や、進めているPJの共有等、取り組んでいるものがおおよそ(正解は別にないけど)同じだったので自信になった 困っている人の声が集まる状態にして、さくっと助けてあげる感じの文化がとてもよい(まさに信頼貯金) 開発チームへの期待値を明示して、開発チーム自身がやりたいと思っていることは支援をしてあげるという立ち位置もよき 「ここからここまでしかやらない」という線引きじゃないところがよき 開発チーム⇄SREの留学も行われてるそうで、お互いのプロセスを体験するのもよい取り組み マネージャーも兼務するらしい 総じてエモかった 今後実施/挑戦したいこと SREのロードマップにある「文化醸成」は地道な種まき(仕掛け)と関係構築の賜物だと思うので、モダナイズやトイル削減といった具体的なものを進めつつ、広く信頼関係を構築していきたい 開発組織に仲間を見つける 全体を通しての感想 SREイベントに参加したの初めてだったので、そもそもの心構え的なものが学べて豊作 ポジション的なところもあって、狙って組織作りのセッションを選んでたけど、どの組織も課題は同じっぽかった 結論SREは 総合格闘技 だし何より信頼関係の構築=対話が大事 マウンテンデュー ばっかり飲んでる新米CKAのSの感想 QAと共に築く、機能性を通じた信頼性担保への取り組み セッション紹介ページ speakerdeck.com セッション概要 新規プロジェクトに携わる中でQAチームと協力し、機能性を担保するためにSREとしてどのように関わり、信頼性向上に取り組んだか 共有したい点、感想等 コミュニケーションとコラボレーションが大切 信頼性の担保と同じくらいコミュニケーションとコラボレーションは大切 必要性があれば仕組み作りが大切 結局は日々のちょっとしたコミュニケーションの継続がコラボレーションにつながる 印象に残った取り組み チームの学習機会を奪わないよう、アド バイス は行うが具体的な例までは提示しない タスクの インパク トが低いものについては爆速で解決することを心がけることによって信頼貯金を増やした 今後実施/挑戦したいこと 社内のイベント等に参加してコラボレーションを増やしていきたい 些細な手助けを心がけて信頼貯金を貯めていきたい もちろん手助けするには知識も必要なため継続的に学習する もっとフランクにコミュニケーションを取っていきたい 全体を通しての感想 基調講演の「信頼性は会話です」が響いた 普段から会話していない人と信頼関係は築かないよなと感じた 総括 以上、『SRE NEXT 2023』に参加したメンバーの 感想戦 の内容をご紹介しました。 セッションを聴いただけではなく、同じ熱量を感じた状態でチームメンバーと感想を共有し合えた点は組織としてよい刺激になったと思います。 ラク スのSRE文化はまだまだ発展途上ですが、他社の皆さまがこれまで取り組まれてきた事例を聞いて、自分たちの進むべき方向性や価値観を再確認できたのはとても大きな収穫でした。 わたし個人としてはオフラインでのイベント参加がコロナ以降初めてだったので、会場の雰囲気を久しぶりに生で味わえてとてもよい機会になりました。(イベントの醍醐味はやはり懇親会!!) スタッフの皆さま、登壇者の皆さま、企画運営本当にお疲れ様でした。そしてありがとうございました! 最後に、唯一撮っていた懇親会のお寿司の写真を添えておきます。 来年のSRE NEXTも今から楽しみですね!
顔の濃さが唯一の アイデンティティ のインフラエンジニア、m_yamaです。15カ月ぶり2度目の登場です。 去年、 ラク スで先行技術検証を行っている「技術推進プロジェクト」で取り組んだ「スケーラブルな監視システム」について、1年たっぷり寝かせた熟成リリースとなります。お目汚し失礼いたします。 VictoriaMetricsとは? VictoriaMetricsのメリット・デメリット VictoriaMetricsのメリット VictoriaMetricsのデメリット VictoriaMetricsの導入方法 1. VictoriaMetricsの構築 2. 監視対象にexporterをインストール 3. vmagentのセットアップ VictoriaMetricsの活用事例 コロプラ(colopl)の場合 Adidasの場合 CyberAgentの場合 VictoriaMetricsでログは見れないの? まとめ VictoriaMetricsとは? VictoriaMetricsは、 オープンソース の時系列データベースと監視ソリューションです。 Prometheusと互換性があり、設定ファイルをそのまま使えたりもするので、プロメテウサーは違和感なく利用できるはず。 ちょっと強い表現になってしまいますが、個人的には Prometheus上位互換 の OSS 監視ツール 、と感じています。 そんなVictoriaMetricsには、2つのバージョンがあります。 single nodeバージョン clusterバージョン ざっくり主要な機能をまとめてみました。以下のグレーバックの コンポーネント をまとめたのがsingle nodeバージョン、分割したのがclusterバージョンになります。 (他にもalertやproxyなどの便利そうな コンポーネント がいくつかあります) clusterバージョンはマイクロサービスで ナウい のでこちらの方が良さそうですが、公式としては 100万データポイント/秒以下 ならsingle nodeバージョンを勧めています。 It is recommended to use the single-node version instead of the cluster version for ingestion rates lower than a million data points per second. https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html 一般的なnode_exporterの場合、1サーバで700メトリクスとちょっとあったので(※)、デフォルトの収集間隔の1分をもとにざっくり単純計算すると、監視対象が7万台ぐらいならsingle nodeバージョンで良さそうです。 ※ AmazonLinux2023の場合 1,000,000(メトリクス/秒) / 800(メトリクス/サーバ) = 1,250(サーバ/秒) 1,250(サーバ/秒) * 60(秒) = 75,000(サーバ/分) もちろんそんなバランスよくタイミングをずらしてデータ収集できるわけはないので要検証ですが、 冗長化 すればかなりの台数を監視できそうではあります。 VictoriaMetricsのメリット・デメリット VictoriaMetricsのメリット Prometheusと互換性がある Grafanaから見る分には違いはない データ圧縮効率が高く、長期保存に向いている TimescaleDBと比べて最大70倍 Prometheusと比べて最大7倍 Prometheusで1TBのデータが120GBまで減ったとの記事もあり (clusterバージョンの場合) コンポーネント がわかれておりスケーラビリティに優れる スケールアウト・インが可能 vmselect: 読み込み。複数人がたくさんのグラフを一度に取得しようとしたときにスケールアウトすればスムーズに読み込まれる(はず) vminsert: 書き込み。監視対象が増えて監視データが増えても手動で追加する必要なし スケールアウトのみ可能 vmstorage: データ保存。重いクエリがたくさん来てもスケールアウトすればスムーズな処理ができる。はず。 ※スケールインはデータロストを伴うので、計画的な拡張が必要 (clusterバージョンの場合) マルチテナントをサポート 複数システムの一元管理もしやすい VictoriaMetricsのデメリット 日本語での導入事例が(Prometheusよりも)少ない コンポーネント が多くやや複雑 ※デメリットに関してはあまり情報を見つけられず、推しが強めな内容になってしまっています。。 みなさんが検証する時に、ダメな点をぜひ教えてください(他力本願) VictoriaMetricsの導入方法 VictoriaMetricsで監視を始めるには、やることが3つあります。 VictoriaMetricsそのものの構築 監視対象にexporterをインストール vmagentのセットアップ ここではシンプルなsingle nodeバージョンで話を進めます。 1. VictoriaMetricsの構築 single nodeバージョンのVictoriaMetricsは1バイナリで動くので、 github からダウンロードして実行するだけで起動します。 # ダウンロード curl -sLO https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.94.0/victoria-metrics-linux-amd64-v1.94.0.tar.gz # 解凍 tar xzf victoria-metrics-linux-amd64-v1.94.0.tar.gz # 実行 ./victoria-metrics-prod -storageDataPath=/path/to/data -retentionPeriod=1y こんだけ。わー簡単。(本番運用するならサービス化とかログとか必要だけども) 起動オプション はいろいろありますが、公式曰く、基本的にデフォルト値を使えば十分、とのことです。助かる。 また公式で ansibleのplaybook も用意されているので、clusterバージョンで複数台のnodeを使う場合は、こちらをベースにコード化しておくと楽そうです。 さらにさらに、各 コンポーネント には公式の docker image もあるので、 k8s とかECS Fargateでも監視 クラスタ ーを構築できそう。EFSをマウントしてデータをそこに入れれば、ほったらかしサーバレス監視システムができそう?やってみたい人生だった。 2. 監視対象にexporterをインストール 監視対象のサーバには、目的に合ったexporter(node_exporter、postgres-exporterなど)をインストールする必要があります。 手順(というにはシンプルすぎるけど)は以下の通り。 curl -sLO https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz ./node_exporter-1.6.1.linux-amd64/node_exporter & こんだけ。わー簡(以下略) その後、 {サーバのIP or ホスト名}:{exporterのポート}/metrics にアクセスすると以下のようなデータが取得できます。 # node_exporterの場合 [ec2-user@hoge-server ~]$ curl -s localhost:9100/metrics | head # HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. # TYPE go_gc_duration_seconds summary go_gc_duration_seconds{quantile="0"} 3.1403e-05 go_gc_duration_seconds{quantile="0.25"} 3.1403e-05 go_gc_duration_seconds{quantile="0.5"} 3.1403e-05 go_gc_duration_seconds{quantile="0.75"} 3.1403e-05 go_gc_duration_seconds{quantile="1"} 3.1403e-05 go_gc_duration_seconds_sum 3.1403e-05 go_gc_duration_seconds_count 1 ※基本的なメトリクスの取得に使われるnode_exporterでは、CPUやメモリなど700ちょっとのメトリクスをリアルタイムで取得できました。 そのメトリクスを、vmagent(後述)が定期的に取得しにくるので、監視対象でやることはexporterを起動しておくだけ。簡単。 exporterも監視対象に個別にインストールとかだと白目剥いちゃうので、ansibleでコード化しておきたいですね。 3. vmagentのセットアップ 監視サーバと監視対象サーバの準備ができたら、監視対象サーバから上記のメトリクスを取得してVictoriaMetricsに投げるやつが必要です。それがこのvmagentです。 さっきとほぼ同じなので書くほどでもないのですが、一応導入手順を書いておきます。 (vmagentはvmutils-*に含まれる複数のバイナリのうちの一つです) curl -sLO https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.94.0/vmutils-linux-amd64-v1.94.0.tar.gz tar xvf vmutils-linux-amd64-v1.94.0.tar.gz ./vmagent-prod -promscrape.config=/path/to/prometheus.yml -remoteWrite.url=https://{victoria-metrics}/api/v1/write こ(以下略) configには監視対象や設定を記載します。詳しくはPrometheusのconfigurationを。 prometheus.io 調べると、vmagentではなくPrometheusを使っている事例が多いのですが、Prometheusに比べてvmagentの方がメモリやCPU、ディスクIO、ネットワーク 帯域幅 を抑えることができるそうです。 すでにPrometheusを使っていてvmagentに乗り換える場合は、Prometheusの設定ファイルを使えるので移行も簡単。(Prometheus単独で使ってきた場合、設定ファイルにremote_write.urlでVictoriaMetricsを指定する必要はあります) VictoriaMetricsの活用事例 コロプラ (colopl)の場合 累計データポイントが1,500億以上の k8s クラスタ ーの監視にVictoriaMetricsを利用しているそうです。Prometheusではスケールアップしても耐えきれなかった 負荷試験 を、VictoriaMetricsでは安定してクリアできたとのこと。 (4年前の情報なので、さらに進化を遂げていそう) [Prometheus Meetup#3] Victoria Metricsで作りあげる大規模・超負荷システムモニタリング基盤 / Monitoring Platform With Victoria Metrics - Speaker Deck Adidas の場合 事例というよりPromCon2019(Prometheusに関連するカンファレンス)で紹介されたPoC的な内容のようですが、Prometheusのリモートストレージとしても検討される時系列DBのInfluxDBやThanosと比較して、圧倒的にリソース消費量が少ないことがわかります。 https://promcon.io/2019-munich/slides/remote-write-storage-wars.pdf CyberAgent の場合 2020年時点で、30以上の Kubernetes クラスタ を横断監視しているとのこと。プロダクトの増加による追加コストを抑えられている、というのは、Prometheusのサービスディスカバリとか省エネなリソース消費的な話かな? (新卒で入社後半年でこの内容はすごい。。) VictoriaMetrics+Prometheusで構築する複数Kubernetesの監視基盤 - Speaker Deck FIFA ワールド カップ カタール 2022の監視にも導入されていた模様。 GMP ( Google Managed Prometheus)のサブ的な使い方? 大規模イベントを支えるクラウドアーキテクチャの実現 / ABEMA Cloud Platform Architecture for Large-scale Events - Speaker Deck VictoriaMetricsでログは見れないの? これまでは名前の通り、メトリクスに特化した監視システムで、ログを見る場合はElasticsearchやGrafana Lokiなどを利用する必要がありました。が、2023/7にvictorialogsがプレビューリリース(v0.1.0)され、ログまでまとめて管理できるようになりそうです。(2023/10/13点でv0.4.1が最新) 公式によると、victorialogsは、ElasticsearchやGrafana Lokiと比べて、最大で30倍のデータを処理できる、とのことです。すごい。 現在携わっている blastmail , blastengine (宣伝)でも、Elasticsearchのログやindexが肥大化してあっぷあっぷなので、こういった新しいツールも上手く活用できればいいなあ、というお気持ちです。 まとめ OSS 監視のVictoriaMetricsについてまとめました。 SaaS の監視システムの料金が無視できなくなってきたらぜひ検討してみてください。
こんにちは、あるいはこんばんは。だいたいサーバサイドのエンジニアの( @taclose )です☆ みなさん、この本を読んだ事ありますか?? 達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践 ISUCONをテーマとしながらWebパフォーマンスの改善の進め方を解説した本で、改善するなら一度は読む事をお勧めします! とはいっても、いざWebパフォーマンス改善をしようと思っても中々糸口が掴めなくて難しいんですよね! 数学でいうなら、練習問題は見たけど、いざ演習やると解法がわからないっていう感じでしょうか(汗 そこで、 TDDハンズオン に引き続き、 弊社ではWebパフォーマンス改善ハンズオンを実施 しました! 今回はそんな ハンズオン開催を行った際のスライド資料や結果、考察をまとめて公開 しようと思います。 対象読者は以下を想定 WEBパフォーマンス改善のハンズオンをわが社でも開催するぞ! WEBパフォーマンス改善について学びたい! では、本題に入ろうと思います。 WEBパフォーマンス改善ハンズオン スケジュール スライドはこちら 学習結果(効果測定と気づき) 反省点・感想 時間が全然足りてない! 経験の少ない人にはどこから取り掛かればよいのか、難しい 最後に 参考資料等 WEBパフォーマンス改善ハンズオン 基本的な流れは、先ほどご紹介した書籍にある private-isu というものを使って擬似的な SNS サービスのパフォーマンス改善を行うといったものです。 これを2日間に分けて座学とハンズオンを合計3時間〜4時間かけて実施しました。 github.com スケジュール 1日目はこのあとに記載しているスライドをみれば詳細がわかりますが、座学としてパフォーマンス改善の方法をレクチャーします。 具体的なチューニング方法がこんなのあるよ!ではなく、 計測→ ボトルネック を見つける→ ボトルネック を解決する→効果測定という流れを経験してもらいました 。 2日目は ボトルネック を見つけて、改善方法を悩んでもらいつつ、ヒントを出しつつハンズオンを実施してもらいました。 スライドはこちら 実際のスライドは以下になります。もし良かったら皆様の会社で、または個人でハンズオンされる際に参考にしてください。 ※コマンドをコピーしたい場合はPDFとしてダウンロードしてくださいね! 学習結果(効果測定と気づき) 最初数百点だったスコアですが、3万点近くまで改善したチームがいました! 1時間足らずでこの結果はすごいですね! いくつかのチームに感想や改善の流れがどうだったかを聞いてみたところ どこが遅いのかわかっても、改善方法がわからなかった アプリ側の ソースコード の修正は苦労した 使い慣れない ミドルウェア のチューニングに苦労し時間切れとなった などなど... 反省点・感想 最初は1時間半x2日を予定していたハンズオンでしたが、みんな自主的に延長して合計4時間近くの開催となりました。 アンケートでも好評だったようで、これはまた来年もやっていいんじゃないかと思えました。 反省すべき点としては、以下のようなところがありました。 時間が全然足りてない! 今回のハンズオンでは大前提としてチームの中で誰かしらは「nginxの設定は出来る。」「 mysql のINDEXを張るコツを知っている」「ある程度はどこを修正すべきか勘所を知っている」そんな前提がある内容だったため、ハンズオン以前の所で時間を消費してしまったチームも発生してしまいました。(運営側の反省すべき点) せっかく ボトルネック がどこかわかっても、どうアプローチすれば良いのかが判断付かず、悠長に調べる時間がなかったようで...申し訳ないOrz 他にも「土日使ってやりたかった」とかの意見もありました。私も確かにもっと時間欲しかった!(切実) とはいえ、アプリも改修して、キャッシュの有効化までトライしているチームもあり、経験者がいるかどうかの差はでかかったようです! みんなも次は色々な改善方法を学んで是非リベンジしてほしいと思います! 経験の少ない人にはどこから取り掛かればよいのか、難しい ヒント出し過ぎても面白くないのかなとアタフタしてたのですが、アンケート結果からすると 難易度が高すぎた と感じました。 ハンズオンの時間は運営側が声かけするのも良いのですが、手が回らないと思います。 なので、 時間経過毎に「こう計測すると ボトルネック わかります。疑わしい所はここなのでコード読んでみましょう。このbreak臭いますね」と計測~改善までの流れを公開していく スタイルが良かったのではないかと思いました。 最後に パフォーマンス改善は基本的には トップダウン 方式(正攻法を適応していく)よりも ボトムアップ 方式(遅い場所を直す)が良い と言われており、それは間違いありません。 でも、 ボトムアップ 方式でやる時に改善方法の知識の引き出しが少ないと改善効果の割に内部品質ばかりが下がるような最悪の事態にもなり兼ねません。 そういう意味では、 ボトルネック な部分では一体何が行われていて、そこを回避する方法はどういったものがあるのか を日々学習していく事が重要になってきます。 最後になりますが、private-isuはDocker版もあります。 参考資料のURL一覧に eichisanden さんの「 private-isuをdockerでセットアップした時のメモ 」等貼っておきますので、是非参考にしながらみなさんもトライしてみてください! ハンズオンみんなお疲れ様でした! ハンズオンの様子(東京・大阪) 参考資料等 書籍:達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践 private-isu/manual.md at master · catatsuy/private-isu · GitHub private-isuをdockerでセットアップした時のメモ private-isuをdockerでやってみた時のメモ
はじめに こんにちは、開発課に所属している新卒 1 年目のke-suke0215です。 Dockerのイメージについて「なんとなく業務で使っているけど、いまいちどうなっているのかわかっていない」という状態だったので、今回はDockerイメージの中身がどのように構成されているのかについて調べてみました。 私のようなDockerをなんとなく使っている方が仕組みを理解する手助けになれば幸いです。 はじめに Dockerイメージの構成要素 そもそもDockerイメージとは Dockerイメージの中身を見る 1. イメージをビルドする 2. イメージをtarファイルとして保存する 3. tarファイルを展開する ハッシュ名のディレクトリ ハッシュ名のJSONファイル manifest.json repositories レイヤーについて 2つのDockerfileからレイヤー構造を理解する まとめ 参考文献 Dockerイメージの構成要素 そもそもDockerイメージとは Dockerイメージは、アプリケーションの実行に必要な情報をまとめたパッケージです。これらのイメージはコンテナ実行の土台となっています。ポータビリティの高さやホストOSから環境を分離できるなどの利点があります。 Dockerイメージの中身を見る ここでは以下のDockerfileをビルドしたDockerイメージの中身を見ていきます。 FROM ubuntu:latest 最新の ubuntu のイメージをベースにするだけの簡単なものです。 中身を見る手順は以下になります。 各コマンドの説明は省略させていただきます。 1. イメージをビルドする 今回は my-ubuntu という名前のイメージでタグは 1.0 としてビルドします。 docker build -t my-ubuntu:1. 0 . 2. イメージをtarファイルとして保存する docker save コマンドを使ってtarファイルにします。 docker save -o my-ubuntu-1. 0 .tar my-ubuntu:1. 0 3. tarファイルを展開する 保存したtarファイルを展開して中身を見れるようにします。 tar xf my-ubuntu-1. 0 .tar これでdockerイメージの中身がどのようになっているのか確認できるようになりました。 どのようなものがあるのか確認してみます。 $ ls 0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40 e8b8228e36aef7aaaacedf7b10514683933b62424e35956c02e5659aefbcf3bd.json manifest.json my-ubuntu-1. 0 .tar repositories my-ubuntu-1.0.tar は展開元のファイルなので、それ以外について詳しく見ていきます。 ハッシュ名の ディレクト リ 0cf20f556e5~~~ です。これはイメージを構成する上で核となっているレイヤーと呼ばれるものです。レイヤーについての詳しい説明は後述します。各レイヤーはユニークな ハッシュ値 によって識別されています。各 ディレクト リにはそのレイヤーの内容と関連する メタデータ が格納されます。 ハッシュ名の JSON ファイル e8b8228e36a~~~.json です。中身は以下のようになっています。 { " architecture ": " amd64 ", " config ": { " Env ": [ " PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin " ] , " Cmd ": [ " /bin/bash " ] , " Labels ": { " org.opencontainers.image.ref.name ": " ubuntu ", " org.opencontainers.image.version ": " 22.04 " } , " OnBuild ": null } , " created ": " 2023-06-28T08:37:42.319109064Z ", " history ": [ { " created ": " 2023-06-28T08:37:40.107416121Z ", " created_by ": " /bin/sh -c #(nop) ARG RELEASE ", " empty_layer ": true } , { " created ": " 2023-06-28T08:37:40.172787047Z ", " created_by ": " /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH ", " empty_layer ": true } , { " created ": " 2023-06-28T08:37:40.235648065Z ", " created_by ": " /bin/sh -c #(nop) LABEL org.opencontainers.image.ref.name=ubuntu ", " empty_layer ": true } , { " created ": " 2023-06-28T08:37:40.292202878Z ", " created_by ": " /bin/sh -c #(nop) LABEL org.opencontainers.image.version=22.04 ", " empty_layer ": true } , { " created ": " 2023-06-28T08:37:42.055763636Z ", " created_by ": " /bin/sh -c #(nop) ADD file:140fb5108b4a2861b5718ad03b4a5174bba03589ea8d4c053e6a0b282f439ff3 in / " } , { " created ": " 2023-06-28T08:37:42.319109064Z ", " created_by ": " /bin/sh -c #(nop) CMD [ \" /bin/bash \" ] ", " empty_layer ": true } ] , " moby.buildkit.buildinfo.v1 ": " eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJmaWxlbmFtZSI6IkRvY2tlcmZpbGUifSwic291cmNlcyI6W3sidHlwZSI6ImRvY2tlci1pbWFnZSIsInJlZiI6ImRvY2tlci5pby9saWJyYXJ5L3VidW50dTpsYXRlc3QiLCJwaW4iOiJzaGEyNTY6MGJjZWQ0N2ZmZmEzMzYxYWZhOTgxODU0ZmNhYmNkNDU3N2NkNDNjZWJiYjgwOGNlYTJiMWYzM2EzZGQ3ZjUwOCJ9XX0= ", " os ": " linux ", " rootfs ": { " type ": " layers ", " diff_ids ": [ " sha256:59c56aee1fb4dbaeb334aef06088b49902105d1ea0c15a9e5a2a9ce560fa4c5d " ] } } この json ファイルにはイメージの メタデータ などが記述されています。また、Dockerfileの記述の中でレイヤーにならないものの情報も入っています。レイヤーにならない記述は CMD , ENTRYPOINT , ENV などがあります。 また、イメージの情報を出力すると以下のようになります。 docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE my-ubuntu 1 . 0 e8b8228e36ae 1 days ago 77 .8MB IMAGE ID がこのファイル名と一致していることがわかります。なのでハッシュ名の JSON ファイルはdockerイメージのidにもなっているのです。 manifest. json このファイルの中身は以下の通りです。 [ { " Config ": " e8b8228e36aef7aaaacedf7b10514683933b62424e35956c02e5659aefbcf3bd.json ", " RepoTags ": [ " my-ubuntu:1.0 " ] , " Layers ": [ " 0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40/layer.tar " ] } ] manifest.json はDockerイメージの中核となるファイルの一つです。 各記述はそれぞれ以下を指しています。 Config :ハッシュ名の JSON ファイル RepoTags :イメージ名とタグ Layers :使用しているレイヤー このことから manifest.json はこのdockerイメージの設計図のような役割を果たしていると言えそうです。 repositories repositories はの中身は以下のようになっています。 { " my-ubuntu ": { " 1.0 ": " 0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40 " } } イメージ名とタグとをハッシュに マッピング することで、具体的なイメージのバージョンや名前を特定する役割を果たします。 レイヤーについて さきほど少し触れましたが、dockerイメージはいくつかの層(レイヤー)で成り立っています。基本的にDockerfileの記述ごとに1つまたは複数のレイヤーが作成されます。( ハッシュ名のJSONファイル の部分でも触れたように、一部レイヤーにならない記述もあります。) レイヤー構成の何がいいかというと、差分のみのレイヤーを作成することでディスクスペースを節約できることです。具体的に見ていきましょう。 2つのDockerfileからレイヤー構造を理解する まず FROM と RUN の記述がある下のようなDockerfile(Dockerfile①とする)からイメージを作成してみます。 FROM ubuntu:latest RUN apt-get update すると以下のレイヤーが作成されます。 FROM ubuntu:latest に基づいて生成されたレイヤー(レイヤーA) RUN apt-get update に基づいて生成されたレイヤー(レイヤーB) 次に下のような FROM と COPY の記述があるDockerfile(Dockerfile②とする)からイメージを作成します。 FROM ubuntu:latest COPY sample.txt /tmp このとき、生成されるのは以下のレイヤーです。 COPY sample.txt /tmp に基づいて生成されたレイヤー(レイヤーC) Dockerfile①と同じ記述である FROM ubuntu:latest のレイヤーは作成されず、すでにあるレイヤーAを使ってイメージを構成します。なのでDockerfile②から生成されるイメージはレイヤーAとレイヤーCからできています。 不足しているレイヤーのみを作成してイメージを生成するため、無駄なレイヤーを増やさずディスク容量を節約できるのです。 まとめ Dockerイメージの内部構成について見てきました。興味がある方の参考になれば幸いです。最後までお読みいただきありがとうございました。 参考文献 Dockerコンテナのレイヤ構造とは? コンテナの作り方
こんにちは。 株式会社 ラク スで先行技術検証をしたり、ビジネス部門向けに技術情報を提供する取り組みを行っている「技術推進課」という部署に所属している鈴木( @moomooya )です。 ラク スの開発部ではこれまで社内で利用していなかった技術要素を自社の開発に適合するか検証し、ビジネス要求に対して迅速に応えられるようにそなえる 「技術推進プロジェクト」 というプロジェクトがあります。 このプロジェクトで「DBセキュリティ」にまつわる検証を進めているので、その中間報告を共有しようかと思います。 本記事における「DBセキュリティ」とは カバーする範囲 カバーしない範囲 DB暗号化方式は大きく分けて3つ OS機能によるディスク暗号化 DB機能による暗号化(透過的暗号化) アプリケーションによる暗号化 「アプリケーションによる暗号化」が最も強いなら DB暗号化を導入する際の懸念点 最大の懸念は性能面 検証する環境や条件 検証環境 検証内容 次回予告 参考 本記事における「DBセキュリティ」とは カバーする範囲 本稿で扱う「DBセキュリティ」については入室管理、アカウント管理、権限管理、データ暗号化、アクセス監視といった部分を対象とし、中でも技術的な検証が必要になるデータ暗号化について検証を進めていきます。 これらは 個人情報の保護に関する法律についてのガイドライン(通則編) にて 10-6 技術的安全管理措置 として記述されています。 カバーしない範囲 「DBセキュリティ」の範囲として定義した通り、 ウェブアプリケーション 経由のセキュリティは扱いません 。 なので DBMS への通常アクセスによるセキュリティに関しては対象外とします。 DB暗号化方式は大きく分けて3つ 暗号化の強度としては以下の ディスク暗号化 透過的暗号化 アプリケーションでの暗号化 の順に強くなっていきます。 OS機能によるディスク暗号化 こちらはDB内のデータを暗号化するのではなく、DBのデータを保持しているディスク自体を暗号化するものになります。 こちらはOSレベルで提供される機能を用いてディスクの暗号化を行います。 ディスク暗号化ではDBサーバーからのディスク窃取への対策となります。一方、DBサーバーのOSにログインを許してしまうと暗号化の効果はありません。 弊社では RHEL 系OSの利用が多いので、今回の検証では LUKS を用いる予定です。 DB機能による暗号化(透過的暗号化) DBMS 自体もしくは、 DBMS への プラグイン によってDB内にデータ格納時に暗号化する方式です。 透過的暗号化方式(TDE)が該当します。 透過的暗号化ではディスク窃取に加えて、 DBMS が書き込むデータファイルの窃取にも対応します。ただし、 DBMS 自体へのログインを許すと復号されたデータが閲覧可能になってしまいます。 弊社では PostgreSQL を利用することが多いのですが、 PostgreSQL には標準で組み込まれた透過的暗号化機能はありません。そのため、 NEC 製の Transparent Data Encryption for PostgreSQL (TDEforPG) を PostgreSQL に組み込んで利用する予定です 1 。 アプリケーションによる暗号化 こちらはアプリケーションの機能として暗号化処理を実装する形式になります。そのためDBに渡されるのはすでに暗号化したデータであり、使用するDBに左右されない方式となります。 アプリケーションで暗号化した場合には、 DBMS にログインされた場合でもデータは暗号化されたままなので閲覧できません。アプリケーションを経由したアクセスでなければ復号したデータは見れません。 PostgreSQL では暗号化ライブラリとして pgcrypto が用意されているのでこちらを利用して検証を行います。 これらの方式の詳細は少し古い記事になりますが、 @IT にて連載されていた こちらのコラムの2ページ目の表 がわかりやすくまとまっています。 「アプリケーションによる暗号化」が最も強いなら 暗号化の強度とカバー範囲だけを考えると、アプリケーションによる暗号化が最も強いですが、DBとのデータ入出力処理を実装する都度、暗号化/復号化の処理を通さなければなりません。 品質管理の観点からもアプリケーション機能の実装数は少ない方が品質維持が容易になるので、DBもしくはOSに任せられる処理は任せた方が無難です。 理想的なデータ暗号化の想定としては データ全体の暗号化はディスク暗号化もしくは透過的暗号化で対応 マイナン バーやクレジットカード番号などの特に機微な情報に限ってアプリケーションでも暗号化 という組み合わせパターンが理想形だと想定しています。 DB暗号化を導入する際の懸念点 最大の懸念は性能面 今回の検証でデータ暗号化をテーマにした理由にもなりますが、データを暗号化する場合は DBにデータを格納するたびに暗号化処理が実行されます。そのためDBへのIO性能が確実に低下します が、どの程度低下するかはサーバー性能やデータ特性などに影響を受けるため、一概に何%低下するとは単純に語れません。 また、列レベルで暗号化する場合には列によってはインデックスが効かなくなる、といった副作用も想像できますし、暗号化方式によってはdump/restore時に影響が出ないかも心配です。 そのため今回の検証では、影響がありそうだと想定される状況ごとに実際の性能劣化度合いがどの程度なのかを計測していくことを中心に計画しています。 もちろん理想の結果としては、処理性能の劣化が無視できる程度であることですがそれは実際に試さないとわかりません……。 検証する環境や条件 検証環境 各環境のベースには AWS の定常パフォーマンス環境のm4.xlargeを用いる予定で、検証環境のバリエーションとしては以下を用意しようと考えています。 暗号化なし環境 ディスク暗号化(LUKS)環境 透過的暗号化(TDEforPG)環境 アプリ暗号化(pgcrypto)環境 ディスク暗号化+透過的暗号化(LUKS + TDEforPG)環境 また、検証スケジュールに余裕があれば AWS のRDSにてTDEオプションがあるようなので、こちらのON/OFFの違いも参考値として計測したいと考えています。 検証内容 実際のアプリケーションの動作や、運用を想定した動作について計測予定です。 ウェブアプリケーション を想定したクエリ SELECT JOINあり / なし サブクエリあり / なし インデックススキャン / フルスキャン INSERT DELETE UPDATE dump / restore速度 DB同期速度 なおDB同期速度については、正攻法で検証するのであれば各検証環境を2台ずつ用意しないといけないと思うのですが、なんとかもう少し楽に検証できないか検討しています。 次回予告 さて今回はDBセキュリティの概要と、導入時に懸念される項目を検証するための計画を立てる話でした。 今後この計画に沿って環境の構築と検証を進めていきます。次回は計測結果をもとにした現実的なDB暗号化プランの検討をしていきたいと思います。 次回記事の投稿は少し先になりますが、2024年3月頃を予定しています。投稿する際には本記事からもリンクを張っておきます。 ※追記:書きました tech-blog.rakus.co.jp 参考 www.ppc.go.jp atmarkit.itmedia.co.jp jpn.nec.com 商用ソフトウェアのためTDEforPGに関する具体的な性能測定結果は公開を差し控える可能性があります。 ↩
はじめに  こんにちは、 ラク スでインフラを担当しているftkenjです。  弊社ではサービスの製品サイトを AWS で運用していますが、リソースの追加・変更が発生するたびにコンソールにログインをして画面をポチポチして行っています。 オンプレよりは楽ですが、 クラウド サービスの利点を生かし切れていませんでした。  そこでサービスでも利用してるTerraformで構成管理を行っていくことにしました。 コード化していく中で苦労したことなどを伝えられればと思います。 目次 はじめに 目次 Terraformって何? きっかけ 苦労したこと ディレクトリの構成 リソースの取り込み 実環境との差分埋め 良かったこと terraformの知見が広がった 運用面の変化 今後について おわりに 参考 Terraformって何?  Terraformとは、HashiCorpがGo言語で開発した オープンソース のツールです。 AWS や GCP といった クラウド サービス上のインフラ構成をコード管理するために使用します。 ここにGitも組み合わせることでインフラ構成をバージョン管理することも可能になります。  最終的にはCI/CDまで実装することができれば、複雑な手順や数時間とかかっていた作業も不要になります。 とはいえ、既存環境をコード化するのも一筋縄とはいかず・・・ きっかけ  新規サーバ・サイトの追加時のたびに AWS のWebコンソールで操作をしていました。 コンソールからの操作はどうしても画面遷移が多く、横にもう一つコンソールを出して既存設定と見比べながら設定することが大変でした。 この作業を楽にしたい、というのがコード化のきっかけになります。 苦労したこと ディレクト リの構成  始めはenvおよびmodules配下それぞれを本番環境と検証環境で分けていました。 しかし、これだとmodulesでコードが重複してしまい煩雑になってしまいます。 そのためmodulesは共 通化 して環境の差分は変数(env)で補完する、という構成にしました。 例) . ┣━ env ┃ ┣━ 検証環境 ┃ ┃ ┗━ main.tf ┃ ┃ ┃ ┗━ 本番環境 ┃ ┗━ main.tf ┃ ┗━ modules ┣━ サービス名1 ┃ ┣━ AWSサービス名1.tf(メインとなるtfファイル) ┃ ┣━ variables.tf ┃ ┗━ (output.tf) ┃ ┣━ サービス名2 ┃ ┣━ AWSサービス名2.tf ┃ ┣━ variables.tf ┃ ┗━ (output.tf) ...  どちらかの環境にしか存在しないリソースも存在していますが、 三項演算子 を使用して制御しています。 詳しくは次回紹介できればと思います。 リソースの取り込み  コードを書くよりも、実環境の状態を取り込む方が大変でした・・・ terraform import は取り込むリソースを指定して実行することで、「terraform.tfstate」というファイルに json 形式で取り込まれます。  ただし、取り込みには対象のリソースのIDやARN( Amazon Resource Name)が必要であり、基本的にTerraformでコード化しているすべてのresource分を行わなければいけません。 細かいところですと、Route53のレコードやELBのListener Ruleが1項目ずつ取り込むことになります。   AWS で新しく環境構築する、もしくは始めたばかりのうちにコード化しておく方が結果的に少ない労力で済みますね・・・  一応「terraformer」という既存環境を一括で取り込める OSS があります。 こちらはtfファイルも自動生成してくれますが、各リソースの設定値がハードコーディングされているなど、生成後に手を加える必要があります。 いち早く既存環境をコード化して運用していきたい、という場合は使ってみるのもアリではないでしょうか。 実環境との差分埋め  すべての取り込みが完了したら「terraform plan」(dry-runのイメージ)の実行です。 これで実環境との差分が出なければ、めでたくコード化完了となりますが・・・  実行後、以下のように「変更の詳細」とadd、change、destroyの総数が表示されます。 特にchange、destroyは実環境が壊れてしまうので注意しなければいけません。 ‐ add:新規追加されるリソース数 ‐ change:変更されるリソース数 ‐ destroy:削除されるリソース数 … ここから上は変更の詳細 … Plan: 8 to add, 0 to change, 0 to destroy. ─────────────────────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. なお取り込みできないresourceも存在するため、 terraform apply の初回実行までaddが残ってしまうこともあります。 その場合は、取り込めないresource以外に変更されない状態するまで差分を埋めていきます。 良かったこと terraformの知見が広がった  これまでTerraformを触ったことがなかったので、試行錯誤しながら進めていました。 ピンポイントでほしい情報が載っている技術ブログが見つからないなどありましたが、 公式ドキュメント が分かりやすく一番重宝しました。 terraform import 実行時に必要なリソースID等も公式ドキュメントで確認することができます。 上の方でも書きましたが、今回コード化していく中で役立ちそうなところを次回紹介できればと思います。 運用面の変化  コード化してからの運用がどうなったかをかければよかったのですが、まだ運用開始できるところまでできていません。 機会があればコード化前後でどういった変化があったのかをお伝えできればと思います。 今後について  コード化までは完了しているため、Terraformの運用/管理を整理していきます。 作成者でしかTerraformから構成変更・追加ができない状態ではコード化した意味がありません。 CI/CDのパイプラインも設計していきたいですが、こちらは先が長くなりそうです。 おわりに  Terraformを触り始めてまだ半年程度ですが、コードを書くこと自体は難しくはありませんでした。 それよりも煩雑にならないための事前設計と既存環境との差分をなくしていくことが大変でした。  あとはなんといっても terraform import です。 IDやARNが必要になるため、どうしても1つずつしか取り込めないのが手間でした。 そういった意味でも、コード化するのであれば最初からTerraformで構築する方が楽だと実感しました。 もしくは AWS であれば、料金がかかってしまいますがCloudFormationをを利用することでコード化の難易度は下がるのかもしれません。 参考 Terraform by HashiCorp [AWS] Terraformerで複数のリソースを一括importしてみた
はじめに 初めまして、新卒1年目のm_you_sanと申します。 初学者向けにReactにおけるメモ化の方法を簡単に紹介させていただきます。 目次は以下の通りです。 はじめに そもそもメモ化って? メモ化の方法 React.memo 使用例 注意点 useCallback 注意点 まとめ そもそもメモ化って? メモ化は簡単に言うと、計算結果を保持して、それを再利用する手法です。 Reactの場合、無駄な コンポーネント の呼び出しを減らすことができ、パフォーマンス性が上がります。 特に レンダリング の負荷が大きい処理、頻繫に再 レンダリング される コンポーネント の子 コンポーネント で、メモ化を利用するとよりパフォーマンス性の向上が見込めます。 メモ化の方法 Reactおけるメモ化方法は主に3つ(React.memo、useCallBack、useMemo)あります。 今回はReact.memoとuseCallBackについて紹介いたします。 React.memo React.memoは コンポーネント の不要な レンダリング の回避を目的として使用します。 メモ化の対象は コンポーネント です。 React.memoはpropsが等価であるかをチェックして再 レンダリング の判断をします。 新しく渡されたpropsと前回のpropsを比較して、等価である場合は再 レンダリング は起きません。 使用例 以下のコードは親 コンポーネント に入力フォームがあるコードです。 親 コンポーネント const App: React.FC = () => { const items = [ "食洗器" , "髭剃り" , "冷蔵庫" ] ; const [ itemList , setItemList ] = useState ( items ); const [ item , setItem ] = useState ( "" ); const addItem = () => { setItemList ( [ ...itemList , item ] ); } return ( <> < input type= "text" placeholder = "欲しいもの" value = { item } onChange = { ( e ) => setItem ( e.target.value ) } / > < button onClick = { addItem } > 追加 < /button > < List itemList = { itemList } / > < / > ) } 子 コンポーネント type ListProps = { itemList: string [] } export const List: React.FC < ListProps > = ( { itemList } ) => { console .log ( "レンダリングされました" ); return ( < ul > { itemList.map (( item , index ) => ( < li key = { index } > { item } < /li > )) } < /ul > ) } この場合、フォームに値が入力される度に、親 コンポーネント で管理しているitemが更新されるので、子 コンポーネント が レンダリング されてしまいます。 こうした レンダリング を防ぐために、子 コンポーネント をメモ化します。 下記のコードの場合、itemListが更新されたときのみ、再 レンダリング が起こるようになります。 つまり、追加ボタンが押されない限り、再 レンダリング は起きません。 子 コンポーネント export const List: React.FC < ListProps > = React.memo (( { itemList } ) => { console .log ( "レンダリングされました" ); return ( < ul > { itemList.map (( item , index ) => ( < li key = { index } > { item } < /li > )) } < /ul > ) } ) 動作を確認すると、メモ化することで、再 レンダリング が防げていることが分かります。 また、追加ボタンを押したときのみ、再 レンダリング が起きているのが分かります。 注意点 関数をpropsとして渡す場合、React.memoでは再 レンダリング を防ぐことができません。 例として、先程のコードに、欲しいものの表示、非表示を切り替えるtoggleShowItems関数を作成し、propsとして子 コンポーネント に渡してみます。 親 コンポーネント const App: React.FC = () => { const items = [ "食洗器" , "髭剃り" , "冷蔵庫" ] const [ itemList , setItemList ] = useState ( items ); const [ item , setItem ] = useState ( "" ); const [ isShow , setIsShow ] = useState ( false ); const addItem = () => { setItemList ( [ ...itemList , item ] ); } const toggleShowItems = () => { setIsShow ( ! isShow ); } return ( <> < input type= "text" placeholder = "欲しいもの" value = { item } onChange = { ( e ) => setItem ( e.target.value ) } / > < button onClick = { addItem } > 追加 < /button > < List itemList = { itemList } isShow = { isShow } toggleShowItems = { toggleShowItems } / > < / > ) } 子 コンポーネント type ListProps = { itemList: string [] ; isShow: boolean ; toggleShowItems: () => void ; } export const List: React.FC < ListProps > = React.memo (( { itemList , isShow , toggleShowItems } ) => { console .log ( "レンダリングされました" ); return ( <> < div > < button onClick = { toggleShowItems } > { isShow? "欲しいものを非表示にする" : "欲しいものを表示する" } < /button > < /div > { isShow && ( < ul > { itemList.map (( item , index ) => ( < li key = { index } > { item } < /li > )) } < /ul > ) } < / > ) } ) この場合、toggleShowItems関数の内容は変更されていないにも関わらず、フォームに入力する度に再 レンダリング が発生してしまいます。 なぜこのようになるのかというと、React.memoでは、関数などのオブジェクト型の等価性をチェックする場合、値そのものではなく参照先データを比較するからです。 仮にtoggleShowItems1を レンダリング 前の関数、toggleShowItems2を レンダリング 後の関数とします。 関数の内容自体は変わりませんが、参照先が レンダリング 前後で変わるため、等価ではないと判断されます。 そのため関数が変更されたと見なされ、再 レンダリング が発生してしまいます。 //レンダリング前 const toggleShowItems1 = () => { setIsShow ( ! isShow ); } //レンダリング後 const toggleShowItems2 = () => { setIsShow ( ! isShow ); } //関数の内容は同じだが、参照先が異なるためfalse console .log ( toggleShowItems1 === toggleShowItems2 ); こうした問題を解決するために使用するのがuseCallbackです。 useCallback useCallbackのメモ化の対象は関数です。 useEffectなどと同様に、第二引数に依存配列を設定します。 下記のコードの場合、isShowが更新されたときに、関数を再生成します。 親 コンポーネント const toggleShowItems = useCallback (() => { setIsShow ( ! isShow ); } , [ isShow ] ); 動作を確認すると、フォームに入力しても レンダリング が発生していないことが分かります。 また、表示切替のボタンを押して、isShowが更新されたタイミングで レンダリング が起きていることが分かります。 このように、関数をメモ化した コンポーネント に渡す場合、useCallbackを使用することで、不要な レンダリング を抑えることができます。 注意点 useCallbackをReact.memoと併用して、不要な レンダリング を防ぐ目的ではなく、単純に関数の再生成を防ぐ目的で使用しても、あまり効果はありません。 理由としては、関数の再生成のコストがuseCallbackの実行コストを上回るケースがあまりないからです。 関数の再生成を防ぐ目的であれば、 useMemo の方が適していると思うので、そちらを使用するのが良いと思います。 まとめ 今回は初学者向けにReact.memoとuseCallbackについて、紹介させていただきました。 メモ化について更に詳しく知りたい方は、 公式ドキュメント を参照していただければと思います。
こんにちは。インフラエンジニアの gumamon です! 最近はSRE的なことも ちょこちょこ やらせて頂いています。 NewRelic、Datadog、モダンな監視(オブザーバビリティ)って良いですよね。 弊社も Kubernetes ( k8s )等を利用した環境が増えてきた折、そろそろ必要になってきた(と思っている)のですが、NewRelic、Datadog等の クラウド サービスは ランニングコスト が安くない。 そこで内製できないかやってみよう!ということになり、試行錯誤をした結果どうにか表題の構成で作ることができたのでご紹介をしたいと思います! この記事では、 k8s を観測対象とし、オブザーバビリティを実現した際の アーキテクチャ 構成、並びに四苦八苦する中で得た観測の勘所( 私見 )についてご紹介します。 目次 目次 オブザーバビリティとは オブザーバビリティ(OSS)の実現事例 全体構成 Elastic Stack Elastic Stack を補うサービス OpentTelemetry関連 取れるデータは手段を選ばず収集する Metrics Log Tracing 収集した情報を分析し、ビジネス自体を観測する 特定リクエストのエラー応答を追跡する k8s環境のリソース使用状況を確認する まとめ 参考 検証環境 ドキュメント オブザーバビリティとは Observabilityと表記され、日本語では「可観測性」と呼ばれる概念です。 より日本語の感覚に寄せた表記をするのであれば、「観測する能力」 (= Observe + Ability)とも言えるかと思います。 観測と監視の違いは何か?と聞かれることがあるのですが、私は「見ている対象が違う」と回答しています。 「顧客体験」を観測する 「システムの正常性」を監視する 言うまでもなく、ビジネス上より価値が高いデータは観測データの方になるかと思います。 近年、 クラウド やコンテナ等の普及によりサービスの構成要素はあらゆるところに分散しています。 これら全体を「監視」し続け、ましてやそこから顧客体験を推察するというのは非常に骨の折れる作業であり、これが今オブザーバビリティに注目が集まっている背景ではないかと私は思います。 さて、改めて 「顧客体験」を観測する方法ですが、こちらは「 ORILLY 入門監視~モダンなモニタリングのためのデザインパターン~ 」が参考になるかと思います。 この記事では深く触れませんが、「顧客体験」の観測に関連する要点は下記である、と書いてあるように私には読み取れました。 ユーザが快適に利用できているかを観測するには ユーザに近い場所から監視せよ サービス利用時のパフォーマンスを測定せよ サービス構成要素から漏れなく情報を収集せよ 収集した情報を分析し、ビジネス自体を観測せよ さらに、上記を実装イメージに寄せてグルーピングしてみると下記になりました。 取れるデータは手段を選ばず収集する (1〜3) 収集した情報を分析し、ビジネス自体を観測する (4) 次のテーマではこれをどう実現したかを見ていきます。 オブザーバビリティ( OSS )の実現事例 全体構成 いきなり結論ですが今回構築した環境の全体像はこうなりました。 ※ k8s 上にデプロイしています。 Elastic Stack arch_all Elastic Stack 緑の コンポーネント がこちらの対象です。 Elasticsearch : Elastic Stack(ES)の核となるサービス。永続化データは全てここに保存する。 Kibana: ESの ユーザーインターフェイス ApmServer: Tracing(後述)データの受け口。OTLPに対応している Fleet: Elastic Agentの管理サーバ Elastic Agent: OS/ k8s のMetrics、Log収集を担当(後述) Elastic Stack を補うサービス 白の コンポーネント がこちらの対象です。 kube-system-metrics (KSM) : k8s API をListenし、メトリクスを生成する(単純な)サービス ElastAlert2 : 3rdパーティ製のアラートマネージャ。ESのアラートマネージャは要有償ライセンス・・(泣) OpentTelemetry関連 青の コンポーネント がこちらの対象です。 stub: 弊社で内製したスタブアプリ(Go)。OTel用に( 計装 )をしている Otel-Collector: OTLP準拠の観測データをバックエンドに フォワ ードする 取れるデータは手段を選ばず収集する 収集対象のデータは、オブザーバビリティでは特にTelemetry(テレメトリ)と呼びます。 Telemetryは特性別に3つに分ける事ができます。 Category Summary Metrics 定期的にグループ化または収集された測定値の集合。統計情報やCPU使用率など Logs 履歴や情報の記録。 コンポーネント の アクセスログ 、エラーログ等 Tracing トランザクション の追跡。特定のWEBリク エス トにレスポンスが返るまでの レイテンシー 、通過 コンポーネント 、等 上記3つは、何れも「手段を選ばず」収集していきます。 今回は下記の実装となりました。 Metrics ElasticAgentを介して収集します。 ※収集経路をオレンジでハイライトしています。 Elastic Stack arch_metrics k8s 周り kube-system-metrics(KSM) )を介して収集します。 KSMは k8s のコントローラ経由で収集したメトリクスを提供するエンドポイントです。 /sys/fs/cgroup/ 等もマウントし参照します。 OS周り /proc 等をマウントし参照します。 ( Metricsではありませんが、 /etc/ 以下もマウントし、ホスト名なども収集します) Log ElasticAgentを介して収集します。 ※収集経路をオレンジでハイライトしています。 Elastic Stack arch_logs k8s 周り * コンテナログ: /var/log/contanirs をマウントし収集します(APPのログは標準出力し、左記に格納します) * システム コンポーネント ログ: 半分は前述コンテナログとして取れます。残りは /var/log/messages 等をマウントして収集します OS周り /var/log/secure 等をマウントして収集します。 Tracing OpenTelemetryを介して収集します。 ※収集経路をオレンジでハイライトしています。 Elastic Stack arch_tracging OTel SDK Tracingの起点であり、追跡用のIDを発行します。 今回は Manual Instrumentation という手法で追跡IDを発行しており、ざっくり説明をするとAPPにOTelのライブラリを読み込ませて API を叩く都度追跡IDを発行をさせています。 OTel Collector APPからTracingを受け、 APM Serverに フォワ ードするのが責務です。 APM Server OTel CollectorからTracingを受け、Elasticsearchに格納するのが責務です。 OTel SDK → APM Serber としても通信は成立するのですが、この場合APPコンテナの設定に APM Serverの情報を含める必要があるなど、プロダクト(を想定したAPP)とESの結合度が高まってしまうため、OTel Collectorを一段挟むことにしました。 では ElasticAgentはどうなのか?という話になるのですが、こちらはそもそもプロダクト(を想定したAPP)が関与しない通信経路であること、 Datadog、NewRelicなどは皆エージェントを持っているというところから、仮にESから他ソリューションに移ることになったとしてもロックインされるリスクは低いと判断し、 Elasticsearchとネイティブに通信できるAgentを使うことを選択しています。 収集した情報を分析し、ビジネス自体を観測する ここから先は、根こそぎ集めたデータの活用フェイズになります。 「ビジネス自体を監視する」というところはプロダクトが目指す方向性となるため、千差万別です。 ここでは、 ユーザーインターフェイス (Kibana)のキャプチャと共に活用例をご紹介します。 特定リク エス トのエラー応答を追跡する OTel SDK が付与した 追跡IDを元に、エラー応答が発生したリク エス ト→レスポンス( トランザクション )を追跡します。 Kiabna stub tracing Kibana stub logs 画面からは以下のことがわかります。 サービス stub のエンドポイント /todos/ で Internal Server Error が発生した 発生時刻は本日 13:56 エラーログから、原因は存在しない URI /todos/hoge にアクセスされた為であることがわかった ---※1 ※1 ログにもOTel SDK が発行した追跡IDを埋めています(Tracingと一致するID)。これをElastic Stack側の APM アプリが解釈し、Tracingと関連するLogとして同じアクセスとしてUIに表示しています。 k8s 環境のリソース使用状況を確認する k8s のMetrics状況から、イン フラリ ソースの使われ方、あるいは しきい値 の設定が適切かを確認します。 Kibana k8s overview 画面からは以下のことがわかります。 メモリが逼迫したPodがいる (これはPod自体に設定したリソースの上限です) Elasticsearch : MEM=max 2GB ElasticAgent: MEM=max 1GB k8s ノード全体のメモリ使用量は4GB/16GB とまだ余裕があります。 Podのメモリ利用効率を改善するか、メモリ利用上限を引き上げることでリソース逼迫を解消できそうです まとめ 今回は Elastic Stack x OpenTelemetryを使ったオブザーバビリティ構成についてご紹介させていただきました。 クラウド サービスを使うケースが大多数だとは思いますが、監視対象への実装では似たようなことを行うことになるかなと思います。 何かの参考にしていただけますと幸いです! ※実装方法については当記事では割愛させて頂きますが、Elastic StackはOperatorをインストールし、(OTel等も含め) kustomizeで書きました。 言うに及ばずですが、自力実装をするには結構なつらみがあり、「まずお試しで」ということであれば有償版をご利用されることをお勧め致します! 以上、最後までお読み頂きありがとうございました! 参考 検証環境 k8s プラットフォーム microk8s (v1.27.5) kubernetes (v1.27) オブザーバビリティ kube-state-metrics (v2.10.0) ECK Operator (v2.9.0) Elastic Stack (v8.9.2) OTel Collector (v0.84.0) elastalert2 (v2.13.2) アプリケーション stub (Go / 弊社内製) Otel SDK ドキュメント ESに関するドキュメント index overview Elastic Cloud on K8s Guide Elasticsearch Kibana Elastic Observability Fleet and Elastic Agent OpenTelemetry 公式Doc kube-state-metrics Github ElastAlert2 公式Doc Github
はじめに 初めましてこんにちは。 ngerukatakataです。 営業上がりの未経験エンジニアとしてそこそこの期間を働いております。 最近AWSEKS環境なんてものを触り始めました。 k8s 環境に触れるのも初めてなうえに、 AWS もそんなに触ったことない人間なので四苦八苦としています。 簡単な面もあればどうしたら実現できるんだ!なんて面にもぶつかったり… 皆さんも k8s に触れるときには同じような苦しみを感じたんじゃないかなぁって思います。 さて、今回は苦労したものの一つ AWS _EKSの『メトリクス監視』についてお話しさせていただければと思います。 今回お話しするメトリクス監視ツールは ADOT( AWS Distro for OpenTelemetry) についてとなります。 目次 はじめに 目次 背景 EKSの監視を始めよう! ADOT とは? OpenTelemetry とは? ADOTをEKSonFargateに導入するには FargateProfileの作成 IAMの作成 ADOTコレクタの作成 Container Insightsでの確認 ADOTをEKSonFargate+EC2に導入するには ノードグループにラベルを追加 Configmapの修正 まとめ 参考 背景 この度新しい運用基盤へのチャレンジということで k8s 環境への取り組みが始まりました。 新しい運用基盤への取り組みが始まるということは、当然のことながら、 新しい監視について検討をしなくてはなりません。 今までの環境は物理または仮想のサーバ環境に対して、Zabbixエージェントを導入して監視を行っておりました。 ただ調べてみると、EKS環境というのは今まで通りzabbixエージェントを仕込んで…というのはどうも難しそう。 今まで通りのやり方を踏襲してやれば楽勝じゃん!とはいかなそうではありました。 そこでいろいろな賢人たちのブログを読み漁りADOTというものに出会いました。 ADOT( AWS Distro for OpenTelemetry)はOpenTelemetryの仕組みを使って、 いい感じにデータを抜き出してくれる仕組み…これでメトリクスも完成だ!としたところで、 どうやら既存のADOT設定はFargate特化、EC2ノードを追加した構成ではうまく動かないことが分かりました。 そこでcadvisorといわれる仕組みを勉強したりOtelの構造を勉強したりなどして、 なんとかFargate+EC2のEKS構成でもADOTを利用したメトリクス監視構成を作成することができました 今回はそんな新しい監視ツールADOTの説明と、 それをEC2同居構成でどのように使えるようにしたかという説明をさせていただければと思います。 EKSの監視を始めよう! まずは早速ADOTについて説明をさせていただきます。 ADOT とは? AWS Distro for OpenTelemetry は、 AWS がサポートする OpenTelemetry プロジェクトの ディストリビューション です。 ADOT CollectorというPodでメトリクスの情報を収集し、Cloudwatchなどに送信するところまでやってくれています。 OpenTelemetry とは? システム監視におけるメトリクスデータなどの収集や送信を標準化し、 特定のベンダに依存しない形でシンプルに収集/送信をするものです。 ADOTはこちらを利用して収集から送信を AWS 用にいい感じにしてくれるものと捉えてもらえればよろしいかと思います。 ADOTをEKSonFargateに導入するには それでは実際に私が実施した ADOTをつかったメトリクス監視の追加方法について、 実例をもとに説明させていただきます。 今回の実例は、以下に示すように - FargateProfileの作成 - IAMの作成 - ADOTコレクタの作成 - Container Insightsでの確認 の流れになっていますので、ごらんの皆様もイメージしやすいかと思います! FargateProfileの作成 ADOTCollectorはFargateで起動するため、 事前にFargateProfileを作成してEKSに認識させなくてはいけません。 弊社ではTerraformを使って AWS の構成管理を行っているので以下のような記述を作ってFargateProfileを作成しました。 module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 18.30.2" 中略 fargate_profiles = { default = { name = "default" selectors = [ { namespace = "default" }, { namespace = "kube-system" } ] subnet_ids = var.private_subnets }, fargate-container-insights = { name = "fargate-container-insights" selectors = [ { namespace = "fargate-container-insights" } ] subnet_ids = var.private_subnets iam_role_additional_policies = ["arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"] } } } IAMの作成 ADOT Collector から、メトリクスデータを CloudWatch に送信するために IAM アクセス許可が必要です。 Terraformを使って以下のような記述を作ってIAM許可ルールを作成しました。 module "eks-fargate-adot_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" version = "3.5.0" create_role = true role_name = "${var.cluster.name}-EKS-Fargate-ADOT-ServiceAccount-Role" role_policy_arns = ["arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"] provider_url = module.eks.cluster_oidc_issuer_url oidc_fully_qualified_subjects = ["system:serviceaccount:fargate-container-insights:adot-collector"] } これはrole_nameの名前でCloudWatchAgentServerPolicyの権限を持ったroleを作成しています。 EKS上のnamespace「fargate-container-insights」のpod「adot-collector」が処理をするときに 本roleにassume出来るようにしています。 該当のEKSには以下のような yaml を実行してnamespaceとServiceAccountを作っておきましょう。 apiVersion: v1 kind: Namespace metadata: name: fargate-container-insights labels: name: fargate-container-insights apiVersion: v1 kind: ServiceAccount metadata: name: adot-collector namespace: fargate-container-insights annotations: eks.amazonaws.com/role-arn: [IRSAARN] ここでいう[IRSAARN]には先ほどTerraformで生成したrole_nameのARNを入力します。 ADOTコレクタの作成 次に以下の yaml を実行してStagtefulsetとしてADOT Collectorを作成しましょう。 以下のようなroleを作成し、 kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: adotcol-admin-role rules: - apiGroups: [""] resources: - nodes - nodes/proxy - nodes/metrics - services - endpoints - pods - pods/proxy verbs: ["get", "list", "watch"] - nonResourceURLs: [ "/metrics/cadvisor"] verbs: ["get", "list", "watch"] さきほど作ったServiceAccountに権限を付与します。 kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: adotcol-admin-role-binding subjects: - kind: ServiceAccount name: adot-collector namespace: fargate-container-insights roleRef: kind: ClusterRole name: adotcol-admin-role apiGroup: rbac.authorization.k8s.io そして重要となるAdotのconfig用のConfigmap、 これがOpenTelemetoryの設定になります。 長すぎるのでコードは閉じておきますが、 「receivers」でデータをどのように受け取るかの設定をし、 「processors」でどのようにデータを取り扱うかの設定をし、 「exporters」で出力先の設定をしています。 ここでは 「receivers」で「cadvisor」というものを使って k8s 環境の情報を取得し、 「processors」で必要な情報をメトリクスデータとして整理し、 「exporters」で「Cloudwatch」宛に出力する設定をしているということだけご認識ください。 >>>>コードを見る<<<< apiVersion: v1 kind: ConfigMap metadata: name: adot-collector-config namespace: fargate-container-insights labels: app: aws-adot component: adot-collector-config data: adot-collector-config: | receivers: prometheus: config: global: scrape_interval: 1m scrape_timeout: 40s scrape_configs: - job_name: 'kubelets-cadvisor-metrics' sample_limit: 10000 scheme: https kubernetes_sd_configs: - role: node tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) # Only for Kubernetes ^1.7.3. # See: https://github.com/prometheus/prometheus/issues/2916 - target_label: __address__ # Changes the address to Kube API server's default address and port replacement: kubernetes.default.svc:443 - source_labels: [__meta_kubernetes_node_name] regex: (.+) target_label: __metrics_path__ # Changes the default metrics path to kubelet's proxy cadvdisor metrics endpoint replacement: /api/v1/nodes/$${1}/proxy/metrics/cadvisor metric_relabel_configs: # extract readable container/pod name from id field - action: replace source_labels: [id] regex: '^/machine\.slice/machine-rkt\\x2d([^\\]+)\\.+/([^/]+)\.service$' target_label: rkt_container_name replacement: '$${2}-$${1}' - action: replace source_labels: [id] regex: '^/system\.slice/(.+)\.service$' target_label: systemd_service_name replacement: '$${1}' processors: # rename labels which apply to all metrics and are used in metricstransform/rename processor metricstransform/label_1: transforms: - include: .* match_type: regexp action: update operations: - action: update_label label: name new_label: container_id - action: update_label label: kubernetes_io_hostname new_label: NodeName - action: update_label label: eks_amazonaws_com_compute_type new_label: LaunchType # rename container and pod metrics which we care about. # container metrics are renamed to `new_container_*` to differentiate them with unused container metrics metricstransform/rename: transforms: - include: container_spec_cpu_quota new_name: new_container_cpu_limit_raw action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_spec_cpu_shares new_name: new_container_cpu_request action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_cpu_usage_seconds_total new_name: new_container_cpu_usage_seconds_total action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_spec_memory_limit_bytes new_name: new_container_memory_limit action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_cache new_name: new_container_memory_cache action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_max_usage_bytes new_name: new_container_memory_max_usage action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_usage_bytes new_name: new_container_memory_usage action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_working_set_bytes new_name: new_container_memory_working_set action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_rss new_name: new_container_memory_rss action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_swap new_name: new_container_memory_swap action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_failcnt new_name: new_container_memory_failcnt action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_memory_failures_total new_name: new_container_memory_hierarchical_pgfault action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "hierarchy"} - include: container_memory_failures_total new_name: new_container_memory_hierarchical_pgmajfault action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "hierarchy"} - include: container_memory_failures_total new_name: new_container_memory_pgfault action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "container"} - include: container_memory_failures_total new_name: new_container_memory_pgmajfault action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "container"} - include: container_fs_limit_bytes new_name: new_container_filesystem_capacity action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} - include: container_fs_usage_bytes new_name: new_container_filesystem_usage action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"} # POD LEVEL METRICS - include: container_spec_cpu_quota new_name: pod_cpu_limit_raw action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_spec_cpu_shares new_name: pod_cpu_request action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_cpu_usage_seconds_total new_name: pod_cpu_usage_seconds_total action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_spec_memory_limit_bytes new_name: pod_memory_limit action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_cache new_name: pod_memory_cache action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_max_usage_bytes new_name: pod_memory_max_usage action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_usage_bytes new_name: pod_memory_usage action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_working_set_bytes new_name: pod_memory_working_set action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_rss new_name: pod_memory_rss action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_swap new_name: pod_memory_swap action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_failcnt new_name: pod_memory_failcnt action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"} - include: container_memory_failures_total new_name: pod_memory_hierarchical_pgfault action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "hierarchy"} - include: container_memory_failures_total new_name: pod_memory_hierarchical_pgmajfault action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "hierarchy"} - include: container_memory_failures_total new_name: pod_memory_pgfault action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "container"} - include: container_memory_failures_total new_name: pod_memory_pgmajfault action: insert match_type: regexp experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "container"} - include: container_network_receive_bytes_total new_name: pod_network_rx_bytes action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} - include: container_network_receive_packets_dropped_total new_name: pod_network_rx_dropped action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} - include: container_network_receive_errors_total new_name: pod_network_rx_errors action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} - include: container_network_receive_packets_total new_name: pod_network_rx_packets action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} - include: container_network_transmit_bytes_total new_name: pod_network_tx_bytes action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} - include: container_network_transmit_packets_dropped_total new_name: pod_network_tx_dropped action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} - include: container_network_transmit_errors_total new_name: pod_network_tx_errors action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} - include: container_network_transmit_packets_total new_name: pod_network_tx_packets action: insert match_type: regexp experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"} # filter out only renamed metrics which we care about filter: metrics: include: match_type: regexp metric_names: - new_container_.* - pod_.* # convert cumulative sum datapoints to delta cumulativetodelta: metrics: - new_container_cpu_usage_seconds_total - pod_cpu_usage_seconds_total - pod_memory_pgfault - pod_memory_pgmajfault - pod_memory_hierarchical_pgfault - pod_memory_hierarchical_pgmajfault - pod_network_rx_bytes - pod_network_rx_dropped - pod_network_rx_errors - pod_network_rx_packets - pod_network_tx_bytes - pod_network_tx_dropped - pod_network_tx_errors - pod_network_tx_packets - new_container_memory_pgfault - new_container_memory_pgmajfault - new_container_memory_hierarchical_pgfault - new_container_memory_hierarchical_pgmajfault # convert delta to rate deltatorate: metrics: - new_container_cpu_usage_seconds_total - pod_cpu_usage_seconds_total - pod_memory_pgfault - pod_memory_pgmajfault - pod_memory_hierarchical_pgfault - pod_memory_hierarchical_pgmajfault - pod_network_rx_bytes - pod_network_rx_dropped - pod_network_rx_errors - pod_network_rx_packets - pod_network_tx_bytes - pod_network_tx_dropped - pod_network_tx_errors - pod_network_tx_packets - new_container_memory_pgfault - new_container_memory_pgmajfault - new_container_memory_hierarchical_pgfault - new_container_memory_hierarchical_pgmajfault experimental_metricsgeneration/1: rules: - name: pod_network_total_bytes unit: Bytes/Second type: calculate metric1: pod_network_rx_bytes metric2: pod_network_tx_bytes operation: add - name: pod_memory_utilization_over_pod_limit unit: Percent type: calculate metric1: pod_memory_working_set metric2: pod_memory_limit operation: percent - name: pod_cpu_usage_total unit: Millicore type: scale metric1: pod_cpu_usage_seconds_total operation: multiply # core to millicore: multiply by 1000 # millicore seconds to millicore nanoseconds: multiply by 10^9 scale_by: 1000 - name: pod_cpu_limit unit: Millicore type: scale metric1: pod_cpu_limit_raw operation: divide scale_by: 100 experimental_metricsgeneration/2: rules: - name: pod_cpu_utilization_over_pod_limit type: calculate unit: Percent metric1: pod_cpu_usage_total metric2: pod_cpu_limit operation: percent # add `Type` and rename metrics and labels metricstransform/label_2: transforms: - include: pod_.* match_type: regexp action: update operations: - action: add_label new_label: Type new_value: "Pod" - include: new_container_.* match_type: regexp action: update operations: - action: add_label new_label: Type new_value: Container - include: .* match_type: regexp action: update operations: - action: update_label label: namespace new_label: Namespace - action: update_label label: pod new_label: PodName - include: ^new_container_(.*)$$ match_type: regexp action: update new_name: container_$$1 # add cluster name from env variable and EKS metadata resourcedetection: detectors: [env, eks] batch: timeout: 60s # only pod level metrics in metrics format, details in https://aws-otel.github.io/docs/getting-started/container-insights/eks-fargate exporters: awsemf: log_group_name: '/aws/containerinsights/{ClusterName}/performance' log_stream_name: '{PodName}' namespace: 'ContainerInsights' region: YOUR-AWS-REGION resource_to_telemetry_conversion: enabled: true eks_fargate_container_insights_enabled: true parse_json_encoded_attr_values: ["kubernetes"] dimension_rollup_option: NoDimensionRollup metric_declarations: - dimensions: [ [ClusterName, LaunchType], [ClusterName, Namespace, LaunchType], [ClusterName, Namespace, PodName, LaunchType]] metric_name_selectors: - pod_cpu_utilization_over_pod_limit - pod_cpu_usage_total - pod_cpu_limit - pod_memory_utilization_over_pod_limit - pod_memory_working_set - pod_memory_limit - pod_network_rx_bytes - pod_network_tx_bytes extensions: health_check: service: pipelines: metrics: receivers: [prometheus] processors: [metricstransform/label_1, resourcedetection, metricstransform/rename, filter, cumulativetodelta, deltatorate, experimental_metricsgeneration/1, experimental_metricsgeneration/2, metricstransform/label_2, batch] exporters: [awsemf] extensions: [health_check] ADOTに接続するためのService設定をClusterIPで設定します。 apiVersion: v1 kind: Service metadata: name: adot-collector-service namespace: fargate-container-insights labels: app: aws-adot component: adot-collector spec: ports: - name: metrics # default endpoint for querying metrics. port: 8888 selector: component: adot-collector type: ClusterIP 上記で設定したConfigmapを元にADOTCollectorをStatefullsetとして作成します。 apiVersion: apps/v1 kind: StatefulSet metadata: name: adot-collector namespace: fargate-container-insights labels: app: aws-adot component: adot-collector spec: selector: matchLabels: app: aws-adot component: adot-collector serviceName: adot-collector-service template: metadata: labels: app: aws-adot component: adot-collector spec: serviceAccountName: adot-collector securityContext: fsGroup: 65534 containers: - image: amazon/aws-otel-collector:v0.15.1 name: adot-collector imagePullPolicy: Always command: - "/awscollector" - "--config=/conf/adot-collector-config.yaml" env: - name: OTEL_RESOURCE_ATTRIBUTES value: "ClusterName=YOUR-EKS-CLUSTER-NAME" resources: limits: cpu: 2 memory: 2Gi requests: cpu: 200m memory: 400Mi volumeMounts: - name: adot-collector-config-volume mountPath: /conf volumes: - configMap: name: adot-collector-config items: - key: adot-collector-config path: adot-collector-config.yaml name: adot-collector-config-volume Container Insightsでの確認 ここまでの設定を行い環境作成が終わり、 k8s 環境にpodを作成すると自動的にContainerInsights上でメトリクスデータが見れるようになっています。 また、こちらの ダッシュ ボードのもととなるメトリクスはCloudwatchメトリクス上でも確認することが可能です。 ADOTをEKSonFargate+EC2に導入するには ここまでの設定でFargateの情報を取得することができるようになりました。 ただし、EC2交じりの構成を組んでいた場合、EC2上のpodのメトリクスは上記の方法では取得できません。 そこで追加で2つの改変を行うことでEC2上のメトリクスも取得できるようにしてみましょう。 ノードグループにラベルを追加 EC2のノードグループにラベルを追加します。 LaunchType:EC2と追加しましょう。 Configmapの修正 次にConfigmapに以下のように修正を加えましょう。 「processors」は現状ではLaunchType:Fargateとなっているもののデータしか収集しないようになっています。 そのためLanchType:EC2も対象となるようにしましょう。 またmemory_utilizationもFargateで作成した場合はPod_memoryというデータになってしまい、 EC2上のPodの情報がうまく取れないので「container_memory_utilization_over_pod_limit」という名前で追加作成しておきます。 最後に「exporters」上に、先ほど作った「container_memory_utilization_over_pod_limit」と「container_memory_working_set」「container_memory_limit」を追加しておきましょう。 apiVersion: v1 kind: ConfigMap metadata: name: adot-collector-config namespace: fargate-container-insights labels: app: aws-adot component: adot-collector-config data: adot-collector-config: | receivers: 中略 processors: # rename labels which apply to all metrics and are used in metricstransform/rename processor metricstransform/label_1: transforms: - include: .* match_type: regexp action: update operations: - action: update_label label: name new_label: container_id - action: update_label label: kubernetes_io_hostname new_label: NodeName - action: update_label label: eks_amazonaws_com_compute_type new_label: LaunchType # rename container and pod metrics which we care about. # container metrics are renamed to `new_container_*` to differentiate them with unused container metrics metricstransform/rename: transforms: - include: container_spec_cpu_quota new_name: new_container_cpu_limit_raw action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate|EC2"} - include: container_spec_cpu_shares new_name: new_container_cpu_request action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate|EC2"} - include: container_cpu_usage_seconds_total new_name: new_container_cpu_usage_seconds_total action: insert match_type: regexp experimental_match_labels: {"container": "\\S", "LaunchType": "fargate|EC2"} 中略 experimental_metricsgeneration/1: rules: - name: pod_network_total_bytes unit: Bytes/Second type: calculate metric1: pod_network_rx_bytes metric2: pod_network_tx_bytes operation: add - name: pod_memory_utilization_over_pod_limit unit: Percent type: calculate metric1: pod_memory_working_set metric2: pod_memory_limit operation: percent - name: container_memory_utilization_over_pod_limit ←追加 unit: Percent type: calculate metric1: new_container_memory_working_set metric2: new_container_memory_limit operation: percent 中略 exporters: awsemf: log_group_name: '/aws/containerinsights/{ClusterName}/performance' log_stream_name: '{PodName}' namespace: 'ContainerInsights' region: ap-northeast-1 resource_to_telemetry_conversion: enabled: true eks_fargate_container_insights_enabled: true parse_json_encoded_attr_values: ["kubernetes"] dimension_rollup_option: NoDimensionRollup metric_declarations: - dimensions: [ [ClusterName, LaunchType], [ClusterName, Namespace, LaunchType], [ClusterName, Namespace, PodName], [ClusterName, Namespace, PodName, LaunchType]] metric_name_selectors: - pod_cpu_utilization_over_pod_limit - pod_cpu_usage_total - pod_cpu_limit - pod_memory_utilization_over_pod_limit - container_memory_utilization_over_pod_limit ←追加 - pod_memory_working_set - container_memory_working_set ←追加 - pod_memory_limit - container_memory_limit ←追加 - pod_network_rx_bytes - pod_network_tx_bytes 後略 こちらのconfigmapを再度適応したADOTCollectorを展開してみます。 そうするとメトリクスをCloudwatchメトリクス上でも確認することが可能です。 ※新しく追加したデータはContainerInsights上では確認が取れませんので注意が必要です。 まとめ さて、実際の流れを通して ADOT の使い方の一例としてEC2podの情報取得方法についてご案内させていただきました。 今回は既存のADOT設定を踏襲するようにしたため、無駄な設定もありもっと改善の余地はあるかと思います。 本記事を参考にADOT使ってみたけどFargateとEC2の両方のメトリクスはどうやってとればいいんだ!って人の参考になれば幸いです。 参考 https://aws.amazon.com/jp/blogs/news/introducing-amazon-cloudwatch-container-insights-for-amazon-eks-fargate-using-aws-distro-for-opentelemetry/ https://opentelemetry.io/docs/ https://kubernetes.io/docs/concepts/cluster-administration/system-metrics/
はじめに ラク スのサービスでは請求書や領収書をはじめ、様々な文書を取り扱っています。 例えば楽楽精算では領収書の読み取り機能を有しており、この機能にはAIを用いた画像認識を活用しています。 このように文書画像を対象としたAI(以下、本記事では文書画像読解AIと呼びます)は、様々なタスクに応用できます。 そこで今回の記事では、文書画像読解AIではどのようなタスクを解くことができるか、代表的なものを紹介します。 また各タスクに適用できるモデルについて、本記事執筆時点でのSOTAモデル *1 をいくつか簡単に紹介します。 文書画像を扱うタスクやモデルにどのようなものがあるか、概要を知りたい方に向けた内容となっております。 目次 はじめに 目次 サマリー 文書画像読解AIのタスク OCR(Optical Character Recognition、光学文字認識) レイアウト解析 (Document Layout Analysis) 文書画像分類 (Document Image Classification) 情報抽出 (Key Information Extraction) DocVQA (Document Visual Question Answering) 文書画像読解AIのSOTAモデル OCR レイアウト解析 文書画像分類 情報抽出 DocVQA モデルの一例 LayoutLM Donut (OCR-free Document Understanding Transformer) 終わりに 参考文献 サマリー AIで解くことができる文書画像タスクには様々なものがある。特に OCR は別タスクの前処理としても扱われることがあり、重要な基礎技術である。 一つの アルゴリズム で複数のタスクに応用できるモデルがある。 画像とテキスト両方の特徴を活用したマルチモーダルAIが、各タスクで高い精度を発揮している。 文書画像読解AIのタスク 上述のように、文書画像読解AIは様々なタスクに活用できます。 本章ではそのうち5つのタスクを紹介します。 各タスクではAIの入出力のフォーマットが異なるのが特徴です。 OCR (Optical Character Recognition、 光学文字認識 ) 画像データに含まれる文字(活字、手書き)を、PCが処理可能な文字データに変換するタスクです。 AIに画像を入力すると、以下のように文字の内容と座標(検出枠)が出力されます。 OCR の結果例 [1] OCR は主に「検出」と「認識」の2段階の処理に分解できます。 (※検出と認識を組み合わせたような Text Spotting というタスク・手法もありますが、本記事では扱いません。) 検出では文書画像中に含まれた文字の位置を推測し、認識では検出した文字が何であるかを推測します。 OCR は後述の別タスク「情報抽出」などの前処理として採用されることもあり、文書画像読解AIの基礎技術と言うことができます。 レイアウト解析 (Document Layout Analysis) 文書画像に含まれる文章、表、タイトル、図などを検出(またはセグメンテーション処理)するタスクです。 検出の場合、AIの入力値は画像、出力値は座標と分類クラスとなります。 以下は論文画像をレイアウト解析した例で、青が図(figure)、緑が文章(text)、黄色が表(table)、赤がタイトル(title)となっています。 論文をレイアウト分析した例 [2] 文書画像分類 (Document Image Classification) 画像の特徴から文書が何の種類か判定するタスクです。例えば請求書、領収書、納品書などへ分類します。 AIの入力は画像、出力は分類したクラスとなります。 情報抽出 (Key Information Extraction) 文書画像に含まれる特定の項目を推論します。例えば請求書に記載された日付、会社名、請求金額などを推測します。 手法によりますがAIの入力は画像と文字情報(座標と文字の内容)、出力は項目名と値のペア(Key- value pair)となることが一般的です。 例えば以下の例では、レシート画像(a)を入力すると(b)のようにAIから出力されます。(c)は正解値であり、この例ではAIが正確に推論できていることがわかります。 レシートから情報抽出した例 [3] DocVQA (Document Visual Question Answering) 正確にはDocVQAというのはデー タセット の名前で、文書の内容に関する質問を投げると、画像に含まれた情報から回答を出力するタスクです。 AIの入力は文書画像と質問内容となります。 以下はその一例で、”Q”が質問内容、”Answer”が正解値、” Donut ”と”LayoutLMv2-Large-QG”がAIの出力です。 DocVQAの例 [3] 文書画像読解AIのSOTAモデル 本章では、前章の5つのタスクについてSOTAモデルについて簡単に紹介します。 SOTAを紹介する上での補足事項です。 全て紹介すると長くなってしまうので、今回は上位5個程度抜粋しました。同じ アルゴリズム でパラメータ設定などが異なるものについては、最良のモデルのみとしています。 今回紹介するSOTAモデルの結果には”Papers With Code” [4]というサイトや各モデルの論文を活用しております。紹介する内容は本記事執筆時点での情報である点はご了承ください。 各タスクによってデー タセット や評価指標が異なりますが、それらについての詳細解説は省略させていただきます。 OCR OCR は検出と認識に分割できると述べました。 したがって本記事では検出: Scene Text Detectionのモデル、認識: Scene Text Recognition のモデルとしてそれぞれ個別に紹介させていただきます。 ※ Tesseract や PaddleOCR などの OCR エンジンについては本記事では触れておりません。 Scene Text Detection 評価データ Total-Textを使用した場合です。 評価指標は F値 を用いています。[5] モデル F値 (%) 報告年 MixNet 90.5 2023 SRFormer 90.0 2023 DPText-DETR 89.0 2022 FAST-B-800 87.5 2021 TextFuseNet 87.5 2020 Scene Text Recognition 評価データ ICDAR2015の場合です。 評価指標はAccuracy(正解率)を用いています。[6] モデル 正解率 報告時期 CLIP4STR 90.6 2023 PARSeq 89.6±0.3 2022 S-GTR 87.3 2021 MATRN 86.6 2021 CDistNet 86.25 2021 レイアウト解析 PubLayNet val というデー タセット を用いた評価です。 評価指標はmAP@IoU[0.50:0.95]を使用しています。[7] モデル mAP@IoU[0.50:0.95] 報告時期 LayoutLMv3 0.951 2022 DiT-L 0.949 2022 Deit-B 0.932 2020 BEiT-B 0.931 2021 文書画像分類 RVL-CDIPという16種類の文書画像が含まれたデー タセット を使っています。 評価指標はAccuracy(正解率)を用いています。[8] モデル 正解率 報告時期 DocFormerBASE 96.17 2021 LayoutLMv3 Large 95.93 2022 LiLT [EN-R] BASE 95.68 2022 LayoutLMv2 Large 95.64 2020 TILT Large 95.52 2021 Donut 95.3 2021 情報抽出 CORDというデー タセット を使った評価です。 評価指標は F値 となります。[9] モデル F値 (%) 報告年 GeoLayoutLM 97.97 2023 LayoutLMv3 Large 97.46 2022 DocFormer Large 96.99 2021 LiLT 96.07 2022 LayoutLMv2Large 96.01 2020 DocVQA DocVQAというデー タセット での評価結果です。 評価指標はANLS(Average Normalized Levenshtein Similarity)となります。[10] モデル ANLS 報告年 ERNIE-Layout Large 0.8841 2022 TILT Large 0.8705 2021 LayoutLMv2 0.867 2020 LayoutLMv3 Large 0.8337 2021 モデルの一例 ここでは上述のモデルのうち、複数のタスクで使用できる「LayoutLM」と「 Donut 」をピックアップして簡単に紹介します。 これらのモデルは学習に使う データ形式 を変えることで、複数のタスクに対応できるように開発されています。 LayoutLM 適用可能タスク: レイアウト解析、文書画像分類、情報抽出、DocVQA OCR による文字情報と、画像の特徴を両方学習させるマルチモーダルモデルです。 これまでにv1〜v3が開発されています。 LayoutLMv3の概要図[2] OCR の結果(文字情報)と画像情報から推論する。 Donut ( OCR -free Document Understanding Transformer) 適用可能タスク: 文書画像分類、情報抽出、DocVQA 画像とプロンプトを与えると結果を出力します。 OCR 処理を使わないのが特徴です。 Donut の概要図 [3] 画像とプロンプトを入力すると、使用したモデルとプロンプトの内容に応じて結果を出力する。 終わりに 本記事では文書画像読解AIについて、AIが扱えるタスクとそのモデルについて簡単に紹介しました。 新しいモデルが次々に開発される分野ですので、今後も注目していきたいと思います。 また現在 ラク スではAIエンジニアを募集しております。このようなモデル検証や アルゴリズム 実装、実際のサービスへの組み込みまで、 一緒に当社のAI開発を推進していただける方は是非こちらの募集情報もご覧ください! エンジニアリングマネージャー/AI・機械学習 | エンジニア職種紹介 | 株式会社ラクス キャリア採用 AIエンジニア | エンジニア職種紹介 | 株式会社ラクス キャリア採用 参考文献 [1] T.Kil et al., “Towards Unified Scene Text Spotting based on Sequence Generation”, https://arxiv.org/pdf/2304.03435v1.pdf [2] Y. Huang et al., “LayoutLMv3: Pre-training for Document AI with Unified Text and Image Masking”, https://arxiv.org/pdf/2204.08387.pdf [3] G. Kim et al., “ OCR -free Document Understanding Transformer”, https://arxiv.org/pdf/2111.15664.pdf [4] Papers With Code, https://paperswithcode.com/sota [5] Scene Text Detection on Total-Text, https://paperswithcode.com/sota/scene-text-detection-on-total-text [6] Scene Text Recognition on ICDAR2015, https://paperswithcode.com/sota/scene-text-recognition-on-icdar2015 [7] Document Layout Analysis on PubLayNet val, https://paperswithcode.com/sota/document-layout-analysis-on-publaynet-val [8] Document Image Classification on RVL-CDIP, https://paperswithcode.com/sota/document-image-classification-on-rvl-cdip [9] Key Information Extraction on CORD, https://paperswithcode.com/sota/key-information-extraction-on-cord [10] Visual Question Answering (VQA) on DocVQA test, https://paperswithcode.com/sota/visual-question-answering-on-docvqa-test *1 : state-of-the-art モデル、最先端の高い性能を達成しているモデル
皆さん、こんにちは!もしくはこんばんは! 楽楽精算プロダクトマネージャーのwekkyyyyです。 前回は、「楽楽精算PdMの業務内容を紹介します」というタイトルで記事を書かせていただきました。 tech-blog.rakus.co.jp 今回は、第二弾として PBIの優先度設定方法のポイントと設定することの狙い というテーマでブログを記載します。 今回このテーマで書こうと思ったきっかけは、 弊社内やPdMコミュニティの中で 「なんでこの案件が優先されるんだろう?他にもあるのに。。。」 「次に何開発するんだろう?できれば少しずつ視野にいれていきたいな。。」 といった疑問を持たれている方が一定数いることを確認でき、そういった方達の一助になる対応事例を提供できれば・・・と思ったことです。 かく言う私も、PdM駆け出しの頃は上記のことを思ってました。 それを不満という形で上司にぶつけてしまい、よくないコミュニケーションを取ってしまっていました。(今思うと非常に恥ずかしいし申し訳ない限りです) 前提 PBIの優先度設定の狙い PBIの優先度設定方法のポイント 注意点 今後の執筆内容(変更可能性あり) ラクスのPdMとして活躍してみませんか? 前提 弊社プロダクトの中で「楽楽精算」での事例を記載しております。 優先度設定方法の 一例 として捉えてください。    ※企業によってそれぞれの状況、考え方があるので適した形を探してください。 PBIの優先度設定の狙い 1. 関係者が、先を見据えて行動できる土台をつくる プロダクトマネージャー目線: 顕在化している課題に対するソリューション案を検討し蓄積していくことを狙っています。 それにより開発リソースの空きが出た際にすぐに開発に要求仕様を渡せるようなるためです。 次に調査すべき課題の計画を事前に立てておき、動き出しを早くすることを狙っています。 調査計画を立てるためには意外と時間がかかると考えているためです。(どういう計画?は別記事で書く予定です) エンジニア目線: 技術改善提案をする際に、効果を増加できる案件があるか事前に把握し提案タイミング、内容を図れるようなることを狙っています。 技術に寄ってしまって、ユーザーニーズ、ビジネスゴールと紐づかないことを避けるためです。 2. 優先度について会話のレベルを上げる 「今の優先度設定方法だと〜の課題がある。なので〜のように変えよう。」というコミュニケーションにしていくことを狙っています。 優先度設定方法の土台がないと、前提認識内容が合わずに関係者コミュニケーションが難しくなることが多いと考えているためです。 PBIの優先度設定方法のポイント 1. 開発カテゴリ分類を決める 楽楽精算では、以下の開発カテゴリに分けています。 2. カテゴリの優先度を決める 楽楽精算では、維持管理案件が基本優先されるようにしています。  ※対応期限により後回しにすることはあります。 3. カテゴリ内の優先度指標を決める 維持管理:対応期限(期日が早いものから) 財務効果:失注、解約MRRの合計MRR(金額が高いものから) を楽楽精算では指標として定めています。 財務効果において、「それで事業目標を達成できるの?」という声がありそうですが、それはまた別記事で説明します。 4. 開発リスト入りの条件を決める ここまでを見ると、維持管理に入れてしまえば何でも優先されるようにみえてしまいます。 ですが、その状態は健全ではないのでチェッカー(事業本部長、開発部長の承認)を置いています。 注意点 上記が基本ルールとなりますが、事業戦略的な事情で一部優先度変わる場合もありえます。 ユーザビリティ 観点で見ないわけではありません。失注や解約に結びつかない事象は優先度が下がるという形です。 バグ、インシデント対応は、別途 しきい値 を決めて対応を進めていく必要があります。 コスト削減案件がある場合 財務効果観点で優先度を決めます。 今後の執筆内容(変更可能性あり) 開発案件につなぐ営業/CSからのVoC収集方法 ラク スのプロダクトマネージャーに必要なスキル エンジニアとのコミュニケーション術 ラク スのPdMとして活躍してみませんか? 楽楽精算PdMは、引き続き人材を募集しております。 是非カジュアル面談からお申し込みいただけると幸いです。 プロダクトマネージャー | エンジニア職種紹介 | 株式会社ラクス キャリア採用
始めに 弊社では、 数行と画像1枚の静的ページを表示させるためだけ に、1台サーバーを構築し保守運用してました。 それだけのために、1台のサーバーを保守運用するの馬鹿らしくね?????? \\\うん!馬鹿らしい/// ということで、 AWS 上に移行すること となりました にしました。 今回は、S3とCloudFrontを利用して静的ページを表示させる設定をご紹介させていただきます。 初歩的な内容となりますので、これから静的ページを作りたいんだけど!といった方向けの内容となります。 始めに 要件 構成について AWSの利用サービス 構成図 実際の設定手順 S3設定 1. バケット作成 2. indexファイルのアップロード CloudFront設定 1. ディストリビューションを作成 2.ポリシー設定 3.接続確認 Route53 設定 1. カスタム SSL 証明書発行 2. CNAME登録 3. ルーティング設定 4.カスタム SSL 証明書設定 5.確認作業 HTTPレスポンスの変更 1. Cloud Front関数 2. Cloud Front紐づけ 3.確認作業 おわりに 要件 今回の要件は下記です。 弊社の ドメイン が利用可能 HTTPレスポンスコード(501 や 503 番台等) が任意のものに変更が可能 https 通信で静的ページが表示可能 弊社のシステム側からのFWの許可設定を追加の必要がない 構成について AWS の利用サービス Route53 : 独自ドメイン を登録するために利用 CloudFront : 独自ドメイン の利用及びレスポンスコードを任意のものに変更するために利用 S3 : 静的ファイル(HTML) を配置 構成図 構成は下記を想定しています。 実際の設定手順 それでは早速設定手順に入らせていただきます。 S3設定 静的ページを保存するためだけに利用しております。 本番サービスで稼働する際には、ログ保存の設定 や 権限の設定などが色々かかわってきますが、 今回は最低限の設定だけとなります。 1. バケット 作成 下記の設定手順で バケット を設定する バケット 名: rakus-tset 他の設定: デフォルト 2. indexファイルのアップロード 下記のファイルを作成した バケット に保存します。 index.html <!DOCTYPE html> <html lang="ja"> <head> <title>hello rakus!</title> </head> <body> <h1>hello rakus!</h1> </body> </html> CloudFront設定 独自ドメイン の利用 及び レスポンスコードを任意のものに変更するために利用します。 まずは、 独自ドメイン を利用せずにhttp通信が可能なところまで設定します。 1. ディストリビューション を作成 ディストリビューション は下記のように設定します。 オリジン ドメイン : 作成したS3 を選択 オリジンアクセス : Origin access control settings (recommended) Origin access control : コン トロール 設定を作成 ※デフォルトで作成 ウェブアプリケーション ファイアウォール (WAF):保護しない 料金クラス : 北米と欧州のみを使用 IPv6 : オフ 他の設定: デフォルト 2.ポリシー設定 該当オリジンのページに戻った際に下記のようにでているため、作成したS3の バケット ポリシーにコピペで設定をします。 3.接続確認 ここまでで下記にアクセスできるようになったため、確認します。 https://ディストリビューションドメイン名/index.html Route53 設定 DNS にCNAME や ルーティングの設定を行います。 この設定を行うことで、CloudFront と 独自ドメイン が紐づき、 独自ドメイン での https 通信を行えるようになります。 1. カスタム SSL 証明書発行 こちらもCloudFrontの画面から設定します。 該当の ディストリビューション の設定から カスタム SSL 証明書 の配下にある [証明書をリク エス ト] を押下します。 設定画面の通りに進んでいきます。 下記の通りで設定していきます。 完全修飾 ドメイン 名: 任意 他の設定: デフォルト 2. CNAME登録 登録後下記のようにでるため、 証明書の表示を押下します。 その後、Route 53 でレコードを作成を押下してRoute53に登録します。 検証が終わるまで待機します。 3. ルーティング設定 Route53 のサーバーへアクセスし、該当ホストゾーンに遷移し、下記の設定を登録します。 レコード名: 設定した ドメイン 名 レコードタイプ : A エイリアス : はい トラフィック のルーティング先 : 作成した ディストリビューション を選択 4.カスタム SSL 証明書設定 該当の ディストリビューション の設定を下記のように変更します。 代替 ドメイン 名 (CNAME) : 先ほど登録したCNAME カスタム SSL 証明書 : カスタム SSL 証明書発行で作成したもの 5.確認作業 下記のコマンド実施します。 curl -I https://ドメイン名/index.html 現状では、レスポンスコードが200番で返ってきます。 HTTPレスポンスの変更 httpレスポンスを400番台や500番台で返したいため、そちらの内容を設定していきます Viewer Response Eventで HTTPレスポンスコードを書き換えます 1. Cloud Front関数 Cloud Frontの関数項目より下記を作成します。 名前: 任意 説明: 任意 説明: 開発コードは下記 ※レスポンスコードが200だった場合 503 番台に変更するようなものとなっております。 function handler(event) { var response = event.response; var contentType = response.headers['content-type'].value; if (response && response.statusCode === 200) { response.statusCode = 503; response.statusDescription = 'test mode'; } return response; } 発行タブより発行を行います。 2. Cloud Front紐づけ 作成した ディストリビューション の画面からビヘイビアを編集します。 関数の関連付け を下記のように設定してください。 ビューワーレスポンス: CloudFront function 先ほど作成した関数 を指定 3.確認作業 下記のコマンド実施します。 curl -I https://ドメイン名/index.html 結果(一部抜粋) HTTP/1.1 503 test mode Content-Type: text/html Content-Length: 143 Connection: keep-alive おわりに S3を静的ページで採用されることは結構あるかと思います。 ドメイン や レスポンス内容の制約により、利用されることを断念するケースもあるかもしれません。 CloudFront functionは非常に便利で色々なことができますので、よろしければ掘ってみてもおもしろいかもしれません。 次は、署名付き URL と署名付き Cookie あたりを触りたいと思います。 最後まで読んでいただきありがとうございました。 皆さまの参考になれば幸いです。
こんにちは!フロントエンド開発課所属の koki _matsuraです! 今回はものすごく今更感が否めないのですが、Reactのv18で発表された「Suspense」とVercel社が提供しているReact Hooksライブラリの「SWR」によって何を解決してくれるのか、 コンポーネント の表示と実装を例に紹介します。 目次は以下のようになっています。 Suspenseとは SWRとは Suspense・SWRが解決すること Suspense・SWR導入におけるコンポーネント表示の変化 Suspense なし SWR なし Suspense あり SWR なし Suspense あり SWR あり Suspense・SWR導入におけるコンポーネント実装の違い Suspense なし SWR なし Suspense なし SWR あり Suspense あり SWR あり まとめ 終わりに Suspenseとは React16.6で実験的な機能として追加され、React18で正式に追加された機能で、「 コンポーネント のローディング状態をハンドリングする」ことが役割となっています。 基本的にこれだけなのですが、これにより コンポーネント 単位でのロードを可能にします。 SWRとは データ取得のためのライブラリです。 名前の由来はHTTPキャッシュ無効化戦略の"stable-while-revalidate"です。 こちらもやっていることはシンプルで特定のデータをキャッシュし、もう一度、必要になった際にキャッシュからデータを返します。また、定期的に裏側でフェッチをし、最新のデータに更新してくれます。 Suspense・SWRが解決すること 結論からいきます。 Suspenseは以下のことを解決します。 データ取得のローディング状態を宣言的にする コンポーネント 単位でのロードを可能にする コンポーネント の責務を明確にする SWRは以下のことを解決します。 キャッシュにより、更新時の長期のローディングがなくなる API 通信が簡略化される フェッチしたデータの管理をしなくてよくなる よくSuspenseは「ローディング状態をハンドリングするもの」のように思ってしまっている方がいます。もちろん間違ってはいませんが、それだけではないということです。 むしろ、Suspenseのいいところは「 コンポーネント の責務を明確にする」部分です。これが本質だと思っています。 SWRはキャッシュの管理や非同期処理状態の管理をしてくれるため、データのロードを待つ必要がなかったり、データの取得を非常にシンプルにしてくれます。 ここからは コンポーネント の表示と実装を例に、実際にSuspenseとSWRが解決していることを紹介していきます。 Suspense・SWR導入における コンポーネント 表示の変化 表示においてSuspenseとSWRを取り入れることでどのような変化があるのかを紹介します。また、更新時においての違いも見ていきます。 下記のパターンで変化を見ていきます。 Suspense なし SWR なし Suspense あり SWR なし Suspense あり SWR あり 下図のようにページに3つの コンポーネント を表示する例で比較します。それぞれの コンポーネント は上から1秒間、2秒間、3秒間かかるフェッチ処理を行っているため、表示するにも1秒間、2秒間、3秒間以上かかります。 Suspense なし SWR なし まずはSuspenseもSWRも使っていない例です。初期表示時と更新時の動画を載せています。 ・初期表示時 ・更新時 初期表示時は「Suspenseを利用しない」というボタンを押したときに該当のページをリク エス トします。そこから3秒間経過したのち、3つの コンポーネント が揃った状態で画面が表示されています。 更新時も3秒後に「◯秒後に表示される コンポーネント _」の後ろについている文字列が一気に変更されていることがわかります。 これはサーバが完全にHTMLを構築してからクライアントに返していることを表しています。 図で表すと以下のようになります。 ページをリク エス ト API サーバへリク エス ト(3つ) 1,2,3秒後にレスポンスをWEBサーバへ返却 WEBサーバは全て揃ってからHTMLを構築 クライアントへHTMLを返却 HTMLを描画し、JSをロード、HTMLにハイドレーションを実行 少し雑に紹介していますが、大まかな流れを表せていると思います。初期表示や更新時に毎回3秒待たされるのは問題です。また、JSのロードやハイドレーションのことを考えると操作できるようになるには3秒よりもかかると考えられます。 ここにSuspenseを導入し、ページのロード単位を コンポーネント 単位にすることで大きな待ち時間を解決してくれます。 Suspense あり SWR なし Suspenseを導入すると以下のように変化します。 ・初期表示時 ・更新時 初期表示時は「SWRなし」というボタンを押すとページがリク エス トされ、すぐにページが遷移しています。そして、1,2,3秒後にそれぞれの コンポーネント が順に表示されていくことが確認できます。取得中は代わりの コンポーネント (今回はローディング コンポーネント )が表示されます。 更新時も同様に、全体が取得中になり、1,2,3秒で コンポーネント が表示されていきます。 WEBサーバはHTMLが未完成の状態でも返してくれることがわかると思います。 図で表すと以下のようになります。少し見にくくなっています。すみません。 ページをリク エス ト API サーバへリク エス ト(3つ) レスポンスがいらない部分だけのHTMLを構築 クライアントへ3で作成したHTM Lを返却 HTMLを描画、JSをロード、ハイドレーション。取得中の部分は代わりの コンポーネント を表示 「1秒後の コンポーネント 」のレスポンスをWEBサーバへ 「1秒後の コンポーネント 」のHTMLを構築 「1秒後の コンポーネント 」を返却 「1秒後の コンポーネント 」を表示し、その部分のJSをロード、ハイドレーション 以下略... 10〜16は6〜9と同じ流れになるので省略しました。 Suspenseを使うことで コンポーネント 単位でのロード(JSのロード・ハイドレーション)を可能にすることにより、初期表示の大きな待ち時間を解決 してくれています。 しかしながら、更新するたびに完全に表示し直すのに結局3秒以上かかるのは問題です。 ここをSWRのキャッシュ管理により解決します。 Suspense あり SWR あり SWRを導入すると、以下のようになります。 ※ SWRを導入すると、 SSR はできません。 CSR になります。 ・初期表示時 ・更新時 初期表示はSuspenseのみを導入した時と見た目は変わりません。 見た目は変わらないのですが裏側ではフェッチと共にデータをキャッシュしており、更新時にはそのキャッシュを返すことですぐに表示することができています。つまり、SWRにより 更新時の長期のローディングが解決 されていることがわかります。 また、裏で再フェッチしてくれているので最新情報を取得できればすぐに コンポーネント が更新されていることが文字列が変更されていることで確認できます。 コンポーネント の表示で比較することでSuspenseの コンポーネント 単位のロードによる表示速度の解決 とSWRの キャッシュ管理による更新時の表示速度の解決 を実際に見て、理解できたと思います。 次は、コード側から見てSuspenseとSWRが解決してくれることを紹介します。 Suspense・SWR導入における コンポーネント 実装の違い こちらも下記の3パターンで変化を見ていきます。先ほどとは違って、SWRだけを導入した場合とそこにSuspenseを導入した場合で紹介させていただきます。 Suspense なし SWR なし Suspense なし SWR あり Suspense あり SWR あり ユーザ情報を表示する コンポーネント を例にします。 Suspense なし SWR なし 下記はSuspense・SWRを共に使っていないUser コンポーネント で、ユーザ情報を表示するためだけのシンプルな コンポーネント です。 const User = () => { const [ user , setUser ] = useState < User >( null ); useEffect (() => { fetchUser () .then (( res ) => setUser ( res )); } , [] ); if ( user === null ) return < Loading / >; return < Contents user = { user } / >; } ; データがnullの間、つまり取得中の間はローディングを表示、nullじゃなければ、ユーザ情報を表示するようになっています。そのユーザ情報はuseEffect内のfetchUser()で取得され、stateにより管理されています。 まず、問題として挙げられるのは、useEffect内でフェッチをしている点です。本来、useEffectは副作用処理を書くためのものであって、冪等性のない処理を書かない方が望ましいです。 また、User コンポーネント の中でLoadingとContent コンポーネント を出し分けが行われている点も問題です。今回は例に挙げなかったのですが、フェッチのエラー処理もすることがあります。そうなると、Error コンポーネント も出し分けに加わることになります。 本来はユーザ情報を表示することのみが仕事のUser コンポーネント がローディング、エラー、ユーザ情報を表示する コンポーネント になってしまいます。 これらの問題をSWRを導入した時、どのように変わるか見ていきましょう。 Suspense なし SWR あり 先ほどの コンポーネント 表示ではSWRによってキャッシュが働くことを確認できたと思います。 コードは以下のようになります。 const User = ()=> { const { data: user , isLoading } = useSWR ( "user" , fetchUser ); if ( isLoading ) return < Loading / >; return user && < Contents user = { user } / >; state管理・useEffectがなくなり、その代わりにuseSWRフックを使っています。 useSWRは API 通信を簡素化してくれるため、 本来書くべきではないuseEffect内でのフェッチ処理やフェッチ後のデータ管理、キャッシュの管理の問題を解決 することができます。 今回は書いていませんが、useSWRはエラーにも対応しています。 SWRだけでも非常にシンプルになりましたが、まだUser コンポーネント はローディングとユーザの出し分けという複数の責務を持ってしまっています。ここにSuspenseを取り入れてみます。 Suspense あり SWR あり Suspenseを取り入れたUser コンポーネント は以下のようになります。 const User = ()=> { const { data: user } = useSWR ( "user" , fetchUser , { suspense: true , } ); return < Contents user = { user } / >; SWRでSuspenseを使いたい時はuseSWRの第三引数でsuspenseオプションをつけるだけです。 変更点はローディングの出し分けがなくなった部分です。 SuspenseがLoading コンポーネント を出す責務を担ってくれました。たった一行減っただけですが、これで コンポーネント の責務の不明確化 が解決できました。 ちなみに、Suspenseを使うときは該当の コンポーネント をSuspense コンポーネント で囲むだけで実装できます。 今回の場合は以下のようになります。 < Suspense fallback = { < Loading / > } > < User / > < /Suspense > fallbackの中にローディング中に表示したい コンポーネント を入れるだけです。 ローディング状態が命令的なものから宣言的なもの になっています。 Suspense・SWRを取り入れることにより従来の コンポーネント の問題点を解決できることが理解できたと思います。 まとめ コンポーネント の表示による違いと実装の違いを例にSuspense・SWRが何をしてくれるのかを説明してきました。 まとめると、SWRは今まで コンポーネント 内で行っていたデータの管理やフェッチの処理をuseSWRフックのみで完結させ、キャッシュまでも柔軟に管理してくれるライブラリです。 そして、Suspenseはローディング状態を宣言的にしてくれることにより、 コンポーネント 自体を非同期処理として扱え、責務を明確にするとともに、 コンポーネント 単位のロードを可能にしてくれました。 終わりに ここまで読んでいただき、本当にありがとうございます。 間違っている点などがあれば、ぜひコメントでおしえてください! この記事がSuspenseやSWRに興味を持つきっかけになってくれれば幸いです。
はじめに こんにちは、 MasaKu です。 弊社では、 PHP に関する最新ニュースの発信や気になるお題について議論するイベント「 PHP TechCafe」を毎月開催しております。 本日は、 PHP TechCafe とはどんなイベントなのかのご説明と、過去開催したイベントの中で特に盛り上がったイベントをご紹介させていただきます。 Web × PHP TechCafe はじめに PHP TechCafeの目的 立ち上げからの経緯 参加対象者とその理由 運営メンバー テーマ選定方針 コンテンツ作り 特に評判の良かったテーマ10選 PHPerのための「PHPと型定義を語り合う」 PHP TechCafe PHPerのための「PHPのリーダブルなコード」を語り合うPHP TechCafe PHPerのための「Laravel10の新機能」を語り合う PHP TechCafe PHPerのための「PHPDoc相談会」PHP TechCafe PHPerのための「PHP8.2の新機能」を語り合うPHP TechCafe PHPerのための「Composer」を語り合うPHP TechCafe PHPerのための「PHPフレームワーク」を語り合うPHP TechCafe PHPerのための「静的解析」を語り合うPHP TechCafe PHPerのための「Xdebugの活用方法」を語るTechCafe PHPerのための「PHPUnit の始め方」について語りあう PHP TechCafe おわりに PHP TechCafeの目的 PHP TechCafe は月に一度 ZOOM で開催されているオンラインイベントです。 エンジニア同士の交流の機会を提供する、エンジニアと技術が交差する憩いの場(カフェ)になれるようなイベントを目指しています。 PHP TechCafe というイベントそのものが学びの場となり、運営メンバーも含め、参加者全員がエンジニアとしてレベルアップしていけるように支援することを目的として開催しております。 立ち上げからの経緯 立ち上げ当時は PHP をテーマにした勉強会ではなく、弊社のカフェスペースを利用した社外向けの もくもく会 でした。 もくもく会 で社外のエンジニアと交流を深めていく中で、技術的なテーマで交流できることの楽しさに気づきました。 もっと技術的なテーマで語り合う方法はないかと検討したところ、自分たちの強みである PHP を軸にしたイベントにしてみてはどうかという方向性に至り、以来模索を重ねてきました 最初は PHP のニュースを紹介するだけのイベントでしたが、イベントに参加してくださった参加者にもっと価値を提供できないかと考え、特集コーナーを設けるなどして現在の形に進化してきました。 参加対象者とその理由 PHP TechCafe の参加対象者 PHP TechCafe の参加対象者は PHP 入門後の初級エンジニア ~ シニアエンジニア としています。 理由としましては、 PHP TechCafe を運営しているメンバーのレベル感や弊社の PHP エンジニアの技術領域もこの辺りに位置しているため、イベントを通して参加者と一緒に成長していきたいと考えているためです。 PHP TechCafe のイベントにはエキスパート以上のエンジニアが参加してくださることもあります。 そういった際には積極的に質問するなどして運営メンバーのレベルアップにもつなげていきたいと考えています。 運営メンバー 運営メンバーは実際にイベントに参加するメインメンバーとイベントのコンテンツ作成に協力していただくサポートメンバーで構成されています。 現在以下のメンバー構成で実施しています。 メインメンバー Y-Kanoh , MasaKu サポートメンバー 8名 弊社の PHP 系プロダクトから各2名ずつ参加しています 上記のうちイベントサポーターは2班に分けて隔月で参加してもらっています。 そのため、普段のイベントはメインメンバー2名とイベントサポーター4名の合計6名で作業をしています。 テーマ選定方針 基本的には運営メンバーが学んでみたい PHP 関連の技術をテーマにしています。 直近の業務で扱っていた部分や、業務に取り入れてみたい技術などがテーマに上がることがあります。 他のパターンとしては、以下のようなものがあります。 新しいバージョンの PHP や フレームワーク が公開されたタイミング 新機能の紹介 各種 PHP 関連イベント( PHPカンファレンス 等)が開催されたタイミング ふりかえり会などを開催 ※イベントのふりかえり会を実施する際は事前にイベントの運営元に許可をいただいて実施しております 参加者が多く集まったり、 SNS やZOOMのチャットで盛り上がった会については、イベントのブラッシュアップを行うなどして繰り返し実施することもあります。 テーマが決まったら、イベント当日はどのような流れで話をすれば盛り上がりそうか、ということを考えながら アジェンダ を作成するようにしています。 コンテンツ作り 選定したテーマを元にサポートメンバーと共にイベント当日に公開する ShowNote を作成します。 担当範囲はテーマ選定の時に作成した アジェンダ を元に分担しています。 各々が調べてきた内容をイベント当日までに持ち寄って情報共有を行い、メインメンバーがしっかりとインプットして当日のイベントに備える、という流れで準備をしています。 特に評判の良かったテーマ10選 以下では、特に評判の良かった回をピックアップさせていただきましたので、ご紹介いたします。 PHPerのための「PHPと型定義を語り合う」 PHP TechCafe rakus.connpass.com 概要 動的型付け言語である PHP は、手軽にコードを記述できる反面、 保守性の担保、または意図しない挙動を防ぐため、常に型を意識してコーディングを行う必要があります。 しかし、手軽に書ける反面、 PHP を始めたばかりの人の中には、あまり型を意識せず記載している人もいるのではないでしょうか? そんな方のために型との付き合い方を語り合いました! ShowNoteはこちら! hackmd.io PHPerのための「PHPのリーダブルなコード」を語り合うPHP TechCafe rakus.connpass.com 概要 PHP で読みやすいコードとはどのようなコードでしょう? 同じ処理でも書き方によって可読性、ひいては保守性は大きく変わります。 ビギナーPHPerに伝えたい「可読性の高いコード」について語りました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「Laravel10の新機能」を語り合う PHP TechCafe rakus.connpass.com 概要 Laravel の初版リリースから 11年目に Laravel10 がリリース予定されました。 Laravel の基礎的な内容をおさらいしつつ、Laravel10 の新機能について取り上げました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「PHPDoc相談会」PHP TechCafe rakus.connpass.com 概要 PHPDocは数が多く、ツールによって対応有無も異なります。 そんなPHPDocについて、イベント運営メンバが疑問に思ったことを中心に議論しました! PHP で型定義されている場合、PHPDocでも型を書いた方がいいか? PhpStorm最新版は配列の型、 連想配列 のkey, value の型を検知してくれる? レガシーシステム とPHPDocの向き合い方 など ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「PHP8.2の新機能」を語り合うPHP TechCafe rakus.connpass.com 概要 PHP8.2 は実用的な機能から破壊的な機能まで、様々な機能が追加されました。 PHP8.2 で実装される機能がどのようなものなのか、どういった用途があるのかについて語り合いました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「Composer」を語り合うPHP TechCafe rakus.connpass.com 概要 PHP の依存性管理ツールである "Composer" について深掘りしました。 何に使うものなのか、どのように使うのか、Packagist とは何なのか 等について語り合いました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「PHPフレームワーク」を語り合うPHP TechCafe rakus.connpass.com 概要 Laravel、 Symfony 、Cake、Slim など、 PHP の フレームワーク について有名どころをリストアップしました。 主催者一同、触ったことがない フレームワーク が多数存在する中、開催までにしっかりと調査して語り合いました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「静的解析」を語り合うPHP TechCafe rakus.connpass.com 概要 静的解析とは、コードを実行することなく行うコード検証のことです。 PHP にも PHP _CodeSniffer や PHPStan などの静的解析ツールが存在します。 PHP でなぜ静的解析が必要なのかや、各静的解析ツールの特徴について、深堀して語り合いました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「Xdebugの活用方法」を語るTechCafe rakus.connpass.com 概要 Xdebug は PHPer による デバッグ のお供であり、よく使われる "ステップ実行" だけでなく、さまざまな機能を提供する拡張ツールです。 実は、 Xdebug は "ステップ実行" だけでなく、様々な機能を有していますので、便利な使い方について語り合いました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp PHPerのための「PHPUnit の始め方」について語りあう PHP TechCafe rakus.connpass.com 概要 PHPUnit に興味はあるけど何から始めればいいの? 学ぶ前に全体像を把握したい!といった方を対象に、 PHPUnit の基本的な知識などを題材にしました。 PHP 初心者の方だけでなく、ベテランエンジニアの方も学び直し・気づきの発掘などの機会になればと思い語り合いました! ShowNoteはこちら! hackmd.io まとめ記事はこちら! tech-blog.rakus.co.jp おわりに PHP TechCafe は現在のオンライン形式になってから 約3年 イベントを継続しています。 イベント運営の苦労もありますが、 PHP 関連のテーマで社外のエンジニアと交流できることは貴重な経験になっています。 これからも PHP TechCafe をどうぞよろしくお願いいたします。 次回の PHP TechCafe は 9月26日 に開催します。 ご参加お待ちしております。 rakus.connpass.com
はじめに こんにちは akihiyo76 です。現在、私のチームではレビュー ガイドライン を明文化して、レビュアーは ガイドライン に従ってコードレビューを行なっています。この ガイドライン は、チームで運用を開始して2年になりますが、チームでも浸透しレビュー時に必ず利用するようになりました。 はじめに コードレビューの課題感 課題改善に向けて 採用したコードレビュー観点 1. Design(設計) 定義 具体例 2. Simplicity(理解容易性) 定義 具体例 3. Naming(命名) 定義 具体例 4. Style(コードスタイル) 定義 具体例 5. Functionality(機能要求) 定義 具体例 6. Test(テスト) 定義 具体例 7. Document(文章) 定義 具体例 指摘対応の要否 具体的な利用方法 指摘例 最後に コードレビューの課題感 私は現在モバイル開発チームに所属しておりますが、メンバーは若手エンジニアが中心です。一方、弊社のサービスは SaaS が中心であるため、これまでモバイル開発の経験者が少ない状況でした。そのため「モバイル技術のセオリーが分からない」という課題がチームにあり、コードレビューに苦労する状況でした。 その結果として、 メンバーの技術力の伸び悩み リリース後に一定数のバグが発生する といった状況でした。 課題改善に向けて そこで、この課題改善に取り組むことにしましたが、 レビュー指摘を類型化して、メンバーの技術力を定量化できないか と考えました。その手段として、コードレビューに対する ガイドライン を作成して、レビューコメントを類型化・ 定量 化することにしました。 一定の観点を持ってレビューコメントを類型化することで、KPIとして計測が可能( 見える化 )になり、弱点分析をすることができるからです。 ガイドライン を作成する上で、 Google が公開するレビューガイドライン を参考にして以下の7つのレビュー観点を設けることにしました。 採用したコードレビュー観点 では、実際にレビュー ガイドライン で採用した観点を紹介します。 No 観点 概要 1 Design 設計が適切か 2 Simplicity 理解容易性 3 Naming クラス、メソッド、変数名などの 命名 4 Style コードスタイル 5 Functionality 機能(要件)を充足しているか 6 Test テストの記述、パターンが適切 7 Document コメント、ドキュメントに関連 特にNo.1 ~ No.4は、 オブジェクト指向 の観点で非常に重要な観点といえます。しかし、これらの観点と概要だけでは判断が難い場面もあるかと思うので、もう少し具体的にコードベースで説明します。 1. Design(設計) 定義 システムにとって適切な責務・振る舞いになっているか。システムとして アーキテクチャ を遵守できているか。また、システム全体として 一貫性ある設計になっているか。 具体例 基本的には、以下のような オブジェクト指向 の基本である SOLID 原則に反するような場合、指摘の対象になります。 関心の分離原則違反(≒ 単一責任原則違反) 密結合 低凝集 DRY 原則違反 etc. 例えば、以下のコードの場合 add() で様々処理を行なっており、責務超過といえるため Design 指摘の対象になります。 class HogeDiscountManager { lateinit var manager: DiscountManager /** * 商品を追加する */ fun add(product: Product): Boolean { if (product.id < 0 ) { // バリデーション 1 throw IllegalArgumentException () } if (product.name.isEmpty()) { // バリデーション 2 throw IllegalArgumentException () } val temp: Int = if (product.canDiscount) { // 条件分岐 1 manager.totalPrice + manager.getDiscountPrice(product.price) } else { manager.totalPrice + product.price } return if (temp < 3000 ) { // 条件分岐 2 manager.totalPrice = temp manager.discountProducts.add(product) true } else { false } } } 2. Simplicity(理解容易性) 定義 システムとして可読性あるコーディングになっているか。 処理ができるだけシンプルな振る舞いになっているか。 具体例 以下のように実装が複雑になる場合、指摘の対象になります。 ネストが深い if 分 複雑な 三項演算子 文 冗長な SQL stream, filter, map を多用したObject整形文 etc. このように分岐が多い if 分は、Simplicity の指摘の対象になります。 // Before.kt val powerRate: float = member.powerRate / menber.maxPowerRate var currentCondition: Condition = Condition.DEFAULT if (powerRate == 0 ) { currentCondition = Condition.DEAD } else if (powerRate < 0.3 ) { currentCondition = Condition.DANGER } else if (powerRate < 0.5 ) { currentCondition = Condition.WARNING } else { currentCondition = Condition.GOOD } return currentCondition 実際のレビューコメントでは、以下のようにネストを解消するように指摘をする場合などに使用します。 // After.kt val powerRate: float = member.powerRate / menber.maxPowerRate if (powerRate == 0 ) { return Condition.DEAD } if (powerRate < 0.3 ) { return Condition.DANGER } if (powerRate < 0.5 ) { return Condition.WARNING } return Condition.GOOD 3. Naming( 命名 ) 定義 変数やクラス、メソッドに責務を意図した明確な名前が付けられているか。英語文法に誤りがないか。 typo もこれに含まれる。 具体例 このような 命名 に関する指摘をする場合に使用します。 振る舞いと一致しない変数名、関数名 責務と一致しない関数名 英文法の誤り etc. 例えば iOS アプリ開発 時においては、Swift Foundation や Cocoa の 命名規則 に準拠しない場合、Naming の指摘対象になります。基本的な 命名規則 は、利用している フレームワーク や言語の特性によるものが判断基準になります。 4. Style(コードスタイル) 定義 コードスタイル言語仕様に準拠しているか。 具体例 コードスタイルも同様に言語仕様や フレームワーク に準拠させることが基本になるため、これに反する場合に使用します。 静的解析違反 不適切なアクセス修飾子 表記違反(スネーク、キャメルなど) etc. 他にもモバイル開発では、公式( Apple 、 Google 等)で公開している ガイドライン 違反している場合もこれに含まれます。コードスタイルの判断はその人の経歴などの主観的な部分も影響するので、コードフォーマッターを導入し、 機械的 な判断基準を設けることもこの指摘点を減らす有効な手段です。 5. Functionality(機能要求) 定義 システムとして外部仕様を充足しているか。作者が意図した通りの振る舞いであるか。 また、システムの通信量、パフォーマンスに懸念がないか。 具体例 主な観点としては、外部機能を充足しているかという点が対象になります。 外部仕様の未充足(不具合) 概要設計書の フローチャート と異なるフローになっている 不要データを送信している etc. 6. Test(テスト) 定義 システムとして適切な自動テストを兼ね備えているか。自動テストの内容で品質を担保できているか。 また、システムを担保するパラメータ群を備えているか。 具体例 テストコードが期待になっていない場合や、テストでのパラメータに考慮漏れがある場合、指摘の対象になります。 対象のメソッドがテストされていない テストパターンが網羅できていない(パタメータテスト、 閾値 テストの不足) 分岐がパターンが網羅されていない 実装上宣言している静的定数値が直接ハードコードされている アーキテクトに準じたテストになっていない 7. Document(文章) 定義 ソースコード 上に記載されている doc、コメントが適切な内容であるか。 また、関連するドキュメントは更新されているか。その内容は適切か。 具体例 ソースコード に関連するコメントだけでなく、プロジェクトで管理している関連ドキュメント(README)も対象になります。 関連ドキュメントの更新漏れ(README など) doc やコメントの内容が不適切、内容が不適切 指摘対応の要否 更にコードレビューの現場では上記の7つの観点に加えて、指摘修正の要否を4つの累計に分けてコメントしています。 観点 概要 MUST PR、MR をマージするためには必ず修正が必要 SHOULD 修正なしにマージすることはできるが、リリースまでには修正が必要 IMO レビューアー観点の意見。修正不要 NITS IMO より細かい意見など。修正不要 このように、コードレビューでマージするために必要な修正は MUST 指摘となります。MUST と SHOULD の使い分けは難しい部分もありますが、これまでのレビュー ガイドライン の運用では、 SQL のパフォーマンスをより良くするための指摘やテストコードの最適化の指摘などで SHOULD は利用される場面もあります。その場合、修正タスクを Issue に積んだ上で(修正スコープの合意)、マージするようにしています。一方、IMO や NITS は修正は不要ですが、修正しない場合はその旨をコメントに返信してもらい、コメントを閉じてからマージする運用をしています(レビュアーとの合意)。 具体的な利用方法 実際にコードレビューをするとき、上記の7つの観点と修正の温度感をこのように交えた Prefix を付けて、コメントをします。 指摘例 MUST(Design): ドメイン ロジックが Controller クラスに実装されてます。 domain 層の対象 package に新しくクラスを作成して実装を移してください。 このとき Prefix の入力を手入力にしてしまうと、入力の手間や入力がミスが生じることもあるので、カスタム script で入力をサポートするようにしています。 最後に 以上のように、私のチームではコードレビュー ガイドライン を作成してルールを明文化することで、技術力を 見える化 させて課題改善を進めています。レビューコメントをこのように分析することで、個人の弱点に合わせたアプローチ方法も見えてきます。このように技術力に対するアプローチとして PDCA サイクルを回すことで、チームメンバーの技術育成を進めております。 最後に簡単にまとめると、コードレビュー ガイドライン を明文化した場合、 指摘数に応じて技術力を 見える化 できる コードレビューで オブジェクト指向 が学べる スキルアップ のためのアクションプランが検討しやすい といった恩恵を受けることできるので、ぜひチームに合ったコードレビュー ガイドライン を作成してみてはいかがでしょうか。
はじめに はじめまして。インフラエンジニアの rkyohei です。 Linux サーバの運用やモニタリングにおいて、性能チューニングや トラブルシューティング にはさまざまコマンドを使用すると思います。その中でも、特にリソース使用状況を詳細に分析するために便利なツールの1つが「vmstat」となります。 vmstatコマンドの存在自体は知っていたけど、オプション、実行結果の見方についてあまり知らなかったのですが、先日業務で使用する機会があり、vmstatコマンドについて調べましたのでこのエンジニアブログでみなさんにご紹介したいと思います。 はじめに vmstatとは何か? vmstatコマンドの基本的な使い方 vmstatコマンド実行結果の見方 vmstatコマンドの実行例 1. vmstatコマンドのみでの実行例 2. 更新間隔、表示回数を含めた実行例 3. -sオプション(メモリ統計情報の表示)を使用した実行例 4. -dオプション(ディスクI/O統計情報の表示)を使用した実行例 最後に 参考文献 vmstatとは何か? vmstatとは Virtual Memory Statistics の略であり、 Linux システム上で 仮想メモリ の統計情報を表示するコマンドです。 vmstatコマンドの基本的な使い方 vmstatコマンドの基本的な使い方についてご紹介します。 vmstat [オプション] [間隔(sec) [回数] ] ※[ ] は省略可能です オプション: ここではvmstatコマンドのオプションについて一部ご紹介します。 オプション 説明 -a 仮想メモリ の詳細情報を表示します。プロセスのステート(実行中、スリープ中など)、ページング、メモリ情報などが含まれます。 -s 仮想メモリ の統計情報のみを表示します。各種メモリスタット、ページング、 スワップ 情報などが表示されます。 -d ブロックデ バイス のIO統計情報を表示します。IOのバイト数、リク エス ト数、転送時間などが表示されます。 -D ディスクの統計情報を1項目1行で表示します。 -p < パーティション > 指定した パーティション に関する情報を表示します。 パーティション を指定して詳細情報を取得することができます。 -S 単位 単位をk,K,m,Mで指定します。 -t タイムスタンプを表示します。 これらのオプションを使用することで、さまざまな情報を取得することができます。例えば、 仮想メモリ の詳細情報や統計情報、ブロックデ バイス のIO統計情報などを利用して、システムの性能やリソース使用状況をより詳細に分析できます。 上記以外にもオプションはありますので、オプションの詳細について興味がある方は、 man コマンドを使用してマニュアルページを参照していただければと思います。 更新間隔: デフォルトでは1秒ごとに情報が表示されますが、必要に応じて変更できます。 表示回数: 指定回数だけ情報を表示した後にコマンドが終了します。 vmstatコマンド実行結果の見方 vmstatコマンドの実行結果の見方について、以下にご紹介いたします。ここではvmstatコマンドをオプション無しで実行した結果を例としています。 # vmstat procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st  0  0 123088 154496      0 1168484    0    0     0     1    1    1  0  0 100  0  0 区分 値 意味 procs r 現在実行待ちのプロセス数。CPUが過負荷であるかどうかを示す指標です。 b スリープ中のプロセス数。スリープ中のプロセスはI/Oの完了を待っています。 memory swpd スワップ されたページの数。 スワップ の量を示す指標です。 free 使用されていないメモリの量。大きな値が望ましいです。 buff ファイルの読み取り結果としてキャッシュされているメモリ量。 cache ファイルシステム がキャッシュしているページの量。メモリ使用効率の指標。 swap si スワップ 領域からメモリにページが転送された回数。 so メモリから スワップ 領域にページが転送された回数。 io bi ブロックデ バイス から受け取ったブロック。(blocks/s) bo ブロックデ バイス に送られたブロック。(blocks/s) system in 1秒あたりの割り込みの数。ハードウェアの負荷を示す。 cs 1秒あたりの コンテキストスイッチ (プロセスの切り替え)の数。 cpu us ユーザープロセスが消費したCPU時間。 sy カーネル プロセスが消費したCPU時間。 id アイドル状態のCPU時間。高いほどCPUがアイドルであることを示します。 wa ディスクI/Oの待機時間。ディスクへのアクセスが遅い場合に増加します。 st 仮想マシン から盗まれた時間を示します。 vmstatコマンドの実行例 vmstatコマンドの実行例と結果についていくつかご紹介します。 1. vmstatコマンドのみでの実行例 vmstatコマンドをオプション無しで実行すると、1回のみ結果が表示されます。 # vmstat procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st  0  0 123088 154496      0 1168484    0    0     0     1    1    1  0  0 100  0  0 2. 更新間隔、表示回数を含めた実行例 以下のように実行することで更新間隔、表示回数を指定することができます。ここでは1秒間隔で5回実行されるように指定しています。 また  -t オプションを併せて使用することでタイムスタンプを結果に表示させることもできます。おそらく トラブルシューティング の際には問題となる事象が再発するまでコマンドを継続して実行する必要があり、cronで定期的に実行したり、引数を使用して実行状態のまま経過監視すると思います。 TeraTerm 等のログ保存設定にタイプスタンプを付与することも可能ですが、 -t オプションを使用しタイムスタンプを表示することで問題が発生した時間のログを探しやすくなります。 # vmstat 1 5 -t procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- -----timestamp-----  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st                 JST  0  0 123088 154320      0 1168484    0    0     0     1    1    1  0  0 100  0  0 2023-08-28 12:17:49  1  0 123088 154224      0 1168484    0    0     0     0  141  202  1  0 100  0  0 2023-08-28 12:17:50  0  0 123088 154224      0 1168484    0    0     0     0  150  218  0  0 100  0  0 2023-08-28 12:17:51  0  0 123088 154256      0 1168484    0    0     0     0  128  189  0  0 100  0  0 2023-08-28 12:17:52  0  0 123088 154256      0 1168484    0    0     0     0  139  192  0  1 100  0  0 2023-08-28 12:17:53 3. -s オプション(メモリ統計情報の表示)を使用した実行例 -s オプションを使用することでメモリの統計情報を1項目1行で表示することができます。 # vmstat -s 1728000 K total memory 405420 K used memory 621268 K active memory 539020 K inactive memory 153972 K free memory 0 K buffer memory 1168608 K swap cache 2097148 K total swap 123088 K used swap 1974060 K free swap 3091882 non-nice user cpu ticks 135539 nice user cpu ticks 1694588 system cpu ticks 2524043051 idle cpu ticks 59367 IO-wait cpu ticks 1517573 IRQ cpu ticks 1268497 softirq cpu ticks 487644 stolen cpu ticks 9009973 pages paged in 20842448 pages paged out 74510 pages swapped in 207951 pages swapped out 1668051957 interrupts 2180661177 CPU context switches 1680577615 boot time 393160 forks 4. -d オプション(ディスクI/O統計情報の表示)を使用した実行例 -d オプションを使用することでディスクI/Oの統計情報を表示することができます。ディスクの性能評価や トラブルシューティング に役立つ情報を確認することができます。 # vmstat -d disk- ------------reads------------ ------------writes----------- -----IO------ total merged sectors ms total merged sectors ms cur sec sda 238129 21641 18019946 506118 893700 503648 41682672 4461928 0 2138 sr0 9 0 3 2 0 0 0 0 0 0 dm-0 181053 0 17319481 448718 1182573 0 40014120 8384242 0 2099 dm-1 74608 0 600776 58836 207951 0 1663608 8075290 0 42 ディスクI/Oの統計情報では3つの項目が表示されます。それぞれ読み取り(reads)と書き込み(writes)、実行中のI/Oです。各項目中のmsとsecは合計時間を示しています。 最後に vmstatコマンドは、 Linux サーバエンジニアにとって重要なモニタリングツールです。リソース使用状況の トラブルシューティング や最適化にとても有効だと考えます。 ただし、オプションや実行結果を理解し、覚えることはなかなか難しいと思います。 コマンドを実行する環境があるのであれば、実際にコマンドを実行することで、オプション、結果にふれてみるのも良いと思います。 参考文献 How to read Vmstat output
はじめに 昨今 書籍や各社Blog記事などでプロダクトマネージャー(以下PdM)の業務内容について記載された媒体が多数でている状況です。 ですが、複数の媒体を参照された方は、こう思われることが多いと考えております。 「見るものによって役割、業務内容違くないか?」 実際、企業・プロダクト・チームといった単位で、PdMの業務内容は変わっていると私も考えております。 弊社 ラク スにも、以下のようにさまざまなプロダクトがございますが、各プロダクトによってPdMの業務内容は異なっています。 その中でも今回は、 「楽楽精算」のPdM業務内容をご紹介します。 スコープ はじめに プロダクト体制 楽楽精算のPdM業務内容 事業KPI貢献に沿った優先順位 PRD(要求仕様書)作成 今後の展望 ラクスのPdMとして活躍してみませんか? プロダクト体制 さっそくPdMの業務内容を説明したいところですが、 まずは楽楽精算を提供・開発する上での体制(概略図)を説明させてください。 (その方が後続の理解がしやすくなるためです) 楽楽精算もARR100億を達成してきたこともあり組織として、大きくなっております。 その中でも特筆する部分は、開発と事業をつなぐ役割としてPdM/PMMを配置している部分です。 楽楽精算プロダクト体制概略図 楽楽精算のPdM業務内容 大きく以下の業務がございます。 事業KPI貢献に沿った優先順位で案件を推進すること 顧客解像度を高めた上で、PRD(要求仕様書)を作成、開発へ渡すこと 事業KPI貢献に沿った優先順位 基本的に、以下図のようにCSから見える「解約原因」営業から見える「失注原因」から財務効果を割り出し案件の優先順位を決めていきます。 会社戦略上 優先することが決定している案件、EOLなどの維持管理案件などはこれの限りではございません。 基本優先順位ロジック PRD(要求仕様書)作成 楽楽精算では、PRDに盛り込む要素は、以下画像のオレンジ部分と定義しており、調査対象に定めています。 (こちらは各PdMによって様々なご意見があるかと思いますが、一旦楽楽精算ではそうしています) 画像は プロダクトマネジメントのすべて から抜粋させていただいております。 (全PdMが読むべき良書と私は思っています) プロダクト4階層  出典:「 プロダクトマネジメント のすべて」p.52の記載を引用し、オレンジ枠は筆者追記 また、調査するための計画Agendaは、現状以下のように定めています。 (ブラッシュアップは続けています) 調査計画Agenda 最終的には、以下のAgendaの内容は最低でもPRDに盛り込むようにしています。 PRD Agenda(一部) 今後の展望 現在以下のようにDACIという フレームワーク を利用して、PdM/PMMの役割分担を決めています。 今後は、インタビュー等の収集業務、プロダクト指標の決定(共に現状0ではないのですが)にも踏み込んでいきたいと考えております。 DACI表(一部) ラク スのPdMとして活躍してみませんか? 今後の展望にも記載の通り、PdMとして役割を広げていきたいと考えております。 そのためにも楽楽精算PdMは、人材を募集しております。 是非カジュアル面談からお申し込みいただけると幸いです。 プロダクトマネージャー | エンジニア職種紹介 | 株式会社ラクス キャリア採用