TECH PLAY

株式会社ラクス

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

941

はじめに こんにちは 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
はじめに こんにちは!新卒1年目の hachimd です! 先日、先輩エンジニアの方に「 Java でソートする時って Collections 使うんですよね?」って話をしたら「いやいや、 Java のソートは Stream が便利だよ!」って教えていただきました。 そこで今回は、Stream を使って、どうやってソートするのか、どう便利なのかを、Collections を使ったソートと比較してゼロから解説してみようと思います。 考え方から言及しているので、ソートの方法が全くわからない人でも大丈夫です。 なお、今回は JDK11 を使用しています。 目次 はじめに 目次 基本的なソートの考え方 Collections を使ったソート 要素の大小関係が明らかなとき 大小比較の方法を制御したい時 Collections.sort のデメリット Stream を使ったソート Stream を使った単純なソート Stream を使った『人オブジェクト』のソート 3 種類の整数を返す処理でソート comparing によるキーの指定だけでソート 複数のキーでソート 最後に 参考書籍   基本的なソートの考え方 ソートは一言で言うと、「昇順 or 降順で並び替えること」と言えます。 ここで、「名前」と「年齢」を持つ『人オブジェクト』を並び替える際に、「まず名前順で並び替え、名前が同じ時は年齢順で並び替える」というルールにすると、単に昇順・降順では並べられないと感じてしまいますが、『人オブジェクト』の大小関係を決める際に「名前を最優先、次点で年齢」と考えると、あくまで『人オブジェクト』の大小関係に従って昇順・降順に並べているのでシンプルになります。 つまり、ソートしたい要素に対して、どのように大小を決定するかを決めてやれば、あとは API を使ってソートができるということです。 【重要】ソートするのに必要なことは、要素の大小関係を決めてあげること Collections を使ったソート ここではまず、大小関係が明らかなものをソートする方法と、大小関係が明らかでない『人オブジェクト』をソートする方法を解説します。 どちらも Stream が使えるようになる以前によく使われていた、 Collections.sort() を使って解説をして、 Collections.sort() のデメリットにも言及します。その後、 Stream を使ったシンプルでわかりやすいソートの方法を解説していきます。 要素の大小関係が明らかなとき 数値や文字列のように大小関係が明らかなときは、大小関係を決めてやらなくてもソートすることができます。また、ソートの結果は自然順序(明らかな大小関係に基づく昇順)になります。 ソートするには Collections.sort() を呼び出すだけです。 List<Integer> numbers = Arrays.asList( 5 , 4 , 8 , 3 , 1 ); // ソートを実行 Collections.sort(numbers); // 結果を出力 for ( int num: numbers){ System.out.print(num + " " ); // 1 3 4 5 8 } 大小比較の方法を制御したい時 次に、age と name をプロパティにもつ Person をソートすることにします。 Person オブジェクトの年齢差を返す ageDifference メソッドも定義しています。 public class Person { private final int age; private final String name; public Person(String name, int age) { this .name = name; this .age = age; } public int getAge() { return age; } public String getName() { return name; } @Override public String toString() { return "Person{" + "age=" + age + ", name='" + name + ' \' ' + '}' ; } public int ageDifference( final Person other) { return this .age - other.age; } } Person の大小関係がどう決まるのかは我々が定義してあげる必要があります。 Java では、要素の大小比較の方法を自分で決定するには Comparator というインタフェースを利用します。 Comparator は関数型インタフェースで、抽象メソッドとして compare メソッドが定義されています。 cpmpare メソッドの実際の処理に「大小比較の方法」を記述していきます。また、この処理はある決まりに従って書く必要があります。具体的には、「2 つの引数をとり、 第 1 引数が第 2 引数より小さいとする場合は負の整数、両方が等しい場合は 0、第 1 引数が第 2 引数より大きいとする場合は正の整数を返す」ような処理になります。 (参考: Comparator インタフェースの compare メソッド ) それでは「まず年齢順で並び替え、年齢が同じ時は名前順で並び替える」というルールで、実際にソートをする処理を見ていきましょう(ここでは Collectons を使ったソートはめんどくさいなと感じてもらえれば大丈夫です)。 まずはコードを見てみましょう。 List<Person> people = Arrays.asList( new Person( "John" , 20 ), new Person( "Sara" , 21 ), new Person( "Jane" , 21 ), new Person( "Greg" , 35 ) ); // compareメソッドをオーバーライドしてPersonオブジェクトの大小比較の方法を定義する // 比較方法:年齢で比較して同じなら名前で比較 Comparator<Person> personComparator = new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { int ageDiff = p1.ageDifference(p2); if (ageDiff != 0 ) { return ageDiff; } else { int nameDiff = p1.getName().compareTo(p2.getName()); return nameDiff; } } }; // 年齢でソートする // 第1引数:ソート対象のpeople // 第2引数:Personの大小比較方法が定義されたComparator Collections.sort(people, personComparator); // 並びを確認 for (Person person : people) { System.out.println(person); } // ソート結果 // Person{age=20, name='John'} // Person{age=21, name='Jane'} // Person{age=21, name='Sara'} // Person{age=35, name='Greg'} それでは解説です。 重要なのは Person オブジェクトの大小比較の方法を定義する部分です。 ここでは、compare メソッドの実装の決まりに従うと、 2 つの Person オブジェクト p1, p2 が引数 p1 が p2 より 小さい とする場合は 負の整数 を返す p1 が p2 より 大きい とする場合は 正の整数 を返す 両方が 等しい 場合は 0 を返す ような処理にする必要があります。 また、並べ方は「まず年齢順で並び替え、年齢が同じ時は名前順で並び替える」というルールですので、Person の大小関係と compare メソッドで返す値の対応は以下のようになります。 p1 の年齢が p2 の年齢より小さければ p1 は p2 より 小さい ( 負の整数 を返す) p1 の年齢が p2 の年齢より大きければ p1 は p2 より 大きい ( 正の整数 を返す) もし年齢が同じなら、 p1 の名前が p2 の名前より小さければ p1 は p2 より 小さい ( 負の整数 を返す) p1 の名前が p2 の名前より大きければ p1 は p2 より 大きい ( 正の整数 を返す) 名前の大小も同じなら、 p1 と p2 の大小は 同じ ( 0 を返す) 上記のコードの実装では 1,2 は年齢差を返すことで実現しています。 3,4,5 は String クラスに用意された compareTo()メソッドを使って実現しています。 なお、Comparatorのcompareメソッドをオーバーライドする部分に関して、 ラムダ式 を使って処理だけを直接引数に渡すことも可能です。 Collections.sort(people, (p1, p2) -> { int ageDiff = p1.ageDifference(p2); if (ageDiff != 0 ) { return ageDiff; } else { int nameDiff = p1.getName().compareTo(p2.getName()); return nameDiff; }}); ※ 補足 compareTo()は String クラスに用意された 2 つの文字列を辞書的に比較するメソッド。 Comparable インタフェースの実装であり、これも、「2 つの引数をとり、 第 1 引数が第 2 引数より小さいとする場合は負の整数、両方が等しい場合は 0、第 1 引数が第 2 引数より大きいとする場合は正の整数を返す」ように実装されている。 (参考: Comparable インタフェースの compareTo メソッド ) Collections.sort のデメリット コードを見ただけでは何をしているか分かりにくい 上の実装のようなコードを理解するには、Comparator インタフェースの compare メソッドをどのように実装するかを知っている必要があります。さらに、正の整数を返すのか負の整数を返すのか常に覚えておくことはなかなか大変です。しかもそのような処理を書く必要があるのでコード自体も長く読みづらいものににってしまいます。 元のリストが破壊されてしまう このデメリットはとても重要です。 Collectons の sort メソッドは、ソートしたいリストの要素を直接並び替えています。プログラミングの世界では、このようにオブジェクトを直接変更するようなメソッドは破壊的メソッドと呼ばれます。破壊的メソッドを使うときは特に注意が必要です。それは、オブジェクトを直接変更してしまうと、後でまたそのオブジェクトを利用する際にソートされているのかされていないのかなどを意識しなければならず、処理を追うのが大変になりバグが混入しやすくなるからです。そのため、元のオブジェクトは変更せず、処理結果の新しいオブジェクトを返すような非破壊的メソッドを利用することが好ましいと言えるでしょう。 Stream を使ったソート それでは Stream を使って見やすくかつ簡潔に記述できるソート方法を解説していきます。 ここまで Collections を使ったソートを説明してきましたが、「Collections を使うソートは大変だな」と感じていただければそれで大丈夫です。 Stream はデータの集まりに対して、さまざまな処理(ソートや絞り込み、各データに対する処理など)を簡潔に記述できるようにするインタフェースです。 Stream を使うことで、正の整数 or 負の整数 or 0 を返す処理(compare メソッドの実装)を意識しなくても柔軟なソートができるようになります。 もちろん今まで通り、 3 種類の整数を返す処理を書いてソートすることもできます。 ここでは、『人オブジェクト』に対して、これまで通り 3 種類の整数を返す処理を Stream を使って書くところから始め、コードを改良していきながら、最後に、compare メソッドの実装を意識しなくてもソートできるような方法を紹介します。 Stream を使った単純なソート Stream を使った『人オブジェクト』のソートに行く前に、数値のように要素の大小関係が明らかなものをソートする方法について触れておきます。 List<Integer> numbers = Arrays.asList( 5 , 4 , 8 , 3 , 1 ); List<Integer> sortedNumbers = numbers.stream().sorted().collect(toList()); Stream でソートをする場合は sorted メソッドを使用します。 大小関係が明らかな場合は、引数なしで使うことで自然順序でソートできます。 また、これは非破壊的メソッドなので、ソート結果を新しい変数に格納しています。 collect(toList()) は sorted メソッドの戻り値型が Stream なので型を List に戻しているだけです。 また、 Integer は Comparable インタフェースを実装している(自然順序が定義されている)ので、以下のように、 naturalOrder() や reverseOrder() を使用して昇順、降順にすることもできます。 // 昇順 List<Integer> sortedNumbers = numbers.stream().sorted(Comparator.naturalOrder()).collect(toList()); // 降順 List<Integer> sortedNumbers = numbers.stream().sorted(Comparator.reverseOrder()).collect(toList()); ※Person クラスが Comparable インタフェースを実装していれば、同様に naturalOrder() や reverseOrder() を使用できます(今回は Comparable は実装していません)。 Stream を使った『人オブジェクト』のソート それでは Stream を使った『人オブジェクト』のソートを解説していきます。 3 種類の整数を返す処理でソート Comparator インタフェースの compare メソッドを実装して大小関係(比較方法)を定義するのはこれまでと同じです。 先ほどの繰り返しになりますが、「2 つの引数をとり、 第 1 引数が第 2 引数より小さいとする場合は負の整数、両方が等しい場合は 0、第 1 引数が第 2 引数より大きいとする場合は正の整数を返すような処理」を渡してあげます。 年齢でソート // Comparatorはラムダ式を使って処理だけを直接渡す List<Person> sortedPeopleByAge = people.stream().sorted((person1, person2) -> person1.ageDifference(person2)).collect(toList()); 名前でソート List<Person> sortedPeopleByName = people.stream().sorted((person1, person2) -> person1.getName().compareTo(person2.getName())).collect(toList()); 大小関係が明らかでない場合は、sorted メソッドの引数に Comparator を入れます( ラムダ式 で入れています)。 Collections のソートと比較して、非破壊的メソッドになっているのは良いことですが、どんな整数を返すか意識する必要があるので、まだ、それほどメリットが感じられません。次は comparing メソッドを使って改良していきます。 (参考: Stream クラスの sorted メソッド ) comparing によるキーの指定だけでソート comparing メソッドは、ソートに使うキーを返す関数を受け取り、そのキーで大小比較する Comparator を返します。 例えば、「Person を受け取って Person の name を返す関数」を受け取ると、Person の name でソートする Comparator を返すことができます。 つまり、「名前の大小関係を使う」ということを指定してあげれば、「3 種類の整数を返す Comparator 」を返してくれます。 言い換えれば compare メソッドを実装した Comparator をキーの指定だけで作ってくれるとても便利なやつです。 List<Person> sortedPeopleByName = people.stream().sorted(comparing((Person person) -> person.getName())).collect(toList()); 3 種類の整数を返す処理ではなく、ソートするキーを返す処理を渡すだけになり、どんな整数を返すのかを考えることが無くなりました! キーを返す処理自体を定数としてやるとコードも見やすくなります。 final Function<Person, String> byName = person -> person.getName(); final Function<Person, Integer> byAge = person -> person.getAge(); List<Person> sortedPeopleByName = people.stream().sorted(comparing(byName)).collect(toList()); List<Person> sortedPeopleByAge = people.stream().sorted(comparing(byAge)).collect(toList()); メソッド参照を使うとさらに簡略化できます。 List<Person> sortedPeopleByName = people.stream().sorted(comparing(Person::getName)).collect(toList()); List<Person> sortedPeopleByAge = people.stream().sorted(comparing(Person::getAge)).collect(toList()); ここまで見やすく簡略化できると、Stream の機能を使わない理由がないのがわかってくるのではないでしょうか。 正の整数を返すんだったかな?負の整数を返すんだったかな?なんて迷うこともなく、コードも記述しやすく理解しやすいものになりました。 (参考: Comparator インタフェースの comparing メソッド ) 複数のキーでソート 最後に、年齢で比較した後に名前で比較して『人オブジェクト』をソートしてみます。 Stream を使うとこの処理も非常に簡単に書けます。comparing に続けて thenComparing メソッドを使うだけです。 List<Person> sortedPeopleByAgeAndName = people.stream().sorted(comparing(Person::getAge).thenComparing(Person::getName)).collect(toList()); 序盤に紹介した、 comparing メソッドの実装を自分で書いて Collections.sort を使ってソートしたコードと比較すると、非破壊的メソッドになりコードも非常にシンプルでわかりやすいものになっていますね。 ソートするなら Stream が圧倒的に便利です。 (参考: Comparator インタフェースの thenComparing メソッド ) 最後に 今回は Stream を使って簡潔にソートをする方法を、従来の Collections を使用した方法と比較して解説しました。 これからソートを使う人も、これまで Collections でソートをしていた人も、ぜひ Stream を使ってシンプルでわかりやすいコードで実装してみてください! 参考書籍 Java による関数型プログラミング Java8 ラムダ式と Stream   エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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
弊社で毎月開催し、 PHP エンジニアの間で好評いただいている PHP TechCafe。 2022年5月のイベントでは「静的解析」について語り合いました。 弊社のメンバーが事前にまとめてきた静的解析ツールやその使い方の情報にしたがって、他の参加者に意見を頂いて語り合いながら静的解析について学びました。 今回はその内容についてレポートします。 rakus.connpass.com 静的解析とは 一般的な使い方 編集中コードの検証 CIに組み込む 実行時の型検査について PHPDocを書かないと型が通らなくなる? テストコードを静的解析の対象に入れるか? PSRとの関係性 代表的なPHP静的解析ツール PHPMD PHP_CodeSniffer (phpcs) PHPStan Psalm PhpStorm PHPコマンドによる文法チェック 編集後記 静的解析とは Wikipedia からの引用が紹介されました。 コンピュータのソフトウェアの解析手法の一種であり、ファイルを実行することなく解析を行うこと。 ソースコード に対して行われることが多く、少数ながらオブジェクトコードに対して行う場合もある。 ja.wikipedia.org 具体例として、 PHP の ソースコード に対してプログラムを実行せずに解析し、問題点を検出するイメージです。 一般的な使い方 今回は以下の二つを挙げて、紹介されました。 編集中コードの検証 IDE を利用し、コーディング中に静的解析を行うことをイメージして紹介されました。 例として「 PHP 文法の誤りを表示」「不適切コードの指摘」「コード補完」を挙げられていました。 その際に、次のような様々な意見がありました。 PHPerならPhpStormを使用していることが多いため、恩恵は受けていると思う。 コードを書いている際に、下に赤い線で表示されている。 コンパイル しない言語で作る場合、実行時エラーとなる前に気づけるのは、非常に心強い味方となる。 CIに組み込む コードを定期的/自動的に静的解析を行うことをイメージして紹介されました。 具体例としては、以下の説明がありました。 GitにPushした際に自動的に解析を行い、不適切なコードやルール違反を指摘する。 静的解析の実行をGit hooksに組み込んでいる場合もある。 GitにPushし、編集したコードで警告が検出されれば、Pushを拒否するようにも出来る。 PhpStormでコミットする際に、チェックを走らせる事や、自分で選んだコード解析ツールを走らせる事もできる。 メリットとして、以下のような事が紹介されました。 コードレビューの観点が明確になる事で、より有益な話に時間を使えるようになる。 人に指摘されるより気が楽である。 実行時の型検査について 参加者から「実行時の型検査」について考えを聞く質問があり、大変盛り上がりました。 (以下が質問と合わせて貼られたリンクになります。) https://stitcher.io/blog/we-dont-need-runtime-type-checks この記事は PHP の現場で紹介された。 PHP ランタイムが型チェックを行うため、メソッドの戻り値の型を PHP の言語機能でintと記載した場合、実行時にStringを戻り値として返却するとタイプエラーで落ちる。 Docコメントに at return int と記載すれば静的解析のときに弾かれるため、二箇所に return を書く必要があるのは面倒である。 これについては、次のような様々な意見がありました。 自社内でもレビュアーによって意見が割れている。 Docコメントだけでなく、メソッドの戻り値の型を記載して欲しい人。 両方記載は面倒くさいため、言語機能としてサポートされている型はメソッドの戻り値の型を記載し、サポートされていない型はDocコメントに記載する人。 レビューコスト下げるために静的解析を入れているが、意見が割れるため返ってコストが上がってしまっている。 これには PHP の歴史的背景もある。 元々はメソッドの戻り値に型指定ができないため、Docコメントへ記載し、静的解析でチェックする事でしのいできた。 メソッドの戻り値に型指定できるようになった今、「Docコメントで静的解析によるチェック」、「言語仕様として縛りを入れる」のどちらが良いのか? 最終的には以下の方針が挙げられました。 今から新規作成の場合は、言語仕様でもチェックした方が良い。 既存処理への修正の場合、元々静的解析チェックのみで統一されていた箇所は、そのまま「Docコメントに戻り値を記載」で統一でも良い。 しかし、Docコメントはただの文字列であるため、言語仕様で縛れるように少しづつ リファクタリング していく方が良い。 PHPDocを書かないと型が通らなくなる? また、質問者様の考えは以下であったようです。 原則、メソッド戻り値の型宣言は記載する。 Docコメントは ジェネリック スのような必要と判断した場合のみ記載する。 上記以外の基準は、PHPStanでレベルを上げた際に、Docコメントを記載しないとエラーと判定された場合に記載する。 これについては、次のような様々な意見がありました。 PHPStanでしかチェックできないものも多いので、今後方針に悩む箇所が発生しそうだ。 PHPStanでレベルを上げる事によりエラーとなる場合は、他のツールを併用する事で、少しづつPHPStanでメンテする範囲を緩める方法もある。 PHP 言語の型指定が表現豊かではないので、表現豊かにすると実行時にそれだけ解析して処理が重くなるため、Docコメントの方に書いておいたほうが PHP 全体の利益になる。 今は言語仕様が過渡期であるため、 PHP の利点(途中でエラー中断せず最後まで実行)を残すためにノーチェックに出来るオプションが希望されている。 メソッド戻り値の型宣言をCS-Fixerで一括で記載する方法もある。 自動置き換えによる信頼性を疑問視しているので、検出だけ担当するのは良さそう。 テストコードが存在すれば品質は担保できる。 最終的には、チーム皆で話し合って方針を決めて行くのが大事だとまとめられました。 テストコードを静的解析の対象に入れるか? 話の流れから「CIとかで解析を回すときにテストコードは解析対象に入れるか否か」のアンケートが取られました。 このアンケートには、次のような様々な意見がありました。 テストコードを解析するメリットが分からない。 プロジェクト内で PHP の有名なライブラリを確認したが、解析対象になっているものが多かった。 変更部分のみテストできる機能があれば便利である。 CS-Fixerを使用しているが、PHPStanは面倒なので除外にすることが多い。 解析の対象に入れてデメリットは特にないため、基本方針として解析の対象としている。 議論の余地はあるようですが、解析対象として入れるほうが優勢でありました。 PSRとの関係性 PSRとは PHP -FIGが策定している PHP の標準規約集です。 規約は各ルールがほぼ独立しており、好きな規約のみを選んで採用することができます。 www.php-fig.org PSR1とか2とか12とかはコーディング規約に関するものなので取り入れている静的解析ツールも多いのではないか PSR2は現在では非推奨だが、昔からの名残で残っているものもある 代表的な PHP 静的解析ツール PHPMD コードの複雑性を検知してくれるものです。 具体例を挙げると、バグになりそうなコード、最適ではないコード、複雑な表現、未使用のパラメータ・メソッド・プロパティなどを検出してくれるツールです。 これについては、次のような様々な意見がありました。 インストール方法は色々あり、最近だとComposerが便利。直接GtiHubから落とすことも出来る。 コマンドで実行した場合、PHPMDで検出対象のファイルや ディレクト リを指定、結果のフォーマット選択、ルールセット指定の手順である。 ルールセットは、コードサイズや、設計関連ルール、ネーミング、未使用コード検出などのルールが予め定義されている。 xml ファイルを配置し、必要なルールのみ適用、または、不要なルールを削除し、個別ルール(最大値や最小値など)へ変更することも可能である。 実行例 [ root@vg110 core ] # vendor/phpmd/phpmd/src/bin/phpmd test .php text codesize,controversial,design,naming,unusedcode /usr/local/vanguardDev/vanguard/vg-back/core/ test .php:5 CamelCaseMethodName The method test_function is not named in camelCase. /usr/local/vanguardDev/vanguard/vg-back/core/ test .php:5 CamelCaseParameterName The parameter $param_string is not named in camelCase. /usr/local/vanguardDev/vanguard/vg-back/core/ test .php:5 CamelCaseVariableName The variable $param_string is not named in camelCase. phpmd.org PHP _CodeSniffer (phpcs) コーディング規約の違反を検出するライブラリです。 PSRや PEAR などの様々なコーディング規約を指定して検査を実施してくれます。 これについては、次のような様々な意見がありました。 独自の規約を追加することも可能である。 インストール方法は色々あり、Composerや GitHub からなど、好みの方法で導入できる。 コーディング規約の違反を自動で修正するphpcbfも付属している。 全て修正する事は出来ず、修正出来た物も目視チェックは必要である。 完全に信用する訳ではなく、phpcsの指摘があるファイルをユーザに通知し、修正は手動で行う運用をしている。 phpcbfは結構正確だが、phpcsに問題がある。 改行やインデントについて、コード上は正常であっても、CS-Fixerがエラーと判定するケースがたまにある。 実行例 $ phpcs /path/to/code/myfile.php FILE: /path/to/code/myfile.php -------------------------------------------------------------------------------- FOUND 5 ERROR ( S ) AND 1 WARNING ( S ) AFFECTING 5 LINE ( S ) -------------------------------------------------------------------------------- 2 | ERROR | Missing file doc comment 20 | ERROR | PHP keywords must be lowercase; expected " false " but found | | " FALSE " 47 | ERROR | Line not indented correctly; expected 4 spaces but found 1 47 | WARNING | Equals sign not aligned with surrounding assignments 51 | ERROR | Missing function doc comment 88 | ERROR | Line not indented correctly; expected 9 spaces but found 6 -------------------------------------------------------------------------------- コーディング規約の指定 # インストールされているコーディング規約を確認 $ phpcs -i The installed coding standards are MySource, PEAR, PSR1, PSR12, PSR2, Squiz and Zend # コーディング規約を指定して実行(デフォルトはPEAR) $ phpcs --standard=PSR12 /path/to/code/myfile.inc # デフォルトのコーディング規約を変更 $ phpcs --config-set default_standard PSR12 規約のカスタマイズ phpcs. xml ファイルを設置して独自のコーディング規約を設定できる。 <? xml version = "1.0" ?> <ruleset name = "Custom Standard" > <rule ref = "PSR12" > <!-- "PSR12" の中で除外するルール --> <exclude name = "Generic.Files.LineLength" /> </rule> <!-- 追加するルール --> <rule ref = "PEAR.WhiteSpace.ObjectOperatorIndent" /> <!-- 除外するファイル・ディレクトリ --> <exclude-pattern> node_modules/ </exclude-pattern> </ruleset> github.com PHPStan こちらも同じくComposerやDockerから利用可能です。 PHARからもダウンロード可能です。 ただし、Composerが無いとPHPStanの 拡張機能 は使えません。 解析の厳密さはオプションで変更可能で、PHPStanの場合はレベル0から9で設定可能です。 レベル0が一番ゆるく、レベル9が最も厳密となっている レベル0は基本的なチェックで、PSRと同等のチェックレベル レベル9では、混合型(mixed)についても厳密であるレベル(混合型(mixed)は、別の混合型(mixed)に渡す操作しか許可されない)になる PHPStanの公式に記載がありますが、以下の理由から使用方法はレベル0から順番に対応していくのが正攻法のようです。 初めからレベル9を設定すると、エラー件数が膨大となり、何から手を付けたらいいか分からなくる。 上位のレベルでエラーを検知した際は、下位のレベルで検知されるエラーも合わせて修正を行わないと、エラーは解消されない 歴史の長いプロジェクトでは、レベル0の設定でも多くのエラーが検出されます。 全ての検出されたエラー対応が困難な場合は、ベースラインの設定を推奨します。 ベースラインを設定することにより、以下のようなことが出来ることが紹介されました。 ある時点で検出したエラー内容を基準とし、それ以降の静的解析の実行で、基準としたエラー内容を報告しないように出来る。 既存コードで検出されているエラーを除外出来るため、新しいコードと変更されたコードで検知されたエラーのみに注力することが出来る。 カスタムルールも設定できるようになっており、 phpstan-rules と phpstan-strict-rules を使用して拡張することが出来る。 これについては、次のような様々な意見がありました。 レベル5からレベル6の間が深い河である レベル6は「タイプヒントの欠落を表示」であるため、クリア難易度が高い ベースラインの概念があることは有難い 別のツールでPHPStanのベースラインと同様の事を行ったが、非常に辛かった ベースラインの概念は他ツールでは、Psalmにもある サイボウズ さんでは「ベースラインを自動実行する」取り組みを行っていて面白そう(※) phpstan.org ※ 情報元の紹介はありませんでしたが、記事化にあたって調べたところ参考になるスライドがありました。  ツールはPHPStanではないですが、適応出来そうです。 効果的な静的解析の CI導入パターンを求めて / Great static analysis with CI - Speaker Deck Psalm こちらもPHPStanと同様で、厳密さをレベルで調節できます。 ただし、レベルの強弱がPHPStanと逆になっており、レベル1が最も厳しく、レベル8が一番ゆるい設定になっています。 良い点として、次のように紹介されました。 検知された違反ルールがなぜ不適切なのか理由が解説されるため、納得感を得やすくなっている。 これがなんで悪いの? と思うときがあるが、理解ができると直すモチベーションも得られるのではないか。 例 psalm.dev PhpStorm 編集中に解析を行い、危険な箇所に対して下線で示すか、ハイライトしてくれます。 解析方法は PHP ファイルをPhpStormで開くだけです。 文法の誤りをハイライトし、重要度によってハイライト部分の色が変更されるため見やすい。 プロファイルの編集で解析内容を編集でき、任意の解析を追加する事もできる。 他の解析ツールをインストールし使用すことも可能。 pleiades.io PHP コマンドによる文法チェック 少し本筋とは違いますが、「静的解析を調べる際に、 php -l で文法チェックができることを見つけた。」と紹介されました。 そのため、文法の間違いについては、静的解析ツールを使用せずにエラー検知する事が出来ます。 これについては、次のような様々な意見がありました。 php -l で静的解析できることを初めて知った。 静的解析かと言われると疑問がある。一応解析は行っているので意味は合っている? 全ファイルを対象に php -l を実行したことが何度かある。 おそらく全ファイルに対して一括置換のような処理を実行したタイミング。 または、 PHP のメジャーバージョンアップ後に粗探しとして調査するタイミング。 PHP のバージョンアップ後に一旦実行し、エラー検知した場合は早急に対応が必要であるため。 編集後記 以上、静的解析の概要と代表的なツールについて取り上げました。 PHP TechCafeでは今まで取り上げることがなかった話題でしたので、主催者/参加者ともに盛り上がりを見せていました! 今後も静的解析の活用方法や、新たなニュースに着目していきたいと思います! 「 PHP TechCafe」では今後も PHP に関する様々なテーマのイベントを企画していきます。 皆さまのご参加をお待ちしております。 connpass.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
技術広報の yayawowo です。 今回は、 Linux 上でファイルや ディレクト リ内容を一覧表示する際に利用する、 『ls コマンド』 について紹介します! ls コマンドの基本をはじめ、活用例をコマンドサンプルを交えて説明させていただきます。 【目次】 ls コマンドとは? ls コマンドの使い方 基本書式 ls コマンドのオプション一覧 表示フォーマット関連 出力フォーマット関連 サイズ関連 時間関連 ソート関連 ls コマンドの活用例 Linux関連のおすすめブログ 終わりに ls コマンドとは? lsコマンドは、ファイルや ディレクト リの一覧を表示するためのコマンドです。 ls コマンドの使い方 基本書式 ls コマンドの基本書式は、以下の通りです。 $ ls [ オプション ] [ ファイル/ディレクトリ ] ファイルや ディレクト リの一覧を表示させるには、対象のファイル/ ディレクト リ名を指定する必要があります。 また、ls コマンドには多くのオプションがあります。 以下にまとめておきますので、用途に合わせて使い分けください。 ls コマンドのオプション一覧 表示フォーマット関連 オプション 説明 -l 詳細情報を表示 -o グループ情報以外の詳細情報を表示 -n 所有者/グループ名の代わりにユーザIDとグループIDとし、詳細情報を表示 -a .(ドット)ファイルも含めて表示 -A 「.」「..」以外の.(ドット)ファイルも含めて表示 -d ディレクト リ内容を表示せず、 ディレクト リ名のみを表示 出力フォーマット関連 オプション 説明 -1 リストを1件1行で表示 -m リストをカンマ区切りで表示 -C リストの出力を列(縦)にソート表示 -x リストの出力を行(横)にソート表示 -q 表示不可能な文字を?で表示 -F ファイル/ ディレクト 名の後にタイプ識別子(*/=>@|のどれか)を付けて表示 -p ディレクト リ名の後にのみ、/を付けて表示 サイズ関連 オプション 説明 -k -l と併用し、1KB単位のサイズ形式で表示 時間関連 オプション 説明 -c -l と併用し、ファイル属性の変更時間(ctime)を表示 -u -l と併用し、最終アクセス日を表示 ソート関連 オプション 説明 -S ファイルサイズが大きい順に表示 -t ファイル更新日が新しい順に表示 -v 数字順にソート表示 -r 並び順を反転させて表示 ls コマンドの活用例 前提としまして、 /home 配下に以下 ディレクト リとファイルを準備しております。 /home  ┗ sample  ┗ sample2  ┗  hoge .txt  ┗ hoge2.txt これを使い、ls コマンドの活用例をご紹介します! 詳細情報を表示(-l) localhost:/home# ls -l total 8 -rw-r--r-- 1 root root 0 Sep 13 09:12 hoge.txt -rw-r--r-- 1 root root 0 Sep 13 09:40 hoge2.txt drwxr-xr-x 2 root root 37 Sep 13 09:12 sample drwxr-xr-x 2 root root 37 Sep 13 09:12 sample2 所有者/グループ名の代わりにユーザIDとグループIDとし、詳細情報を表示(-n) localhost:/home# ls -n total 12 -rw-r--r-- 1 0 0 0 Sep 13 09:12 hoge.txt -rw-r--r-- 1 0 0 0 Sep 13 09:40 hoge2.txt drwxr-xr-x 2 0 0 37 Sep 13 09:12 sample drwxr-xr-x 2 0 0 37 Sep 13 09:12 sample2 .(ドット)ファイルも含めて表示(-a) localhost:/home# ls -a . .. hoge.txt hoge2.txt sample sample2 リストを1件1行で表示(-1) localhost:/home# ls -1 hoge.txt hoge2.txt sample sample2 ディレクト リ名の後にのみ、/を付けて表示(-p) localhost:/home# ls -p hoge.txt hoge2.txt sample/ sample2/ ファイルサイズが大きい順に表示(-S) ※ -l と -S の複数オプション利用のため、コマンド実行時は -lS と定義しています。 localhost:/home# ls -lS total 8 drwxr-xr-x 2 root root 37 Sep 13 09:12 sample drwxr-xr-x 2 root root 37 Sep 13 09:12 sample2 -rw-r--r-- 1 root root 0 Sep 13 09:12 hoge.txt -rw-r--r-- 1 root root 0 Sep 13 09:40 hoge2.txt ファイル更新日が新しい順に表示(-t) ※ -l と -t の複数オプション利用のため、コマンド実行時は -lt と定義しています。 localhost:/home# ls -lt total 8 -rw-r--r-- 1 root root 0 Sep 13 09:40 hoge2.txt -rw-r--r-- 1 root root 0 Sep 13 09:12 hoge.txt drwxr-xr-x 2 root root 37 Sep 13 09:12 sample2 drwxr-xr-x 2 root root 37 Sep 13 09:12 sample 並び順を反転させて表示(-r) ※ -l と -t と -r の複数オプション利用のため、コマンド実行時は -ltr と定義しています。 # ファイル更新日順に表示 localhost:/home# ls -lt total 8 -rw-r--r-- 1 root root 0 Sep 13 09:40 hoge2.txt -rw-r--r-- 1 root root 0 Sep 13 09:12 hoge.txt drwxr-xr-x 2 root root 37 Sep 13 09:12 sample2 drwxr-xr-x 2 root root 37 Sep 13 09:12 sample # ファイル更新日順を反転し、表示 localhost:/home# ls -ltr total 8 drwxr-xr-x 2 root root 37 Sep 13 09:12 sample drwxr-xr-x 2 root root 37 Sep 13 09:12 sample2 -rw-r--r-- 1 root root 0 Sep 13 09:12 hoge.txt -rw-r--r-- 1 root root 0 Sep 13 09:40 hoge2.txt Linux 関連のおすすめブログ tech-blog.rakus.co.jp Linux コマンドのまとめ記事です! 本ブログがあれば、 Linux 操作に困らないと思います。 tech-blog.rakus.co.jp Linux 上でエディタを触る機会が多い方向け! vi コマンドの基本的な使い方を目的別にまとめております。 tech-blog.rakus.co.jp Linux 上に存在するログを調査するにあたり、良く用いる文字列検索を行う grep コマンドの記事です。 grep コマンドの中でも、実務でよく使っている検索方法を併せて紹介しています! tech-blog.rakus.co.jp 2つのファイルを比較して違い(差分)を出力するdiff コマンド。 diffコマンドの表示オプションを知りたい方は、是非ご確認ください。 tech-blog.rakus.co.jp Linux 上でファイルや ディレクト リ検索時に利用するfind コマンドについて紹介! find コマンドの基本をはじめ、活用例をコマンドサンプルを交えて説明しております。 終わりに いかがでしたでしょうか? 今回はファイルや ディレクト リの一覧を表示したい際に良く用いる、ls コマンドをご紹介させていただきました。 また、ls コマンドのオプションも今回ご紹介させていただきましたが、利用用途に合わせて使い分けていただくことで業務効率を上げることができます! ls コマンドは、 Linux を触る方にとっては基本のキかと思います。 もし、ls コマンドを触るときに「どのオプション使うんだっけ?」と不安になった方は是非本記事をご参考いただけますと幸いです。 最後までお読みいただきありがとうございました! ◆ その他おすすめブログ ・ sar コマンド【使い方 まとめ】 ・ iptables まとめ【Linux ファイアウォール】 ・ sed コマンド【使い方 まとめ】 ・ Linuxのファイル操作でよく使うLinuxコマンド ・ 初心者のためのawkコマンド ・ 実務で使える!基本的なシェル(Linux)コマンドの話 ~forとsed~ ・ 【Linux】今振り返りたい、プロセスって何? エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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
弊社で毎月開催し、 PHP エンジニアの間で好評いただいている勉強会「 PHP TechCafe」。 2022年3月のイベントでは『PHPerのための「Laravel9について語る」』をテーマに語り合いました。 弊社のメンバーがLaravel9の新機能などの情報を元に、他の参加者に意見をいただいて語り合いながらLaravel9について理解を深めました。 今回はその内容についてレポートします。 rakus.connpass.com PHP TechCafeとは PHPerのための「Laravel 9 について語る」 Laravel9の特徴 PHP8対応 LTSが消えた ハイペースなマイナーバージョンアップ Laravel9の新機能や変更点 Symfony Mailer Flysystem 3.x Improved Eloquent Accessors / Mutators Enum Eloquent Attribute Casting Implicit Route Bindings With Enums Forced Scoping Of Route Bindings Controller Route Groups Full Text Indexes / WhereClauses Slot Name Shortcut Checked / Selected Blade Directives whereNot clause Improved Ignition Improved route:list CLI Output アップデートの落とし穴 app/Http/Middleware/TrustProxies.php の修正 $headersの修正 編集後記 PHP TechCafeとは 本題に入る前に「 PHP TechCafe」について軽く説明します。 「 PHP TechCafe」とは弊社が主催しているエンジニア向けのイベントで、エンジニア同士で技術について話し合う憩いの場(カフェ)です。 月に1回開催し、コロナ禍の現在はオンラインで開催しています。 対象者は PHP 入門の初級エンジニアからシニアエンジニアを幅広くカバーし、学びの場を提供してエキスパートまでの自己成長を支援することを目的にしています。 「 PHP TechCafe」は以下の3部構成です(日によって変わることもあります)。 ライトニング トーク (LT) PHPer's NEWS 特集 2022年3月の特集は『PHPerのための「Laravel 9 について語る」』をテーマに開催いたしました。 今回はその中で紹介された内容や盛り上がったポイントをレポートします。 PHPerのための「Laravel 9 について語る」 Laravel9の特徴 まずはお約束のLaravel9の特徴からです。 PHP8対応 PHP8に対応し、8未満には対応しなくなりました。 LTSが消えた どんどんバージョンを上げていきましょう!、という意味かもしれませんね。 ハイペースなマイナーバージョンアップ Laravel9は、短いスパンで次々とマイナーバージョンアップを重ねていることにも注目が集まり、話題となりました。 マイナーバージョンアップで新機能がいくつも追加されています。 意外と使えそうな機能もよくあるので、新機能を使いたくなったら上げなきゃいけないですね。 Laravel9の新機能や変更点 Symfony Mailer Laravel標準の メーラー がSwift Mailerから Symfony Mailerに変更されました。 そもそもSwift Mailerってメンテナンスが止まっているのでLaravelもこちらに乗り換えたようです。 結構互換性の無いものもあるので、しっかり見て変更しましょう。 インターフェースも変わってしまったんですね。Classを呼んでるところだけ置き換えたらOKという感じでもないですね。しっかり置き換えないといけない。 Sending Emails with Mailer (Symfony Docs) Flysystem 3.x Storage ファサード によって提供されており、ファイル操作系処理を強化するFlysystemがバージョンアップしました。 S3とか FTP などを利用している場合はドライバのアップデートが必要です。 挙動も変わっているんですよね。PutやWrite、Writestreamなどを使うとデフォルトでファイルを上書きするようになるので、注意しないといけないです。 存在しないファイルを指定すると以前はNullが返ってきていましたが、例外が返ってくるようになりました。 File Storage Abstraction for PHP - Flysystem Improved Eloquent Accessors / Mutators これはEloquentのモデルについてです。 Laravel9 以前 <?php public function getNameAttribute ( $ value ) { return strtoupper ( $ value ) ; } public function setNameAttribute ( $ value ) { $ this -> attributes [ 'name' ] = $ value ; } ↑ モデルにアクセサーとミューテタに プレフィックス 付きのメソッドを定義する必要がありました。 Laravel9 <?php use Illuminate\Database\Eloquent\Casts\Attribute; public function name () : Attribute { return new Attribute ( get : fn ( $ value ) => strtoupper ( $ value ) , set : fn ( $ value ) => $ value , ) ; } ↑ プレフィックス なしのメソッドに戻り値の型を指定(Attribute)することで、アクセサーとミューテタが定義可能となりました。 ルーティング 9.x Laravel Enum Eloquent Attribute Casting PHP8.1以上で稼働させている場合のみ動作して、モデルに挿入する値を Enum 型でキャスト可能にします。 <?php use App\Enums\ServerStatus; /** * The attributes that should be cast. * * @var array */ protected $ casts = [ 'status' => ServerStatus :: class , ] ; Eloquent:ミューテタ/キャスト 9.x Laravel Implicit Route Bindings With Enums ルートパラメータを Enum 型で指定することができます。 型に一致しないパラメータが指定されたら、404が返されます。 Enum の定義 <?php enum Category : string { case Fruits = 'fruits' ; case People = 'people' ; } ルーティングの記載 <?php Route :: get ( '/categories/{category}' , function ( Category $ category ) { return $ category -> value ; }) ; Enum が使えるようになったから、こういうところにも使っていこうっていうことですかね。 どこを見たら良いかわかりやすいですね。 404にしてくれるのがありがたい。無理やり動かすのは流石につらいので。 ルーティング 9.x Laravel Forced Scoping Of Route Bindings 親のモデルと子のモデルを使用してRoute model bindingを行いたい場合、子のモデルにはカスタムキーを使用する必要がありました。 Laravel9 以前 <?php use App\Models\Post; use App\Models\User; Route :: get ( '/users/{user}/posts/{post:slug}' , function ( User $ user , Post $ post ) { return $ post ; }) ; それが、scopeBindings()を使用することで、カスタムキーなしでも子のモデルに対してRoute model bindingを行えるようになりました。 Laravel9 <?php use App\Models\Post; use App\Models\User; Route :: get ( '/users/{user}/posts/{post}' , function ( User $ user , Post $ post ) { return $ post ; }) -> scopeBindings () ; Route model bindingとは、例えばこのコードの場合、RouteパラメータのところにUserやPostやモデル名を指定しています。UserのところにID1が入ったとしたらID1のUserのモデル インスタンス を自動的に取ってきてくれるというものです。 今まではカスタムキーで”post:slug”を定義する必要があったのですが、それがLaravel 9からScope Bindingを使えば不要となり、シンプルに書きやすくなったようですね。 ルーティング 9.x Laravel Controller Route Groups 全てのルートに共通のコントローラをグループとして定義できるようになりました。 Laravel8 <?php # 何度もController名を書かないといけない use App\Http\Controllers\OrderController; Route :: get ( '/orders/{id}' , [ OrderController :: class , show ]) ; Route :: post ( '/orders' , [ OrderController :: class , store ]) ; Laravel9 <?php use App\Http\Controllers\OrderController; Route :: controller ( OrderController :: class ) -> group ( function () { Route :: get ( '/orders/{id}' , 'show' ) ; Route :: post ( '/orders' , 'store' ) ; }) ; コントローラごとにグループ分けができるようになりました。 この変更にはこんな声がありました。 ヤンチャな実装する人だったら、まとまりを無視して変なところに定義を書いたりすることがありますが、Laravel9だとそこは強制されるのでヤンチャできなくなりますね。 概念としてコントローラが上になったような気がして、個人的にはちょっと嫌だなって思いますね。コントローラがルートを決めたんじゃないかというニュアンスになるので。 ルートとコントローラが密結合になったんじゃないかという印象を持ちました。ここまでやるならそもそも定義書かなくてよくない?という感じもしますね。 ルーティング 9.x Laravel Full Text Indexes / WhereClauses 指定した列に FULLTEXT INDEX を作成できます。※ MySQL か PostgreSQL <?php $ table -> text ( 'bio' ) -> fullText () ; FULLTEXT INDEX がある列に対して、WHERE句を作成することもできます。 <?php $ users = DB :: table ( 'users' ) -> whereFullText ( 'bio' , 'web developer' ) -> get () ; これすっごくありがたいです。どデカいテキストの部分一致検索みたいなやつは、 巨大掲示板 とかになってくると Elasticsearch にするとかでないと対応できなかったんです。 PostgreSQL にしろ MySQL にしろテーブルとかインデックスの型の定義がすごく独特で大変なんです。しかも標準 SQL に定義されていない機能なので正直ダルいんですよ…。だから欲しかった人は本当に欲しかったものだと思います。 今まではCreate Indexとか SQL を直書きしないと駄目ですよね。それを検索するときもwhereじゃなくてwhereRawとか使って SQL 直書きするしかなかったんですよ。それが公式にサポートされたから作れるようになったんですね。 データベース:クエリビルダ 9.x Laravel Slot Name Shortcut Blade関連ですね。 X-slotタグを短く書けるようになりました。 Laravel9 以前 <x-alert> <x-slot name = "title" > Server Error </x-slot> </x-alert> Laravel9 < x-slot : title> Server Error </x-slot> これはサッと読めるようになるので、どんどん使っていきたいですね。   Bladeテンプレート 9.x Laravel Checked / Selected Blade Directives こちらもBlade関連。 @checked  チェックボックス をONに指定できる <?php < input type = "checkbox" name = "active" value = "active" @checked ( old ( 'active' , $ user -> active )) /> @selected セレクトボックスにてデフォルトで選択されている項目を指定できる <?php < select name = "version" > @ foreach ( $ product -> versions as $ version ) < option value = "{{ $ version }}" @selected ( old ( 'version' ) == $ version ) > {{ $ version }} </ option > @ endforeach </ select > @checkedって書いていたらそこの チェックボックス をオンに出来ますよとか、@selectedにしたらデフォルトで選択されている項目を指定できます。今まで 三項演算子 で書いていたものがBladeで書けるようになって、痒いところに手が届くようになりました。 Bladeテンプレート 9.x Laravel whereNot clause クエリビルダでずっとサポートされていなかった、whereNotがようやく使えるようになりました。 whereNotは何回かリジェクトされて9.3でやっと取り込まれた経緯があるそうです。 できることなら肯定形で書きたいです。でも SQL の構文として存在するものはサポートしてほしい気はしますね。今までそれをあえて入れなかったのは、否定形を入れずに綺麗なものを目指すんだという強い意思があったのかもしれないですね。 SQL の構文としてはあるけど、Laravelには無いから今回取り入れたという感じですかね。あえて書きたいケースもありますし。他のすべてのメソッドにはNotがあるけれど、whereにだけ無いと。 データベース:クエリビルダ 9.x Laravel Improved Ignition 開発中の例外発生時などに表示されるIgnitionが新しくなりました。 ダークテーマや「エディタで開く」を選べるようになりました。 リリースノート 9.x Laravel Improved route:list CLI Output route:listの CLI が見やすくなりました。 最近 CLI の見やすくなるアップデートが割と多いですね。 本当に見やすくなりました。ただ一方で PHPUnit Printerに外部ライブラリを入れて綺麗にしていた人は一気に全部崩れたみたいです。なので外して公式にし直すというのがウチではありました。 以前は色も何もついてなくて、”…”もついてなくて、縦バーで区切ってましたね。 リリースノート 9.x Laravel アップデートの落とし穴 アップデートガイドのところでここの PHP ファイルだけ開いて修正してね、ということです。ここだけ手作業ということで、ハマりそうですね。 app/Http/Middleware/TrustProxies. php の修正 変更前 use Fideveloper\Proxy\TrustProxies as Middleware 変更後 use Illuminate\Http\Middleware\TrustProxies as Middleware $headersの修正 変更前 protected $headers = Request::HEADER_X_FORWARDED_ALL; 変更後 protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_POST | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB ; Composerをアップデートしてもダメで、Bootstrapのキャッシュを消さなきゃいけないのでこの作業が必要になるようです。 ここはつまづきますね。「キャッシュ周りでアップデートしたのに何で古い方を読みにいってるの?」ってことが起こってました。 Upgrade Guide - Laravel - The PHP Framework For Web Artisans 編集後記 メジャーバージョンアップだけではなく、マイナーバージョンアップでも次々に機能追加がされたりとさらに開発が活発化している印象でした。 LTSがなくなったのは我々開発者としては痛いところですが、これは長期サポートのコストをLaravelの開発をどんどん前に進めることに充てるというアグレッシブな思想があるからかもしれませんね。 ここでは語りつくせない変更もまだまだあるので、リリースノートは要チェックです。 今後もLaravelの進化から目が離せません! 「 PHP TechCafe」では今後も PHP に関する様々なテーマのイベントを企画していきます。 皆さまのご参加をお待ちしております。 connpass.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
はじめに 皆さんこんにちは、開発課のthree_yagiです。 現在の業務では不具合などが発生したときの原因調査を担当することが多く、本番環境のログの調査をよく行っています。 Linux 上に存在するログを調査するにあたって、必須ともいえるのが文字列検索を行う grep コマンドです。 こちらの記事では、 grep コマンドについて私が実務でよく使っている検索方法と併せて紹介していきます。 目次 はじめに 目次 grep コマンドの基本 基本構文 よく利用するオプションと正規表現 オプション 正規表現 利用例(基本編) 基本の検索 AND検索 OR検索 NOT検索 大文字と小文字を区別しないで検索 マッチした行数を表示 マッチした行のうち〇行だけ表示 マッチした行の前後の行も表示する 正規表現で様々な条件で検索する 利用例(応用編) grepした文字をハイライト表示 圧縮ファイルをgrepする grepコマンド まとめ   grep コマンドの基本 grep は 検索対象のファイルから指定した文字列を含む行を表示する Linux コマンドです。 正規表現 とオプションを駆使することで様々な条件での検索が行えます。 基本構文 $ grep [オプション] 検索文字列 対象ファイル よく利用するオプションと 正規表現 実際によく利用しているオプションについて記載します。 これらを使いこなせれば一通りの検索はできるかなと思います。 オプション オプション 説明 -E 拡張正規表現 (ERE)を利用する ※このオプションなしでも 正規表現 の「. * ^ $ [ ]」は利用可能 -e [検索文字列] オプションの引数として検索文字列を指定する このオプションを複数回使用することでOR検索が可能 -v 結果を反転する。検索文字列を含む行以外を表示 -i 検索文字列の大文字と小文字を区別しない -c マッチした行ではなく、マッチした行数を表示 -m [数字] [数字]行分だけ表示する -B [数字] マッチした行の前 [数字] 行を表示 -A [数字] マッチした行の後 [数字] 行を表示 -C [数字] マッチした行の前後 [数字] 行を表示 -h 出力する行の前にファイル名を付けないようにする -n 頭にそのファイル内での行数を表示 正規表現 正規表現 を利用することで柔軟な検索ができるようになります。 拡張正規表現 の列が「〇」の 正規表現 はオプションで「-E」を指定する必要があります。 正規表現 意味 拡張正規表現 . 任意の 1 文字 * 直前の 1 文字または パターンが 0 回以上繰り返す ^ 行の先頭を表す $ 行の末尾を表す [ ] 括弧内の任意の 1 文字が存在する ( ) パターンのグループ化 〇 + 直前の 1 文字 または パターンが 1 回以上繰り返す 〇 ? 直前の 1 文字 または パターンが 0 回または 1 回だけ存在 〇 {n} 直前の 1 文字 または パターンが n 回繰り返す 〇 | 2 パターンのうちどちらか片方 〇 利用例(基本編) 実際の利用例を見ながら利用方法について確認してみましょう。 検索対象のテストファイル(test.txt)には 都道 府県がローマ字で記載されたファイルを使用しています。 # テスト用ファイル $ cat test .txt hokkaido aomori iwate miyagi akita yamagata ︙ miyazaki kagoshima okinawa 基本の検索 まずは単純に指定した文字列を含む行を表示してみます。 # 【検索条件】'yama' を含む行を表示 $ grep ' yama ' test .txt yamagata toyama yamanashi wakayama okayama yamaguchi AND検索 grep の結果を |(パイプ) でつなぐことで1つ目の grep の検索結果に再度 grep をかけることになり、AND検索が可能です。 # 【検索条件】'yama' と 'oka'を含む行を表示 $ grep ' yama ' test .txt | grep ' oka ' okayama OR検索 OR検索はオプションの「-e」を使って検索文字を複数指定する方法と、 正規表現 を利用する方法があります。 正規表現 を利用する場合は 拡張正規表現 の「|」を使用するので、オプション「-E」を指定する必要があります。 どちらも結果は同じになるのでお好きな方を利用してください。 # 【検索条件】'yama' または 'oka' を含む行を表示 # 「-e」を使ってOR検索 $ grep -e ' yama ' -e ' oka ' test .txt # 正規表現でOR検索 $ grep -E ' yama|oka ' test .txt # 結果は同じ yamagata toyama yamanashi shizuoka wakayama okayama yamaguchi fukuoka NOT検索 ○○を含む行以外を検索したい場合は、検索結果を反転する「-v」オプションを利用します。 #【検索条件】 'yama'を含まない行を表示 $ grep -v ' yama ' test .txt hokkaido aomori iwate miyagi akita fukushima ibaraki ⁝ 複数指定する場合 #【検索条件】 'yama' と 'oka'を含まない行を表示 # OR検索の結果を「-v」で反転させればどちらも含まない行を表示できます $ grep -v -e ' yama ' -e ' oka ' test .txt $ grep -v -E ' yama|oka ' test .txt ( 結果省略 ) 大文字と小文字を区別しないで検索 デフォルトでは文字の大文字と小文字を区別した状態で検索を実行します。 大文字と小文字を区別せずに検索する場合は「-i」オプションを利用します。 # オプションなしだと'Yama'を含む行しか対象とならないのでいずれの行も出力されません $ grep ' Yama ' test .txt # オプションありだと'yama'も対象となるので結果が出力されます $ grep -i ' Yama ' test .txt yamagata toyama ⁝ マッチした行数を表示 「-c」オプションを用いることで検索結果の行ではなく行数が表示されます。 #【検索条件】'yama'を含む行の合計の行数を表示 $ grep -c ' yama ' test .txt 6 マッチした行のうち〇行だけ表示 「-m」オプションで結果を表示する行数を指定できます。 #【検索条件】'yama'を含む行のうち頭から2行分だけ表示 $ grep -m 2 ' yama ' test .txt yamagata toyama マッチした行の前後の行も表示する 「-B」「-A」「-C」オプションを利用することでマッチした行の前後の行も表示できます。 「-B」「-A」で前と後それぞれの表示する行数を指定でき、「-C」で前後まとめて行数を指定できます。 ログを調査する際にはよく使うオプションです。 #【検索条件】 'tokyo' を含む行を前後1行も含めて表示 # 「-B」「-A」で前後それぞれ指定 $ grep -B 1 -A 1 ' tokyo ' test .txt # 「-C」でまとめて指定 $ grep -C 1 ' tokyo ' test .txt chiba tokyo kanagawa #【検索条件】 'tokyo' を含む行を前3行,後2行も含めて表示 $ grep -B 3 -A 2 ' tokyo ' test .txt gunma saitama chiba tokyo kanagawa niigata 正規表現 で様々な条件で検索する #【検索条件】'〇a〇a〇a'という文字列を含む行を表示 # '.'は任意の1文字 $ grep ' .a.a.a ' test .txt yamagata kanagawa yamanashi wakayama okayama kagawa nagasaki # こちらも同じ条件での検索となる # '( )' グループ化 # '{n}'直前の文字のn回の繰り返し $ grep -E ' (.a){3} ' test .txt #【検索条件】'a'で始まる行を表示 # `^`は行頭を意味する $ grep ' ^a ' test .txt aomori akita aichi #【検索条件】'a'で始まり先頭以外にも'a'を含む行を表示 # '*'は直前の文字の0回以上の繰り返し # '.'と組み合わせた '.*' は任意の0 or n文字を表す $ grep ' ^a.*a ' test .txt akita 利用例(応用編) grep した文字をハイライト表示 --color=always で検索した文字列に色を付けることができます。 検索結果が大量のときや、 正規表現 で検索していてどこからどこまでがマッチしているか確認したいときなどに便利です。 $ grep --color = always -E ' (.a){3} ' test .txt このように表示されます 圧縮ファイルを grep する ログファイルなどは圧縮された状態で保存されていることが多いかと思います。 圧縮されたファイルに対しては zgrep コマンドを利用することで grep と同じように検索できます。 grep と同じオプションや 正規表現 も利用でき、使用方法としては grep と同じになります。 $ zgrep -i ' Yama ' test .gz grep コマンド まとめ grep コマンドは Linux を利用するにあたって必須ともいえるコマンドです。 使いこなせるようになればファイルの検索などのスピードが劇的にアップします。 今回紹介したのは一部のオプション、 正規表現 となります。 他にも有用なものはたくさんあるのでぜひご自身で調べてみてください。 ◆ Linux の理解をより深めたい方へ以下関連おすすめブログ ・ ls コマンド 【使い方 まとめ】 ・ よく使うLinuxコマンド一覧【最新版】 ・ sar コマンド【使い方 まとめ】 ・ iptables まとめ【Linux ファイアウォール】 ・ sed コマンド【使い方 まとめ】 ・ vi コマンド【使い方まとめ】 ・ Linuxのファイル操作でよく使うLinuxコマンド ・ 初心者のためのawkコマンド ・ 実務で使える!基本的なシェル(Linux)コマンドの話 ~forとsed~ ・ 【Linux】今振り返りたい、プロセスって何?   エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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
こんにちは! ラク スでインフラ開発部というところに所属している akiponx です。 今回は CDN の一つ。 AWS が提供しているCloudFrontで WordPress を高速化してみた話を書いてみます。 使ったサービスは ACM ... Amazon の無料 SSL証明書 Route53 ... DNS EC2 ... オリジンサーバ ELB ... ALBを利用 S3 ... ログ置き場 CloudFront ... CDN CloudFront Functions ... IP制限, Basic認証 AWS WAF ... アクセス制限 あたりを使って実装していきます。 目次 目次 1. CloudFrontとは? 2. CloudFront用語 オリジンサーバ エッジサーバ ディストリビューション オリジンドメイン 3. CloudFrontの料金 CloudFront 無料枠 CloudFront オンデマンド料金 4. CloudFrontを使う準備 5. CloudFrontの設定方法 6. CloudFrontでWordPressを配信する デフォルトのビヘイビアの編集 7. CloudFrontでWordPressを配信するときの注意点 おまけ アクセス制限 8. まとめ 1. CloudFrontとは? CDN の一つがCloudFrontです。 CDN とは、Contents Delivery Network の略で、コンテンツを配信するネットワークという意味合いです。 CloudFrontは AWS が提供する CDN で世界中にエッジサーバが存在しているため、どこからアクセスしても爆速でレスポンスが返ってきます。 2. CloudFront用語 CloudFrontを語るには、以下の用語はおさえておきたいところです。 CDN の基本用語とも被るところはありますので「知ってる!」という方はすっ飛ばしてください。 オリジンサーバ エッジサーバ ディストリビューション オリジン ドメイン キャッシュビヘイビア では順番に説明していきます。 オリジンサーバ CloudFrontが配信するコンテンツが置いているサーバのことです。( AWS でいうとEC2とかS3とか) CloudFrontのエッジサーバにキャッシュがなかった場合、オリジンサーバまでリク エス トが届きます。 エッジサーバ 世界中に存在しているCloudFrontのキャッシュサーバたちのことです。 ブラウザでアクセスすると、まずエッジサーバにリク エス トが行くように DNS の設定を行います。   エッジサーバがキャッシュを持っていなかったときは、オリジンサーバにリク エス トが行きます。 逆にエッジサーバがキャッシュを持っていれば、そのままエッジサーバがキャッシュされたページをブラウザに返します。 ディストリビューション CloudFrontが配信するサイト…というか設定…のことを言います。 複数のサイトを1つの ディストリビューション で設定することも可能ですが、サイトごとにキャッシュの設定を変更したい場合などもあるので個人的には1 ディストリビューション につき、1サイトとしたほうが柔軟性は高いと思います。 本記事では1 ディストリビューション 、1サイト構成で説明していきます。 オリジン ドメイン ブラウザからアクセスさせたい ドメイン のことです。 例えば、 https://rakus.co.jp/ へのアクセスをエッジサーバ経由にしたい場合、CloudFrontのオリジン ドメイン 部分に rakus.co.jp を設定します。 3. CloudFrontの料金 CloudFront 無料枠 1TBのデータ転送(アウト) 1,000万件のHTTP, HTTPS のリク エス ト 200万件のCloudFront Functionsの呼び出し CloudFront オンデマンド料金 エッジサーバからインターネットへのデータ転送(アウト)の料金 (GB 単位) 10TBまで0.114(USD) 次の40TBまで0.098(USD) 次の100TBまで0.086(USD) オリジンサーバからエッジサーバへのデータ転送(アウト)の料金 (GB 単位) 0.060USD HTTPメソッドリク エス ト料金(1万件当たり) HTTP: 0.0090(USD) HTTPS : 0.0120(USD) CloudFront Functions (100万件当たり) 0.10(USD) 詳しくは 公式 をご覧ください。 4. CloudFrontを使う準備 ドメイン を用意 ( DNS 登録含む) ACM ( AWS が提供している無料の SSL証明書 ) ELB or S3 (コンテンツがおかれているオリジンサーバ) ※ ACM は 米国東部( バージニア 北部)(us-east-1)リージョン で発行しなければCloudFrontでは使えません。 ※オリジンサーバの構築場所に東京リージョンを使う場合、東京リージョンで ACM の発行も必要です。  (2か所で設定するのめんどくさいですが。) 5. CloudFrontの設定方法 さて、それではそろそろ実際にCloudFrontの始め方、設定方法を見ていきましょう。 まずは、CloudFront ディストリビューション 作成ボタンをクリックします。 続いて各項目を入れていきます。 オリジン CloudFrontにオリジンサーバを教えてあげる項目です。 オリジン ドメイン ELBかS3の バケット を指定(今回はELB) マッチビューアー アクセスできる プロトコル を選択 両方アクセスさせたいときはマッチビューワー オリジンパス 「/」を入れればOK 名前 基本的には何でもいいですが、サイト名とかわかりやすいのにするといいと思います デフォルトのキャッシュビヘイビア CloudFrontのキャッシュを持たせる設定を行う項目です。 初期設定ではCloudFront経由でアクセスした全リク エス トに対してキャッシュの設定されてしまいますが、後で URI 毎にキャッシュの設定が可能です。 オブジェクトを自動的に圧縮 Yesにしておくとよい。転送量が減って気持ち節約になります ビュー ワープロ トコルポリシー Redirect HTTP to HTTPS にしておくのが無難ですかね 許可されたHTTPメソッド サイトに合わせて設定してください 静的サイト…GET,HEAD 動的サイト…GET,HEAD,OPTIONS,PUT,POST,PATCH,DELETE 今回は WordPress なのでこっち キャッシュキーとオリジンリク エス ト 基本的に触らないくていい 設定 エッジサーバについての設定を行う項目です。 料金クラス すべてのエッジロケーションを使用する AWS WAF ウェブ ACL アクセス制限やWAFをいれたい場合はここで設定を追加 ※ もちろん AWS 側でルールを作る必要あり 代替 ドメイン アクセスさせたい ドメイン 名を記載(複数記載OK) CloudFrontを使う準備に記載した ドメイン を入力してもらえればOK 例) www.rakus.co.jp, rakus.co.jp など カスタム SSL証明書 ACM で取得した証明書をプルダウンから選択 標準ログ記録 必要な場合はオンにして、ログを保存するS3 バケット を設定 IPv6 対応していなければオフに 以上でCloudFrontの初期設定は完了です。 最期に「 ディストリビューション の作成」をクリックしましょう。 6. CloudFrontで WordPress を配信する さて、 ディストリビューション の作成が完了したので続いては WordPress 特有の設定を入れていきましょう。 オリジンサーバに WordPress をインストール ELBの設定 今回の投稿では上記については割愛させてもらいます。 <m(__)m> 「 WordPress インストール」というような記事でググって入れてください <m(__)m> 最低限、CloudFrontで WordPress を配信するだけなのであればここまでの設定で配信可能です。(多分。) が、このままでは WordPress の管理画面にアクセスできず、 記事の更新 WordPress のアップデート プラグイン のインストール などなどが管理画面上から行えないため、辛いです。 そのため、 WordPress 管理画面へのアクセスができるようにキャッシュをコン トロール しなければいけません。 その設定を入れていきます。 作成した ディストリビューション を編集するために設定画面に遷移します。 ビヘイビア > ビヘイビアを作成 をクリック パスパターン パスパターンを指定して、指定したパスにアクセスが来たときのキャッシュの挙動を設定 キャッシュしてはいけないパスパターン /wp-admin/* ( WordPress をインストールしたパスに合わせてください。) *. php * preview * オリジンとオリジングループ ディストリビューション 名が出てくるので作成した ディストリビューション の名前に合わせてください ビューワー デフォルトのキャッシュビヘイビアで設定した物と同じ設定を入れる キャッシュキーとオリジンリク エス ト Legacy chache settings ヘッダー:すべて (キャッシュしないように設定) 上記の設定でキャッシュしてはいけないパスパターンを作成してください。 - キャッシュしてはいけないパスパターン - /wp-admin/* (WordPressをインストールしたパスに合わせてください。) - *.php - \*preview\* デフォルトのビヘイビアの編集 デフォルトのビヘイビアを編集する前にキャッシュポリシー、オリジンリク エス トポリシーを作成します。 デフォルトのビヘイビアに作成したキャッシュポリシー、オリジンリク エス トポリシーをアタッチするイメージです。 ■ キャッシュポリシー まずはキャッシュポリシーを作成します。 CloudFrontトップ画面 > ポリシー > キャッシュ > キャッシュポリシーを作成 名前 わかりやすい名前なら何でも。 別の ディストリビューション にもアタッチできるので汎用的なキャッシュポリシー名にするのも良き 説明 適当に説明文を入れる キャッシュキーの設定 ヘッダー Host, CloudFront-Forwarded-Proto を入れてください Host:ALBが振り分けられなくなります CloudFront-Forwarded-Proto:EC2が80番しか開けていない場合これがないと表示崩れます 設定できたら作成をクリックして、キャッシュポリシーを作成します。 ■ オリジンリク エス トポリシー 続いて、オリジンリク エス トポリシーの作成をやっていきます。 CloudFrontトップ画面 > ポリシー > オリジンリク エス ト > オリジンリク エス トポリシーを作成 名前 キャッシュポリシーと一緒 説明 キャッシュポリシーと一緒 オリジンリク エス トの設定 ヘッダー キャッシュポリシーと一緒 クエリ文字列 * preview * cookie comment_author_* wordpress _test_ cookie wordpress _logged_in_* wp-settings-* 設定できたら作成をクリックして、オリジンリク エス トポリシーを作成します。 ここまでできたら、デフォルトのビヘイビアを編集していきます。 ■ デフォルトのビヘイビア さて、キャッシュポリシーとオリジンリク エス トポリシーが出来たので、デフォルトのビヘイビアの編集をしていきます。 CloudFrontトップ画面 > ディストリビューション > 作成した ディストリビューション > ビヘイビア > デフォルト > 編集 キャッシュキーとオリジンリク エス ト キャッシュポリシー さっき作ったキャッシュポリシーを選択 オリジンリク エス トポリシー さっき作ったオリジンリク エス トポリシーを選択 編集箇所はここだけです。 変更を保存しましょう。 さて設定もできたので、CloudFront経由でアクセスしてみましょう。 できた!! 7. CloudFrontで WordPress を配信するときの注意点 CloudFrontで WordPress を配信することができるようになりました。 私が初めてこの作業をやったときにハマったポイントを、いくつか記載しておきます。 LB配下に WordPress をインストールしたときはwp-config. php に とある記述 が必要 管理画面にアクセスできなかった… キャッシュの設定はご注意を 記事を更新するたびにCloudFrontのキャッシュをクリアしないといけない Wodpressに プラグイン があるのでそいつを使うと便利 テーマはなどの更新は多分検知されない… というところでしょうか。 おまけ アクセス制限 CloudFrontでアクセス制限を掛けたいときってありますよね?(例えば検証機とか。) 接続元がCloudFrontの IPアドレス になっていた、ELBのIPになっていたりとサーバ側で制限をかけるのが結構めんどくさいです。 そんな時に役立つのが AWS WAF, CloudFront Functions です! IP制限したいな~と思ったら下記を試してみてください<m(__)m> AWS WAF CloudFront Functions また、CloudFront以外からのアクセスを遮断したい!みたいなこともあるかと思います。 悪い人がhostsファイルなどでサーバに直接アクセスできるようにしてくるかもしれません。 そんな時は一つの方法として、CloudFrontが付与したカスタムヘッダーをLBが判定する方法が手っ取り早くて楽だと思います。 しかし、最近だともう少し楽にできる方法もあるみたいです。 お時間あれば調べてみてください。 8. まとめ 長い記事を呼んでいただき、ありがとうございました。 CloudFrontを使うと運用がめんどくさくなることもありますが、サイト自体が軽量化でき、 SEO 対策にもなります。 以前、 WordPress で作った弊社オンデマンドサイトはアクセス集中が起きると必ず落ちていましたが、CloudFront経由で配信するようにしてからは(多分)1度も落ちていません。 アクセス集中で困っている! すぐ高負荷になって困っている! という方は、一度CloudFrontや他の CDN の構成を検討してみてもいいかもしれないです。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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