TECH PLAY

株式会社ラクス

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

927

はじめに こんにちわ。cappy_potterです。 MailDealer と ChatDeaeler という弊社サービスのインフラ運用チームのリーダを担当しています。 前回、 こちらの記事 で、 『チームとして障害対応時間削減に向けて取り組んだこと』 について 紹介させていただきました。 tech-blog.rakus.co.jp その際、記事の中で、取り組み実施後に同様の障害が発生したことについて触れ、取り組み実施前に比べて 関係者への情報共有の時間をおよそ半分にできたと記載しました。 ( 以前は同様の障害で「42分」かかっていたものが、「22分」に短縮できた。 ) あれからさらに半年が経過したところで、再度大きな障害が発生したのですが、今回については 効果的に動くことができず、 サービス復旧までに多大な時間を要してしまいました 。 ( 関係者への情報共有についても、障害発生から「 30分 」かかってしまいました。 ) なぜ今回の障害については迅速に動くことができなかったのか?ということと、今後どうすればよいのか ということについて、チーム内で振り返りを行った結果を中心にお話させていただきたいと思います。 はじめに 発生した障害の内容について 対応に時間がかかった要因 要因への対策 【要因①への対策】 【要因②への対策】 その他の対応について 発生した障害の内容について 平日の朝8時前に、外部からの DDoS攻撃 を防御するためのセキュリティ機器が 突如誤作動を起こし、正常な通信をブロックしてしまうという事象が発生しました。 これにより、弊社がサービス提供を行っている多数のサーバについて、外部からアクセスしづらい 状態が発生してしまいました。 また、このような状況が起こっているということを把握し、 サービス復旧のための対処を 行うまでに「 86分 」という時間を要してしまいました。  ※サービス復旧の方法としては、誤作動を起こしていたDDoS対策用機器の電源を落とし、   異常なブロックが発生しないようにする、というものでした。 対応に時間がかかった要因 以前、障害対応時間削減のための取り組みを実施し、実際、その後に発生した同様の障害に ついては、比較的迅速に対応できていたのですが、なぜ今回は時間を要してしまったのか、 チーム内で振り返りを行いました。 その結果、以下のようなことが要因として挙げられました。   ①現状、どこまで対応が進んでいるのか、把握しづらい状況だった    ∟ 誰が何をしているのか、あと何の対応をしなければいけないのか、よくわからない状態だった。    ∟ 後から対応に参加した人が現状を把握できない状態だった。   ②アラート検知しているサーバの共通項の絞込みに時間がかかり、障害箇所の特定に時間がかかった    ∟ どの仮想基盤上で稼働しているか、どのネットワーク機器を経由しているか、など。   ③コミュニケーションツール(Zoom)の準備が遅かった    ∟ 障害発生の時間が、出勤前・出勤中の時間帯(平日の朝8時前)であったことから、      まずはチャットベースでやり取りを開始しており、そのままの流れでずっと対応してしまっていた。     (テキストベースのツールだと、やり取りに時間がかかる)   ④障害対応の司令塔が情報共有・報告者を兼ねていて、メンバへの指示が後手後手になっていた。    ∟司令塔自身が、関係者への情報共有のための文章を考え、入力するのに時間を取られていた。   ⑤他部署の関係者が障害対応メンバの手を止めてしまっていた    ∟ 情報共有が適切にできていなかったため、他部署の関係者が障害対応を行っているメンバに対して      直接状況確認を行おうとて、対応の手を一時的に止めてしまう状況が発生していた。   ⑥役割分担の際、2人に対して同じ役割を分担したことにより、混乱が生じてしまった    ∟ 役割としては、ざっくりとしたものになっているため、具体的な実施内容については      2人で相談した上で進めてほしかったが、うまくいかなかった。   ⑦指示されたことと異なる対応を行っている者がいた    ∟ 似たような役割(「障害発生箇所調査」と「影響範囲調査」)があることにより、     見間違えが発生していた。 要因への対策 前項にて、対応に時間がかかった要因の洗い出しを行いましたが、全てに対応しようとすると 対策実施までに時間がかかりそうなため、ポイントを絞って対応することとします。 具体的には、要因①②に対し、以下のように対応する予定です。 なお、以下の対策を実施することにより、要因④~⑦についても、いくぶん改善できると 見込んでいます。 【要因①への対策】  ・具体的にやるべきこと、確認すべきことなどを箇条書きにしたリストを作成する。    ∟ 基本的に、上から順に実施していくものとする。    ∟ リストには「対応者」欄、「実施状況」欄を設け、リアルタイムに更新していく。    ∟ リストは スプレッドシート で作成することにより、複数人での同時編集を可能とする。    ∟ 障害対応開始時、まずこのリストを関係者で共有する。     (これを見れば、どこまで進んでいるのかがわかるようにする。)  ・調査結果の保存場所も、上記リストに記載しておくことにより、調査結果(ログなど)を   どこに格納するかを迷う時間や、パスを共有する時間を削減する。 【要因②への対策】  ・現状、各サーバごとのOSや ミドルウェア のバージョン、 IPアドレス 等の情報を管理するための   データベースがあるため、「どの仮想基盤上で稼働しているか」「どのネットワーク機器を   経由しているか」などの情報を記載するための項目を追加し、絞込みが行えるようにする。    ∟ これにより、障害発生箇所の推測を早める。   ※上記データベースは、弊社の 楽楽販売 を利用しています。 その他の対応について 前回の記事の中で、サービス復旧を早めるための取り組みとして、以下の2点について 実施予定であると記載しましたが、こちらの状況について報告します。 ●各機器への疎通・ステータス確認、サーバの正常性確認の自動化   → 弊社で稼動中のjenkinsサーバにて、あらかじめ以下を登録しておき、ワンクリックで     確認できるようにしました。    ・主要機器に対する Ping 疎通確認用ジョブ    ・ Firewall のログ確認用ジョブ(エラーログ確認用)    ・スイッチのステータス確認用ジョブ    ・サービス提供用サーバのWeb管理画面ログインテスト用ジョブ ●アラート検知を契機とした自動復旧の仕組み作り   → 一部のサーバについて、Zabbixサーバでサービス停止を検知した際に自動的に     サービス再起動コマンドが実行されるようにしました。 以上、最後までお読みいただき、ありがとうございました。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは! ラク ス入社1年目の koki _matsuraです。 本日は、 Redux-Toolkitの基本的な状態管理や仕組み をTodoアプリ作成を通して、ご紹介させていただきます。 こちらの記事は 「Reactの状態管理ライブラリ基礎学習」の2部目 です。 前回の「Redux編」を読んでいない方は下記のリンクからお読みいただけると嬉しいです。 Reduxの仕組みを知ることでよりRedux-Toolkitの使いやすさが理解できると思います。 tech-blog.rakus.co.jp Reactの状態管理ライブラリを勉強している方、状態管理ライブラリについて簡単に知りたい方などのお役に立てればなと書かせていただきました。 アジェンダ は以下の通りです。 Redux-Toolkitとは 概要 構成図 Todoアプリ作成 仕様説明 プロジェクト作成 初期設定 ディレクトリ構成 Todo型の定義 Sliceの定義 Storeの定義 TodoContainer.tsxの定義 TodoPresenter.tsxの定義 Providerの定義 アプリの起動 Todoの追加機能 Todoの削除機能 完了・未完了の切り替え機能 終わりに Redux-Toolkitとは 概要 名前の通り、Reduxを用いた開発を効率的に行うためのツールキットです。 Reduxと比べて、最大のメリットはコード量が減ることです。詳しくは下の構成図で説明させていただきます。他にも、可読性が上がることやTypeScriptとの相性がいいこともメリットです。 Reduxの公式はRedux-Toolkitの記述法を標準にしてほしく、使用することを強く勧めています。 今後、Reduxを導入したい方はRedux-Toolkitで始めると簡単に状態管理できると思われます。 構成図 Redux-Toolkitでは下図のように状態管理をしています。 Reduxと比べると、少しシンプルになっているのがわかります。具体的にいうと、ActionCreatorがなくなりました。実際になくなっている訳ではないのですが、ユーザが意識する必要がなくなりました。 また、それぞれの機能をSliceという単位で切り分けます。Sliceの中には機能ごとのState、Reducerを管理することができるので、Reduxと比べて、管理する状態が増えてもコードの見通しが悪くなりにくいです。 Sliceの中にActionCreatorもあるのですが、先ほども書いた通り、意識する必要がなくなるので省いています。 Redux-Toolkitの良さはこれだけでも十分なのですが、個人的に一番メリットに感じているのはStateのイミュータブル性を意識しなくていいことです。ReduxではStateの更新方法が直接変更するのではなく、新しいStateを作り出して返すというもので、コードで書いてみると躓きやすいです。ですが、イミュータブル性を意識しなくていいのでStateを直接変更するような方法で更新することが可能になります。 これに関しては、実際にコードを書くと有り難みが身に沁みます...。 Todoアプリ作成 仕様説明 Todoアプリを作成する前にTodoアプリの仕様と構成を説明します。 構成は以下の画像のようになります。 入力フォームと送信ボタン、Todoのリストを載せる部分で構成されます。 また、それぞれのTodoには内容に加え、完了ボタン、削除ボタンがあります。 仕様を説明します。 ・Todoの追加 画像上部のタイトル・内容の入力フォームに適当なテキストを入力し、送信ボタンを押すことでTodoリストに入力したTodoが追加されます。 ・Todoリストの表示 画像下部のTodoリストは古いもの(ID昇順)から順に表示されます。最も新しいものは最後尾に表示されます。 ・Todoの完了 それぞれのTodoについている完了ボタンを押すと、該当するTodoが未完了から完了に変化します。 また、完了しているTodoには「戻す」ボタンが表示されており、これは完了ボタンの逆の働きをします。 ・Todoの削除 それぞれのTodoについている削除ボタンを押すと、該当するTodoがリストから削除され、表示からも消えます。 以上が今回作成していくTodoアプリの仕様になっています。 プロジェクト作成 プロジェクトの作成は下記のコマンドを入力します。 私はプロジェクト名を「redux-toolkit-todo」としましたが、お好きなプロジェクト名をつけていただいて問題ありません。 npx create-react-app [プロジェクト名] --template typescript 初期設定 Redux-Toolkitを用いて、開発するには「react-redux」「@reduxjs/toolkit」を入れなければなりません。 下記のコマンドでプロジェクトに入り、それらのライブラリを入れます。 cd redux-toolkit-todo npm i react-redux @reduxjs/toolkit ディレクト リ構成 Redux-Toolkitを用いた時のsrcは以下のような ディレクト リ構成にします。 app ディレクト リとcommon ディレクト リ、features ディレクト リ、features ディレクト リの中にtodos ディレクト リを作成します。 app ディレクト リ App. tsx を移動 store.tsを新規作成 「App. tsx 」を移動させた理由として、ReduxのStoreにアクセスできるのはProviderで囲われた コンポーネント だけで、「App. tsx 」の中身を囲って、Todoアプリ全体で状態を共有したかったからです。同じ ディレクト リに移動させることでどの コンポーネント でProviderが使われているか分かりやすくなります。 common ディレクト リ todo.type.tsの新規作成 rootState.type.tsの新規作成 「todo.type.ts」は今回のTodoアプリで出てくるTodoのタイプを定義し、「rootState.type.ts」には現在のStateのタイプを定義しています。色々なファイルから使われると思われるのでcommon ディレクト リに作成しました。 features/todos ディレクト リ todoSliceの新規作成 TodoContainer. tsx の新規作成 TodoPresenter. tsx の新規作成 Reduxと違う構成をしているのはtodos ディレクト リ内だけです。 「todoSlice.ts」はReduxで言うと、State・Reducer・Actionを一つにまとめたようなものです。 「TodoContainer. tsx 」はTodoアプリのロジック部分を、「TodoPresenter. tsx 」は表示部分を担当します。 Todo型の定義 「todo.type.ts」にTodo型を記述します。 //todo.type.ts export type Todo = { id : number , title : string , content : string , isCompleted : boolean } Sliceの定義 Sliceを定義していきます。 Sliceの中にはState、Reducer、Actionを記述します。 Stateには適当なデータを2つ入れておきます。 基本的な書き方は以下のようになります。 //todoSlice.ts import { createSlice } from "@reduxjs/toolkit" ; import { Todo } from "../../common/todo.type" ; const state = { todos: [ { id: 1 , title: "テスト1" , content: "テスト1の内容" , isCompleted: false } , { id: 2 , title: "テスト2" , content: "テスト2の内容" , isCompleted: false } ] as Todo [] } export const todoSlice = createSlice ( { name: 'todoSlice' , initialState: state , reducers: { //Actionを記述する } } ) createSlice関数に、「name」、「initialState(State)」、「reducer」をオブジェクトにして渡しています。 「name」というのは、Reduxでは出てこなかったのですが、Sliceの名前を示します。また、Actionのタイプのprefixとして用いられます。 なので、Redux-ToolkitではあまりActionのタイプを意識する必要がなくなるのです。 これで最も基本的なSliceを定義できます。 Storeの定義 Sliceを定義できたので、次はStoreを定義していきます。 Storeの定義方法もReduxとは少し変わってきます。 次のようにして、作成できます。 //store.ts import { configureStore } from "@reduxjs/toolkit" import { todoSlice } from "../features/todos/todoSlice" export const store = configureStore ( { reducer : todoSlice.reducer } ) configureStore関数の中でreducerにtodoSlice内のReducerを渡すことで登録できます。 configureStore関数に登録するReducerが単数の場合は、それがStoreのルートリデューサーとなります。 複数の場合は、combineReducersでReducerをまとめてから登録することをお勧めします。 また、configureStore関数にはreducer以外にも、middleware、devTools、preloadedState、enhancersもオプションとしてあります。 TodoContainer. tsx の定義 Slice側は仮ではありますが実装できたので、TodoContainer. tsx を定義します。 このファイルではTodoアプリのロジック部分を担当します。 RootState型とTodoPresenterはまだ定義していないのでエラーが出ていても問題ありません。 //TodoContainer.tsx import { useSelector } from "react-redux" export const TodoContainer = () => { const todos = useSelector (( state : RootState ) => state.todos ) const args = { todos , } return < TodoPresenter { ...args } / > } 「rootState.type.ts」に下記のようにRootState型を定義します。 //rootState.type.ts import { store } from "../app/store" ; export type RootState = ReturnType <typeof store.getState > 「store.getState」はインポートしたStoreから全てのStateを取得できます。その型をRootStateに入れています。 今回の場合はtodosのみを管理しているためToDoのリスト型でも問題はなかったのですが、管理する状態が複数になった時のためにこのような型を紹介させていただきました。 この型を「TodoContainer. tsx 」にインポートすれば、RootStateのエラーは消えます。 TodoPresenter. tsx の定義 このアプリではTodoアプリの表示部分を担当します。 Todoリストを表示します。 まずは、色々な機能を作る前に基盤を作りたいので下記のようなコードにします。 //TodoPresenter.tsx import React , { useState } from "react" import { Todo } from "../../common/todo.type" type TodoPresenterProps = { todos : Todo [] } export const TodoPresenter : React.FC < TodoPresenterProps > = ( { todos , } ) => { const [ title , setTitle ] = useState ( "" ); const [ content , setContent ] = useState ( "" ); return ( <> < form > < label > タイトル: < input type= "text" value = { title } onChange = { e => setTitle ( e.target.value ) } / > < /label > < label > 内容: < input type= "text" value = { content } onChange = { e => setContent ( e.target.value ) } / > < /label > < button type= "button" > 送信 < /button > < /form > < div >------------------------ - < /div > < h1 > Todoリスト < /h1 > { todos.map (( todo : Todo )=> { return ( < React.Fragment key = { todo.id } > < div > { todo.title } : { todo.isCompleted ? "完了" : "未完了" } < /div > < div > 内容: { todo.content } < /div > < button type= 'button' > { todo.isCompleted ? "戻す" : "完了" } < /button > < button type= 'button' > 削除 < /button > < /React.Fragment > ) } ) } < / > ) } 入力部にはタイトルと内容の入力フォームとまだ機能のついていない送信ボタンを配置しています。 出力部にはTodoリストをmap関数で出力しています。それぞれのTodoにつくボタンも現時点では機能がついていません。 一旦、これで置いておきます。 Providerの定義 Stateを使いたいルート コンポーネント を囲う形で使います。 TodoContainerをルート コンポーネント にStateを使いたいので、「App. tsx 」を次のように書き換えます。 //App.tsx import { Provider } from "react-redux" ; import { TodoContainer } from "../features/todos/TodoContainer" ; import { store } from "./store" ; function App () { return ( < div className = "App" > < Provider store = { store } > < TodoContainer / > < /Provider > < /div > ); } export default App ; アプリの起動 下記のコマンドで起動してみましょう。 npm start 自動で開くと思いますが、開かない方は「 http://localhost:3000/ 」にアクセスしてください。 次のようにStateを定義したときに入れたサンプルデータが2件分、表示されていればうまくいっています。 Todoの追加機能 送信ボタンを押すと、Todoを追加できるようにします。 手順を説明します。 SliceでTodo追加ActionをReducersに加え、そのActionをエクスポート ContainerでTodo追加Actionをインポートし、そのActionに追加したいTodoを入れて、Sliceに流す関数を作成 Presenterで送信ボタン押下時に2で作成した関数を実行する 早速、実装していきます。 Todo追加Actionは「add」という名前にします。 「todoSlice.ts」のsliceのreducersを下記のように書き換えてください。 //todoSlice.ts reducers: { add: ( state , action: PayloadAction < Todo >) => { state.todos.push ( action.payload ) } } Reduxとかなり違った書き方をしたと思いますが、ReduxのReducerとの大きな違いは以下2つが挙げられます。 ・ Switch文による分岐 Reduxではdispatchにより送られてくるActionのタイプをSwitch文で分岐させていたのですが、Redux-ToolkitではSwitch文を書かなくても問題ありません。 ・ イミュータブル性 Reduxは原則としてStateの値は変更してはならず、前のStateにActionを施したオブジェクトを返す仕組みでした。今回のようなものだとそれほど苦労しませんが、ネストが深いオブジェクトの場合はかなり苦労します。 ですが、Redux-ToolkitではImmerというライブラリが変更をイミュータブルにしてくれるので、直接変更するような書き方で問題ありません。 addActionを作成できたので、エクスポートします。「todoSlice.ts」の最後尾に次のコードを追加します。 //todoSlice.ts export const { add } = todoSlice.actions Sliceで追加する処理は書けたので、Containerでの処理を書いていきます。 「todoContainer. tsx 」でエクスポートしたaddActionをインポートし、addActionに追加したいTodoを加えて、Sliceに流す関数を作成します。 todosとargsの間に加えてください。 //todoContainer.tsx const maxID = todos.length ? todos.slice ( -1 ) [ 0 ] .id : 0 ; const dispatch = useDispatch (); const addTodo = ( title: string , content: string ) => { const newTodo : Todo = { id: maxID+ 1 , title: title , content: content , isCompleted: false , } dispatch ( add ( newTodo )) } maxIDはTodoリストの最大のIDを取得してきます。もし、Todoが0個の場合は0を返すようにします。 argsにaddTodo関数を追加して、「TodoPresenter. tsx 」に渡しましょう。 「TodoPresenter. tsx 」では、送信ボタンを押下時にaddTodo関数を実行するようにしたいです。 なので、addTodo関数を実行し、その後に入力内容を空にするsendTodo関数を作成します。その関数を送信ボタン押下時に実行させるように下記のコードを「TodoPresenter. tsx 」に追加します。 //TodoPresenter.tsx const sendTodo = () => { addTodo ( title , content ); setTitle ( "" ); setContent ( "" ); } //省略 < button type= "button" onClick = { () => addTodo ( title , content ) } > 送信 < /button > 送信ボタンを押すことでTodoを追加できるようになっているかと思います。 Todoの削除機能 追加処理と仕組みは同じです。 それぞれのTodoについている削除ボタンを押すと、リストから削除されるようにします。 Todo削除Actionは「remove」という名前にします。 「todoSlice.ts」のsliceのreducersにremoveActionを書き加え、そのActionをエクスポートします。 //todoSlice.ts remove: ( state , action: PayloadAction < number >) => { state.todos = state.todos.filter (( todo ) => todo.id !== action.payload ) } //省略 export const { add , remove } = todoSlice.actions 「TodoContainer. tsx 」にaddTodo関数と同様にremoveActionをインポートし、このActionをSliceに流すremoveTodo関数を作成します。 //TodoContainer.tsx const removeTodo = ( id: number ) => { dispatch ( remove ( id )) } argsにremoveTodo関数を渡して、「TodoPresenter. tsx 」では、削除ボタンを押したときに削除したいTodoのidを引数にしてremoveTodo関数を実行するようにします。 下記のように「TodoPresenter. tsx 」の削除ボタンを変更してください。 //TodoPresenter.tsx < button type= 'button' onClick = { () => removeTodo ( todo.id ) } > 削除 < /button > 削除ボタンを押すことでTodoを削除できるようになっているかと思います。 完了・未完了の切り替え機能 それぞれのTodoについている完了ボタンを押すと、タイトルの横の「未完了」テキストが「完了」テキストになるようにします。また、完了ボタンは「戻る」というテキストのボタンに変化します。 この戻るボタンを押すと、完了ボタンとは逆の操作をします。 今回も手順は同じです。まずは、Sliceのreducersに完了・未完了切り替えActionを作ります。 「updateComplete」という名前にします。Container側から対象のTodoのIDが送られてくることを想定して下記のようにします。 また、エクスポートもしておきます。 //todoSlice.ts updateComplete: ( state , action: PayloadAction < number >) => { state.todos = state.todos.map (( todo ) => todo.id === action.payload ? { ...todo , isCompleted: ! todo.isCompleted } : todo ) } //省略 export const { add , remove , updateComplete } = todoSlice.actions 「TodoContainer. tsx 」にtoggleCompleteActionをインポートし、このActionをSliceに流すtoggleComplete関数を作成します。 //TodoContainer.tsx const toggleComplete = ( id: number ) => { dispatch ( updateComplete ( id )); } argsにtoggleComplete関数を渡して、「TodoPresenter. tsx 」では、完了ボタンを押したときに対象のTodoのidを引数にしてtoggleComplete関数を実行するようにします。 下記のように「TodoPresenter. tsx 」の完了ボタンを変更してください。 //TodoPresenter.tsx < button type= 'button' onClick = { () => toggleComplete ( todo.id ) } > { todo.isCompleted ? "戻す" : "完了" } < /button > 完了ボタンを押すと、それぞれのTodoタイトルの横の「未完了」が「完了」に切り替わることが確認できると思います。 終わりに Redux-Toolkitを用いたTodoアプリの作成を通して、基本的な使い方や仕組みをご紹介させていただきました。 Reduxと比べると、State・Reducer・ActionをSliceで管理するというのが特徴的だったと思います。また、そのおかげでファイル数も少なく、記述量も少なくなりました。 Stateの更新もミュータブルにできるので単純で分かりやすい印象を受けたのではないでしょうか。 ここまで読んでいただきありがとうございました。 第3部ではRecoilの基礎について同じような形でまとめたので、一緒に読んでいただけると嬉しいです。 tech-blog.rakus.co.jp エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに こんにちは。 開発課のmoryosukeです。 OSS -DBの勉強をする過程で第二、第三正規形ってどんなことするんだっけ?そもそも部分関数従属、推移関数従属ってなんだっけ?と混乱することが多くありました。同じような方がいらっしゃいましたらぜひ参考にしてみてください。 目次 はじめに 目次 正規形とは 非正規形 第一正規形 第二正規形 部分関数従属 第三正規形 推移関数従属 まとめ 正規形とは データの重複をなくし整合的にデータを取り扱えるようにデータベースを設計することを、 データベースの正規化 と呼びます。 正規化を行っておくと、データの追加・更新・削除などに伴うデータの不整合や喪失が起きるのを防ぎ、メンテナンスの効率を高めることができます。 正規化には第一正規形から第五正規形がありますが、ほとんどの場合は第三正規化まで行えば、実務上は問題ないとされています。 そのため、ここでは、第三正規形までを紹介いたします。 非正規形 非正規系は正規化されていないデータです。 非正規形 注文番号 顧客ID 顧客名 電話番号 商品ID 商品名 単価 数量 金額 0001 0001 田中 000-0000-0000 0001 マルゲリータ 1,200 2 2,400 0002 てりやき 1,000 1 1,000 第一正規形 第一正規化は、非正規形のテーブルに次の作業を行います。 主キーを設定する 繰り返し現れる列のデータをグループ化して、別のテーブルに切り離す 導出項目(他の属性から算出できる項目)を削除する 今回の場合 注文番号を主キーとして設定します。 非正規系のテーブルから繰り返し現れる列を切り離し、下記のような「注文明細テーブル」にします。 金額は、単価✕数量から算出できる導出項目であるため、削除します。 注文テーブル 注文番号 (主キー) 顧客ID 顧客名 電話番号 0001 0001 田中 000-0000-0000 注文明細テーブル 注文番号 (主キー) 商品ID (主キー) 商品名 単価 数量 0001 0001 マルゲリータ 1,200 2 0001 0002 てりやき 1,000 1 第二正規形 第二正規形は、第一正規型のテーブルから部分関数従属属性であるものを除きます。 部分関数従属 まず、関数従属とは「ある属性(列)の値Xが決まると、別の属性の値Yが自動的に決まる」という関係です。 そして、部分関数従属は、「XがABからなる場合、AまたはBが決まるとYが決まる」という関係です。 今回の場合、注文明細テーブルの複合主キーの一部である商品IDが決まれば商品名と単価が決まる、部分関数従属となっています。 つまり、複合主キーの一部の列の値から導き出せる列があれば、それらを別のテーブルに分割するということです。 よって、以下の手順で第二正規化が行なえます。 商品名と単価は、第一正規形の注文明細テーブルの複合主キーの一部である商品IDにより決まる部分関数従属であるため、商品IDだけを主キーとして「商品ID→商品名、単価」となるように、「商品テーブル」に分割する 「商品テーブル」と「注文明細テーブル」を関連付けられるように、「注文明細テーブル」のIDは、「商品テーブル」を参照する外部キーとして設定する 注文テーブル 注文番号 (主キー) 顧客ID 顧客名 電話番号 0001 0001 田中 000-0000-0000 注文明細テーブル 注文番号 (主キー) 商品ID (主キー・外部キー) 数量 0001 0001 2 0001 0002 1 商品テーブル 商品ID (主キー) 商品名 単価 0001 マルゲリータ 1,200 0002 てりやき 1,000 第三正規形 第三正規化は、第二正規形から推移従属属性であるものを除きます。 推移関数従属 主キー以外の項目に従属する関係のことで、 「主キーXが決まるとYが決まり、Yが決まるとZが決まる」という関係です。 今回の場合、注文テーブルの非キー属性である顧客IDが決まると、顧客名、電話番号が決まるという関係を指します。 つまり、上記 推移関数従属部分を、顧客IDを主キーとした顧客テーブルに分割することによって、第三正規形となります。顧客テーブルと注文テーブルを関連付けられるように、注文テーブルの顧客IDは、顧客テーブルを参照する外部キーとして設定します。 注文明細テーブルと商品テーブルは、第二正規化からそのままです。 注文テーブル 注文番号 (主キー) 顧客ID (外部キー) 0001 0001 顧客テーブル 顧客ID (主キー) 顧客名 電話番号 0001 田中 000-0000-0000 まとめ 当初1つのテーブルだったものを正規化を行うことで以下の4つのテーブルに整理されました。 注文明細テーブル 注文番号 (主キー) 商品ID (主キー・外部キー) 数量 0001 0001 2 0001 0002 1 商品テーブル 商品ID (主キー) 商品名 単価 0001 マルゲリータ 1,200 0002 てりやき 1,000 注文テーブル 注文番号 (主キー) 顧客ID (外部キー) 0001 0001 顧客テーブル 顧客ID (主キー) 顧客名 電話番号 0001 田中 000-0000-0000 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは~nanchiuです。なんなん 私は前職(某SIベンダー)で新卒配属されてから1年半ほど VMware vSphereのテクニカルサポートをしていました。 その時の経験を活かして初心者でもわかるように VMware vSphereの主な機能や少しだけ仕組みに突っ込んだ話をしたいと思います。 この記事の対象者 VMware って何なん!?って感じの初心者 VMware vSphereの機能の概要は知っているけどもう少し仕組みを知りたい人 目次 この記事の対象者 目次 はじめに VMwareとは? 仮想化とは? VMware vSphereのメリット VMware vSphereの主なコンポーネント VMware vSphereの機能 ESXiの主な機能 スナップショット thinディスク オーバーコミット vCenter Serverを利用した主な機能 vMotion Storage vMotion vMotionおよびStorage vMotionの同時実行 仮想マシンテンプレート、クローン High Availability(HA) vSphere Fault Tolerance(FT) Hotadd DRS storage DRS vSAN   はじめに VMware とは? 読み方は ヴイエムウェア です。 VMware は仮想化を実現するための製品、もしくはその開発元である企業名を指します。 VMware の製品は様々なものがありますが、ここでは VMware の主要製品である VMware vSphereの機能や仕組みについてご紹介します。 仮想化とは? 機能紹介の前に、そもそも仮想化とはソフトウェアを利用してサーバなどのハードウェアリソース(CPU、メモリ、HDD)を、 論理的に統合や分割することができる技術のことです。 例えば1台のハードウェア(PCやサーバー)で複数のOSを動作させたりできます。 ソフトウェアによる仮想化は以下の種類があります。 用語について 物理HW:物理的なハードウェア。 ホストOS:ホストとなるOS。物理HWの上で動作する。 仮想HW:ソフトウェアによって作りだされた仮想的なハードウェア。 ゲストOS:仮想HWの上で動作するOS。 仮想マシン :仮想HW、ゲストOSをセットで 仮想マシン と呼びます。英語ではvirtual machineなので VM と呼ぶことも多いです。 ホスト型: Windows や Mac などのOSに専用のソフトウェア( VMware Workstation Player、 VirtualBox など)を入れ、その上でゲストとなるOSを動作させることができます。 ハイパーバイザー型: ハードウェア上にハイパーバイザーと呼ばれるソフトウェアを導入し、その上でOSを動作させることができます。 ホスト型との違いはホストとなるOSがないことです。 これによりホスト型と比べてホストOS分のオーバーヘッドがなくなります。 要するにその分処理が速いということになります。 コンテナ型: 近年よく聞きますよね。上記二つは仮想化ソフトウェアの上でゲストOSを動作させていますが、 コンテナはアプリとそのアプリが必要な ミドルウェア 、ライブラリ等がセットになったもの(コンテナ)を動作させています。 OSを動作させるにはハードウェアリソースを多く消費しますが、コンテナはアプリに必要なリソースしか消費しません。 ここだけ読むとコンテナを使えばええやん?と思うかもしれませんが、それぞれメリデメがありますし、用途によって選択肢が変わってきます。 実は VMware vSphereでコンテナを動かすこともできるのですが、この辺のネタだけでブログが書けそうなので今回はあまり触れないことにします。 VMware vSphereのメリット VMware の主要製品である VMware vSphereは上述したようにハイパーバイザー型の仮想化製品です。 一般的にはサーバーの仮想化として利用します。 サーバーの仮想化では以下のメリットがあります。 1台の物理サーバー上に複数の 仮想マシン を稼働させることができるため物理サーバーの台数が減ります。 このため、物理サーバーの管理コストやラック費用などの設備費用を低減させることができます。 ビジネスの変化に迅速に対応できるようになります。 仮想マシン に対して柔軟にリソースを追加できますし、 ハードウェアの調達を待つことなく新しく 仮想マシン を作ることができます。 レガシーなシステムを延命することができます。古いOSが最新のハードウェアで対応していない場合も 仮想化することで利用できるケースがあります。 さらに、 VMware vSphereが提供する機能で様々な業務の効率化、可用性の向上、コスト削減が見込めます。 利用できる機能については後述したいと思います。 VMware vSphereの主な コンポーネント 機能を紹介する前に前提知識として主な コンポーネント を紹介します。 主な コンポーネント vCenter Server : 管理 コンポーネント 。後述するHAやvMotionを実現するために必要です。 通常、ユーザはブラウザからvCenter Serverに接続してオペレーションを行います。 vCenter ServerはESXi上の 仮想マシン として作成することも可能です。 ESXi : VMware vSphereにおけるハイパーバイザです。主に 仮想マシン への動的なリソース割り当てを制御しています。 ※ストレージはローカルディスクを利用する、共有ストレージ装置を利用する、 VMware vSphereの機能であるvSANを利用する方法があります。 後述しますが、それぞれ利用できる機能が異なります。 VMware vSphereの機能 ESXiの主な機能 vSphereの主要な機能はvCenter Serverを導入することで利用できますが、なしでも使える便利機能を紹介します。 スナップショット スナップショット取得時の 仮想マシン の状態を保存することができます。 ゲストOS内で設定を変えた場合など、以前の状態に戻したい時に便利です。 thinディスク 仮想マシン のディスクをthinで作成しておくと、利用した分だけの容量が消費されます。 例えば、仮想ディスクを100GBで作成しておいても実際に利用している領域が20GBであればその分しか消費されません。 オーバーコミット CPUやメモリを物理サーバーのリソースのキャパシティを超えて 仮想マシン に割り当てることができます。 ただし、状況によっては 仮想マシン の性能が著しく劣化するため設計・運用には注意が必要です。 CPUのオーバーコミット 物理CPU 1コアを1仮想CPUとして マッピング (ハイパースレッディング有効時は論理スレッド) 。 仮想CPUの合計がESXiホストの搭載CPUを超える場合、非常に短い時間単位で交代しながら割り当てられます。 この時、よりCPU負荷の高い 仮想マシン が優先されますが制限や予約、優先度を 仮想マシン 単位でチューニング可能です。 メモリのオーバーコミット メモリもオーバーコミットが可能です。 以下の仕組みでメモリをやりくりしています。 透過的ページ共有(TPS) 仮想マシン 間の同一内容のメモリページを共有します。 ただし、セキュリティ的な懸念からデフォルト無効になっています。 バルーニング ゲストOSのインアクティブなメモリを強制 スワップ アウトさせます。 そうして空いたメモリ領域を別の VM で利用します。 メモリ圧縮 メモリを圧縮します。ディスクに スワップ するよりは1,000倍高速らしいです。 スワップ ディスクにメモリを スワップ します。 ディスクへのアクセスになるためメモリに比べるとめちゃくちゃ遅くなります。 また、メモリも制限や予約、優先度を 仮想マシン 単位で設定できます。 vCenter Serverを利用した主な機能 vCenter Serverを導入することで利用できるvSphereの目玉機能を紹介します。 ※ライセンスによって利用できる機能は変わってくるためその辺は公式サイトなどを参照してください。 vMotion 仮想マシン を起動したままダウンタイムなしで別のESXiホストに移動できる機能です。 いわゆるライブ マイグレーション のことです。 メモリの情報をコピーするのでOS上の処理もそのままの状態で移動できます。 物理的に違うホストに移動するのになぜダウンタイムなしで移動できるの!?と思ったかもしれませんが大まかな仕組みは以下です。 前提として、仮想ディスクは各ESXiホストからアクセス可能なストレージ(共有ストレージやvSAN)に格納されている場合の動作になります。 ①メモリをNW経由でコピー 移行元が稼働した状態ですので、都度メモリの更新が入りますが移行元と移行先で差分がなくなるまで転送します。 当然、ネットワークが遅かったり、移行元のメモリが頻繁に更新される場合はvMoitonの時間が長くなったり最悪失敗したりします。 ②移行先ESXiで対象 仮想マシン のファイルをロック 仮想マシン は カプセル化 (ファイル化)されています。 複数のESXiで更新しないよう単一のESXiで 仮想マシン のファイルはロックされており、ここでそれが移動先のESXiに切り替わります。 ③ RARP をL2スイッチに送信し、 MACアドレス テーブルを更新 NW経路の変更は TCP セッションより下の階層で処理されるため切断されません。 Storage vMotion 仮想マシン を停止することなく、ストレージデータ(仮想ディスク)を移動することが可能です。 ストレージのタイプに依存しないためローカルディスク、ストレージ装置間の移行もできます。 例えばストレージ装置のメンテナンス時や移行に利用できます。 vMotionおよびStorage vMotionの同時実行 共有ストレージ不要で別のESXiホストへオンラインのまま 仮想マシン とそのデータを移動できます。 仮想マシン テンプレート、クローン テンプレート: 仮想マシン からテンプレートイメージを作成し、複数の 仮想マシン に展開できます。 クローン: 仮想マシン をクローン(複製)できます。起動状態でも可能です。 同等の構成のサーバを複数作成したい場合はこれらの機能が役立つかと思います。 High Availability(HA) 物理サーバの障害で 仮想マシン が停止しても、リソースの空いているサーバで自動的に 仮想マシン を再起動(無停止ではない)します。 ダウンタイムを最小にすることが目的の機能です。 HAの設定にはvCenter Serverが必要ですが、ESXi間でハートビートにより死活監視をしているためvCenter Server障害時でも HAの機能は発動します。 vSphere Fault Tolerance(FT) 本番環境として稼働している 仮想マシン と同じ環境を、別のESXi上に復旧用の 仮想マシン としてコピーしています。 メモリの情報もフルsyncしているのでハードウェア障害時にも一切のダウンタイムなく、 仮想マシン をフェールオーバーする事が可能です。 Hotadd 仮想マシン を停止せずにデ バイス を追加認識させる機能です。 起動状態のままCPUなど追加できますが、ゲストOSが機能に対応している必要があります。 DRS 特定のESXiに負荷が集中した際、自動的に 仮想マシン を別のリソースの空いているESXiにvMotionで移動させ、 全体のロードバランスを行います。 移動対象はポリシーで設定できるため、例えばこの 仮想マシン とこの 仮想マシン は同一ESXiで稼働させない といった制御も可能です。 storage DRS I/O負荷に応じて最適なストレージへ 仮想マシン の元データを移動し、負荷を分散します。 vSAN 図は論理的なイメージです。 ローカルストレージを仮想化し複数の筐体で単一(共有)のデータストアを作成できます。 なお、vSAN自体はESXiに機能が含まれていますが、利用するにはvSAN用のライセンスが別途必要です。 柔軟に拡張でき、共有ストレージ装置を構築する必要がない。(vSANのみで共有ストレージ必須のHAなどの機能が利用可能) 必要なデータを他のESXサーバへ多重コピーするので RAID は不要。 vSANを利用すると 仮想マシン はESX1で動作しているが、 仮想マシン のデータはESXi2とESXi3に保存されているといった 構成も普通です。 データが自身のホストのローカルディスクにない場合、IOが遅くならないか?という懸念がでるかと思いますが 主に以下の構成により高速なIOを実現しています。 ディスクがキャッシュディスクとキャパシティディスクに分かれています。データの書き込みは高速なキャッシュディスクに。 後から容量の大きなキャパシティディスクに書き込まれます。 10Gbps以上のネットワーク。物理筐体を跨いで書き込み先のESXiのローカルディスクに書き込みを行うためネットワーク帯域は 10Gbps以上である必要があります。 以上で主要機能の説明は終わりです。 どんな機能があるか、どういった仕組みで実現されているのかなんとなくイメージがつきましたでしょうか。 参考になれば幸いです。なんなん   エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 https://rakus.hubspotpagebuilder.com/visit_engineer/ rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
技術広報の飯野です。 いつも ラク スエンジニアブログをお読みいただき、ありがとうございます! 先日(2022/11/9)開催となりました、今年度5回目の ラク スMeetup。 今回は、 カイゼン /チームビルディング/プロジェクトマネジメント をテーマに開催! 各プロダクト開発に携わる弊社のバックエンドエンジニアの3名が登壇しました。 なお、本イベントは以下のような方にオススメとなっております。 ・ プロダクトライフサイクル に合わせた課題と事例を聞きたい方 ・ SaaS 開発の品質や生産性の向上を目指している方 ・ローンチしたばかりのプロダクトのチームビルディングに悩んでいる方 ・新機能開発で手戻りを防ぎたい方 ・プロジェクトマネジメントの具体的な事例を知りたい方 ・ ラク スのプロダクト、組織に興味がある方 ・ SaaS 開発に携わるエンジニアの話が聞いてみたい方 イベント内容の詳細は以下をご確認ください。 rakus.connpass.com 発表内容のご紹介 新サービスのプロジェクト推進に向けた、トライ&エラー 楽楽精算の開発課題から学ぶ、改善取り組み 大規模案件における手戻りを防ぐ要件定義・開発事例 次回のラクスMeetupは? 当日のタイムテーブル 申し込み方法 ラクスのエンジニア/デザイナーと話をしてみたい方へ 終わりに 発表内容のご紹介 新サービスのプロジェクト推進に向けた、トライ&エラー 登壇:川﨑 智彦 [所属:楽楽明細開発2課/担当プロダクト: 楽楽電子保存 ] speakerdeck.com 発表内容 2022年1月、電子帳簿保存システム「楽楽電子保存」をローンチしました。 私が入社した2022年5月段階では、楽楽電子保存開発は立ち上がってから間もないため手探りで システム開発 を進めている状況でした。 まだまだ試行錯誤中ですが、プロジェクト全体で取り組んできた内容についてお話しします。 フロントエンド/バックエンド/デザイナの役割 チーム間のコミュニケーション タスクや進捗状況の 見える化 オフショアチームへの作業分担 楽楽精算の開発課題から学ぶ、改善取り組み 登壇:坂田 光 [所属:楽楽精算開発課/担当プロダクト: 楽楽精算 ] speakerdeck.com 発表内容 楽楽精算開発チームはお客様により良いサービスをお届けすべく機能開発に取り組むとともに、品質や生産性の向上を目指して日々改善を行っています。 本発表では以下内容を中心にご紹介します。 直近の開発事例で浮き彫りになった課題 チーム全体の課題 息が長いサービスであるが故のレガシーな課題 それぞれに対する改善事例 大規模案件における手戻りを防ぐ要件定義・開発事例 登壇:西角 知佳 [所属:楽楽勤怠開発1課/担当プロダクト: 楽楽勤怠 ] speakerdeck.com 発表内容 楽楽勤怠では7月に「 工数 管理」という機能をリリースしました。 工数 管理はそれだけで1つのサービスになるほど作り込みの幅の広い機能であり、限られた期間の中で何を実現して何を実現しないかのスコープの決定が重要となりました。 また、スコープ調整した結果の開発期間は6ヶ月と大規模なものとなり、ビジネスサイドからの実現希望時期に応えるうえで大きな手戻りは許されない状況下での開発となりました。 このような状況で期日内でのリリースを無事完遂した事例を、要件定義者 兼 実装者の視点から紹介します。 イベント当日はたくさんの方にご視聴、そしてコメントやご質問をいただきました。 お申し込み、ご参加いただいた皆さま本当にありがとうございました! 次回の ラク スMeetupは? 次回の ラク スMeetupは、2022/12/7(水)に開催します。 タイトルは 『システムを”楽”に運用したい!〜自動化, CI/CDの道〜』 です。 弊社インフラ開発部のエンジニア3名が登壇します。 当日のタイムテーブル 当日のタイムテーブルは以下の通りです。 1つでもご興味のある内容がございましたら、お気軽にご参加ください。 内容 所属:登壇者 18:50 入室開始(途中参加OK!) 19:00 オープニング 19:10 SRE課が開発中システムのCI/CDで取り組んでいるGitOpsの話 SRE課:今本 光 19:35 ラク スサービスを支えるAnsible活用のこれまでとこれから 大阪インフラ開発課:上畑 圭史 20:00 メール配信サービス「blastmail」の M&A 後の軌跡 ~初めてのシステムに向き合う~ 東京インフラ開発2課/課長:柏木 達仁 20:25 クロージング 申し込み方法 以下3つのメディアをご用意しております。 ◆自社申し込みサイト career-recruit.rakus.co.jp ◆connpass rakus.connpass.com ◆TECHPLAY techplay.jp 皆様のご参加、お待ちしております! ラク スのエンジニア/デザイナーと話をしてみたい方へ 弊社では、一緒に働くエンジニア/デザイナーを積極的に募集しております! 現在募集している職種は、以下サイトよりご確認ください。 career-recruit.rakus.co.jp 「まだ応募する段階では…」 という方は、是非 カジュアル面談 もご検討ください。 【こんな方におすすめ】 ポジションが経験にマッチするか確認したい 働き方/環境・体制/事業・プロダクト/文化/制度を詳しく知りたい 応募前に選考の概要を聞きたい(人物像、基準など) エンジニア・デザイナーの人となりを知りたい 以下申し込みフォームとなります。 rakus.hubspotpagebuilder.com 「イベントに登壇していた社員と話してみたい」 といったご要望がございましたらその旨をご記入の上、ぜひお気軽にお申し込みください。 終わりに ラク スMeetupでは現場最前線のエンジニア/デザイナーから ラク スの SaaS 開発ならではの技術・運用ノウハウや、 新しい取り組みの成果や失敗談、プロダクト開発/運用で得た知見等の技術情報をお届けしております。 今後も様々なイベントを計画しております、ぜひご参加ください。 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申し込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申し込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは! ラク ス入社1年目の koki _matsuraです。 本日は、Reduxの基本的な状態管理や仕組みをTodoアプリ作成を通して、ご紹介させていただきます。 この記事は 「Reactの状態管理ライブラリ基礎学習」全3部作の1部目 です。 Reactの状態管理ライブラリを勉強している方、状態管理ライブラリについて簡単に知りたい方などのお役に立てればなと書かせていただきました。 アジェンダ は以下の通りです。 Reduxとは 概要 構成図 Todoアプリ作成 仕様説明 プロジェクト作成 初期設定 ディレクトリ構成 Stateの定義 Reducerの定義 Storeの定義 ActionCreatorの定義 TodoContainer.tsxの定義 TodoPresenter.tsxの定義 Providerの設定 アプリの起動 Todoの追加機能 Todoの削除機能 完了・未完了の切り替え機能 終わりに Reduxとは 概要 JavaScript によるSPAは複雑化し続けており、Reactが導入され、Viewとロジック部分を切り離せはしましたが、State(状態)の管理は開発者に委ねられています。 Reduxでは、このStateの問題に下記の3原則を取り入れ、状態変化の流れを制限することで解決します。 - Single source of truth (ソースは一つだけ) アプリケーションの状態は一つのStore内に一つのオブジェクトでツリー型で格納されます。 状態が一つのStoreで管理されるため、 デバッグ や開発が簡単になります。 - State is read-only (状態は読み取り専用) 状態を変更できるのはActionを持ったオブジェクトのみです。つまり、ビューやコールバックが状態を直接的に変更することはできません。 - Changes are made with pure functions (変更は純粋関数で行われる) アクションがどのようにStateを変更するかはReducerに記述されます。 Reducerは前のStateとActionより、次の状態を返す、副作用のない純粋な関数です。注意点として、状態を変更しているのではなく、新しい状態を返しています。 また、開発の際にはアプリケーションで一つのReducerを用意しておき、巨大化してくればReducerを分割することもできます。ただ、分割方法はユーザで決めなければならないのが欠点かもしれません。 構成図 下図はReduxがどのように状態管理をしているかを簡単に示したものになっています。本来であれば、ComponentとReducerの間には API などの処理を行うMiddlewaresが挟まりますが、今回は省かせていただきました。 ComponentはユーザーのイベントからActionCreatorにActionの生成を依頼し、生成されたActionをReducerに対し、dispatchします。Reducerは前回のStateとdispatchされたActionから新たなStateを作り出し、それをStateに返します。StateはComponentに対して、更新を通知し、新しいStateを取得するという流れになっています。 また、Reduxが参考にしているFluxという デザインパターン ではActionCreatorがActionの生成・Actionのdispatchまでを担当するのが一般的なのですが、ReduxではテストのしやすさからActionCreatorはActionを生成するだけの役割の方がいいかもしれません。 Todoアプリ作成 仕様説明 Todoアプリを作成する前にTodoアプリの仕様と構成を説明します。 構成は以下の画像のようになります。 入力フォームと送信ボタン、Todoのリストを載せる部分で構成されます。 また、それぞれのTodoには内容に加え、完了ボタン、削除ボタンがあります。 仕様を説明します。 ・Todoの追加 画像上部のタイトル・内容の入力フォームに適当なテキストを入力し、送信ボタンを押すことでTodoリストに入力したTodoが追加されます。 ・Todoリストの表示 画像下部のTodoリストは古いもの(ID昇順)から順に表示されます。最も新しいものは最後尾に表示されます。 ・Todoの完了 それぞれのTodoについている完了ボタンを押すと、該当するTodoが未完了から完了に変化します。 また、完了しているTodoには「戻す」ボタンが表示されており、これは完了ボタンの逆の働きをします。 ・Todoの削除 それぞれのTodoについている削除ボタンを押すと、該当するTodoがリストから削除され、表示からも消えます。 以上が今回作成していくTodoアプリの仕様になっています。 プロジェクト作成 プロジェクトの作成は下記のコマンドを入力します。 私はプロジェクト名を「redux-todo」としましたが、お好きなプロジェクト名をつけていただいて問題ありません。 npx create-react-app [プロジェクト名] --template typescript 初期設定 プロジェクト作成後、Reduxを使うために必要になるので、下記のコマンドで作成したプロジェクトに移動して、reduxとreact-reduxをインストールします。 cd redux-todo npm i redux react-redux ディレクト リ構成 Reduxを使う準備もできましたので、次は ディレクト リを構成します。 Reduxにおける ディレクト リ構成は様々ありますが、今回は役割がわかりやすいように store action(actionCreator) state reducer に分けた構成にします。 「src」 ディレクト リ以外は特に触らないので、「src」以下の画像を載せます。 app ディレクト リとcommon ディレクト リ、features ディレクト リ、features ディレクト リの中にtodos ディレクト リを作成します。 app ディレクト リ App. tsx を移動 store.tsを新規作成 App. tsx を移動させた理由として、ReduxのStoreにアクセスできるのはProviderで囲われた コンポーネント だけで、「App. tsx 」の中身を囲って、Todoアプリ全体で状態を共有したかったからです。同じ ディレクト リに移動させることでどの コンポーネント でProviderが使われているか分かりやすくなります。 common ディレクト リ todo.type.tsの新規作成 rootState.type.tsの新規作成 「todo.type.ts」は今回のTodoアプリで出てくるTodoのタイプを定義し、「rootState.type.ts」には管理している全てのStateのタイプを定義しています。色々なファイルから使われると思われるのでcommon ディレクト リに作成しました。 features/todos ディレクト リ todoAction.tsの新規作成 todoReducer.tsの新規作成 todoState.tsの新規作成 TodoContainer. tsx の新規作成 TodoPresenter. tsx の新規作成 「todoAction.ts」は構成図で表すと、ActionCreatorの役割を果たします。 「todoState.ts」は状態の定義、「todoReducer.ts」はActionを受けて、状態を変更する役割を果たします。 「TodoContainer. tsx 」はTodoアプリのロジック部分を、「TodoPresenter. tsx 」は表示部分を担当します。 Stateの定義 最初はStateを定義します。 Stateは状態のことです。 StateはTodoのリストを管理するので、Todo型の配列を初期値にします。 Todo型はまだ定義していないので、「todo.type.ts」に下記のように定義します。 //todo.type.ts export type Todo = { id : number ; title : string ; content : string ; isCompleted : boolean ; } それぞれのTodoは「id」「title」「content」「isCompleted」を持ちます。 Todo型を定義できたので、Stateを「todoState.ts」に下記のように定義します。 適当なデータを2つ入れておきます。 //todoState.ts import { Todo } from "../../common/todo.type" ; export const state = {[ { id: 1 , title: "テスト1" , content: "テスト1の内容" , isCompleted: false } , { id: 2 , title: "テスト2" , content: "テスト2の内容" , isCompleted: false } ] as Todo []} Reducerの定義 Stateが定義できたので、Reducerを定義します。 今回のTodoアプリの仕様では、「追加」「削除」「完了・未完了のスイッチ」の機能が必要ですが、一旦、何もしないReducerにしておきます。 //todoReducer.ts import { state as initialState } from "./todoState" ; export const todosReducer = ( state = initialState , action: any ) => { return state } Reducerでは、第一引数に前のState、第二引数にActionを受け取ります。 ActionはActionCreatorで作成されるもので「type」を必ず持っており、必要に応じて、「payload」を持ちます。 Storeの定義 状態管理の元となるStoreを定義します。「store.ts」に下記のように書きます。 //store.ts import { legacy_createStore as createStore } from 'redux' import { todosReducer } from '../features/todos/todoReducer' export const store = createStore ( todosReducer ) createStoreに引数でReducerを入れることでstoreが出来上がります。 注意点 : 現在、createStoreは公式から推奨されていないので、1文目のインポート文を入れないとエラーが起きます。 ActionCreatorの定義 ActionCreatorは名前の通り、Actionを作る役割をします。 Actionを作る役割と言っても、typeとpayloadをオブジェクトで返すだけです。 typeの名前とpayloadの型がReducerのものと合わせる必要がありますが、まだReducerの方で処理を書いていないので、こちらを基準にしていきます。 下記のようにしましょう。 //todoAction.ts import { Todo } from "../../common/todo.type" ; /** Todoを加えるアクションを返す */ export const addTodoAction = ( newTodo : Todo ) => { return { type : "ADD" , payload: newTodo } } /** Todoを変更するアクションを返す */ export const toggleCompleteAciton = ( id : number ) => { return { type : "TOGGLE_COMPLETE" , payload: id } } /** Todoを削除するアクションを返す */ export const removeTodoAction = ( id : number ) => { return { type : "REMOVE" , payload: id } } TodoContainer. tsx の定義 このファイルではTodoアプリのロジック部分を担当します。 useSelectorを使うことでStateを取得できます。Stateの型はRootStateという名前にします。 RootStateとTodoPresenterはまだ定義していないためエラーが出ていても問題ありません。 //TodoContainer.tsx import { useSelector } from "react-redux" export const TodoContainer = () => { const todos = useSelector (( state: RootState ) => state.todos ) const args = { todos , } return < TodoPresenter { ...args } / > } 「rootState.type.ts」に下記のようにRootState型を定義します。 //rootState.type.ts import { store } from "../app/store" ; export type RootState = ReturnType <typeof store.getState > 「store.getState」はインポートしたStoreから全てのStateを取得できます。その型をRootStateに入れています。 今回の場合はtodosのみを管理しているためToDoのリスト型でも問題はなかったのですが、管理する状態が複数になった時のためにこのような型を紹介させていただきました。 このRootState型をTodoContainerにインポートすれば、型エラーはなくなります。 TodoPresenter. tsx の定義 このアプリではTodoアプリの表示部分を担当します。 Todoリストを表示します。 まずは、色々な機能を作る前に基盤を作りたいので下記のようなコードにします。 //TodoPresenter.tsx import React , { useState } from "react" import { Todo } from "../../common/todo.type" type TodoPresenterProps = { todos : Todo [] } export const TodoPresenter : React.FC < TodoPresenterProps > = ( { todos , } ) => { const [ title , setTitle ] = useState ( "" ); const [ content , setContent ] = useState ( "" ); return ( <> < form > < label > タイトル: < input type= "text" value = { title } onChange = { e => setTitle ( e.target.value ) } / > < /label > < label > 内容: < input type= "text" value = { content } onChange = { e => setContent ( e.target.value ) } / > < /label > < button type= "button" > 送信 < /button > < /form > < div >------------------------ - < /div > < h1 > Todoリスト < /h1 > { todos.map (( todo : Todo )=> { return ( < React.Fragment key = { todo.id } > < div > { todo.title } : { todo.isCompleted ? "完了" : "未完了" } < /div > < div > 内容: { todo.content } < /div > < button type= 'button' > { todo.isCompleted ? "戻す" : "完了" } < /button > < button type= 'button' > 削除 < /button > < /React.Fragment > ) } ) } < / > ) } 入力部にはタイトルと内容の入力フォームとまだ機能のついていない送信ボタンを配置しています。 出力部にはTodoリストをmap関数で出力しています。それぞれのTodoにつくボタンも現時点では機能がついていません。 一旦、これで置いておきます。 Providerの設定 必要なファイルはすべて完了しました。しかし、これだけでは状態を管理できません。 ReduxではStateを共有したい コンポーネント をProvdierで囲むことで機能します。 今回の場合では、TodoContainer内だけでStateを共有したいです。 なので、「App. tsx 」の元のコードを消して、次のようなコードに変えてください。 //App.tsx import React from 'react' ; import { Provider } from 'react-redux' import { store } from "./store" import { TodoContainer } from '../features/todos/TodoContainer' ; function App () { return ( < div className = "App" > < Provider store = { store } > < TodoContainer / > < /Provider > < /div > ); } export default App ; これでTodoContainer内でStateの情報を共有できるようになりました。 アプリの起動 下記のコマンドで起動してみましょう。 npm start 自動で開くと思いますが、開かない方は「 http://localhost:3000/ 」にアクセスしてください。 次のようにStateを定義したときに入れたサンプルデータが2件分、表示されていればうまくいっています。 Todoの追加機能 送信ボタンを押すと、Todoを追加できるようにします。 手順を説明します。 ContainerでTodoを加えるActionをReducerに流す関数を作成 Presenterで送信ボタン押下時に1で作成した関数を実行するコードを加える ReducerでActionに応じた処理をするコードを加える Todoを加えるActionをReducerに流す関数の名前は「addTodo」にします。次のコードを「TodoContainer. tsx 」のtodosとargsの間に加えてください。 //TodoContainer.tsx const maxID = todos.length ? todos.slice ( -1 ) [ 0 ] .id : 0 ; const dispatch = useDispatch (); const addTodo = ( title: string , content: string ) => { const newTodo : Todo = { id: maxID+ 1 , title: title , content: content , isCompleted: false } dispatch ( addTodoAction ( newTodo )); } maxIDはTodoリストの最大のIDを取得してきます。もし、Todoが0個の場合は0を返すようにします。 argsにaddTodo関数を追加して、「TodoPresenter. tsx 」に渡しましょう。 「TodoPresenter. tsx 」では、送信ボタンを押下時にaddTodo関数を実行するようにしたいです。 なので、addTodo関数を実行し、その後に入力内容を空にするsendTodo関数を作成します。その関数を送信ボタン押下時に実行させるように下記のコードを「TodoPresenter. tsx 」に追加します。 //TodoPresenter.tsx const sendTodo = () => { addTodo ( title , content ); setTitle ( "" ); setContent ( "" );   } //省略 < button type= "button" onClick = { () => addTodo ( title , content ) } > 送信 < /button > これで、送信ボタンを押下時に、addTodo関数を実行できます。 ReducerでこのAction(type: "ADD", payload: newTodo)に合う処理を書きます。 「todoReducer.ts」を次のように書き換えます。 //todoReducer.ts export const todosReducer = ( state = initialState , action : any ) => { switch ( action. type) { case "ADD" : return { todos: [ ...state.todos , action.payload ] } default : return state ; } } Reducerでは、action.typeを見て、処理を変えます。 action.typeは「todoAction.ts」で定義したものと一致させないといけません。 また、注意点としてReduxの原則にも書いてありますが、Stateを直接変更するのではなく、前のStateとActionから新しいStateを作り出すようにします。これが個人的に少し躓きやすい点かなと思います。 これで、追加の処理が書けました。実際に、入力部にタイトルと内容を入力して送信ボタンを押すと、既存のリストの下に追加されていることが確認できると思います。 Todoの削除機能 追加処理と仕組みは同じです。 それぞれのTodoについている削除ボタンを押すと、リストから削除されるようにします。 「TodoContainer. tsx 」にaddTodo関数と同様にremoveTodo関数を次のように作ります。 //TodoContainer.tsx const removeTodo = ( id: number ) => { dispatch ( removeTodoAction ( id )) } argsにremoveTodo関数を渡して、「TodoPresenter. tsx 」では、削除ボタンを押したときに削除したいTodoのidを引数にしてremoveTodo関数を実行するようにします。 下記のように「TodoPresenter. tsx 」の削除ボタンを変更してください。 //TodoPresenter.tsx < button type= 'button' onClick = { () => removeTodo ( todo.id ) } > 削除 < /button > 削除ボタン押下時に、removeTodoが実行されるようになったので、ReducerでこのAction(type: REMOVE, payload: id)に合う処理を書きます。 次のようにswitch分にcaseを増やすような形で書いてください。 //todoReducer.ts case "REMOVE" : return { todos : state.todos.filter (( todo ) => todo.id !== action.payload ) } これで、削除ボタンを押すと、該当のTodoがリストから消えるようになります。 完了・未完了の切り替え機能 それぞれのTodoについている完了ボタンを押すと、タイトルの横の「未完了」テキストが「完了」テキストになるようにします。また、完了ボタンは「戻る」というテキストのボタンに変化します。 この戻るボタンを押すと、完了ボタンとは逆の操作をします。 「TodoPresenter. tsx 」の完了ボタンとタイトル横のテキストのコードを見てみると、todo.isCompletedで切り替えられることがわかります。 なので、isCompletedを切り替えられる関数を作りましょう。 //TodoPresenter.tsx < div > { todo.title } : { todo.isCompleted ? "完了" : "未完了" } < /div > < div > 内容: { todo.content } < /div > < button type= 'button' > { todo.isCompleted ? "戻す" : "完了" } < /button > まずは、「TodoContainer. tsx 」にtoggleComplete関数を次のように作ります。 //TodoContainer.tsx const toggleComplete = ( id: number ) => { dispatch ( toggleCompleteAciton ( id )) } argsにtoggleComplete関数を追加し、下記のように「TodoPresenter. tsx 」の完了ボタンを押下時にtoggleComplete関数が実行するようにします。 //TodoPresenter.tsx < button type= 'button' onClick = { () => toggleComplete ( todo.id ) } > { todo.isCompleted ? "戻す" : "完了" } < /button > 最後はReducerでActionを受け取り、isCompletedを切り替える処理を書きましょう。 次のコードをswitch文のcaseとして追加することで実装できます。 //todoReducer.ts case "TOGGLE_COMPLETE" : return { todos: state.todos.map (( todo ) => { if ( todo.id !== action.payload ) return todo return { ...todo , isCompleted : ! todo.isCompleted } } ) } 完了ボタンを押すと、それぞれのTodoタイトルの横の「未完了」が「完了」に切り替わることが確認できると思います。 終わりに これで仕様通りのTodoアプリをReduxを使って作成できました。 かなり定義するものが多く、ファイル数が多いなと思われたのではないでしょうか。 私自身も、最初使った時はそのように感じました。 Todoアプリのように小さい規模のものだとReduxは少し冗長的で面倒に感じるのですが、大きな規模のアプリになっていくと、それぞれの役割に細かく分け、単方向なデータフローで状態を管理する構成の恩恵を受けやすくなるのかもしれません。 また、今回はそれぞれの役割が分かりやすくなるようにわざとファイルを細かく分けていたのですが、StateとAction、Reducerは密な関係になることが多いので、一つのファイルで管理することもあります。 今回はTodoアプリを通して、Reduxの基本的な仕組みや特徴を紹介させていただきました。 第2部ではReduxを使いやすくしたRedux-Toolkitについて、第3部では実験段階の状態管理ライブラリRecoilについて同じようにTodoアプリ作成を例に紹介させていただいています。良ければ下記のリンクから「Redux-Toolkit編」「Recoil編」も読んでいただけると嬉しいです。 tech-blog.rakus.co.jp tech-blog.rakus.co.jp エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに こんにちは C2ZTAk6 です。 日々管理しているシステムで、サービス停止が発生した際、原因究明を行う前に、サービスの自動復旧が出来る仕組みはないかと考えたことはないでしょうか。 今回は、 Zabbixという オープンソース ・ソフトウェア( OSS )のアクションという設定を活用し、サービス停止が発生した際に自動で対象のサービスを復旧出来る仕組み について、お話させて頂ければと思います。 目次 はじめに 目次 Zabbixとは Zabbixのアクションとは Zabbix自動復旧について メリット デメリット Zabbixアクションの設定をしてみよう! 前提条件 sudo権限を付与 アクション設定 テスト 概要 最後に Zabbixとは Zabbixとは、 オープンソース ・ソフトウェア( OSS )の統合監視ソフトウェア です。 サーバの死活監視、リソース(Cpu/Memory/Disk)監視、プロセス監視、ネットワーク監視、ログ監視だけでなく、各種 ミドルウェア まで幅広い監視に対応します。 Zabbixのアクションとは アクションとは、名前の通り「行動」のことを指し、アクションの設定では、主に障害発生時の「異常」をトリガーとして、「自動的に何かをさせる」という設定を行うことが出来ます。 例:「 httpd サービス停止が発生した場合に、アラートメールを運用担当者へ通知する」 Zabbix自動復旧について メリット ①サーバログイン不要! Zabbixアクションを活用し、サービス停止のアラートが発報されたことをトリガーにサービス再起動の仕組みを導入することで、夜間や休日に障害が発生しても、いちいち商用サーバにログインして、復旧手順を元にコマンドを実行し、サービス復旧をさせる必要がなくなる。 ②運用コスト削減! Zabbixアクションで障害が自動的に復旧するので、運用担当者の運用コストを減らすことが出来る。 ③属人化防止! 障害復旧手順が複雑で、障害を解消できる人が属人化していた場合はZabbixアクションの自動復旧の仕組みを導入することで、属人化を防ぐことも出来る。 デメリット ①初期費用発生 Zabbixアクションを活用した自動復旧の仕組みを導入する際は、初期導入のプロジェクト 工数 などの費用がかかる。 ※導入時は、担当者の 工数 が発生し費用はかかるが、導入が完了すれば、以降の運用 工数 削減が見込める。 Zabbixアクションの設定をしてみよう! Zabbixアクションを活用し、 httpd プロセス自動復旧の設定をしていきます! 前提条件 監視サーバ OS: Red Hat 系 ミドルウェア :Zabbixがインストールされていること 監視設定: httpd プロセス監視のアイテム、トリガーが設定されていること 監視対象サーバ OS: Red Hat 系 ミドルウェア :ZabbixAgent、 apache がインストールされていること sudo権限を付与 ・監視対象サーバ側でコマンドを実行 [ root@bweb11 ~ ] # chmod 660 /etc/sudoers [ root@bweb11 ~ ] # echo " zabbix ALL=NOPASSWD: ALL " >> /etc/sudoers [ root@bweb11 ~ ] # echo " zabbix ALL=NOPASSWD: /etc/systemd/system/httpd.service " >> /etc/sudoers [ root@bweb11 ~ ] # chmod 440 /etc/sudoers [ root@bweb11 ~ ] # cat /etc/sudoers | tail -n 2 zabbix ALL =NOPASSWD: ALL zabbix ALL =NOPASSWD: /etc/systemd/system/httpd.service ※最後尾に設定が追加されていることを確認してください [ root@bweb11 ~ ] # cat /etc/zabbix/zabbix_agentd.conf | grep EnableRemoteCommands = 1 EnableRemoteCommands = 1 ※リモートコマンドが実行出来るように、 1 になっていることを確認する  設定が 1 になっていなければ、修正のうえ、zabbix-agentを再起動してください。 アクション設定 ・監視サーバ側でアクションの設定を行っていきます ①設定 > アクション > アクションの作成 を押下。 ②アクション:以下項目を入力 項目 名前: auto restart httpd service ※ 命名規則 については、分かりやすい名前で入力してください。 実行条件:ラベルA、名前:トリガー名含む Apache processes is down ※トリガーとして設定されている情報を入力する必要がある。今回は既にトリガーとして、設定済みとなる「 Apache processes is down 」を入力させて頂きます。 ③実行内容 > 追加を押下。 ④実行内容の詳細:以下項目を入力後、Addを押下。 項目 実行内容のタイプ:リモートコマンド ターゲットリスト: bweb11.mdomain ※上記はテスト機です。こちらについては、各自変更してください。 タイプ:カスタム スクリプト 次で実行:Zabbixエージェント コマンド: sudo systemctl restart httpd ⑤設定完了後、追加を押下。 テスト 概要 監視対象サーバにログインして、 httpd プロセスを手動で停止し、3分後の監視で httpd プロセスが停止したことによるトリガー(「 Apache processes is down 」)発報後、Zabbixアクションが動作し、監視対象サーバの httpd プロセスが起動されていることを確認する。 ①監視対象サーバ側でコマンドを実行 手動で httpd プロセスを停止する [ root@bweb11 ~ ] # systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded ( /etc/systemd/system/httpd.service ; enabled ; vendor preset: disabled ) Active: active ( running ) since Mon 2022-10-31 17:59:41 JST; 1min 24s ago Process: 1984892 ExecStop =/usr/local/vanguard/apache/bin/apachectl stop ( code =exited , status = 0 /SUCCESS ) Process: 1985084 ExecStart =/usr/local/vanguard/apache/bin/apachectl start ( code =exited , status = 0 /SUCCESS ) Main PID: 1985088 ( httpd ) Tasks: 9 ( limit: 10580 ) Memory: 13 .6M CGroup: /system.slice/httpd.service tq1985088 /usr/local/vanguard/apache/bin/httpd -k start tq1985090 /usr/local/vanguard/apache/bin/httpd -k start tq1985091 /usr/local/vanguard/apache/bin/httpd -k start tq1985092 /usr/local/vanguard/apache/bin/httpd -k start tq1985093 /usr/local/vanguard/apache/bin/httpd -k start tq1985094 /usr/local/vanguard/apache/bin/httpd -k start tq1985095 /usr/local/vanguard/apache/bin/httpd -k start tq1985096 /usr/local/vanguard/apache/bin/httpd -k start mq1985097 /usr/local/vanguard/apache/bin/httpd -k start 10 月 31 17:59:41 bweb11.mdomain systemd [ 1 ] : Starting The Apache HTTP Server... 10 月 31 17:59:41 bweb11.mdomain systemd [ 1 ] : Started The Apache HTTP Server. [ root@bweb11 ~ ] # systemctl stop httpd [ root@bweb11 ~ ] # systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded ( /etc/systemd/system/httpd.service ; enabled ; vendor preset: disabled ) Active: inactive ( dead ) since Mon 2022-10-31 18:04:37 JST; 1s ago Process: 1985282 ExecStop =/usr/local/vanguard/apache/bin/apachectl stop ( code =exited , status = 0 /SUCCESS ) Process: 1985178 ExecStart =/usr/local/vanguard/apache/bin/apachectl start ( code =exited , status = 0 /SUCCESS ) Main PID: 1985182 ( code =exited , status = 0 /SUCCESS ) 10 月 31 18:01:18 bweb11.mdomain systemd [ 1 ] : Starting The Apache HTTP Server... 10 月 31 18:01:18 bweb11.mdomain systemd [ 1 ] : Started The Apache HTTP Server. 10 月 31 18:04:36 bweb11.mdomain systemd [ 1 ] : Stopping The Apache HTTP Server... 10 月 31 18:04:37 bweb11.mdomain systemd [ 1 ] : httpd.service: Succeeded. 10 月 31 18:04:37 bweb11.mdomain systemd [ 1 ] : Stopped The Apache HTTP Server. ②監視サーバ側で確認 ダッシュ ボードから、設定したアクションが動作したことを確認する ③監視対象サーバ側でコマンドを実行 httpd プロセスが、自動で復旧していることを確認する [ root@bweb11 ~ ] # systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded ( /etc/systemd/system/httpd.service ; enabled ; vendor preset: disabled ) Active: active ( running ) since Mon 2022-10-31 18:05:18 JST; 31s ago Process: 1985282 ExecStop =/usr/local/vanguard/apache/bin/apachectl stop ( code =exited , status = 0 /SUCCESS ) Process: 1985363 ExecStart =/usr/local/vanguard/apache/bin/apachectl start ( code =exited , status = 0 /SUCCESS ) Main PID: 1985367 ( httpd ) Tasks: 9 ( limit: 10580 ) Memory: 13 .7M CGroup: /system.slice/httpd.service tq1985367 /usr/local/vanguard/apache/bin/httpd -k start tq1985370 /usr/local/vanguard/apache/bin/httpd -k start tq1985371 /usr/local/vanguard/apache/bin/httpd -k start tq1985372 /usr/local/vanguard/apache/bin/httpd -k start tq1985373 /usr/local/vanguard/apache/bin/httpd -k start tq1985374 /usr/local/vanguard/apache/bin/httpd -k start tq1985375 /usr/local/vanguard/apache/bin/httpd -k start tq1985376 /usr/local/vanguard/apache/bin/httpd -k start mq1985377 /usr/local/vanguard/apache/bin/httpd -k start 10 月 31 18:05:18 bweb11.mdomain systemd [ 1 ] : Starting The Apache HTTP Server... 10 月 31 18:05:18 bweb11.mdomain systemd [ 1 ] : Started The Apache HTTP Server. 最後に いかがでしたでしょうか。 今回はZabbixアクションを活用し、サービス自動復旧の仕組みについて、ご紹介させて頂きました。 本記事がITを学ぶ方にとって、少しでも助けになれば幸いです。 最後までお読みいただきありがとうございました。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに TextField RadioGroup SelectForm CheckboxGroup DatePicker コンポーネント使用側実装例 おわりに 本記事を執筆するにあたって、 マナリンク Tech Blog運営 さんの React Hook Form(v7)を使ったコンポーネント設計案 piyoko さんの MUI v5 + React Hook Form v7 で、よく使うコンポーネント達を連携してみる という記事を参考にさせていただきました。いつも非常にわかりやすい記事をありがとうございます。 はじめに こんにちは、 ラク スフロントエンド開発課の斉藤です。 React Hook Form v7 + MUI v5 + zod v3を使ったよく使う コンポーネント の実装例を調査しており、 こちらの記事 を参考に実装を進めてみました。しかし RadioGroup や DatePicker を atom 化しようとすると何点かハマりポイントがあったので、どなたかの参考になればと思い本記事を執筆するに至りました。 実装例を紹介する前に各 コンポーネント を実装するにあたって考慮したことをまとめておきます。React Hook Form(以下RHF)を用いたうえで扱いやすい コンポーネント はどういったものかを考えたとき、以下の条件を満たすと良いのではないかと考えました。 コンポーネント とバリデーションロジックが切り離されている マナリンクさんの記事のように コンポーネント がview層とロジック層に分かれている コンポーネント を使用する側はnameとRHFのcontrolプロパティを渡すだけで値を管理できる CheckboxGroupはチェックされたCheckboxの値をstring型の配列で返すようにする RadioGroup、SelectFormは選択された値をstring型で返すようにする DatePickerは値をDate型で返す これらを満たすように各 コンポーネント を実装してみたので、紹介していきます。 今回紹介する環境はcreate viteで作っています。 yarn create vite yarn add react-hook-form @hookform/resolvers @mui/material @emotion/react @emotion/styled zod TextField TextField に関しては 参考記事 とほぼ同じ実装です。 import { FormHelperText , TextField as MuiTextField , TextFieldProps as MuiTextFieldProps , } from "@mui/material" ; export type TextFieldProps = MuiTextFieldProps & { inputRef?: MuiTextFieldProps [ "ref" ] ; errorMessage?: string ; } ; export const TextField: React.FC < TextFieldProps > = ( { inputRef , errorMessage , ...rest } ) => { return ( <> < MuiTextField ref = { inputRef } error = { !! errorMessage } { ...rest } / > { !! errorMessage && < FormHelperText error > { errorMessage } < /FormHelperText > } < / > ); } ; import { DeepMap , FieldError , FieldValues , useController , UseControllerProps , } from "react-hook-form" ; import { TextField , TextFieldProps } from "./TextField" ; export type RhfTextFieldProps < T extends FieldValues > = TextFieldProps & UseControllerProps < T >; export const RhfTextField = < T extends FieldValues >( props: RhfTextFieldProps < T > ) => { const { name , control } = props ; const { field: { ref , ...rest } , fieldState: { error } , } = useController < T >( { name , control } ); return ( < TextField inputRef = { ref } { ...rest } { ...props } errorMessage = { ( error && error.message ) || props.errorMessage } / > ); } ; RadioGroup RadioGroup にはRadioPropsListとして value とlabelをプロパティに持つ配列を渡せるようにします。labelが画面に表示される ラジオボタン 横の文言で、 value が実際にRHFが受け取る値になります。例えば ラジオボタン で「りんご」を選択したとき、バックエンド側には「 apple 」と英名で情報を送信したい場合が良くあるのでこのように分けています。 import { FormControl , FormControlLabel , FormHelperText , Radio , RadioGroup as MuiRadioGroup , } from "@mui/material" ; import type { RadioGroupProps as MuiRadioGroupProps } from "@mui/material" ; type RadioProps = { value: string ; label: string ; } ; export type RadioGroupProps = MuiRadioGroupProps & { inputRef?: MuiRadioGroupProps [ "ref" ] ; errorMessage?: string ; radioPropsList: RadioProps [] ; } ; export const RadioGroup: React.FC < RadioGroupProps > = ( { inputRef , radioPropsList , errorMessage , ...rest } ) => { return ( < div > < FormControl error = { !! errorMessage } > < MuiRadioGroup ref = { inputRef } { ...rest } > { radioPropsList.map (( el ) => ( < FormControlLabel key = { el.value } value = { el.value } label = { el.label } control = { < Radio / > } / > )) } < /MuiRadioGroup > < /FormControl > { !! errorMessage && < FormHelperText error > { errorMessage } < /FormHelperText > } < /div > ); } ; import { useController } from "react-hook-form" ; import type { FieldValues , UseControllerProps , DeepMap , FieldError , } from "react-hook-form" ; import { RadioGroup , RadioGroupProps } from "./RadioGroup" ; export type RhfRadioGroupProps < T extends FieldValues > = RadioGroupProps & UseControllerProps < T >; export const RhfRadioGroup = < T extends FieldValues >( props: RhfRadioGroupProps < T > ) : JSX. Element => { const { name , control , ...rest } = props ; const { field: { ref , ...restControllerProps } , } = useController < T >( { name , control } ); return < RadioGroup inputRef = { ref } { ...restControllerProps } { ...rest } / >; } ; SelectForm SelectForm は機能としてはRadioGroupに近いのでほぼ同じ実装になっています。ただselectedValueをプロパティとして渡せるようにしないと、現在選択されている値をフォーム上に表示することができないので渡しています。RHFを使うと「現在選択されている値」の情報はuseControllerから持ってこれるので、そのままview層に渡します。 またMuiの Select コンポーネント をスタイリングせずにそのまま使うと、以下のgifのようにwidthが極端に小さいコンポートとなってしまいました。実際に使うときにはお好みのスタイリング手法でwidthを設定したほうが良いでしょう。ただMuiの コンポーネント にスタイルを当てたい場合は公式に用意されている styled() を使うのがオススメです。 import { FormControl , FormHelperText , InputLabel , MenuItem , Select , } from "@mui/material" ; import type { SelectProps as MuiSelectProps } from "@mui/material" ; type SelectProps = { label: string ; value: string ; } ; export type SelectFormProps = MuiSelectProps & { inputRef?: MuiSelectProps [ "ref" ] ; errorMessage?: string ; selectPropsList: SelectProps [] ; selectedValue: string ; } ; export const SelectForm: React.FC < SelectFormProps > = ( { inputRef , errorMessage , selectPropsList , selectedValue , label , ...rest } ) => { return ( < div > < FormControl > < InputLabel > { label } < /InputLabel > < Select ref = { inputRef } value = { selectedValue } label = { label } { ...rest } > { selectPropsList.map (( props ) => ( < MenuItem key = { props.value } value = { props.value } > { props.label } < /MenuItem > )) } < /Select > < /FormControl > { !! errorMessage && < FormHelperText error > { errorMessage } < /FormHelperText > } < /div > ); } ; import { useController } from "react-hook-form" ; import type { FieldValues , UseControllerProps , DeepMap , FieldError , } from "react-hook-form" ; import { SelectForm , SelectFormProps } from "./SelectForm" ; export type RhfSelectFormProps < T extends FieldValues > = Omit < SelectFormProps , "selectedValue" > & UseControllerProps < T >; export const RhfSelectForm = < T extends FieldValues >( props: RhfSelectFormProps < T > ) : JSX. Element => { const { name , control } = props ; const { field: { ref , onChange , value: selectedValue , ...rest } , fieldState: { error } , } = useController < T >( { name , control } ); return ( < SelectForm inputRef = { ref } onChange = { ( e ) => onChange ( e ) } { ...rest } { ...props } selectedValue = { selectedValue } errorMessage = { ( error && error.message ) || props.errorMessage } / > ); } ; CheckboxGroup CheckboxGroup にはチェックした値が配列として返ってくるようにしています。例えば「りんご」「ばなな」をチェックすると ["apple", "banana"] が返ってきます。 ロジックとしては RhfCheckboxGroup の handleChange とRHFの onChange 関数を使って実現しています。 handleChange でチェックした値の配列を作り、 onChange 関数の引数に渡すことでRHFに作成した配列の情報を渡すことができます。 import React from "react" ; import { Checkbox , FormControlLabel , FormGroup , FormHelperText , } from "@mui/material" ; import type { FormGroupProps } from "@mui/material" ; type CheckboxProps = { value: string ; label: string ; } ; export type CheckboxGroupProps = FormGroupProps & { inputRef?: FormGroupProps [ "ref" ] ; errorMessage?: string ; checkBoxPropsList: CheckboxProps [] ; checkedValues: string [] ; } ; export const CheckboxGroup: React.FC < CheckboxGroupProps > = ( { inputRef , checkBoxPropsList , checkedValues , errorMessage , ...rest } ) => { return ( < div > < FormGroup ref = { inputRef } { ...rest } > { checkBoxPropsList.map (( props ) => ( < FormControlLabel key = { props.value } control = { < Checkbox value = { props.value } checked = { checkedValues.includes ( props.value ) } / > } label = { props.label } / > )) } < /FormGroup > { !! errorMessage && < FormHelperText error > { errorMessage } < /FormHelperText > } < /div > ); } ; import React from "react" ; import { DeepMap , FieldError , useController } from "react-hook-form" ; import type { FieldValues , UseControllerProps } from "react-hook-form" ; import { CheckboxGroup , CheckboxGroupProps } from "./CheckboxGroup" ; export type RhfCheckboxGroupProps < T extends FieldValues > = Omit < CheckboxGroupProps , "checkedValues" > & UseControllerProps < T >; export const RhfCheckboxGroup = < T extends FieldValues >( props: RhfCheckboxGroupProps < T > ) : JSX. Element => { const { name , control } = props ; const { field: { ref , onChange , value: checkedValues , ...rest } , fieldState: { error } , } = useController < T >( { name , control } ); const handleChange = ( e: React.ChangeEvent < HTMLInputElement >) => { let newCheckedValueList: string [] = [] ; if ( e.target.checked ) { // チェックボックスがチェックされた時、チェックされた値を重複値の無い配列に追加 newCheckedValueList = [ ... new Set ( [ ...checkedValues , e.target.value ] ) ] ; } else { // チェックボックスが外された時は、チェックが外された値を配列から削除 newCheckedValueList = [ ...checkedValues ] .filter ( ( value ) => value !== e.target.value ); } return newCheckedValueList ; } ; return ( < CheckboxGroup inputRef = { ref } onChange = { ( e: React.ChangeEvent < HTMLInputElement >) => onChange ( handleChange ( e )) } { ...rest } checkBoxPropsList = { props.checkBoxPropsList } checkedValues = {[ ...checkedValues ]} errorMessage = { ( error && error.message ) || props.errorMessage } / > ); } ; DatePicker DatePicker はMuiの構成上view層とロジック層を分けることができませんでした。renderInputでRhfTextFieldを直接渡すことでRHFに対応させるようにしています。またMuiのデフォルトだと DatePicker のplaceholderに y/mm/dd と表示されてしまうので、inputPropsからplaceholderを設定するようにしています。また defaultValue={undefined} を指定しないと型エラーが出てしまうので設定しています。 Muiの DatePicker はカレンダーアイコンから日付を選択することもできるし、TextFieldに直接日付を入力することもできます。直接日付を入力した際は文字列をDate型としてparseしたいのでdate-fnsのparse関数を用いています。また数値以外の文字列を入力できるとinvalid dateとなってしまうので、onChange内で 正規表現 を用いて入力できないようにしています。 またMuiの DatePicker はバックスペースなどで入力した日付を全て消すと値としてはnullが入るのでzod側ではnullを許容するようにしています。 import { DatePicker } from "@mui/x-date-pickers" ; import { parse } from "date-fns" ; import { useController } from "react-hook-form" ; import type { FieldValues , UseControllerProps } from "react-hook-form" ; import { RhfTextField } from "./RhfTextField" ; /** 日付フォーマットyyyy/MM/ddを文字列とみなした時の長さは10 */ const DATE_FORMAT_LENGTH = 10 ; export type RhfDatePickerProps < T extends FieldValues > = UseControllerProps < T >; export const RhfDatePicker = < T extends FieldValues >( props: RhfDatePickerProps < T > ) => { const { name , control } = props ; const { field: { onChange , value } , } = useController < T >( { name , control } ); const onSelectDate = ( e: Date | null ) => { onChange ( e ); } ; const onChangeText = ( value: string ) => { // MUIのDatePickerはデフォルトで10文字より多く入力できてしまうため、10文字を超えた分は省略する // ex) yyyy/MM/dd{任意の文字}のように入力できてしまう if ( value.length > DATE_FORMAT_LENGTH ) { onChange ( parse ( value.slice ( 0 , DATE_FORMAT_LENGTH ), "yyyy/MM/dd" , new Date ()) ); return; } onChange ( parse ( value , "yyyy/MM/dd" , new Date ())); } ; return ( < DatePicker value = { value || null } onChange = { ( e: Date | null ) => onSelectDate ( e ) } renderInput = { ( params ) => ( < RhfTextField { ...params } inputProps = {{ ...params.inputProps , placeholder: "yyyy/MM/dd" , }} error = { !! errors [ name ]} onChange = { ( e ) => { // 数値以外を弾く if ( !/^\d*$/ .test ( e.target.value )) return; onChangeText ( e.target.value ); }} defaultValue = { undefined } name = { name } control = { control } / > ) } / > ); } ; コンポーネント 使用側実装例 参考までにこれまでに紹介した コンポーネント の使用側実装例を掲載しておきます。 import "./App.css" ; import { styled , Button } from "@mui/material" ; import { z } from "zod" ; import { useForm , SubmitHandler } from "react-hook-form" ; import { zodResolver } from "@hookform/resolvers/zod" ; import { RhfTextField } from "./components/RhfTextField" ; import { RhfRadioGroup } from "./components/RhfRadioGroup" ; import { RhfSelectForm } from "./components/RhfSelectForm" ; import { RhfCheckboxGroup } from "./components/RhfCheckboxGroup" ; import { RhfDatePicker } from "./components/RhfDatePicker" ; const Form = styled ( "form" )( { display: "flex" , flexDirection: "column" , gap: "16px" , alignItems: "center" , width: "100%" , padding: "16px" , } ); const Flex = styled ( "div" )( { display: "flex" , gap: "16px" , } ); const schema = z. object ( { text: z. string () .min ( 1 , { message: "Required" } ), radio: z. string () .min ( 1 , { message: "Required" } ), select: z. string () .min ( 1 , { message: "Required" } ), checkbox: z. string () .array () .min ( 1 , { message: "Required" } ), date: z .date () .nullable () .refine (( date ) => date !== null , "Required" ), } ); type Inputs = z.infer <typeof schema >; const defaultValues: Inputs = { text: "" , radio: "" , select: "" , checkbox: [] , date: null , } ; const props = [ { label: "りんご" , value: "apple" , } , { label: "みかん" , value: "orange" , } , { label: "ばなな" , value: "banana" , } , ] ; function App () { const { control , handleSubmit , reset } = useForm < Inputs >( { defaultValues: defaultValues , resolver: zodResolver ( schema ), } ); const onSubmit : SubmitHandler < Inputs > = ( data ) => console .log ( data ); return ( < Form onSubmit = { handleSubmit ( onSubmit ) } > < RhfTextField label = "Text" name = "text" control = { control } / > < RhfRadioGroup name = "radio" control = { control } radioPropsList = { props } / > < RhfSelectForm label = "Select" name = "select" control = { control } selectPropsList = { props } / > < RhfCheckboxGroup name = "checkbox" control = { control } checkBoxPropsList = { props } / > < RhfDatePicker name = "date" control = { control } / > < Flex > < Button type= "submit" > 送信 < /Button > < Button onClick = { () => reset () } > リセット < /Button > < /Flex > < /Form > ); } export default App ; おわりに よく使う コンポーネント をRHF化して子 コンポーネント として作成することができました。これらの コンポーネント を組み合わせ、zodと連携することで様々なバリデーション機能を持ったフォームを作成することができると思います。 DatePicker に関しては若干ゴリ押しの実装になってしまった感が否めませんが自分の実力ではこれが限界でした...。 未だにRHFの底が見えていないのでどんどん使い倒してマスターできるようになっていきたいです。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに PhpStormとは 料金 ダウンロード・初期設定 ダウンロード 初期設定 コードスタイルの設定 フォントの設定 日本語化 その他のプラグイン 基本操作 編集 検索 ナビゲーション コード補完 基本的な補完 ステートメント補完 後置コード補完 その他の補完 DBクライアント 設定方法 操作方法 Gitクライアント 設定方法 操作方法 Xdebugとの連携 設定方法 ステップデバッグの手順 デバッグ接続を有効にする 処理を止めたい任意の行をクリックして、ブレークポイントをセットします。 ステップデバッグでできること デバッグセッション再開 ステップオーバー(F8) ステップイン(F7) ステップアウト(Shift + F8) 強制ステップイン 変数の確認・更新 おわりに はじめに こんにちは、ryo479です。 みなさんは普段の開発において、どういった IDE (エディタ)を使用されているでしょうか? VSCode は勢いがあり、 Vim の人気は根強いですが、 PHP で開発されているのであれば PhpStorm もおすすめです。 以下では、PhpStormについての概要、基本的な使用方法や、便利な機能などについて解説します。 PhpStormとは PhpStormはJetBrains社製の、 PHP 開発者に向けて設計された IDE ( 統合開発環境 )です。 統合開発環境 とはその名の通り、開発に便利な様々なツールが統合された環境で、PhpStormでは例えば以下のような機能が含まれています。 強力なコード補完付きのエディタ 様々な リファクタリング 機能 DBクライアント Gitクライアント デバッガとの連携機能 ■公式サイト www.jetbrains.com 料金 PhpStormは有料です。 30日間の無料体験版が準備されているので、一度試しに使ってみるのがおすすめです。 また、PhpStormを安く使用できるいくつかの「特別オファー」があるので、確認してみて下さい。 購入 PhpStorm:価格とライセンス、割引 - JetBrains Toolboxサブスクリプション ダウンロード・初期設定 ダウンロード PhpStormの インストーラ ーは以下のページからダウンロードできます。 使用しているOSに合った インストーラ ーをダウンロードしましょう。 ダウンロード PhpStorm:超高速でスマートなPHP IDE ダウンロードができたら インストーラ ーを起動し、手順に沿って進めるだけです。 PhpStormのインストールが完了した旨の表示がされればOKです。 初期設定 起動後にプロジェクトを作成し、開発していくことになりますが、その前にいくつかの便利な設定を紹介しておきます。 コードスタイルの設定 PhpStormには、設定されたコードスタイルに従ってコードをフォーマットする機能が備わっています。 事前にコードスタイルを設定しておきましょう。 「Files > Settings > Editor > Code Style > PHP 」を選択します。 「Set from...」をクリックし、選択肢からコードスタイルを設定できます。 PHP コーディング規約(PSR)の最新版であるPSR12を選択するのがおすすめです。 コードフォーマットは以下で実行できます。 選択範囲をフォーマットする場合 ⇒「Code > Reformat Code」をクリック(もしくは「Ctrl + Alt + L」) ファイル全体をフォーマットする場合 ⇒「Code > Reformat File」をクリック(もしくは「Ctrl + Alt + Shift + L」) フォントの設定 「Files > Settings > Editor > Font」を選択。 フォントの種類やサイズ、行間のスペースを設定できます。 好みのフォントに設定しておきましょう。 日本語化 「Files > Settings > Plugins」を選択し、 プラグイン を「japanese」で検索しましょう。 「Japanese Language Pack」 プラグイン をインストールし、PhpStormを再起動すれば日本語化されています。 その他の プラグイン 使用する環境に合わせて、 プラグイン を追加しておきましょう。 例えば、以下のような プラグイン が存在します。 Laravel .env files support BashSupport など 基本操作 開発において頻繁に使用するであろう、おすすめの基本操作を記載しておきます。 ショートカットコマンドの一覧は以下をご確認ください。 https://pleiades.io/sites/willbrains.jp/keymap/pdf/shortcut_phpstorm_windows.pdf 編集 説明 ショートカット コードの生成(Getter, Setter, コンストラクター など) Alt + Insert コメント化/コメント解除 Ctrl + / 自動インデント Ctrl + Alt + L 選択範囲のコードをフォーマット Ctrl + Alt + L ファイル全体をフォーマット Ctrl + Alt + Shift + L 検索 説明 ショートカット どこでも検索 Shift2回 ファイル名で検索 Ctrl + Shift + N クラス名で検索 Ctrl + N ファイル内検索・置換 Ctrl + F / Ctrl + R プロジェクト内検索・置換 Ctrl + Shift + F / Ctrl + Shift + R 使用箇所を検索 Alt + F7 / Ctrl + F7 ナビゲーション 説明 ショートカット クラス、変数、メソッドの定義元にジャンプ Ctrl + B / Ctrl + Click 前のカーソル位置に移動 Ctrl + Alt + ⇐ 次のカーソル位置に移動 Ctrl + Alt + ⇒ 行番号を指定して移動 Ctrl + G コードブロックの最初・最後に移動 Ctrl + ] / Ctrl + [ 最近使用したファイルに切替 Ctrl + Tab 最近使用したファイルを表示 Ctrl + E コード補完 基本的な補完 PhpStormは自動補完の機能が備わっており、入力中に自動で候補を出してくれます。 自動補完の設定は「Files > Settings > Editor > General > Code Completion」で確認できます。 「Show suggestions as you type」にチェックが入っていれば有効です。 手動で補完を行いたい場合は「Ctrl+Space」を押下してください。 ステートメント 補完 「Ctrl+Shift + Enter」でメソッド宣言やif文のブロックなどを補完してくれます。 if ↓ if () { } 後置コード補完 例えば以下のように書いて補完を実行(Tabを押下)すると、 function m(arg) { arg.if } if文が完成します。 function m(arg) { if (arg) { } } .if 以外にも事前に定義されたテンプレートが複数存在します。 後置コード補完の設定は「Files > Settings > Editor > General > Postfix Completion」から確認できます。 その他の補完 その他にも便利な補完機能がありますので、ぜひドキュメントを確認してみて下さい。 コード補完 | PhpStorm DBクライアント PhpStormにはDBクライアントの機能も備わっています。 PhpStormから直接DBを確認したり操作したりすることができるため便利です。 設定方法 「View > Tool Windows > Database」を選択します。 設定画面が開くので「+ボタン > Data Source」より、使用するDBを選択してください。 DBの接続情報入力画面が表示されます。 情報を入力して「Test Connection」で接続テストを実施しましょう。 緑のチェックマークが出れば接続成功です。 「OK」をクリックして設定を終わります。 操作方法 DatabaseタブからDBを選択し、内容を確認できます。 また、consoleに SQL を入力し、実行ボタンを押下することで SQL を実行できます。 Gitクライアント PhpStormにはGitのクライアント機能も備わっています。 PhpStormからGitを操作できます。 設定方法 GitのパスをPhpStormに設定します。 「Files > Settings > Version Controll > Git」を選択します。 設定画面が表示されるので、「Path to Git executable」にGitのPathを設定します。 操作方法 画面の右下にGitのブランチ名が表示されています。 クリックすると新しいブランチの作成や、ブランチの切り替えなどの操作が実行できます。 より詳しい操作は、以下の記事などを参考にして下さい。 tech-blog.rakus.co.jp Xdebug との連携 設定方法 設定については、開発環境によって方法が異なります。(ローカル デバッグ 、リモード デバッグ など) 公式ドキュメントなどの資料をご確認いただき、環境に合った設定をしてください。 Debug with PhpStorm: Ultimate Guide | PhpStorm ステップ デバッグ の手順 ステップ デバッグ とは、コードの任意の行で処理を止めて行う デバッグ のことです。 PhpStormにおけるステップ デバッグ の手順を簡単に記載します。 デバッグ 接続を有効にする まずは受話器のマークをクリックして、 デバッグ 接続を有効にしてください。 処理を止めたい任意の行をクリックして、 ブレークポイント をセットします。 アプリケーションを動作させると、 ブレークポイント を設定した箇所で処理が停止します。 ステップ デバッグ でできること ステップ デバッグ 実行中に使用できる機能について一部を紹介します。 デバッグ セッション再開 次の ブレークポイント へ処理が進みます。 ステップオーバー(F8) 次の行に処理が進みます。 ステップイン(F7) 停止している行のメソッド内に入ります。 ステップアウト(Shift + F8) 現在のメソッドから抜け、呼び出し元のメソッドに移動します。 強制ステップイン ステップインでは入ることのできない サードパーティー ライブラリのメソッドへも入ることができます。 変数の確認・更新 停止している時点における変数の値を確認できます。 値の編集も可能です。 おわりに PhpStormの基本的な機能についてざっくりと記載しました。 PhpStormには他にも様々な機能が備わっていますので、ドキュメントを確認してみて下さい。 ちなみにPhpStormの名称は「PHPStorm」ではなく「PhpStorm」だそうです。 speakerdeck.com エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは。インフラエンジニアの gumamon です! 近年、 Kubernetes 等の登場により、アプリケーションのスケールアウトはとても簡単になりました。対して、データベース(DB)のスケールアウトは依然として困難です。 「 RDBMS 」⇒ データの一貫性は保てるが、スケールアウトが難しい 「NoSQL」⇒ データの一貫性を保てないが、スケールアウトが容易 DBのスケールアウトを考えるとこの2択に行きつく、というのが今までの常識だったかと思いますが、 『どっちも!』が出来てしまう第3の選択肢が登場しました。 データの一貫性を保て、且つスケールアウト容易な『NewSQL』! 最近、NewSQLの一つである yugabyteDB の検証をする機会がありましたので、 アーキテクチャ と検証結果を紹介します。 目次 目次 ここがすごいぞ yugabyteDB! yugabyteDBのアーキテクチャ キーコンセプト NewSQLの肝①: Raft (分散合意アルゴリズム) とは? NewSQLの肝②: Table Shardingとは? yugabyteDBにおける Raft , Table Sharding yugabyteDBの検証結果 PostgreSQLとの互換性 データ移設 可用性・分断耐性 まとめ ここがすごいぞ yugabyteDB! はじめに、個人的なyugabyteDBの推しポイントを紹介します。 どれもインフラエンジニア目線では、垂涎の機能ばかりです。 ◆ PostgreSQL 完全互換である 内部構造的には全く別物でありながら、PostgreSQL11と(ほぼ)完全な互換性があります。 psql コマンドでアクセスできますし、APPの接続先を PostgreSQL からyugabyteDBに変更するだけで利用可能です。 ◆全ノード Read/Write 可能 yugabyteDBでは全てのノードに対してRead/Write可能です。 RDBMS におけるActive,Standbyのようなノード間の上下関係が存在しません。ノード構成時に レプリケーション の構成を考える必要もありません。 ◆ノード故障に伴うダウンタイムゼロ yugabyteDBは レプリケーション 係数(以下 rep)という変数を持ち、その変数に応じてデータを複製します。例えば、rep=5 と設定した場合、データは5重に複製され、2ノードまでのノード故障に耐えることができます。さらに ノード故障から規定時間(デフォルトでは15分)を経過すると、生存しているノードのデータが別ノードに再度複製され、冗長性が回復します。つまり、99ノード(物理機)で クラスタ を構築したとしてもノードの故障率を考慮する必要があまり無い、と言えます。 ◆ノード追加・削除のダウンタイムゼロ yugabyteDBは無停止で クラスタ 構成の変更が可能です。 ノード追加 ⇒ 追加ノードでサービスを起動すると自動で クラスタ に参加。データが クラスタ 全体で再配置(平坦化)されます。 ノード削除 ⇒ 削除予定 ノードでBlackListコマンドを打つと、データが他ノードに移動します。後は空になったノードを停止するだけです。 ◆無料です Apache2.0 ライセンスの OSS です。これだけの機能を持ちながら完全無料! ※有償のマネージドサービスもあります 。 yugabyteDBの アーキテクチャ つぎに、yugabyteDBがどのように前述の機能を実現しているかを紹介していきます。全てを書き起こすと果てしない長さになってしまうので、今回はyugabyteDBの概要と、NewSQLの「肝」にフォーカスしてご紹介します。 詳しい情報は 公式ドキュメント をご参照ください。読みやすいです! キーコンセプト yugabyteDBの アーキテクチャ は階層化された設計になっています(2層構造)。 yugabyteDB構造図 ◆Yugabyte Query Layer クライアントへの応答を返す上位層です。 YSQL API ⇒ PostgreSQL11 互換の応答を返す API YCQL API ⇒ Casandora 互換の応答を返す API 特徴的なのは Pluggable QueryEngine です。これは API をどんどん追加できるように設計されており、今後も新たな API が追加予定とのことです。(実際、初期のyugabyteDBはYCQLのみで、後からYSQLが追加されています。) RDBMS 、NoSQLの インターフェイス を併せ持つDBというのは中々無いのでは?と思います。 ◆DocDB Document Store 分散ドキュ メントス トアーです。 Raft Consensus Replication ⇒分散合意 アルゴリズム を用いた レプリケーション (後述。NewSQLの肝①) Sharding & Load Balancing ⇒テーブルシャーディング(後述。NewSQLの肝②) DistributedTransactionManager&MVCC ⇒データの一貫性を担保。ACID特性を持つ Custom RocksDB Storage Engine ⇒ Facebook にも採用されているKey- Value ストア データ保管場所の実体はKey- Value ストアですが、これに3つの コンポーネント を追加することで、一貫性、可用性、分断耐性を併せ持つドキュ メントス トアとして機能しています。 NewSQLの肝①: Raft (分散合意 アルゴリズム ) とは? ひらたく言うと、「柔軟に多数決を採決し、一貫した結論を出す アルゴリズム 」と言えるかと思います。 Kubernetes を構成する コンポーネント etcd でも利用されており、「一貫性、可用性、分断耐性を併せ持つことはできない」とする「CAP定理」を打破することが出来ます。Raftで ググる と「よくわからない・・」という記事が沢山ヒットしてしまうのですが、やっていることは中々にシンプルです。ただ、静止画で説明をするのが難しいので詳しく知りたい方は こちらのサイト をご参照頂ければと思います。とても分かりやすいです! 以下では アルゴリズム の肝になる部分のみ、かいつまんで説明してみます。 Raftの概要 クラスタ ー構成 Leader x1 ⇒リーダー選挙に当選したnode。意思決定権を持つ Follower x n ⇒リーダー選挙に落選したnode。意思決定権を持たない 主要な動作 リーダー選挙 ⇒Leader不在の クラスタ ーで発生。Leaderを選出 ログ複製   ⇒Leaderの意思決定をWAL(Write Ahead Log)でFollowerに伝達(複製) ※重要なこと クラスタ ー参加nodeは全nodeに対して疎通確認(HeartBeat)している リーダー選挙時、 過半数 のnodeと疎通が取れない場合、自分はLeaderに立候補しない。もしLeaderであれば辞任(Followerに降格)する。 ログ複製時、 過半数 のFollowerからLeaderにレスポンスが返ってきたらLeaderはクライアントにCOMMITを返す NewSQLの肝②: Table Shardingとは? ひらたく言うと、大きなテーブルを「 メタデータ +テーブルxn」に「行」分割する機能と言えるかと思います。テーブルシャーディングを行うと、小分けにしたテーブルは検索性が向上します。また、異なるnodeに配置することで、ハードウェアリソースの負荷を分散することもできます。 Sharding テーブル分割は「分散キー」を元に、実行されます。yugabyteDBの場合、デフォルトではPRIMARY KEYをHASH化したものが「分散キー」となり、PRIMARY KEYの値に関係なくランダムに分割・配置します。特定のルールに従って分割・配置することも可能ですが、その場合は特定のnodeにアクセスが集中する「 ホットスポット 」が生まれやすくなるため、注意が必要です。 yugabyteDBでは、分割したテーブルのことを Tablet ( タブレット )と呼びます。 yugabyteDBにおける Raft , Table Sharding yugabyteDBを rep=3 , node=4 で構成した例で説明をします。 Document Store 上図はyugabyteDB内のあるテーブルです。テーブルのデータ(緑)が3つの タブレット に分割され、 メタデータ (青)で管理されています。各 タブレット は rep=3に従い、3重に複製されています。また、複製されたデータは各々Raftを形成しており、何れか1つの タブレット のみがLeaderとなっています。上図はyugabyteDBのDocument Storeの動作イメージです。この上位層に(4nodeにまたがる形で)Query Layerが展開されており、各nodeの何れかがクエリを受け取ると、Query Layerは対応する タブレット のLeaderにRead/Write命令を投げる、という仕組みになっています。nodeが増えるとクエリが分散される。nodeが増えずとも タブレット が違えばクエリの並列実行が可能で、且つTransaction Managerによりデータの一貫性も担保される、という仕組みになっているようです。 yugabyteDBの検証結果 今回、とある弊社商材のDBを PostgreSQL からyugabyteDBに置換してみました(既存データの移設も含む)。サマリーですが、その結果を紹介してみたいと思います。 PostgreSQL との互換性 完全互換と謳うだけあって、リリースレベルのテストを行ってもほぼ問題が発生しませんでした。ただ、実態は PostgreSQL を模した API であり、一部本家 PostgreSQL と挙動が異なったのでご紹介です。 テーブルロック PostgreSQL には多様なロックモードがありますが、yugabyteDBで現在実装されているのは ACCESS SHARE のみでした。 その他ロックモードは開発中 とのこと。 トランザクション 競合時の挙動 PostgreSQL11で トランザクション が競合した場合、後発 トランザクション は先行 トランザクション が完了するまで待ってからCOMMITする動きをする(後勝ちとなる)のですが、yugabyteDBにおいては後発 トランザクション にエラーが返ります。 こちらも現在開発中 とのこと。 データ移設 COPYコマンドですんなり移設できました。 前述したPRIMARY KEYのHASHが自動実行され、 PostgreSQL 上の元テーブル(1000万行)はバラバラの タブレット に分割されました。複合キーを使っていたテーブルについても問題なし。 可用性・分断耐性 この検証結果は素晴らしかったです!推しポイントにも書きましたが、 node故障、追加、削除、ネットワークの分断(※)の発生において、ヒトが行うべき作業はほぼ何もありません 。やるべきことは サービスの起動・停止 それだけです。 ※yugabyteDBはAZ(Availability Zone)の概念を持っており、どのnodeがどのAZに所属しているかを設定する必要があります。これはネットワークの分断に備える為に重要なことです。例えばrep=3 , node=6の クラスタ を3つのAZに2nodeずつ配置したとします。この際AZの設定をyugabyteDBにしていないと、node1,node2,node3でRaftを組むような タブレット が出て来て、ネットワーク分断が発生した時、一部の タブレット のみ孤立しているAZ側で多数決を可決してしまう状況が発生する可能性があります。 まとめ yugabyteDBの アーキテクチャ 、検証結果について紹介させて頂きました! yugabyteDBは RDBMS +NoSQLの良いとこ取りDB (NewSQL) (ほぼ) PostgreSQL11完全互換 可用性・分断耐性は素晴らしい。データの再配置までしてくれる yugabyteDBの 知名度 はまだまだ(?)なのですが、NewSQLであるCloudSpanner( GCP ) やTiDB( OSS / MySQL 互換)は国内でもちらほら導入事例を聞くようになって来ました。 CloudSpannerは ドラクエ ウォーク等、巨大なリソースを必要とする(?)サービスにも採用されているようです。NewSQLのスケーラビリティ、可用性には未来を感じる・・!と個人的には思っています。当記事をご覧になり、気になった方はぜひ使ってみてください。今回はOS上に直接構築をしましたが、 Kubernetes の知見がある方はHelmChartが出ているので、そちらを使った方がスムーズかなと思います。 以上、最後までお読み頂きありがとうございました! 参考: yugabyteDB 公式ドキュメント エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
技術広報の yayawowo です。 いつも ラク スのエンジニアブログをお読みいただき、ありがとうございます! 今年度4回目となる ラク スMeetupは、 『 アーキテクチャカイゼンで課題解決に挑む、エンジニア達 』 でした! テーマは『 アーキテクチャ 』です。 各プロダクトの アーキテクチャ カイゼン に挑んだ弊社のバックエンドエンジニアの3名が登壇しました! なお、本イベントは以下のような方にオススメとなっております。 ◆ こんな方にオススメ! ・ ラク スのプロダクト、組織に興味がある方 ・マイクロサービス、DDDに興味がある方 ・長期プロダクトの リファクタリング を検討している方 ・ アーキテクチャ の カイゼン を検討している方 ・プロダクトのルール作りに興味がある方 ・ SaaS 開発に携わるエンジニアの話が聞いてみたい方 発表の紹介 レガシーになりゆくシステムとの向き合い方 アプリアーキテクチャを明文化しチームの開発効率をアップ ドメインの異なる新機能開発におけるアーキテクチャ検討 次回のラクスMeetupは? 当日のタイムテーブル 申込方法 ラクスのエンジニア/デザイナーと話をしてみたい方へ 終わりに 発表の紹介 それではここから各発表内容と資料を共有させていただきます! イベントの詳細は以下をご確認ください。 rakus.connpass.com レガシーになりゆくシステムとの向き合い方 登壇:井上 大輔 [所属:楽楽勤怠開発1課/担当プロダクト: 楽楽勤怠 ] speakerdeck.com トップバッターは、楽楽勤怠開発1課に所属する井上さんの発表です。 ローンチ2年が過ぎた、楽楽勤怠との向き合い方を発表頂きました! ◆ 発表内容 私が担当している「楽楽勤怠」は2019年度にチームが発足し、今期で4年目になる比較的新しいサービスです。 現在 PMF 達成に向けて新機能の開発に邁進している一方で、システムの複雑化が進み保守性や変更容易性が低くなっており、長期的な目線に立つと生産性の低下を招いたり、障害発生率が高まってしまいます。 そのような事態にならないよう自分たちを守るために、チームとして取り組んでいることを紹介いたします。 アプリ アーキテクチャ を明文化しチームの開発効率をアップ 登壇:佐藤 晶彦 [所属:楽楽精算モバイル開発課/担当プロダクト: 楽楽精算 ] speakerdeck.com 続いて、楽楽精算モバイル開発課に所属する佐藤さんの発表です。 アーキテクチャ ガイドライン として具体的に明文化した内容を発表頂きました! ◆ 発表内容 弊社のモバイル開発チームは、2020年に組織化された新しいチームであり、解決すべき以下の課題を抱えております。   ・技術力向上 ・開発スタイルの習得 ・学習・実装コストの削減   特に優先度が高い技術的課題へのアプローチとして「アプリ アーキテクチャ の明文化」を行い、現在開発を進めております。   一方で弊社の Android 開発では基本 アーキテクチャ として MVVM を採用していますが、モバイル アーキテクチャ は現在も進化しています。 そのため、弊社では オブジェクト指向 や ドメイン 駆動設計等のトレンドも踏まえて、これまでのMVVM アーキテクチャ を進化させた アーキテクチャ の検討を進めております。   本セッションでは、 ・ アーキテクチャ ガイドライン として具体的に明文化した内容 ・弊社で採用した オブジェクト指向 型の アーキテクチャ   を中心に、 アーキテクチャ を明文化して実際の開発に臨んだ話を紹介いたします。 ドメイン の異なる新機能開発における アーキテクチャ 検討 登壇:川上 正博 [所属:楽楽明細開発2課/担当プロダクト: 楽楽明細 ] speakerdeck.com ラストは、楽楽明細開発2課に所属する川上さんの発表です。 楽楽明細の新機能追加に伴う、 アーキテクチャ カイゼン の取り組みをお話しいただきました! ◆ 発表内容 楽楽明細は現在、モノリシックな構成で作成されています。 近年著しく成長しているサービスですが、最近大きな機能の追加開発案件が発生しました。 業務領域( ドメイン )の異なる機能となるため、楽楽明細本体とは分割されたシステム構成としての アーキテクチャ 検討が必要となりました。 マイクロサービスとしてシステム分割を検討したいところですが、さまざまな課題があり、定められた開発期間内で対応するのも困難な状況です。 この新規機能追加に対しての、 アーキテクチャ を軸とした取り組みのお話をします。 次回の ラク スMeetupは? 次回の ラク スMeetupは、2022/11/9(水)に 『【 ラク スMeetup】開発戦略/チームビルディング/新機能開発』 を開催します! テーマは、『開発戦略/チームビルディング/新機能開発』です。 各プロダクトの開発に携わる弊社のバックエンドエンジニアの3名が登壇します! 当日のタイムテーブル 当日のタイムテーブルは以下の通りです。 1つでもご興味のある内容がございましたら、お気軽にご参加ください。 時間 内容 登壇者 18:50 入室開始(途中参加OK!) 19:00 オープニング 主催者 19:10 新サービスのプロジェクト推進に向けた、トライ&エラー 川﨑 智彦 19:35 楽楽精算の開発課題から学ぶ、改善取り組み 坂田 光 20:00 大規模案件における手戻りを防ぐ要件定義・開発事例 西角 知佳 20:25 クロージング 主催者 申込方法 申込ページは以下3つございますので、どれか1つからご選択ください。 ◆ 自社申込ページ career-recruit.rakus.co.jp ◆ connpass rakus.connpass.com ◆ TECHPLAY techplay.jp 皆様のご参加、お待ちしております😊 ラク スのエンジニア/デザイナーと話をしてみたい方へ 当社では、一緒に働くエンジニア/デザイナーを積極的に募集しております! 現在募集している職種は、以下サイトよりご確認ください。 career-recruit.rakus.co.jp 「まだ応募する段階では…」 という方は、是非 カジュアル面談 もご検討ください! 【こんな方におすすめ】 ポジションが経験にマッチするか確認したい 働き方/環境・体制/事業・プロダクト/文化/制度を詳しく知りたい 応募前に選考の概要を聞きたい(人物像、基準など) エンジニア・デザイナーの人となりを知りたい 以下申込フォームとなります。 rakus.hubspotpagebuilder.com 「イベントで登壇していた●●さんと話してみたい・・・」 などご要望がありましたらその旨をご記入の上、お申込みください! お気軽にどうぞ 😊 終わりに 『 アーキテクチャ カイゼン で課題解決に挑む、エンジニア達』はいかがでしたでしょうか? 最前線で活躍しているバックエンドエンジニア3名から、各プロダクトの アーキテクチャ カイゼン に挑んだ技術取り組みを発表させていただきました。 本発表が SaaS 開発に携わるエンジニア/デザイナーの皆様にとって、一つでもご参考になれば幸いです。 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
インストール リポジトリ RPMのインストール PostgreSQLの設定変更 TimescaleDB拡張機能を有効にする 参考資料 こんにちは、ヤマウチです。 前回は、TimescaleDBについて紹介しました。 tech-blog.rakus.co.jp 今回は、TimescaleDBのインストールとTimescaleDB 拡張機能 を有効にするまでの手順について紹介します。 インストール TimescaleDBのインストールには ① RPM でインストールする方法 ② ソースコード からインストールする方法 がありますが、今回は「① RPM で CentOS にインストールする方法」を紹介します。 リポジトリ RPM でインストールする場合は、以下2つの リポジトリ を使用します。 PostgreSQL の リポジトリ https://download.postgresql.org/pub/repos/yum/ TimescaleDBの リポジトリ https://packagecloud.io/timescale/timescaledb [注意点] 1. PostgreSQL の リポジトリ にもTimescaleDBのパッケージ(timescaledb_12-2.7.2-1.rhel7. x86 _64. rpm )がありますが、このブログを書いている(2022-10-06)時点では Apache -2機能のみでCommunity機能を使えないパッケージになっています。ご注意ください。 圧縮機能はCommunity機能でないと使えないため、圧縮機能を試したい場合は 2.TimescaleDBの リポジトリ のパッケージを使用してください。 RPM のインストール PostgreSQL の リポジトリ 設定 yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL- $( rpm -E %{centos} ) -x86_64/pgdg-redhat-repo-latest.noarch. rpm TimescaleDBの リポジトリ 設定 tee /etc/yum.repos.d/timescale_timescaledb.repo <<EOL [timescale_timescaledb] name=timescale_timescaledb baseurl=https://packagecloud.io/timescale/timescaledb/el/ $( rpm -E %{rhel} ) / \$ basearch repo_gpgcheck=1 gpgcheck=0 enabled=1 gpgkey=https://packagecloud.io/timescale/timescaledb/gpgkey sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt metadata_expire=300 EOL PostgreSQL とTimeScaleDBの RPM をインストール yum install timescaledb-2-postgresql-12 ※ PostgreSQL14をインストールする場合は、 yum install timescaledb-2-postgresql-14 のように最後のバージョン番号を変えてください。 PostgreSQL の設定変更 postgresql.conf に設定を追加 vi postgresql.conf shared_preload_libraries を以下のように編集する。 shared_preload_libraries = ' timescaledb ' # 対象行を書き換える。すでに設定がある場合は , 区切りで追加する。 timescaledb.telemetry_level = basic # TimescaleDBに情報を送信する場合は basic(デフォルト)、送信しない場合はoffに設定する PostgreSQL を再起動して設定を反映させる systemctl restart pgsql TimescaleDB 拡張機能 を有効にする 拡張をインストールするDBに接続 psql -U postgresql -p 5433 DB名 拡張をインストール DB名 = # CREATE EXTENSION IF NOT EXISTS timescaledb; インストールされた拡張を確認 DB名 = # \dx インストール済みの拡張一覧 名前 | バージョン | スキーマ | 説明 -------------+------------+------------+------------------------------------------------------------------- timescaledb | 2 . 7 . 2 | public | Enables scalable inserts and complex queries for time-series data ( 1 行 ) ※timescaledb の行が表示されていれば、TimeScaleDB 拡張機能 が有効になっています。 これでTimescaleDBを利用する準備が完了しました。 次回以降、圧縮機能も検証する予定です。 参考資料 RHEL/CentOS | Timescale Docs Telemetry | Timescale Docs エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
初めに 皆さんこんにちは。 開発課のm_tkoです。 Dockerについて、 「とりあえず手軽にアプリケーションの実行環境作れるらしいぞ」 という印象だけで生きていたので、仕組みをいまいち理解できていませんでした。 一体どういう仕組みになっているのか、まずは大枠だけでも理解しようと思いこの記事を作成しました。 最後までお付き合いいただけると嬉しいです。 目次 初めに 目次 Dockerの超概要 Dockerの構成 Dockerコンテナとは? 概要 利点 Dockerイメージとは? 概要 利点 補足 Dockerボリュームとは? 概要 利点 Dockerの実行 Dockerコマンドとは? 概要 コマンドについて Dockerコンテナの操作 Dockerイメージの操作 Dockerボリュームの操作 基本操作イメージ 補足 Docker Composeとは? 概要 利用方法について コマンドについて まとめ 参考 参考文献 参考サイト Dockerの超概要 Dockerについて一言でまとめると Docker社が開発したコンテナを用いた仮想環境を作成・配布・実行するためのプラットフォーム になります。 初心者の自分からすると、  「そもそもコンテナって何? 仮想マシン を使ったときとどう違うの?」  「作成・配布・実行ってどうやってやるの?」 という疑問が浮かびました。 この二つを理解するために、まず構成と実行に分けつつ、以下キーワードにフォーカスを当てて見ていくことにします。 Dockerの構成 Dockerコンテナ Dockerイメージ Dockerボリューム Dockerの実行 Dockerコマンド Docker Compose ※元々は Linux に特化した技術でしたが、近年では Windows や macOS で実行するための仕組みがDocker社から提供されています。  この記事では Linux での利用を想定して解説していきます。 Dockerの構成 基本構成と利点について記載します。 Dockerコンテナとは? 概要 一つのOS上で、CPU・メモリ・プロセス空間などが独立した仮想環境のことをコンテナと呼びます。 従来の仮想化では、 VMWare などの仮想化ソフトウェアを用いて、ホストOS上にゲストOSを構築する形式でした。 しかし、DockerコンテナはホストOS上にゲストOSなしで独立した仮想環境として構築されます。 Dockerコンテナを利用する場合は、Docker Engineをインストールすることでコンテナの操作を行うことができます。 仮想マシン とDockerの違い 利点 低コストに仮想環境が作成できる 両者の違いを簡単にまとめると、 仮想マシン を用いた場合: → OSから構築が必要 コンテナエンジンを用いた場合: → 一つのOS上で独立した環境を構築できる となります。 コンテナは一つのOS上で構築が可能であるため、OS関連のリソースを増やすことなく低コストに仮想環境が作成できます。 ポータビリティがある コンテナ内のみでアプリケーションの動作環境が完結するため、コンテナを丸ごとコピーして他サーバに持ち出すことが可能です。 Dockerイメージとは? 概要 Dockerコンテナ自体は空の箱のようなものなので、活用するためにはアプリケーションやライブラリを入れる必要があります。 そのDockerコンテナ作りを支援するために、アプリケーションの実行に必要なものを一通り揃えた アーカイブ パッケージがDockerイメージです。 Dockerイメージの中には、 アプリケーション本体 ライブラリ フレームワーク 基本コマンド などが入っています。 Dokcer社が運営している「 Docker Hub 」という クラウド サービスで公開されており、そこからダウンロードすることが可能です。 利点 少ない手順でアプリケーションの構築ができる  DokcerコンテナもDockerイメージも用いずに構築しようと思うと、 そのアプリケーションが動作する言語やライブラリのインストール データを保存するためのDBをインストール それらを適切に設定  といった手順が必要になります。 しかし、DokcerコンテナとDockerイメージを用いた場合、Dockerイメージの中にアプリケーションの実行に必要なものや設定が一通り揃っているため、従来よりも少ない手順で構築が可能です。 補足 Dockerfileについて Dockerイメージを作成するための手順を記したテキストファイルのことを、Dockerfileと呼びます。 このファイルを読み込むことでDockerイメージを作成することも可能です。 Dockerイメージの中身について Dockerイメージには、必ずしもアプリケーション本体が入っているわけではありません。 Ubuntu や CentOS など、 Linuxディストリビューション だけで構成されているものもあり、これを用いて独自のコンテナを自由に作ることも可能です。 Dockerボリュームとは? 概要 Dokcerコンテナで利用するデータを保持するためのDocker Engine上の領域のことを、Dockerボリュームと呼びます。 Dokcerコンテナ上の ディレクト リからマウントすることが可能です。 Dockerボリュームのイメージ 利点 永続化が可能 Dokcerコンテナ内にデータを保存した場合、Dokcerコンテナを破棄すると一緒に消えてしまいます。 しかし、DokcerボリュームにマウントしておくとDokcerコンテナを破棄した際にもデータは保持されます。 管理方法が汎用的 Dokcerコンテナは、DokcerボリュームだけでなくDocker Engineがインストールされているサーバの ディレクト リに対してマウントすることも可能です。 しかし、この場合 ディレクト リの位置は管理者によって異なる可能性があります。 Dokcerボリュームの場合、Docker Engineを通して管理が行われるため、物理的な配置先を意識する必要なく汎用的に利用することができます。 Dockerの実行 基本的な操作・実行方法について記載します。 Dockerコマンドとは? 概要 Dockerの起動や停止は基本的にコマンドを用いて実行します。 そのコマンドのことをDockerコマンドと呼びます。 書式は以下のようになります。 docker コマンド オプション コマンドについて 基本的な操作を行う上で使用するコマンドを一部紹介します。 その他の全てのコマンドについては Dockerコマンドリファレンス をご参照ください。 Dockerコンテナの操作 ◆ Dockerコンテナの作成・起動・停止のために使用するコマンド コマンド 操作 docker create 新しいDockerコンテナを作成する docker start 作成済みのDockerコンテナの起動 docker run 新しいDockerコンテナを作成&起動する docker stop 作成済みのDockerコンテナを停止する docker rm 作成済みのDockerコンテナを削除する ◆ 情報を参照するために使用するコマンド コマンド 操作 docker ps Dockerのコンテナ一覧を表示する docker logs Dockerコンテナのログを表示する docker inspect Dockerコンテナの詳細情報を表示する Dockerイメージの操作 docker image のあとにコマンドを指定することでDockerイメージの操作ができます。 ◆ Dockerイメージの作成・読み込み・削除をするために使用するコマンド コマンド 操作 docker image build DockerfileからDockerイメージを作成する docker image pull レジストリ からDockerイメージを取得する docker image load Dockerイメージを読み込む docker image rm Dockerイメージを削除する ◆ 情報を参照するために使用するコマンド コマンド 操作 docker image ls Dockerイメージの一覧を表示する docker image inspect Dockerイメージの詳細情報を表示する docker image history Dockerイメージの更新履歴を表示する Dockerボリュームの操作 docker volume のあとにコマンドを指定することでDockerボリュームの操作ができます。 ◆ Dockerボリュームの作成・削除をするために使用するコマンド コマンド 操作 docker volume create Dockerボリュームを作成する docker volume rm Dockerボリュームを削除する docker volume prune DockerコンテナからマウントされていないDockerボリュームを全て削除する ◆ 情報を参照するために使用するコマンド コマンド 操作 docker volume ls Dockerボリュームの一覧を参照する docker volume inspect Dockerボリュームの詳細情報を参照する 基本操作イメージ コンテナ作成~停止までのイメージ図は以下になります。 Dockerコンテナ作成~停止イメージ図 補足 docker run について docker run は以下の3つの操作をまとめて実行するコマンドになります。 Dockerイメージの取得( docker image pull ) DockerイメージからDockerコンテナを作成( docker create ) 作成したDockerコンテナを起動( docker start ) Docker Composeとは? 概要 複数のDockerコンテナをまとめて起動や停止などができるツールをDocker Composeと呼びます。 例えば、アプリケーション本体のDockerコンテナとデータベースのDockerコンテナを組み合わせて構成する場合などに活用できます。 利用方法について Docker Composeは「docker-compose.yml」というファイルに定義された内容を基にコンテナの操作を行います。 このファイルでは主に以下を定義します。 Dockerイメージをビルドするための情報 Dockerコンテナが利用するネットワークの情報 利用するDockerボリュームの情報 ※詳細な記載内容については Docker ドキュメント- Compose ファイル リファレンス をご参照ください。 Dockerコンテナなどの操作と同様、Docker Composeもコマンドでの実行となります。 コマンドを実行するとdocker-compose.ymlの内容を元にアプリケーションの構築を行います。 コマンドについて Docker Composeの基本的な操作を行う上で使用する、コマンドを一部紹介します。 docker-compose のあとにコマンドを指定することで操作ができます。 ◆ アプリケーションの起動・停止をするために使用するコマンド コマンド 操作 docker-compose up Dockerコンテナの作成もしくは再起動する docker-compose run Dockerコンテナの作成もしくは再起動する(サービスの指定が必要) docker-compose start Dockerコンテナを起動する docker-compose buid Dockerイメージを構築する docker-compose down Dockerコンテナを停止&削除する docker-compose stop Dockerコンテナを停止する docker-compose rm Dockerコンテナを削除する ◆ 情報を参照するために使用するコマンド コマンド 操作 docker-compose ps Dockerコンテナの一覧を表示する docker-compose images Dockerイメージの一覧を表示する docker-compose logs Dockerコンテナのログを表示する まとめ 以上がDockerの概要についての解説となります。 本当に大枠しか触れられていませんが、Dockerに興味がある方の参考になったら幸いです。 最後までお読みいただきありがとうございました。   参考 参考文献 さわって学ぶクラウドインフラ docker基礎からのコンテナ構築 Dockerのことが全くわからず現場で肩身の狭い思いをしているプログラマー向けの本 参考サイト docker docs コンテナとは何か解説、従来の仮想化と何が違う?DockerやKubernetesとは? いまさら聞けないDocker/Kubernetes Docker入門(第一回)~Dockerとは何か、何が良いのか~ Docker入門(第四回)~Dockerfileについて~ Docker入門(第六回)〜Docker Compose〜 コマンド一覧(イメージ管理編) コマンド一覧(Docker Compose編) エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは。 インフラエンジニアをしていますknmriiです。 今回はジョブ管理ツール「Rundeck」の紹介とインストール方法、簡単な使用方法を紹介していきます。 最速でRundeckジョブのテスト実行ができるような記事構成 にしていきたいと思いますのでぜひお試し下さい。 インフラエンジニアの皆様にとって、作業自動化の助けになれば幸いです。 Rundeck Rundeckとは システム要件を確認 Rundeckをサーバにインストールしてみる 事前にJavaをインストール Rundeckインストール Rundeckサーバの起動 ログインしてみる プロジェクトを作成する まずは、RundeckサーバのローカルでJOBを実行してみる Rundeckサーバから別サーバにSSH経由でJOBを実行してみる 最後に Rundeck Rundeckとは OSS のジョブ管理ツールです。 以下のような特徴があります。 コミュニティ版を無料で利用可能 ssh 経由でJOBを実行できるツール エージェントレス コマンドや スクリプト の実行が可能 JOBデータのgit管理可能 管理対象のサーバを登録可能 タグ管理可能 定期実行可能(cron表記もOK) 公式サイトは こちら システム要件を確認 Rundeckサーバのシステム要件が以下になります。 OS Red Hat Enterprise Linux Oracle Linux CentOS Debian Ubuntu Windows Server Java Java 8 もしくは 11 CPU 2 CPUs 2 CPUs per instance RAM 4 GB RAM 4GB for the JVM instance Storage 20 GB hard disk Database Mysql version Mariadb version Postgres version Oracle version Log store File system S3 compatible object store Amazon EC2 の場合 m3.medium以上 ただし接続対象が100ホストを超える場合はm3.xlarge 以上の インスタンス サイズが必要 Webアクセス用ブラウザ Mozilla Firefox or Google Chrome その他の HTML5 準拠の Web ブラウザも動作する可能性がありますが、完全にはテストまたはサポートされていません。 必要なリソースはそこまで大きくないことが分かりますね。 どの環境でも立てることができるスペックかなと思います。 Rundeckをサーバにインストールしてみる 早速Rundeckサーバを構築して行きます。 公式ドキュメントは こちら です。 記事の情報が古くなって機能しなくなった場合も、上記公式ドキュメントを参照してください。 なお、今回は RedHat 系OSにCommunity版をインストールしていきます。 事前に Java をインストール Java のインストール方法はいろいろとありますので割愛します。 今回はJava8をインストールしました。 # java -version openjdk version "1.8.0_xxx" Rundeckインストール まずは、 リポジトリ ファイルを追加します。 以下のような リポジトリ ファイルを格納する、 ディレクト リにファイルを作成します /etc/yum.repos.d/rundeck.repo [rundeck] name=rundeck baseurl=https://packages.rundeck.com/pagerduty/rundeck/rpm_any/rpm_any/$basearch repo_gpgcheck=1 gpgcheck=0 enabled=1 gpgkey=https://packages.rundeck.com/pagerduty/rundeck/gpgkey sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt metadata_expire=300 念のため読込可能か確認してみましょう # yum clean all # yum repolist 読み込んだプラグイン:fastestmirror, langpacks Loading mirror speeds from cached hostfile リポジトリー ID リポジトリー名 状態 rundeck/x86_64 rundeck 331 repolist: 331 このような表記になればOKです。 そして、 yum コマンドを使ってinstallしていきます。 # yum install rundeck 読み込んだプラグイン:fastestmirror, langpacks Loading mirror speeds from cached hostfile 依存性の解決をしています --> トランザクションの確認を実行しています。 ---> パッケージ rundeck.noarch 0:4.6.1.20220914-1 を インストール --> 依存性解決を終了しました。 依存性を解決しました ============================================================================================================================================================================================================================================= Package アーキテクチャー バージョン リポジトリー 容量 ============================================================================================================================================================================================================================================= インストール中: rundeck noarch 4.6.1.20220914-1 rundeck 253 M トランザクションの要約 ============================================================================================================================================================================================================================================= インストール 1 パッケージ 総ダウンロード容量: 253 M インストール容量: 272 M Is this ok [y/d/N]: y Downloading packages: rundeck-4.6.1.20220914-1.noarch.rpm | 253 MB 00:00:09 Running transaction check Running transaction test Transaction test succeeded Running transaction インストール中 : rundeck-4.6.1.20220914-1.noarch 1/1 検証中 : rundeck-4.6.1.20220914-1.noarch 1/1 インストール: rundeck.noarch 0:4.6.1.20220914-1 完了しました! 以上でインストール作業自体は完了です。 Rundeckサーバの起動 それでは、インストールが完了したRundeckを起動してみましょう。 以下のようなコマンドで起動可能です。 # systemctl start rundeckd # service rundeckd start 起動したら状態を確認してみましょう。 activeになっていたらおそらく起動が完了しています。 Rundeckサーバーに Java のインストールを忘れていて起動しなかった話は、内緒です。 # systemctl status rundeckd ● rundeckd.service - SYSV: rundeckd, providing rundeckd Loaded: loaded (/etc/rc.d/init.d/rundeckd; bad; vendor preset: disabled) Active: active (running) since 金 2022-09-30 14:46:42 JST; 5s ago Docs: man:systemd-sysv-generator(8) Process: 9288 ExecStart=/etc/rc.d/init.d/rundeckd start (code=exited, status=0/SUCCESS) Main PID: 9296 (runuser) CGroup: /system.slice/rundeckd.service ‣ 9296 runuser -s /bin/bash -l rundeck -c java -Drundeck.jaaslogin=true -Djava.security.auth.login.config=/etc/rundeck/jaas-loginmodule.conf -Dloginmodule.name=RDpropertyfilelogin -Drdeck.co... Rundeckサーバのlogを確認し、以下のような文字が出ていたらアクセス可能になっているはずです。 # tail /var/log/rundeck/service.log Grails application running at http://localhost:4440 in environment: production ログインしてみる 初回起動時は、以下のようなURLでwebアクセス可能になっています。 ◆ローカルにインストールした場合 http://localhost:4400/ ただし、アクセス用端末とRundeckサーバが別の場合は、上記ではアクセス出来ないので注意が必要です。 このように、設定ファイルを書き換えてサービスを再起動する必要があります。 # vim /etc/rundeck/rundeck-config.properties grails.serverURL=http://localhost:4440 ↓ grails.serverURL=http://{RundeckサーバのIPアドレスもしくはFQDN}:4440 # systemctl restart rundeckd 再起動が完了したら以下のようなURLを作成してアクセスします。 http://{RundeckサーバのIPアドレスもしくはFQDN}:4400/ プロジェクトを作成する Create New Project をクリックして、Project名や説明を入力します。 こちらの設定は後から細かく設定できますので、今回は全て Rundeck_testPj としておきます。 設定後、 作成 をクリックしてProjectの作成は完了です。 まずは、RundeckサーバのローカルでJOBを実行してみる ジョブ のタブ内に 新しいジョブを作成 というボタンがあるのでそこをクリックします。 今回のJOBはテスト用JOBなので testJob としておきます。 ジョブ編集画面に入るので、今回は Options と ワークフロー を触ってみます。 OptionsではJobに渡したい変数を作成することができます。 今回のテスト用JOBではtest_ value を作成してみました。 Optionsでは、リストから任意の値を選択させる設定や入力を強制する設定を入れることが可能です。 次に、実行する処理を作成します。 ワークフロー → スクリプト を選択すると スクリプト 入力欄ができますのでこちらに処理を書いていきます。 #!/bin/bash echo " コマンドの標準出力がログに残る " echo $( uname -n ) echo " Optionsで設定した内容もスクリプト内で呼び出し可能です " echo " @option.test_value@ " JOBの編集が完了したら、 保存 をクリックしましょう。 いよいよ実行可能なJOBが作成できました。 試しに実行してみます! 処理がうまくいけば以下のような画面になり、ログが出力されます。 コマンドの標準出力がログに残る rundeck Optionsで設定した内容もスクリプト内で呼び出し可能 TEST_VALUE!!! Rundeckサーバのローカルで何か処理を動かしたい場合は、この方法でJOBを作成することで実行可能になります。 Rundeckサーバから別サーバに SSH 経由でJOBを実行してみる Rundeckサーバから別サーバへのJOB実行をするには、まずは接続先のノードの情報を登録する必要があります。 手段は複数ありますが、今回は一番簡単な読み込みファイルを作成して情報を書き込んでいく方法を紹介します。 PROJECT SETTING → EDIT NODE → Add a new Node Source と進みます。 ファイルの設定画面になるので Format:resourceyaml File Path:/var/lib/rundeck/projects/Rundeck_testPJ/resource.yaml (rundeckが読み込める適当なディレクトリを指定します) を入力します。 また、Generateにチェックを入れると自動でファイルが作成されるので手間が減ります。 設定が完了したら 保存 をクリックし、 Edit を開くと先ほど作成したファイルが表示されるのでそのファイルの Modify をクリックします。 ファイルが編集できるようになっていますので、以下を追記します。 <remotenode>: nodename: remotenode hostname: remotenode osVersion: 3.10.0-1160.el7.x86_64 osFamily: unix osArch: amd64 description: remort server osName: Linux username: root tags: '' 追記をしたら 保存 します。 パスワードをRundeckに読み込ませて SSH 接続させることも可能ですが、今回はより作業を簡易化するためRundeckサーバ → リモートノードに SSH 鍵認証の設定をしておきました。 JOBの作成方法はローカルとほぼ同じです。 先ほど作成したRundeckサーバ上でローカル実行したJOBの実行対象を変更し、リモートノードで実行してみます。 JOB編集画面の Nodes タブを開き、 Dispatch to Nodes を選択します。 その後、ノードフィルターに先ほど追加したリモートノード名を入力し、 マッチしたノード に登録したリモートノードが出現したことを確認して保存します。 これで設定は完了ですので、実行してみます。 コマンドの標準出力がログに残る remotenode Optionsで設定した内容もスクリプト内で呼び出し可能 TEST_VALUE!!! ログが出力されました! 最後に 今回は、RundeckをインストールしてJOBのテスト実行をすることろまでを解説しました。 まだまだ紹介できていない機能が豊富にありますので、今後機会がありましたら第二弾の解説を作成したいと思います! 公式のドキュメントも丁寧にかかれていますので気になる方はそちらをご確認ください。 それではまたの機会に... エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
技術広報の yayawowo です。 SQL で条件分岐を用いるには、どのように記述すれば良いでしょうか? 今回は、SELECT文やUPDATE文で利用する条件分岐方法として、「CASE式」をご紹介します! CASE式の使い方を習得いただくため、お手元で実行可能な SQL 文付きで解説します。 是非、実践しながら習得ください! ※本説明では、 PostgreSQL 9.6を利用します。 テーブルの準備 CASE式をマスターしよう CASE式の基本的な使い方 式を使った場合 曖昧判定(LIKE句)を使った書き方 複数の条件分岐を使った書き方 CASE式をUPDATEでも使ってみよう CASE式 まとめ ◆ 【 SQL 入門】 PostgreSQL 関連記事 ・ 【SQL入門】INSERT まとめ ・ 【SQL入門】UPDATE まとめ ・ 【SQL入門】DISTINCT 使い方 ・ RDBMSとDBMSについて【初心者向け】 ・ SQLの基本【まとめ】 ・ 【RDBMS】PostgreSQLインストール・コマンド入門編 テーブルの準備 まず、CASE式の解説に入る前に今回使うテーブルを作成します。 テーブルの列定義とCREATE文は以下の通りです。 ◆ 列定義 列名 データ型 PK animal _no integer ○ animal_name text animal_breed text animal_sex text ◆ SQL 文 --テーブル作成SQL文 CREATE TABLE sample_animal ( animal_no integer primary key, animal_name text, animal_breed text, animal_sex text ); 上記の通り、テーブルが完成しましたのでデータも入れていきたいと思います。 ◆ SQL 文 --SQL文:データ追加 INSERT INTO sample_animal (animal_no, animal_name, animal_breed, animal_sex) VALUES ( 1 , ' 犬 ' , ' 柴犬 ' , ' 女 ' ); INSERT INTO sample_animal (animal_no, animal_name, animal_breed, animal_sex) VALUES ( 2 , ' 犬 ' , ' 柴犬 ' , ' 男 ' ); INSERT INTO sample_animal (animal_no, animal_name, animal_breed, animal_sex) VALUES ( 3 , ' 犬 ' , ' チワワ ' , ' 男 ' ); ◆実行結果 animal _no animal_name animal_breed animal_sex 1 犬 柴犬 女 2 犬 柴犬 男 3 犬 チワワ 男 データ準備までできましたので、早速CASE式の説明に入っていきたいと思います! CASE式をマスターしよう CASE式の基本的な使い方 まずは、CASE式の基本的な使い方をご説明します。 基本的な書式は以下の通りです。 SELECT文を用いてテーブル検索をする際に、良く利用します! ◆ 書式 SELECT *, CASE [条件の対象となるカラム名] WHEN [条件A] THEN [処理A] WHEN [条件B] THEN [処理B] WHEN [条件C] THEN [処理C] ELSE 処理D END FROM [テーブル名]; 「WHEN」の後に条件式を記載し、「THEN」の後にその条件を満たした際の処理を記述します。 「ELSE」には、どの条件式にも当てはまらない際の処理を書きましょう! また、最後にENDで閉じるのをお忘れなく! では、上記の書式を使って先ほど作成したテーブルを検索してみます。 柴犬なのか、柴犬以外なのかをCASE式を利用して見てみます。 ◆ 例1 animal_breedが「柴犬」👉「柴犬です」と表示         それ以外👉「柴犬以外です」と表示 ◆ SQL 文 --CASE式:柴犬判定 SELECT animal_no,animal_name, CASE animal_breed WHEN ' 柴犬 ' THEN ' 柴犬です ' ELSE ' 柴犬以外です ' END FROM sample_animal; ◆実行結果 animal _no animal_name case 1 犬 柴犬です 2 犬 柴犬です 3 犬 柴犬以外です 上記の通り、正しく検索ができましたでしょうか? 式を使った場合 先ほどのCASE式は、WHENの後に指定した条件と要素が=(イコール)の際に利用する記述方法です。 こちらの記述方法以外に、条件に式を書いて判定する方法もあります。 条件に式を使った例は、以下の通りです。 CASEの後ろに [条件の対象となるカラム名] を記載しておりましたが、式を使う場合は不要です。 ◆ 例2 animal _no が3以上 👉「柴犬以外です」と表示      それ以外👉「柴犬です」と表示 ◆ SQL 文 --CASE式:柴犬判定(式を使った場合) SELECT animal_no,animal_breed, CASE WHEN 3 <= animal_no THEN ' 柴犬以外です ' ELSE ' 柴犬です ' END FROM sample_animal; ◆実行結果 animal _no animal_breed case 1 柴犬 柴犬です 2 柴犬 柴犬です 3 チワワ 柴犬以外です 曖昧判定(LIKE句)を使った書き方 CASE式の条件式では、LIKE句を使うことで ワイルドカード を利用できます。 早速例題を見てみましょう! ◆ 例3 animal_breedに「柴」がつく👉「柴犬です」と表示          それ以外👉「柴犬以外です」と表示 ◆ SQL 文 --CASE式:柴犬判定(LIKE句を使った場合) SELECT animal_no,animal_breed, CASE WHEN animal_breed LIKE ' %柴% ' THEN ' 柴犬です ' ELSE ' 柴犬以外です ' END FROM sample_animal; ◆実行結果 animal _no animal_breed case 1 柴犬 柴犬です 2 柴犬 柴犬です 3 チワワ 柴犬以外です チワワの場合は、「柴犬以外です」と判定されていることが分かると思います。 また、LIKE句にて利用する検索方法を一覧にてまとめましたので、こちらもご参考ください。 検索名 検索方法 前方一致検索 LIKE '●%' 後方一致検索 LIKE '%●' 部分一致検索 LIKE '%●%' 完全一致検索 LIKE '●' 否定検索 NOT LIKE '%●%' ※部分一致にて否定検索する際の記述 複数の条件分岐を使った書き方 複数の条件を 入れ子 (CASE式の中にCASE式)構造にして、記述することもできます。 ◆ 例4 条件①  animal_sexが「男」 且つ、   条件②    animal_breedが「チワワ」👉「人気」と表示            それ以外👉「おすすめ」と表示 それ以外👉「定番」と表示 ◆ SQL 文 --CASE式:複数条件での判定 SELECT animal_no,animal_breed,animal_sex, CASE animal_sex WHEN ' 男 ' THEN CASE animal_breed WHEN ' チワワ ' THEN ' 人気 ' ELSE ' おすすめ ' END ELSE ' 定番 ' END FROM sample_animal; ◆実行結果 animal _no animal_breed animal_sex case 1 柴犬 女 定番 2 柴犬 男 おすすめ 3 チワワ 男 人気 いかがでしょうか? CASE式を 入れ子 構造にすることで、複数条件を指定した処理を実現することができます。 用途に応じて使い分けてください。 CASE式をUPDATEでも使ってみよう CASE式は、UPDATE文でも利用可能となっております。 早速以下例題をやって、理解を深めてみましょう。 ◆ 例5 animal_sexが「女」👉「男」      それ以外👉「女」 現在テーブルに登録されているanimal_sex(性別)を変更します。 ◆ 変更前の結果 animal _no animal_name animal_breed animal_sex 1 犬 柴犬 女 2 犬 柴犬 男 3 犬 チワワ 男 では、UPDATEをかけていきます。 ◆ SQL 文 --CASE式:UPDATE文への応用 UPDATE sample_animal SET animal_sex = CASE animal_sex WHEN ' 女 ' THEN ' 男 ' ELSE ' 女 ' END ; --全件検索 SELECT * from sample_animal; ◆ 変更後の結果 animal _no animal_name animal_breed animal_sex 1 犬 柴犬 男 2 犬 柴犬 女 3 犬 チワワ 女 性別が正しく更新されましたでしょうか? CASE式は、SELECT文だけでなく、UPDATE文でも応用することができますので覚えておきましょう。 CASE式 まとめ いかがでしたでしょうか? 今回は、 SQL 入門としまして『CASE式 まとめ』をご紹介させていただきました。 実際にお手元で SQL を動かすことで、より理解を深めることができたのではないでしょうか。 改めまして、本記事がCASE式を学ぶ方にとって、少しでもお役たてれば幸いです。 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは。 株式会社 ラク スで先行技術検証をしたり、ビジネス部門向けに技術情報を提供する取り組みを行っている「技術推進課」という部署に所属している鈴木( @moomooya )です。 ラク スの開発部ではこれまで社内で利用していなかった技術要素を自社の開発に適合するか検証し、ビジネス要求に対して迅速に応えられるようにそなえる 「技術推進プロジェクト」 というプロジェクトがあります。 このプロジェクトで「WEBアプリケーションのDockerコンテナ移行」にまつわる検証を進めているので、その中間報告を共有しようかと思います。 本検証での想定環境 CIに不必要な部分は後回し 既存アプリでコンテナ化の障害になった部分 OSコマンドを利用している ミドルウェアとの密結合 オンライン系とバッチ系の密結合 ひとまず目指す状態 プロセス相乗りの影響 ログが複数出力される まとめ 続きの記事も書きました。 tech-blog.rakus.co.jp 本検証での想定環境 Docker Compose k8s も下期に検証できたらいいなぁ GitLab CI CIでの動作をひとまず目標として本番運用に必要な検証は後回し 対象のサービスは20年以上運用されたレガシーな PHP アプリ LAMP 全盛期に開発されたアプリ 構成を一部抜粋するとこんな感じ。 WEBサーバーにDBサーバーも相乗りしている構成で、詳しくは後述しますが PHP プログラムと Postfix /Cronは密結合している状態です。 CIに不必要な部分は後回し Dockerコンテナ化してCIを回しやすくする 1 ことを最優先として、本番運用に必要な要件や理想論的な設計は無理に追わないようにします。 CIを活発に回しやすくすることで、その後の改善難易度が下がると想定しています。 ログ収集の仕組み DBや設定ファイルの永続化 冗長性確保 例えば、上記のような観点はついでの範囲でやれるならやるが無理はしない。 Dockerコンテナ化を最優先。 既存アプリでコンテナ化の障害になった部分 OSコマンドを利用している まず、解決課題に上がったのがこちらの課題でした。 PHP プログラムから、 awk や find などのOSコマンドを呼び出している部分がありました。 今では ウェブアプリケーション からOSコマンドを叩くことはあまりしないと思いますが、今から約20年前―― LAMP 2 が提唱されて間もない頃です――には、OSとアプリケーションの分離といった概念も今ほど浸透していなかったように思います 3 。 各種処理を実装するにあたって、今ほどライブラリエコシステムも充実していなかったということもあり、OSコマンドを活用することは当時の状況では合理的だったと思います。 いわゆる、 レガシーシステム と呼ばれる ウェブアプリケーション では多く採用されていると思われます。 なお、当該アプリケーションは Linux + Apache + PostgreSQL + PHP のLAPP構成です。 しかし、Dockerコンテナ化を進めるにあたっては課題となります。 解決策としてはいくつかの方法が考えられます。 OSコマンドで行っている処理を PHP 実装に置き換える コンテナイメージにOSコマンドをインストール ホストのOSコマンドを呼び出す権限を付与 理想としては1の方法なのですが、利用しているOSコマンドの種類が20以上、利用箇所も100を超える部分で利用しています。 改修コストを掛けたところでサービスの顧客提供価値は変わりません。 となると、ビジネス的にはゴーサインは出せないでしょう。 3の方法はそもそもこんなことができると思ってもいなかったのですが、社内で中間発表を行った際にこんな事ができると教えてもらいました。 ただし、この方法はセキュリティ面もさることながら 4 、コンテナ化して得られるはずのポータビリティがDockerコンテナ実行環境のホストOSにインストールされているコマンドによって、オミットされてしまうので避けたいです。 となると、現実的な案としては2の方法になるかと思います。 「Dockerコンテナでの実行は余計なコマンドがない分セキュアになる」というメリットが薄れはするものの、現行の余計なOSコマンドまで揃っている状態に比べれば、利用するOSコマンドしか存在しない分だけセキュアになりますし、対応するコストもDockerfile内でコマンドインストール命令を1行追加するだけです。 コンテナイメージのサイズが若干増えるデメリットもありますが、全体で見れば微々たるデメリットだと思います。 最終的にはOSコマンドへの依存はなくしたいですが、Dockerコンテナ化してテスタビリティを上げてからのほうが依存の解消もやりやすそうです。 Dockerコンテナ化を優先するのであれば、2の方法が最良だと思います。 ミドルウェア との密結合 次の課題はこちらです。 この課題は、詳しく見ると2つに分けることができて PHP プログラムから設定ファイルを書き換えて ミドルウェア をリロードしている ミドルウェア の動作をトリガーに PHP プログラムが実行されている の2つとなります。 素直にコンテナ設計をしていくと ミドルウェア は PHP コンテナとは別コンテナになると思いますが、上記の処理が含まれている場合に素直に分けることができません。 まず1つ目の課題については、王道な設計として 設定ファイルをDockerコンテナ外に出して永続化 永続化された設定ファイルを更新 Dockerコンテナを再起動 があります。 しかしこの設計ですと… 設定ファイルを永続化するストレージをどうする? リロードした場合と再起動した場合で ミドルウェア にアクセスできないタイミングが増える? と課題が増えます。 2つ目の課題については ミドルウェア によるトリガーをHTTPリク エス トなどに変換して投げる仕組みと、リク エス トを受け付けて PHP プログラムを起動する仕組みを作れば対応できそうです。 しかし、例によって該当箇所が複数あること、この改修を行っても顧客提供価値が変わらないことから初手では取りにくい対応です。 この課題については、まだ既存処理の理解を進めている最中なのでまた方針が変わるかも知れませんが、今回はCIに利用できるところまで持っていくという前提です。 そのため、最初は割り切って PHP コンテナに相乗りさせようと思っています。 同一コンテナ内であれば既存の処理通り、設定ファイルのリロードもできますし、 PHP プログラムの起動も可能になります。 ただし、この方針はCIで動作させることを前提としたものになります。 本番運用を視野に入れると設定ファイルが永続化されないといった問題や、ログファイル出力の課題が出てきます。 それでもDockerコンテナ化によってCI環境が簡略化され、活発にCIを活用することができるようになれば問題となっている部分の解決もやりやすくなると考えています。 まずはDockerコンテナ化。 オンライン系とバッチ系の密結合 3つ目の課題は、先述の ミドルウェア の件と似ているのですが、オンライン系とバッチ系が1つのコードベースになっていることです。 既存の仕組みではCronから PHP プログラムを呼び出すことで実行制御を行っています。 これ自体は問題ないのですが、コンテナ アーキテクチャ におけるバッチ実行制御というと以下のようなものが王道かと思います。 バッチ処理 を実行するバッチコンテナの外にスケジューラーを用意 スケジューラーが バッチ処理 を実行するバッチコンテナを起動 バッチコンテナは処理が終わったら停止 今回はCIでの利用をターゲットに定めているので、スケジューラーによる実行制御は必要ありません。 そのため、課題として見えているものの顕現しているわけではないので、あまり問題にならないので外部スケジューラーの準備や、外部スケジューラーからの起動を受け付ける仕組みの導入は見送っています。 大雑把に対応するのであればバッチ系のコードを分離できていない以上、オンライン系も含めたコードベースを持つバッチ用コンテナイメージを用意して、コンテナ起動時の引数で実行するプログラムを選択するような仕組みが一番楽でしょうか。 オンライン系が混在しているため、 ソースコード の修正が入ったときにはバッチ用コンテナイメージも更新しなければならないのが手間ではありますが…。 バッチ系の分離もオンライン系と共有している ソースコード の扱いをどうするかなど、コード管理上の課題に波及してしまうので今回はDockerコンテナ化を最優先として後回しにしています。 ひとまず目指す状態 ここまで触れた課題を考慮した結果、まず目指す状態を上記の状態と設定しました。 見ての通り、ほとんどそのままコンテナ化します。 PostgreSQL コンテナ部分については本番運用時はコンテナではなく VM だったり、ベアメタルだったりという選択肢もあると思いますが、CIで利用する分にはデータも永続化せずに毎回リセットされた方が都合が良いです。 ただし、プロセスを相乗りするのはDockerコンテナの作法から外れることは間違いないので、(今回はあまり大きな問題ではないですが)後述のように別の課題が生まれてきます。 プロセス相乗りの影響 他にも課題となる影響があるかも知れませんが、プロセスの相乗りによって発生する4つ目の課題と認識しているのはログ出力についてです。 ログが複数出力される stdout, stderrの2種類で対応できないログ出力が行われる。 ログの種類 access .log (stdoutに出力) error.log (stderrに出力) php .log cron maillog php .log, cron, maillogの扱いを考えないといけない。 本番で扱うとしたら Dockerコンテナ内でログエージェントを持つ すべてstdout/stderrにラベル付きで出力してDockerコンテナ外で分離する の2択かと思います。 CIで使えればいい(=エラー検知されたら人力で確認できればいい)ので 「3. ホストをマウントしてログファイル出力」 で対応します。 冗長化 とか考え出すと問題が出てきそうですが、今回は後回しにします。 まとめ OSコマンドの利用を排除する ミドルウェア の設定ファイルをアプリケーションから更新しない どうしても必要なら設定管理マネージャーとなるサービスを立てて最低限の独立性を保つ せめて バッチ処理 だけでも 疎結合 にする できれば、単体のアプリケーションとして実行可能にできると理想的 今から設計、開発する場合には最初からDockerコンテナで動作させるケースが多いと思うので自然と回避できる部分が多いと思いますが、 LAMP 環境全盛期の レガシーシステム なアプリケーションをリアーキテクトする場合の参考にはなるかと思います。 これらの方針をもとに下半期に検証を進めて、実際にやってみてどうだったか報告できればと思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com 現状でもCIは回しているが、CI実行するバージョンを切り替えるたびにCI用DBの マイグレーション が必要になるなど不便な状況になっている。 ↩ Linux + Apache + MySQL + PHP / Perl / Python という構成。 ↩ 歯切れが悪いのは20年も前となると私が学生だった頃で、趣味で ウェブアプリケーション も書いていたもののプロダクションレベルで携わってきたわけではないのでちょっと自信がないためです。 ↩ 現状がOS上で直接動作している状況なので、それに比べるとセキュリティリスクが高いわけではないとは思います。 ↩
アバター
こんにちは。開発課のtaku_76です。 最近業務で機能開発をしているときに、要件として実現する内容が単純であっても手を加えるコードが複雑であったため機能改修に時間がかかることがありました。 そこから リファクタリング の意識が強くなったため、社内で行われた リファクタリング の輪読会に参加したり、個人的に書籍を読んだりしているので今回は リファクタリング の基礎について記事を書こうと思います。 リファクタリングとは リファクタリングのメリット 可読性の向上 コードの変更が容易になる 開発スピードが向上する リファクタリングの対象 わかりにくい名前 重複コード 変更可能なデータ 長い関数 基本的なリファクタリングの紹介 関数として切り出す 条件の分解 条件の統合 ガード節による置き換え 最後に 参考書籍 リファクタリング とは リファクタリング とは、ソフトウェアの外部の振る舞いを保ったままで内部の構造を改善していくことです。 リファクタリング によってコードの可読性が上がったり、改修時のコードの変更を容易にしたりすることができます。 可読性が高まることで、設計時や実装、テストなどの工程の助けにもなりますので結果的に今後の開発にも役立ちます。 リファクタリング のメリット リファクタリング を行うメリットとして次のようなことがあります。 可読性の向上 リファクタリング を行うことで可読性が上がり、ソフトウェアを理解しやすくなります。 リファクタリング を行う前はコードを読むのに時間がかかったり、意図のわからないコードがあり理解が困難なことがあります。 それらに対して リファクタリング を行うことでコードの目的がわかるようになり、実現したいことを明確に表現することができます。 コードの変更が容易になる 整理されたコードは変更が簡単に行えます。 仮に重複コードがあった場合、同じ変更を複数箇所で行う必要があります。 しかし リファクタリング によって重複コードを除いておくことで一箇所の変更だけで済み、修正漏れなどの心配もありません。 また、複雑な条件分岐が存在している場合は1つ条件を加えることの難易度が上がります。 複雑な条件分岐は、意図しない デグレ を発生される危険性があります。 開発スピードが向上する リファクタリング によって内部設計が優れているコードは、新規開発時にどこを変更すれば良いかすぐ判断ができます。 また、うまくモジュール化されているとコードを修正するために理解する箇所が限定されます。 機能開発を進めていく中で、テスト時にバグが見つかったとしても デバッグ が容易ですぐ対応することもできます。 このように開発時に無駄なことが省かれるので、開発スピードを向上することに繋がります。 リファクタリング の対象 リファクタリング の対象をいくつか紹介します。 他にも様々なパターンがありますが、コードを読むときに記載しているようなことがあれば リファクタリング を行うきっかけとなります。 わかりにくい名前 コードの理解を進めるために大切なのは適切な名前付けです。 そのためクラス、関数、変数などに意図のわからない名前や、名前と異なる処理が混じっている場合には変更する必要があります。 また、良い名前が思いつかない時は設計が固まっていない可能性がありますので設計を見直しましょう。 重複コード 同じコードの構造が複数存在している場合は、1箇所にまとめることでコードが改善されます。 重複コードがあると、コピーされた箇所に出くわすたびに、差分がないか注意する必要があります。 そして修正時には重複部分をもれなく同様に修正しなければなりません。 変更可能なデータ 変更可能なデータは予期せぬ挙動や、厄介なバグを引き起こす原因となりやすいです。 仕様変更で処理が変わったときに意図しない値に書き変わる可能性もあるため、設計時に可変にすべきか不変にすべきか注意が必要です。 長い関数 周知の事実ですが、関数が長くなればなるほどコードの理解が難しくなります。 長いコードを見つけた時は関数として切り出せる処理がないか確認しましょう。 その中でパラメータや一時変数が多すぎる関数は、関数を切り出してもその分だけパラメータの受け渡しが必要になりますので先に一時変数を減らす必要があります。 基本的な リファクタリング の紹介 リファクタリング の手法は書籍で多く紹介されています。 今回は簡単な例ですが基本的な リファクタリング と条件分岐の リファクタリング をいくつか紹介します。 ※考え方を重視しているのでクラス設計は考慮していません。 関数として切り出す 処理ごとのまとまりを独立した関数として切り出します。(関数名に注意) コードを読んでいて何をしているのか調べなければならない箇所があるのなら、目的を示す名前で関数として抽出するべきです。 関数にすることで目的がすぐ分かるため中身を細かく気にする必要がなくなります。 JavaScript で以下に簡単な例を示します。 function printOwing(invoice) { printBanner(); let unpaidMoney = calculateUnpaidMoney(); // 明細の印字 console.log( `name: ${invoice.customer} ` ); console.log( `amount: ${unpaidMoney} ` ); } 上記の関数で、コメントで「明細の印字」と補足している箇所があります。 このような何をするかを説明したコメントで始まるコードが見つかったときは、必要に応じて関数に切り出すことでコードが見やすくなります。 function printOwing(invoice) { printBanner(); let unpaidMoney = calculateUnpaidMoney(); printInvoiceDetails(unpaidMoney); function printInvoiceDetails(unpaidMoney) { console.log( `name: ${invoice.customer} ` ); console.log( `amount: ${unpaidMoney} ` ); } } 関数を切り出す際の注意点として、目的にふさわしい 命名 をしなければ逆に理解しにくいコードのとなるので 命名 には注意が必要です。 また、逆に関数にすることで分かりにくくなってしまっているコードに関しては関数を取り除いてインライン化を行う場合もあります。 条件の分解 複雑な条件の処理は、プログラムを複雑にする原因の一つです。 様々な条件に応じて処理をするコードを書くだけで、長い関数となり読みにくくなります。 その結果、そのコードの「意図」を理解するのが難しくなります。 解決策として、必要に応じて意図に沿った名前の関数の呼び出しに置き換えることで意図を明確にできます。 条件分岐の場合は、条件判定と条件ごとの処理をそれぞれ関数に置き換えることがおすすめされています。 JavaScript で例として以下のような、土日だけ割引される料金計算があるとします。 if (days [ today.getDay() ] == "土曜日" || days [ today.getDay() ] == "日曜日" ) { price = quantity * plan.specialRate; } else { price = quantity * plan.regularRate + plan.regularServicePrice; } まず、曜日判定の条件記述を抽出します。 if (specialDayOfWeek()) { price = quantity * plan.specialRate; } else { price = quantity * plan.regularRate + plan.regularServicePrice; } function specialDayOfWeek() { return days [ today.getDay() ] == "土曜日" || days [ today.getDay() ] == "日曜日" ; } 次に、then節を関数に抽出します。 if (specialDayOfWeek()) { price = specialPrice(); } else { price = quantity * plan.regularRate + plan.regularServicePrice; } function specialDayOfWeek() { return days [ today.getDay() ] == "土曜日" || days [ today.getDay() ] == "日曜日" ; } function specialPrice() { return quantity * plan.specialRate; } 最後にelse節を関数に抽出します。 if (specialDayOfWeek()) { price = specialPrice(); } else { price = regularPrice(); } function specialDayOfWeek() { return days [ today.getDay() ] == "土曜日" || days [ today.getDay() ] == "日曜日" ; } function specialPrice() { return quantity * plan.specialRate; } function regularPrice() { return quantity * plan.regularRate + plan.regularServicePrice; } 好みもあると思いますが、金額計算を参考 演算子 にしてもよいかもしれません。 price = specialDayOfWeek() ? specialPrice() : regularPrice(); このように修正することで、金額は特別な曜日だったら割引され、そうでなければ通常価格であると直感で分かるかと思います。 条件の統合 複数の条件判定がありそれぞれ条件は異なりますが、結果が同じ場合があります。 このような条件記述は単一の結果を返す条件判定に統合します。 条件を統合するメリットは以下2つあります。 複数の判定をまとめることで、行っている判定が1つであるという意図を明示できる 条件判定を抽出して関数としてまとめることができる 注意点として、条件判定を統合しても他箇所に影響がないかを事前に確認する必要があります。 また、複数の判定が別々のもので単一の判定としてまとめることで可読性が落ちるようならこの リファクタリング は行いません。 以下過程は割愛しますが、 JavaScript で簡単な例を示します。 if (player.accountLevel < 100) return 0; if (player.loginPeriod < 100) return 0; それぞれ条件結果が同じなので、条件判定を取り出し論理 演算子 を使って統合します。 結果として得られた条件判定を関数化することで判定は1つである意図が明示できます。 if (noBonusAccount()) return 0; function noBonusAccount() { return player.accountLevel < 100 || player.loginPeriod < 100; } ガード節による置き換え 条件には以下2つの形式があります。 then節とelse節の両方が正常動作 正常動作と例外的な動作 例外的な動作に対しては、成立した時点でリターンすることをガード節と呼びます。 ガード節を使用することで主要な処理を明確に伝えることができます。 また、コード上ではネストを減らすことができるので可読性の向上につながります。 function getPayAmount() { let result; if (isDead) { result = deadAmount(); } else { if (isSeparated) { result = separatedAmount(); } else { if (isRetired) { result = retireAmount(); } else { result = normalPayAmount(); } } } return result; } deadAmount()とseparatedAmount()とretireAmount()は例外的な動作として扱われているため、成立した段階でreturnするように修正します。 function getPayAmount() { if (isDead) return deadAmount(); if (isSeparated) return separatedAmount(); if (isRetired) return retireAmount(); return normalPayAmount(); } 最後に 今回は リファクタリング の初歩ということで概要と例の紹介をしました。 読んだ本の内容をすぐにすべて反映するということはできませんが、どうすれば可読性の向上、変更を容易にできるか常に意識して機能改修していきたいと思います。 参考書籍 www.amazon.co.jp www.amazon.co.jp エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに ラク スでメールディーラーを開発している hirobex です。 2022/09/24(土) ~ 09/25(月)の3日間に渡って PHP Conference 2022が開催されました。 phpcon.php.gr.jp ラク スはブロンズスポンサーとして協賛し、 エンジニアも4名が登壇した他、多くのエンジニアが参加しました。 そこで、今回は参加者による参加レポートを紹介させていただきます。 もくじ はじめに もくじ 09/24(土)1日目 Track 1 リリースして11年経過したPHPアプリケーションにPHPStanを導入した PHP メモリ管理術 Track 2 なるべくJavaScriptを書かないでSymfonyのUIをリッチにする、『Symfony UX』 Laravel を低速化する技術 フィーチャートグルを使って素早く価値を検証する ローンチから16年目のWebサービスに、どうやってフィーチャートグルを実装しているか Track 3 PHPで学ぶシステム設計 依存関係のコントロール編 PHPバージョンアップのための依存ライブラリ更新との付き合い方 正規化理論ことはじめ -数学的背景から理解する正規化の初手- 09/25(日)2日目 Track 1 PHPの今とこれから2022 Track 2 少人数チーム開発でのレガシープロダクトとの向き合い方 ラクスからの登壇セッションのご紹介 Slimでサブシステムを構築してレガシーサービスにモダンな光を差し込ませた話 PostgreSQL + TimeScaleDBでログ管理検討 自作したプログラムを Packagist に登録して世界中の PHPer にインストールしてもらおう PHPerが再利用可能な情報提供でオフショア先とコード品質向上に取り組む まとめ PHPerのためのコミュニティ PHPTechCafe 09/24(土)1日目 Track 1 リリースして11年経過した PHP アプリケーションにPHPStanを導入した report by id:Jazuma speakerdeck.com Chatwork株式会社 山下祐さんによる発表です。 本番環境でエラーが発生することを防ぐために静的解析ツール(PHPStan)を導入した事例が紹介されました。 (導入前はnullを参照する・ライブラリのアップデートで型チェックが厳格化した等のエラーが発生していました。) 導入への道のり 静的解析によって解決したい問題を決める⇒その問題を解決できる解析レベルを決定する baselineを作成し、既存のエラーは検知対象外にする(エラーはあるものの本番環境では動いているため) baseline: 検知対象外のエラーを定義したファイル CIでPHPStanを実行する 導入して得られたメリット 問題のあるコードにリリース前に気づけるようになった 思い切った リファクタリング が気軽にできるようになった レビューコストの削減 ライブラリアップデートが安心してできるようになった。 感想 既存のエラーは本番環境の動作に影響しないため、検知対象外にするという運用がコストとリターンのバランス的に良いと思いました。 PHP メモリ管理術 report by hiro_ji speakerdeck.com PHP のメモリ不足が発生する仕組みと、メモリ消費量を節約する方法について解説していただきました。 メモリ消費量の削減ポイント 変数に値を乗せすぎない バッファや スワップ を上手に使用する memory_limitを設定する場合は2*N(Nは 自然数 )MB GC が走るタイミングを調節する 私自身、Allowed memory size of...に遭遇した際は盲目的にメモリを増やすということが多かったため、 実務に活かせる非常にためになる内容でした。 Track 2 なるべく JavaScript を書かないで Symfony のUIをリッチにする、『 Symfony UX』 report by mrstsgk_rks speakerdeck.com Symfony UXを使うことで得られるメリットを Symfony UXの種類と特徴、注意点とともに解説していただきした。 個人的には、「 JavaScript で設定するデータを PHP で記述」できることが、一番魅力的に感じました。 他のメリットとしては、 必要以上に JavaScript を書かなくてよくなる 必要以上に API を作る必要がなくなる などを挙げていただいています。 JSをなるべく書かずにフロントエンドをリッチにできるので、 PHP の活用法が広がって非常に勉強になる発表でした。 Laravel を低速化する技術 report by mrstsgk_rks speakerdeck.com Laravelを高速化することは考えたことはよくありますが、低速化させることを考えるのは斬新だと感じました。 以下の2種類の視点からの低速化を紹介していただきました。 インフラ視点 アプリケーション視点 フィーチャートグルを使って素早く価値を検証する report by rakusMorita speakerdeck.com 流行りのフィーチャートグルを使って、試験的に試作機能を一部のユーザにデリバリーすることで、 その機能の使用データを集められ、リスクを低減した状態で本実装に移せるというものでした。 フィーチャートグルには リリーストグル 実験トグル 運用トグル 権限トグル の4種類があり、今回はその中の 「実験トグル」 を使った検証例の紹介でした。 コードを変えることなく、一部のユーザにだけ機能を表示し、プロダクトの仮説検証やデータによる意思決定を行うために役立つというものです。 検証用の機能として、必要最低限の実装で、不要ならそのまま削除できるようにします。 その機能を本採用するなら実装について再検討し、本格的に実装するという流れです。 新しい開発スタイルを知ることができました。 フィーチャートグルについて詳しく知りたい方向けには こちら のスライドが紹介されていましたので、気になる方はチェックしてみてください。 ローンチから16年目の Webサービス に、どうやってフィーチャートグルを実装しているか report by rakusMorita speakerdeck.com レガシーコードで書かれた Webサービス にフィーチャートグルを導入した貴重なお話でした。 リリーストグルを使用し、開発速度向上の効果があったようです。 導入方針は下記の通りです。 必要最低限の機能で実装する 導入することに比重をおく  →レガシーコードは一旦そのままにし、導入後に リファクタリング を行う とにかく導入して、開発速度が上がったときに リファクタリング を行うという考えは、多くの現場でも役に立つ気がしました。 Track 3 PHP で学ぶシステム設計 依存関係のコン トロール 編 repory by: id:Jazuma speakerdeck.com 成瀬 允宣さんによる発表です。 プログラムにおける依存 例えばユーザの入力値を受け取って インスタンス を生成しDBに保存する... という処理をするクラスは、 インスタンス のもとになるクラス、DBアクセスを行うライブラリに依存する ⇒ DBアクセス用ライブラリに有料化・サービス終了等の変更があるとプロダクトコードが大きな影響を受ける 依存関係逆転の原則 上位モジュール( ビジネスロジック に近いクラス)が下位モジュール(具体的な技術要素を扱うクラス)に依存してはいけない 実装クラスが抽象クラスに依存させるべきである というルールから成り立つ原則です。 プログラム以外における依存 サービスの仕様を特定の人物のみが把握している(人への依存) あるチームのサービスが複数のサービスから利用されている(チームへの依存) 複数のサービスから依存されている場合、障害発生時の影響が大きく、機能改修のリードタイムが落ちる 成瀬さんはチームへの依存をメインコンテンツとして取り上げていました。 チームへの依存の制御 上で挙げた あるチームのサービスが複数のサービスから利用されている というケースの場合、 API を提供することでサービスへの依存を制御することができるとのことでした。 PHP バージョンアップのための依存ライブラリ更新との付き合い方 report by hiro_ji speakerdeck.com PHP バージョンアップ時の注意点や依存ライブラリの管理施策について、過去事例を交えて紹介していただきました。 特にRenovateを用いた定期的なライブラリ更新については、 PHP バージョンアップ時の負担を削減できる有用な運用方法だと感じました。 Renovate とは? 更新できるライブラリがあれば、そのライブラリを更新するためのプルリク エス トを自動的に作成してくれるツール 正規化理論ことはじめ -数学的背景から理解する正規化の初手- report by id:akikuchi_rks DB設計をする際に重要な概念である正規化について、数学的背景に着目して解説していただきました。 関数従属性や情報無損失分解など難しそうな単語が出てくる分野ではありますが、適宜例を用いて解説してくださっているので数学が苦手な方にも分かりやすい発表になっていました。 私自身、正規化の概念については既になんとなく理解していましたが、第二、第三正規化で行う関数従属性に沿った分解が「関数従属性に沿った分解は必ず情報無損失分解となる」という数学的背景があることまでは知らなかったため、勉強になりました。 今回の発表では第1〜第2正規化のみの解説であったので、今後こちらの発表者の方の違う正規化の解説があれば是非聞きたいです。 09/25(日)2日目 Track 1 PHP の今とこれから2022 report by shimizu_s PHP という言語は知っているけども、ほかの言語と比べて何がどう違うのかよくわからない... という PHP 初心者の方にお勧めできる講演でした。 話の主な概要としましては、 PHP にはどのような長所があるのかから始まり、これまでの PHP の歴史を踏まえてこれからの課題や改善点、そしてPHP8.2の変更点となっております。 自分は今年から PHP を使った開発をしており、公演内容は非常に分かりやすく為になりました。 私見 ですがその中でも為になった点は、 歴史:2021年11月に PHP 財団の設立と、それまでに至った経緯 改善点:PHP8では JIT による高速化 PHP8.2の変更点:動的プロパティの廃止 です。 特に動的プロパティの廃止については、今後に向けて、現在の開発でも留意すべきと感じました。 Track 2 少人数チーム開発でのレガシープロダクトとの向き合い方 report by id:Jazuma speakerdeck.com Growfit株式会社 くろきりさんの発表です。 くろきりさんの参画時、プロダクトへの技術的負債が蓄積していました。 (Fatコントローラー・エラー処理の未実装等) はじめは順調に負債の返済が進んだものの、問い合わせ対応や定型運用作業等 日々のタスクに時間がとられる状況になりました。 そこで、「より開発に集中できる環境」「 リファクタリング を継続的に行う仕組み」の作成が始まりました。 より開発に集中できる環境を作るために、カスタマーサポートチームの定型作業を管理画面から実行できるようにする・ タスク管理を導入し、納期や優先度を設定するといった仕組みが導入されました。 Growfitさんではデザインや文言修正依頼の頻度が高いため、Viewの リファクタリング を優先的に行う方針を定めて リファクタリング が進められました。 (対象箇所が多いため、効果がより見込める箇所に絞って リファクタリング するという意図があったとのことです) ラク スからの登壇セッションのご紹介 ここからは弊社から登壇させて頂いたセッションの内容をご紹介します。 Slimでサブシステムを構築してレガシーサービスにモダンな光を差し込ませた話 report by id:radiocat www.docswell.com サービス開始から15年のレガシーなサービスに新たなサブシステムを構築するにあたり、モダンな技術要素を取り入れた話です。 PHP でモダンな フレームワーク と言えばLaravel一択な雰囲気もある中で、開発要件に必要な機能や学習コストなども考慮してLaravelとSlimを比較したうえで フレームワーク を選定しました。 Slimの特徴や実装にあたって苦労した点なども紹介しています。 PSR準拠の機能拡張や書きやすさを重視した ユニットテスト の導入はレガシーな既存システムにも応用しやすいアプローチであり、レガシーサービスにモダンな光を差し込ませるひとつの事例となる話でした。 PostgreSQL + TimeScaleDBでログ管理検討 report by id:radiocat speakerdeck.com PHP のテーマではありませんが、 PostgreSQL に関する事例の紹介です。 TimeScaleDBは PostgreSQL の 拡張機能 です。 時間とともに増大する時系列データを一定期間経過後に圧縮する機能があります。 このTimeScaleDBを使って、全体の50%以上を占める膨大な操作ログデータを圧縮して管理する方法を検証した事例を紹介しました。 以下が検証の結果のまとめです。 圧縮後のデータサイズは約10%になり、圧縮効果が非常に高いことがわかった ただし、圧縮されたデータが検索対象になる場合は解凍処理を挟むため非常に遅くなる 分割カラムを効果的に活用することで検索速度が遅くなるのを回避することができる 分割カラムは検索のキーとなるカラムを分割カラムに指定することでレコードを分けて圧縮するため、検索速度低下を防ぐことができる仕組みです。 TimeScaleDBの機能を活用することで検索速度を保ちつつ、データを圧縮してデータサイズを抑えたデータベース運用が可能になります。 自作したプログラムを Packagist に登録して世界中の PHPer にインストールしてもらおう report by id:radiocat speakerdeck.com Packagistは PHP のツールやライブラリを一般に公開するための リポジトリ です。 Packagistに登録されたものを実際に利用するにはComposerを使うため、Composer用のパッケージの定義を行って、Packagistの公式サイトで登録するまでの流れがスライドで紹介されています。 自作したプログラムをローカルに眠らせている人はぜひこのスライドを参考のうえPackagistに登録してみてください。 PHPerが再利用可能な情報提供でオフショア先とコード品質向上に取り組む report by id:radiocat speakerdeck.com オフショアのコード品質を上げるために取り組んでいる具体的な事例の紹介です。 弊社は ベトナム のオフショアチームと開発していますが、 ベトナム では国の事情の違いもあって母国語の技術情報が日本のように潤沢にはありません。 そのため、コード品質を上げるために日本のエンジニアからのフィードバックをたくさん行い、その内容を学習教材として活用してもらう取り組みを行っています。 根気強く続けていくことが大切であり、以下のような対策を行って継続的に取り組んでいます。 納期の制約があるため、対応必須のフィードバックと今後のために読んでおいてほしいフィードバックに分ける 過去のフィードバックを事例集として再利用可能な形で蓄積する まとめ PHP バージョンアップやレガシー改善のセッションが多くあり、 レガシーシステム と向き合う苦労が伝わってきました。 PHP で作られた製品が長く利用されている証拠だと思います。 PHPerのためのコミュニティ PHPTechCafe ラク スでは PHP に特化したイベントを毎月開催しております。 その名も「 PHPTechCafe 」!! 次回は10/24(月)に『 PHP のリーダブルなコード』 をテーマに開催します! まだまだ参加者を募集していますので、ぜひお気軽にご参加ください。 👉 PHPerのための「PHPのリーダブルなコード」を語り合うPHP TechCafe 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに こんにちは、開発課に所属している新卒 1 年目の ke-suke0215 です。 今回、 axois について調べてみました。 axios は非同期で API 呼び出しを簡単に行うことができるライブラリです。 しかし、簡単がゆえに内側でどのように動いているか曖昧な人も多いのではないでしょうか。 axios の中身を理解するには、よく一緒に使われる async と await の理解が重要になってきます。 また、 async と await の理解には Promise についての理解が必要になり、Promise は非同期処理の記述に使うものです。 なので、非同期処理、Promise、async と await、最後にメインの axios の順で説明していきます。 サンプルプログラムは TypeScript で記述しています。 目次 はじめに 目次 非同期処理とは Promise について pending, resolve, reject の 3 つの状態がある Promise を動かしてみる 状態を変更する then と catch async と await axios の中身で行われていること まとめ 非同期処理とは 非同期処理は字からも分かる通り、同期処理ではない処理のことです。 具体的には下のように表されます。 同期処理:実行した処理の終了を待って次の処理に進む動き 非同期処理:実行した処理の終了を待たずに次の処理に進む動き 言葉だけだと分かりづらいため、実際のプログラムを見ながら説明していきます。 ここでは 2 つの引数(文字列)を受け取り、 setTimeout を用いて 1 秒後に 1 つ目の引数を表示し、その直後に 2 つ目の引数を表示するプログラムを考えたいと思います。 実行時には引数に "引数1" と "引数2" を入れているので、実行した1秒後に 引数1 引数2 と表示されるのが期待する振る舞いです。 const sampleFunc = ( str1: string , str2: string ) : void => { // 1秒後に実行 setTimeout (() => { console .log ( str1 ); } , 1000 ); console .log ( str2 ); } ; sampleFunc ( "引数1" , "引数2" ); しかしこのプログラムを実行すると、下のように直ちに 引数2 が表示され、その 1 秒後に 引数1 が表示されます。 引数2 引数1 これが非同期処理です。 setTimeout で 1 秒待っている間、TypeScript は処理の終了を待たずに次に進んでしまいます。なので 引数2 が先に表示されてしまうのです。 期待する振る舞いを実現するためには、 setTimeout の処理が終了してから次に進んでほしいので、ここでやりたいことは非同期処理を同期処理のように扱うことです。 Promise について Promise は一言で表すと『非同期処理の状態を監視するためのオブジェクト』です。 非同期処理を同期処理のように扱える書き方の 1 つになります。 具体的に説明していきます。 pending, resolve, reject の 3 つの状態がある これが Promise を考える上で重要な概念になってきます。3 つの状態の意味は下記のようになっています。 pending:待機(処理の完了を待っている) resolve:解決(処理が成功) reject :拒否(処理が失敗) Promise は常にこの 3 つのうちどれかの状態になっています。 はじめは待機を表す pending になっており、処理の内容によって状態を変化させます。 期待通りであれば成功を意味する resolve 、期待にそぐわない場合は失敗を意味する reject に変更するのが基本的な使い方です。 Promise を動かしてみる 先程のプログラムに少し変更を加え、 setTimeout の中で条件分岐を追加して Promise を使ってみます。 第 1 引数に文字列を入れて関数を実行すれば成功とし、1 秒後に第 1 引数を表示して直後に第 2 引数を表示します。 第 1 引数が空文字なら失敗とし、第 1 引数が空文字である旨を伝えるメッセージを出力します。 まだ説明していない記述もありますが、一旦スルーしてください。 const sampleFunc = ( str1: string , str2: string ) : void => { new Promise < void >(( resolve , reject ) => { setTimeout (() => { if ( str1 !== "" ) { console .log ( str1 ); resolve (); // 状態をresolve(成功)に変更 } else { reject (); // 状態をreject(失敗)に変更 } } , 1000 ); } ) .then (() => { console .log ( str2 ); } ) . catch (() => { console .log ( "第1引数が空文字です" ); } ); } ; sampleFunc ( "引数1" , "引数2" ); 今回は先程と同様 "引数1" と "引数2" を引数にいれているので、実行した 1 秒後に 引数1 引数2 と出力されます。 Promise を使用していない記述では 引数2 が先に表示されていましたが、今回は期待通りの動きです。 プログラムの詳細を見ていきましょう。 状態を変更する コメントでも記載していますが、6 行目の resolve() と 8 行目の reject() が状態を成功と失敗に変更する記述です。 これは Promise を インスタンス 化する際の引数に入れる、コールバック関数の 2 つの引数が変更する関数名に該当します。 str1 が空文字ではない場合は状態を resolve にしたいので、値を出力してから resolve() を実行し、空文字の場合は reject() を実行することで状態を reject にしています。 then と catch Promise が非同期処理を制御できるのは、状態によってその後の処理を変える仕組みを持っているからです。 Promise の処理の後には、 .then と .catch を続けることができます。 具体的には、Promise の中の処理が終了したとき状態が resolve であれば then の引数の中が、 reject であれば catch の引数の中が実行されるようになっています。 今回は resolve() が実行されて処理の終了時には Promise の状態が resolve になっているので、then の引数の中身が実行され、catch は無視されます。仮に引数である str1 を空文字で実行した場合、then は無視されて catch の引数の中身が実行される動きとなります。 したがって、特定の処理(今回の場合は setTimeout )の終了を待ってから次の処理を行いたい場合、 Promise の中で最初の処理を実行 状態を resolve に変更 then の引数に次の処理を記述 このようにすることで実現できます。 以上が Promise における非同期処理の基本的な説明です。 async と await ここまでで見てきた Promise は、次に実行したい処理を then や catch に入れていました。 ですが async と await を使うと then などを使用しなくても非同期処理を同期処理のように動かすことができるので、よりシンプルに書くことができます。 Promise の節に記述したプログラムを async と await に書き換えたものを見てみます。 const sampleFunc = async ( str1: string , str2: string ) : Promise < void > => { await new Promise < void >(( resolve , reject ) => { setTimeout (() => { if ( str1 !== "" ) { console .log ( str1 ); resolve (); // 状態をresolve(成功)に変更 } else { reject (); // 状態をreject(失敗)に変更 } } , 1000 ); } ); console .log ( str2 ); } ; sampleFunc ( "引数1" , "引数2" ); 出力結果は Promise の時と同様に下記のようになります。 引数1 引数2 順を追って説明します。 async は同期処理として扱いたい一連の処理を記述した関数の前につけます。 次に、await です。await には以下のようなルールがあります。 await の次で Promise を インスタンス 化する async をつけた関数の中に書く Promise オブジェクトの前に await をつけることで、その Promise オブジェクトが値を返すのを待つようになります。 つまり Promise 内の処理が終了するまで次の処理に進まなくなるのです。 これにより非同期処理を同期処理のように扱うことができます。 await をつけた処理が終了したときに状態が resolve であればそのまま次の処理に進み、 reject であればエラーとなります。 今回は省略していますが、 try と catch で囲むことでエラー後の処理を書くことができます。 この例では制御する非同期処理は 1 つですが、複数ある場合には async と await で then などを省略できるメリットがより顕著に感じられるはずです。 axios の中身で行われていること ようやく本題の axios についてです。 冒頭でも述べましたが、axios は非同期で API 呼び出しを行うことができます。 また、先程説明した async,await と一緒に使われることが多いです。 例として郵便番号をパラメータとして渡すと住所を返す API を使用します。 引数に郵便番号を入れるとその住所を取得する関数を作成し、実行してみます。 それではサンプルプログラムを見ていきましょう。 import axios from "axios" ; const sampleFunc = async ( zipcode: string ) : Promise < void > => { // APIを呼び出す const response = await axios. get( "https://zipcloud.ibsnet.co.jp/api/search" , { params: { zipcode: zipcode } , } ); console .log ( response.data ); } ; sampleFunc ( "7830060" ); こちらのプログラムを実行すると、次のような出力となります。 { message: null, results: [ { address1: '高知県', address2: '南国市', address3: '蛍が丘', kana1: 'コウチケン', kana2: 'ナンコクシ', kana3: 'ホタルガオカ', prefcode: '39', zipcode: '7830060' } ], status: 200 } きちんと住所の情報が帰ってくることが確認できます。 それではプログラムの解説に移ります。 axios はライブラリなので、1 行目でインポートしています。 今回は get を使用していますが、post や delete を使うこともできます。 引数に URL を入れ、必要であればパラーメタを指定することで API を呼び出すことができます。 先程までは setTimeout を利用し、明示的に 1 秒処理を待っていたのでわかりやすかったです。 API の呼び出しについても具体的な待機時間は場合によりますが、通信するのに一定の時間がかかるため await を使用しないとデータを取得する前に console.log(response.data); が動いてしまいます。 axios は「Promise ベース」と言われますが、この面で 2 つのことを自動的にやってくれています。 Promise の インスタンス 化 resolve と reject へ自動的に状態を変更 await のルールを説明した部分で、 await の次で Promise を インスタンス 化する と書きました。 今回のプログラムでは await の後に axios が来ており、一見 Promise を インスタンス 化していないように思えます。 しかしこのとき axios は、裏側で自動的に Promise を インスタンス 化してくれているのです。 また、通信が成功したかどうかによって Promise の状態を、resolve もしくは reject に変えてくれています。 これらによって Promise をあまり意識せずに「async と await つけて axios で API 呼び出せばいい感じに動く」ということが実現されています。 まとめ axios が内側でどのような処理を行っているのかを、遡って見てきました。 Promise から順を追って見ていくと、わかりやすかったと思います。 内側の処理を理解することで、実行する位置やエラーの拾い方などで迷うことが減るのではないでしょうか。 参考にしていただければ幸いです。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは! ラク ス入社1年目の koki _matsuraです。 本日は、Next.jsとVercel、Supabaseを用いて簡単なアプリを高速で開発する手順についてお話しできればと思います。 アジェンダ は以下の通りです。 Next.jsとは ReactとNext.jsの違い Next.jsの特徴 Vercelとは Supabaseとは ToDoアプリ作成 Supabaseにデータベースを用意 VercelでNext.jsプロジェクトを作成・デプロイ・GitHub連携 VercelとSupabaseの連携 GitHubからクローン Vercelから環境変数を取得 Supabaseのデータベースに接続 コード編集 終わりに 参考文献 Next.jsとは Next.jsはReactベースのアプリケーション フレームワーク です。 公式サイトではNext.jsとはReactを用いたWeb アプリ開発 で生じる問題を全て解決する フレームワーク だと紹介されています。 ReactとNext.jsの違い サーバの有無 これがNext.jsとReactの1番の違いです。 Next.jsはサーバー機能を持っているのですが、Reactはサーバー機能を持っていません。そのため、Next.jsは単体でWEBアプリを動かすことができます。Reactはサーバーを別に用意し、サーバー用のモジュールや ディレクト リなどを考えないといけないため、学習のコストが高くなります。 フレームワーク かライブラリか Next.jsはアプリケーション フレームワーク としてWEBアプリケーションの土台として使われ、Reactは部分的にDOMの値で表示を変化させることに特化しているViewライブラリとして使われます。 部分導入 Next.jsは フレームワーク なので、部分導入はできません。一方、Reactはライブラリなので Ruby on Rails や Django の フレームワーク に取り入れることが可能になっています。 Next.jsの特徴 画像の最適化 Next.jsにはimgタグの代わりとなるImageタグがあり、これを使うことで画像の次世代フォーマットであるWebPへ自動で変換し、高速に画像を表示することができます。 ファイルベースルーティング 通常、WEBページを表示したい場合は特定のフォルダ配下にindex.htmlを配置します。しかし、Next.jsでは導入時点で"pages"というフォルダがあり、その配下にファイルを配置すると自動でパスが生成されます。 ハイブリッド レンダリング Reactでは CSR (Client Side Rendering)にのみ対応しています。 CSR はページのサイズが大きい時にローディング時間が長くなってしまうというデメリットがあり、 SEO 的にも悪くなると考えられます。 一方、Next.jsでは CSR だけでなく、 SSR (Server Side Rendering)にも対応しており、ページごとに個別に レンダリング 方法を設定できます。また、プレ レンダリング も可能で、最初にページにアクセスする際にHTMLを提供するため、 レンダリング の速度を大幅に軽減することが期待できます。 ファストリ フレッシュ ソースコード に変更があったときにその箇所のみが再描画する機能です。これにより効率的に開発を進めることができます。 以上のことから、WEB アプリ開発 における開発者の手間を省いてくれる フレームワーク になっています。 Vercelとは Next.jsを開発したVercel Inc.が提供してる ホスティング サービスです。 GitHub などの リポジトリ と連携することで簡単に作成したWEBアプリを数十秒でデプロイできます。 Supabaseとは SupabaseはSupabase Pte. Ltd.が開発している開発者向け オープンソース のデータベースプラットフォームです。 SupabaseはFirebaseと比較し、大きな違いとして、データベースが挙げられます。FirebaseはNoSQLを用いているのに対し、Supabaseは PostgreSQL を用いています。また、Supabaseの管理画面はとても直感的で個人的には調べなくても操作しやすいです。なので、普段からリレーショナルデータベースを触っている人からすれば、Firebaseよりも学習コストを少なく始められるものになっています。 ToDoアプリ作成 これから実際に先ほど紹介させていただいた「Next.js」「Vercel」「Supabase」を用いて、入力した文字列がリスト型に出力される基本的な機能しか持たないToDoアプリを作成、デプロイするまでを手順を追って説明したいと思います。 Supabaseにデータベースを用意 まずはSupabaseにデータベースを用意していきます。 Supabase のサイトで「 Sign in」しましょう。 アカウントをお持ちでない方は「Start your project」をクリックしてください。 下記の画面が表示されれば、「 Sign in with GitHub 」を押し、 GitHub と連携しましょう。 成功すれば、下記のような画面が表示されます。この画面左上にある「New Project」をクリックします。 Organization・Name・Database Password・Regionはご自由に設定してください。 設定が終われば、「Create new project」をクリックします。 ホーム画面が表示されていれば成功です。 では、本題となるデータベースを作成します。サイドバーから「 SQL Editor」を選択後、左上に表示される「New query」をクリックします。 すると、エディターが表示されますので下記のようなコードを入力し、右下の「RUN」をクリックし実行させます。 ToDoアプリを作成するため、ID、タイトル、達成したか、作成した時間をカラムとして持たせます(今回の記事ではID・タイトルのみでも問題ありません)。 テーブルが作成されたかは、サイドバーから「Database」をクリックし、表示された画面にコード通りのテーブルがあるかで判断できます。 正常に実行されていれば、下記の画像のようになります。 以上でSupabase上でデータベースが作成できました。 次はVercel上でNext.jsのプロジェクトを作成し、デプロイ、 GitHub 連携していきましょう。 VercelでNext.jsプロジェクトを作成・デプロイ・ GitHub 連携 Vercel のサイトで「Continue with GitHub 」をクリックしましょう。 下記のような画面が表示されるので、右上から「Add New Project」をクリックしましょう。 Next.jsのプロジェクトを作成したいのでテンプレートを選択しましょう。 下記の画像ではGit リポジトリ を作成しています。自分のアカウントを「GIT SCOPE」で選択し、「REPOSITORY NAME」には自由に名前をつけてください。 「Create private Git Repository」はチェックを入れるとprivate リポジトリ として作成されます。 「Create」をクリックするとGit リポジトリ が作成され、自動でデプロイまでしてくれます。 デプロイ完了後、 ダッシュ ボードに戻ると下記の画像のようになっています。 「View Git Repository」をクリックすると自分のGitアカウントに リポジトリ が作成されていることが確認できます。「Visit」を押すと、作成したプロジェクトがデプロイされていることが確認できると思います。 以上で、Vercel上でNext.jsプロジェクトを作成、デプロイ、 GitHub との連携が完了しました。 次はVercelとToDoアプリ用のデータベースを作成したSupabaseを連携をします。 VercelとSupabaseの連携 まず、先ほどの作成したプロジェクトが表示されている画面から「Settings」に移動します。 「Browse Marketplace」をクリックし、「Supabase」を検索します。 検索結果に二つ出てきますが、「Supabase」と書かれている方を選択します。 「Add Integration」をクリックしましょう。 Supabaseをどのアカウントに統合するのかを選択します。 この画面ではどのプロジェクトにSupabaseを統合するのかを設定します。 「All Projects」を選択すれば、すべてのプロジェクトに統合されます。今回はToDoアプリだけに統合したいので、「Specific Projects」を選択します。そして、該当するプロジェクトを選択しましょう。 選択が終われば、「CONTINUE」をクリック。 Vercel上で先ほど作成したプロジェクトとSupabaseで作成したデータベースをそれぞれ選択し、「Add Integration」をクリックして連携します。 これでVercelとSupabaseの連携は終わりました。 次は作成されたGit リポジトリ をクローンします。 GitHub からクローン 自身の GitHub にアクセスすると、先ほどVercel上で作成したプロジェクトがあるので、 SSH をコピーしましょう。 CLI を開き、下記のコマンドを実行し、ローカル環境にクローンします。 クローンができれば、好きなコードエディターで開き、下記のコードでローカル開発サーバーを起動します。 起動が終われば、 http://localhost:3000 にアクセスしてみましょう。 テンプレートと同じプロジェクトが表示されれば、成功です。 Vercelから 環境変数 を取得 Vercelで作成したプロジェクトには、下記のような連携したSupabaseの 環境変数 が「Settings」の「Environment Variables」にあります。 Vercel上の 環境変数 を取得するために CLI で下記のコマンドでVercelにログインします。 「Ok to proceed?」には「y」と入力すると、ブラウザが開くので、「Continue with GitHub 」を選択します。ブラウザに「 CLI Login Success」と表示されるとログイン成功です。 次にVercelプロジェクトをローカル環境にクローンしたプロジェクトにリンクします。下記のコマンドを入力します。 該当するプロジェクトなら「y」を入力します。 プロジェクトをリンクできたので、下記のコマンドを入力することでプロジェクトの 環境変数 を取得します。 成功すれば、プロジェクトに「.env」が作成されます。ここにはSupabaseの 環境変数 が含まれています。 この 環境変数 はGitにあげてはいけないのでファイル名を「.env.local」に変更し、無視されるようにします。 以上で、Vercelの 環境変数 を取得できました。 次はこの 環境変数 を用いて、Supabaseのデータベースに接続します。 Supabaseのデータベースに接続 Supabaseで作成したデータベースに接続するためにはSupabaseクライアントが必要となるので、下記の2つのコマンドで型定義とSupabaseクライアントをインストールします。 インストールが完了すれば、「utils/supabase.ts」を作成し、次のコードを入力します。このコードでは、Supabaseクライアントを初期化しています。 Supabaseクライアントを用いて、データベースを操作する準備ができたので、実際にコードを編集していきます。 コード編集 現時点のコードはNext.jsのテンプレートのものなので、コードを編集します。入力部と出力部を用意して、シンプルなToDoアプリを作りましょう。 入力部の コンポーネント 「/components/InputToDoForm. tsx 」を作成します。コードは下記のようにします。 pushTodo関数でデータベースのテーブルtodosに入力内容を追加しています。 fetchTodosは入力部、出力部両方で使用するのでHooks化しています。 出力部の コンポーネント 「/components/ToDoList. tsx 」を作成します。コードは下記のようにします。 fetchTodosを記述している「Hooks/useAddTodo.ts」を作成します。コードは下記のようにします。 fetchTodosはデータベースtodosから全件取得してきます。 2022/10/11 上記コードのuseEffect内で誤りがあったため修正いたしました。 コンポーネント の作成、内部の処理ができたので、テンプレートのコードが記述されている「pages/index. tsx 」を下記のように編集します。 編集が終われば、ローカル環境を立ち上げ、 http://localhost:3000 にアクセスします。入力部と"todoリスト"の文字列があれば問題ありません。 入力部に適当な文字列をうち、その文字列が"todoリスト"の文字列の下にリストで表示されていれば、完成です。 「テスト1」「テスト2」「テスト3」と3度入力した場合は以下のようになります。 入力と出力に問題はなかったので、 GitHub にプッシュします。 そして、プッシュ後、Vercelにログインすると自動でビルド・デプロイをしてくれます。 Vercelの該当プロジェクトの画像がテンプレートのものからToDoアプリのものに変わっていればデプロイが終わっています。 終わりに Next.jsとVercel、Supabaseを用いた簡単なアプリを開発する手順についてご紹介させていただきました。 Supabaseのデータベース作成は非常に簡単で、Vercelも GitHub の連携さえすれば、自動でビルド・デプロイしてくれるので開発だけに集中できました。 是非、Next.jsのアプリケーションを作ろうとしている方はSupabase+Vercelを使ってみてください。 参考文献 supabase.com reffect.co.jp weseek.co.jp zenn.dev エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター