TECH PLAY

株式会社ラクス

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

941

こんにちは、3年目エンジニアのaa_cryingです。 あっという間に3年目です。時が経つのは早いものですね...。 業務でJenkinsのジョブを作る・改善する機会がありました。 その際にシェルについて悩んだ末に色々学ぶことが出来たので、今回はその内容をアウトプットしたいと思います。 以下目次です。 変数を分割してfor文で回したい ファイルの○行目に追記したい おわりに 変数を分割してfor文で回したい 複数の対象に同じ処理を実行したい場合等に便利なのが for文 です。 様々な言語にfor文は存在しますが、シェルの場合の基本文法は以下のような形です。 for 変数 in リスト do 処理... done 今回は、1つの変数を区切り文字で分割し、for文で回す方法です。 「指定したブランチが存在する場合にチェックアウトする」シェルを例として以下に記載します。 # ブランチ名をカンマ区切りで複数入力 echo "input branches, comma delimited. (ex. master,develop,...)" read branches # ブランチ毎に処理を実行 for branch in `echo $branches | sed "s/,/ /g"`; do if [[ $(git branch -a | grep $branch) = "" ]] ; then echo "branch $branch does not exists." exit 1 fi git checkout -b $branch origin/$branch done branches という変数にはカンマ(,)区切りで複数のブランチを入力します。 入力されたbranchesをリストとして認識させるため、カンマを空白に置換したいです。 ここで sed を使用します。 sed "s/aaa/bbb/g" と記述すると、「aaa」で検索しヒットした全てを「bbb」に置換します。 g を忘れてしまうと、検索してヒットした最初のものだけ置換されてしまうので注意です! 今回は、 sed "s/,/ /g" と記述したので、カンマを全て半角スペースに置換してくれます。 後はfor文がリストとして認識し、回してくれます。 ファイルの○行目に追記したい postgreSQL を使っている方だと、pg_hba.confにサーバ名を追記なんてことは日常茶飯事だと思います。 echo "ほにゃらら" >> ファイル を使用して最後尾に追記という方法もありますが、記載場所によってはうまく動作しないこともありますよね。。 viで開いて更新 or FTP ソフトを使って開いて書き換え...というのも面倒... そこで、今回は「行数を指定して挿入する」方法を紹介します。 echo "Pg connection source server IP." read SERVER1 echo "Pg connection destination server IP." read SERVER2 echo "PGDATA path." read PGDATA sudo ssh-keygen -R ${SERVER2} sudo ssh ${SERVER2} bash <<SHELL # pg_hba.confに追記して再起動 sed -i -e "100i host all all ${SERVER1}/32 trust #test" ${PGDATA}/pg_hba.conf systemctl restart postgresql.service SHELL SERVER1 は接続元サーバのIP、 SERVER2 は接続先サーバのIPとします。 「接続先サーバに SSH 接続し、接続先サーバのpg_hba.confの100行目に接続元サーバの情報を追記する」という処理を行っています。 sed コマンドと言えば、 正規表現 を用いてファイルや標準出力の文字列置換をする使用方法が有名ですが、行の追加や挿入、削除、はたまた入出力まで出来てしまうマルチなコマンドです。 行数を指定して挿入する場合は、挿入する文言の先頭に 行数i と記載するだけです。 また -i オプションは、付けることでファイルを直接書き換えることが出来ます。 今回のようにファイルを書き換えて保存したい場合は、 -i オプションを付けないと書き換わりません。 手動でやっていると postgresql の再起動を忘れがちだったり (私の場合) しますが、シェルに記載しておくと一気にやってくれるので便利ですね。(小声) おわりに 今回は、業務で使えるシェルの基本的な知識を紹介しました。 本当はもう少しボリュームのある記事にしようと思っていたのですが、時間があまり取れず紹介は2つだけに。。 まだまだ勉強不足ですので知識を深め、今後もこのような形で学んだ技術を発信できたらと思います! また、 Linux コマンドの一覧表を以下ブログにてまとめておりますので、ご参考ください。 よく使うLinuxコマンド一覧【最新版】 Linux の理解をより深めたい方へ以下関連おすすめブログ ・ ls コマンド 【使い方 まとめ】 ・ find コマンド 【使い方 まとめ】 ・ iptables まとめ【Linux ファイアウォール】 ・ sed コマンド【使い方 まとめ】 ・ vi コマンド【使い方まとめ】 ・ Linuxのファイル操作でよく使うLinuxコマンド ・ 初心者のためのawkコマンド ・ 【Linux】今振り返りたい、プロセスって何? エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
こんにちは。y_kwmtです。 はじめに 少し前にVue.jsとFirebaseを用いて Google アカウントのログイン機能と Markdown 書式のメモを作成する機能を作成しました。 tech-blog.rakus.co.jp 以下のサイトを参考にして Markdown 書式のメモを応用したチャット機能を作成したので、 本記事では実装の準備、実装方法について紹介していきます。 cr-vue.mio3io.com はじめに 実装する機能 チャット機能 データベース作成 Vue.jsでFirebaseを使用する チャット機能の実装 動作確認 おわりに 実装する機能 チャット機能 メッセージを送信、受信できる 送信したメッセージを Markdown 書式で表示 ページを再訪問しても送信したメッセージが残っている ※前回の記事で作成したメモ機能は使わないので一旦なくします データベース作成 プロジェクト内の「Database」を選択して「Realtime Database」からデータベースをテストモードで作成します。チャットのメッセージとメッセージ送信者の情報をレコードとして追加します。 Vue.jsでFirebaseを使用する firebase モジュールをインストールして main.js で読み込みます。 npm install firebase Firebaseのプロジェクトの「Settings」を開いて 「Firebase SDK snippet」の スクリプト 部分を追記してください。 import firebase from firebase import Vue from 'vue' import App from './App.vue' import 'firebase/firestore' ; // ここから var firebaseConfig = { apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" , authDomain: "XXXXXXXXX.firebaseapp.com" , databaseURL: "https://XXXXXXXX.firebaseio.com" , projectId: "XXXXXXXXX" , storageBucket: "XXXXXXXXX.appspot.com" , messagingSenderId: "XXXXXXXXX" , appId: "X:XXXXXXXXXXXX:web:XXXXXXXXXXXXXXXXXXXX" , measurementId: "X-XXXXXXXXXX" } ; // Initialize Firebase firebase.initializeApp(firebaseConfig); firebase.analytics(); // ここまで new Vue( { el: '#app' , render: h => h(App) } ) チャット機能の実装 前回投稿したブログで登場した、メモを作成する画面「Editor.vue」を改修し、 ログイン後の画面にチャット機能を追加します。 先ほど作成したデータベースにチャットのメッセージ、メッセージ送信者、 Google アカウントの画像パスがレコードとして保存されます。 <template> <div id= "home" > <h1>エディター画面</h1> <p> <span> {{ user.displayName }} </span> <button @click= "logout" >ログアウト</button> </p> <transition-group name= "chat" tag= "div" class = "list content" > <!-- chatにはキー、送信者名、Googleアカウントの画像パス、メッセージが入っています。 --> <section v- for = "{ key, name, image, message } in chat" :key= "key" class = "item" > <div class = "item-image" ><img :src= "image" width= "40" height= "40" > {{ name }} </div> <div class = "item-detail" > <div class = "item-message" > <!-- メッセージがMarkdown書式で表示されます。 --> <div v-html= "message" ></div> </div> </div> </section> </transition-group> <!-- 入力フォーム --> <form action= "" @submit.prevent= "doSend" class = "form" > <!-- Enterを押すとメッセージが送信されます。Enter + Shiftで改行します。 --> <textarea v-model= "input" :disabled= "!user.uid" @keydown.enter.exact.prevent= "doSend" ></textarea> <div> <!-- ボタンを押すとメッセージが送信されます --> <button type= "submit" :disabled= "!user.uid" class = "send-button" >Send</button> </div> <div> プレビュー<div v-html= "preview()" ></div> </div> </form> </div> </template> <script> import marked from "marked" ; // 改行を <br> タグに変換するモジュール export default { name: 'editor' , props: [ "user" ] , data() { return { chat: [] , // 取得したメッセージを入れる配列 input: '' // 入力したメッセージ } } , created() { // データベースからメッセージ情報取得 const ref_message = firebase.database().ref( 'message' ) if ( this .user) { this .chat = [] // message に変更があったときのハンドラを登録 ref_message.limitToLast(10).on( 'child_added' , this .childAdded) } else { // message に変更があったときのハンドラを解除 ref_message.limitToLast(10).off( 'child_added' , this .childAdded) } } , methods: { logout: function () { firebase.auth().signOut(); } , preview: function () { return marked( this .input); } , childAdded(snap) { const message = snap.val() this .chat.push( { key: snap.key, name: message.name, image: message.image, message: marked(message.message) } ) } , doSend: function () { if ( this .user.uid && this .input.length) { // firebase にメッセージを追加 firebase.database().ref( 'message' ).push( { message: marked( this .input), name: this .user.displayName, image: this .user.photoURL } , () => { this .input = '' // フォームを空にする } ) } } , displayTitle: function (text) { return text.split( /\n/ ) [ 0 ] ; } } } </script> 動作確認 実際の画面がこちらです。メッセージを入力するとプレビューが表示され、 送信すると Markdown 書式でメッセージが表示されます。 黒く塗りつぶしている部分は Google アカウントの名前を表しています。 おわりに 今回はVue.jsで Markdown 書式を応用したチャット機能を紹介しました。 データベースなどの設定やApp.vueを2回呼び出してしまうエラーなどに少し時間がかかってしまいました。 今後はVue.jsだけでなく、他のフロント系の技術を学習して紹介していきたいと思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
はじめに 実装に使った物 実装する機能 リマインダーを実装してみる 動作確認! おわりに 参考にした記事 はじめに こんにちは。新卒2年目のrs_chankoです。 まだまだ慣れない社会人。そんな中で思ったことがあります。 物忘れが酷すぎる。 これは元々分かっていたんですが、最近特に思います。 会社では朝にスケジュールを確認しますが、 プライベートではスケジュールを見る習慣がなく、忘れてしまいがち…。 「そうだ、LINEなら毎日見るし、自分で作っちゃうか。」 そう思い立ち、GASとLINE Messaging API を使ってリマインダーを作ることになりました。 実装に使った物 GAS( Google Apps Script) LINE Messaging API Google スプレッドシート 実装する機能 日付と予定を スプレッドシート に登録すると、登録されたデータを参照してメッセージとして送ってくれる (忘れがちなので)7時、12時、19時に今日と明日の予定を教えてくれる リマインダーを実装してみる LINE Messaging API とGASの接続方法は先輩がすでに書いていたのでそのまま参考にしました。 tech-blog.rakus.co.jp しかしそもそも JavaScript もあまりちゃんと書いたことがなく、GASやMessaging API も使ったことがない。 ひとまず練習するか、ということでお試しにオウム返し bot を作ってみました。 note.com これは簡単にできました。 さて、本題です。 ひとまずオウム返しで使った API 周りのコードを元に実装していきます。 //LINEのアクセストークン var channel_access_token = "アクセストークン" ; var headers = { "Content-Type" : "application/json; charset=UTF-8" , "Authorization" : "Bearer " + channel_access_token } ; //返信する function sendLineMessageFromReplyToken(token, replyText) { var url = "https://api.line.me/v2/bot/message/reply" ; var headers = { "Content-Type" : "application/json; charset=UTF-8" , "Authorization" : "Bearer " + channel_access_token } ; var postData = { "replyToken" : token, "messages" : [{ "type" : "text" , "text" : replyText }] } ; var options = { "method" : "POST" , "headers" : headers, "payload" : JSON.stringify(postData) } ; return UrlFetchApp.fetch(url, options); } //指定のuserIdにメッセージを送る function sendLineMessageFromUserId(userId, text) { var url = "https://api.line.me/v2/bot/message/push" ; var postData = { "to" : userId, "messages" : [{ "type" : "text" , "text" : text }] } ; var options = { "method" : "POST" , "headers" : headers, "payload" : JSON.stringify(postData) } ; return UrlFetchApp.fetch(url, options); } 準備が整いました。 それではまず Bot にやってほしいことをまとめます。 予定の登録 予定の削除 登録・削除時の返事 指定時刻の通知 登録されている予定の確認 上記ができるよう実装します。 //8桁日付のフォーマット function formatDateForDisplay (date) { var year = date.substr(0,4); var month = date.substr(4,2); var day = date.substr(6,2); var result = year + "/" + month + "/" + day; return result; } //Date型日付のフォーマット function formatDate (date, format) { format = format.replace( /yyyy/g , date.getFullYear()); format = format.replace( /MM/g , ( '0' + (date.getMonth() + 1)).slice(-2)); format = format.replace( /dd/g , ( '0' + date.getDate()).slice(-2)); return format; } ; //日付をシートに記載する function setDate(date, plan, row) { if (isNaN(date) || String (date).length != 8) { return '日付が正しくありません' ; } setFromRowAndLine(date, row, 0); return setPlan(plan, row); } //予定をシートに記載する function setPlan(plan, row) { setFromRowAndLine(plan, row, 1); return '予定を登録しました。' ; } //トリガー起動で当日と翌日の予定を返す function getTodayAndNextDayMessage() { var date = new Date (); var userId = ’LINEのユーザーID’; //今日の予定を取得 var remindText = getDatePlan(date); //明日の予定を取得 date.setDate(date.getDate() + 1); remindText += " \n " + getDatePlan(date); return sendLineMessageFromUserId(userId, remindText); } //指定日の予定を返す function getDatePlan(date) { if (date instanceof Date ) { date = formatDate(date, 'yyyyMMdd' ); } var day = formatDateForDisplay(date); var remindText = day + " \n " ; var plans = searchPlans(date); for ( var i = 0; i < plans.length; i++) { remindText += getFromRowAndLine( 'webhook' , plans [ i ] ); remindText += " \n " ; } remindText = remindText.slice(0,-1); if (remindText.length === 10) { remindText += " \n 明日は予定がありません。" ; } return remindText; } //受け取ったメッセージに対して返信する function doPost(e) { var webhookData = JSON.parse(e.postData.contents).events [ 0 ] ; var message, replyToken, replyText, userId; message = webhookData.message.text.split( " \n " ); replyToken = webhookData.replyToken; userId = webhookData.source.userId; var processing = message [ 0 ] ; var planDate = message [ 1 ] ; var plan = message [ 2 ] ; var row = getLastRow(); switch (processing) { case '登録' : replyText = setDate(planDate, plan, row); break ; case 'キャンセル' : replyText = cancel(planDate, plan); break ; case '確認' : replyText = planDate === undefined ? getDatePlan( new Date ()) : getDatePlan(planDate); break ; case 'test' : replyText = testSheet(); break ; default : replyText = "予定の登録・キャンセルができます。 \n\n 登録 \n 20200101 \n 予定 \n\n の形式で入力してください。" ; break ; } return sendLineMessageFromReplyToken(replyToken, replyText); } //予定を削除する function cancel(date,plan) { deleteRowOfDateAndPlan(date, plan); return '予定の登録をキャンセルしました。' } //トリガーをセットする function setTrigger() { const time = new Date (); time.setHours(7); time.setMinutes(00); ScriptApp.newTrigger( 'getTodayAndNextDayMessage' ).timeBased().at(time).create(); time.setHours(12); ScriptApp.newTrigger( 'getTodayAndNextDayMessage' ).timeBased().at(time).create(); time.setHours(19); ScriptApp.newTrigger( 'getTodayAndNextDayMessage' ).timeBased().at(time).create(); time.setDate(time.getDate() - 1); //前日の予定を削除する deleteRowOfDate(formatDate(time, 'yyyyMMdd' )); } //トリガーを削除する function delTrigger() { const triggers = ScriptApp.getProjectTriggers(); for ( const trigger of triggers) { if (trigger.getHandlerFunction() == "getTodayAndNextDayMessage" ) { ScriptApp.deleteTrigger(trigger); } } } このコードではLINEで実際に送るメッセージの生成・トリガーのセット・削除をしています。 GASのトリガーって定期実行をすると1時間の範囲内で実行されるらしいんですよね。 ちゃんと同じ時間に送ってほしいので、今回は指定の時間に通知用のメソッドをトリガーとしてセットするメソッドを定期実行にしました。 スプレッドシート とのテキストのやりとりは下記の実装をします。 //スプレッドシート var spreadsheet = SpreadsheetApp.openById( "スプレッドシートのID" ); var sheet = spreadsheet.getSheetByName( 'スプレッドシートのシート名' ); //受け取った日付の予定が記載されている行を返す function searchPlans(date) { var data = sheet.getDataRange().getValues(); var plans = [] ; for ( var i = 0; i < data.length; i++) { if (data [ i ][ 0 ] == date) { plans.push(i + 1); } } return plans; } //列を指定してデータを取得する function getFromRowAndLine(sheetName, row) { var data = sheet.getDataRange().getValues(); return data [ row - 1 ][ 1 ] ; } //列を指定してデータを書き込む function setFromRowAndLine(val, row, line) { sheet.getRange(row + 1, line + 1).setValue(val); } function getLastRow() { return sheet.getLastRow(); } //日付と予定から行を削除する function deleteRowOfDateAndPlan(date, plan) { var lastRow = getLastRow(); for ( var i = 1; i <= lastRow; i++) { if (sheet.getRange(i, 1).getValue() == date && sheet.getRange(i, 2).getValue() == plan) { sheet.deleteRows(i); } } } //指定の日付の行を削除する function deleteRowOfDate(date) { var lastRow = sheet.getDataRange().getLastRow(); for ( var i = 1; i <= lastRow; i++) { if (sheet.getRange(i, 1).getValue() == date) { sheet.deleteRows(i); } } } これで実際に スプレッドシート に日付と予定を書き込んだり、参照することができるようになりました。 こんな感じでLINEから送られてきた予定が書き込まれます。 動作確認! それでは実際にメッセージを見てみましょう。 おおおお! 予定の登録や確認ができ、19時に予定の通知がきました! そんなこんなで完成です。 おわりに 「リマインダー」について何も調べていなかったんですが、 作り終わってから調べたところ、すでにリマインド Bot が存在していました。。。 remine.akira108.com しかし作ってしまったものは作ってしまったので、今後はカスタマイズをして自分の好みのリマインダーを作っていこうと思います。 あとはLINEに予定を登録することを忘れないように…。 参考にした記事 qiita.com tonari-it.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
こんにちは。y_kwmtです。 はじめに 昨年にVue.jsの学習を行うためにこちらの書籍を読みました。 https://www.amazon.co.jp/Vue-js%E3%81%A8Firebase%E3%81%A7%E4%BD%9C%E3%82%8B%E3%83%9F%E3%83%8BWeb%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9-%E6%8A%80%E8%A1%93%E6%9B%B8%E5%85%B8%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%EF%BC%88NextPublishing%EF%BC%89-%E6%B8%A1%E9%82%8A-%E9%81%94%E6%98%8E/dp/4844398350 www.amazon.co.jp こちらの書籍を参考に、 Google アカウントでログインする機能と markdown 書式でメモを作成する機能を作成しました。昨年の ラク ス アドベントカレンダー にVue.jsのプロジェクトをFirebaseでデプロイする方法について記事を投稿したので、本記事を閲覧する前にご確認いただけると幸いです。 qiita.com ログイン機能 src ディレクト リの下にcomponents ディレクト リを作成し、そこに「Home.vue」と「Editor.vue」を作成してください。 mymarkdown ― src ― components ― Home.vue(ログイン画面) | | | └ Editor.vue(メモ作成画面) | └ App.vue(画面切り替え) まずは Google アカウントでログインする機能を作成します。ログインする前の画面「Home.vue」を実装します。 Home.vue <template> <div id= "home" > <h1> {{ msg }} </h1> <button>Googleアカウントでログイン</button> </div> </template> <script> export default { name: 'home' , data() { return { msg: "Welcome to MyMarkDown" } ; } } ; </script> ログイン状態によって画面を切り分ける「App.vue」を作成します。 createdでログイン状態を確認しています。 この画面で「Home.vue」と「Editor.vue」を読み込んで、ログイン状態により表示を変えています。 App.vue <template> <div id= "app" > <Home v- if = "!isLogin" ></Home> <Editor v- if = "isLogin" :user= "userData" ></Editor> </div> </template> <script> import Home from "./components/Home.vue" ; import Editor from "./components/Editor.vue" ; export default { name: 'app' , data () { return { isLogin: false , userData: null } } , created: function () { firebase.auth().onAuthStateChanged(user => { console.log(user) if (user) { this .isLogin = true ; this .userData = user; } else { this .isLogin = false ; this .userData = null ; } } ); } , components: { Home: Home, Editor: Editor } } </script> <style lang= "scss" > #app { font-family: 'Avenir' , Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin- top : 60px; } h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style> まだログイン機能が実装されていないので、初期表示はこちらの画面(Home.vue)となります。ログイン機能を実装するためにまずFirebaseの「Authentication」の「 sign -in method」で Google アカウントでのログインを有効に設定します。「 Google 」をクリックし、「有効にする」ボタンをクリックし、プロジェクトのサポートメールを設定します。「 Google が有効になりました」と出ればOKです。 続いて「Home.vue」でログイン機能を実装します。methodsにログイン機能を追加します。 Home.vue <template> <div id= "home" > <h1> {{ msg }} </h1> <button @click= "googleLogin" >Googleアカウントでログイン</button> </div> </template> <script> export default { name: 'home' , data() { return { msg: "Welcome to MyMarkDown" } ; } , methods: { googleLogin: function () { firebase.auth().signInWithRedirect( new firebase.auth.GoogleAuthProvider()); } } } ; </script> 「 Google アカウントでログイン」ボタンをクリックすると Google アカウントの認証画面に移動し、 ここで承認すればログインすることができます。すると次のような画面が表示されます。 見出し「エディター画面」の下にはアカウント名とログアウトボタンが表示されます。(アカウント名は伏せさせていただきます) また、ログアウト機能をこの後作成するメモ機能の画面「Editor.vue」に追加します。 Editor.vue <template> <div id= "home" > <h1>エディター画面</h1> <p> <span> {{ user.displayName }} </span> <button @click= "logout" >ログアウト</button> </p> </div> </template> <script> export default { name: 'editor' , props: [ "user" ] , data() {} , methods: { // ログアウト機能 logout: function () { firebase.auth().signOut(); } } } </script> ログアウトを実行するとログイン画面が表示されます。 メモ機能 続いて入力内容を markdown 書式でプレビューを表示する機能を作成します。 そのためにマークダウン用のライブラリをインストールします。 次のコマンドを実行してインストールしてください。 npm install --save-dev marked これはmarkedという markdown 書式をHTMLに変換するnpmモジュールです。 続いてEditor.vueを編集します。 Editor.vue <template> <div id= "home" > <h1>エディター画面</h1> <p> <span> {{ user.displayName }} </span> <button @click= "logout" >ログアウト</button> </p> <div> メモリスト <div v- for = "(memo, index) in memoList" > <div v- if = "index!=0" v-html= "displayMemo(memo.markdown)" ></div> </div> <textarea class = "markdown" v-model= "memoList[memoIndex].markdown" ></textarea> <div><button v-on:click= "addMemo" >メッセージ追加</button></div><br> プレビュー<div v-html= "preview()" ></div> </div> </div> </template> <script> import marked from "marked" ; export default { name: 'editor' , props: [ "user" ] , data() { return { memoList: [ { markdown: "" , } ] , memoIndex: 0 } ; } , methods: { // ログアウト機能 logout: function () { firebase.auth().signOut(); } , // markdown書式でのプレビュー表示 preview: function () { return marked( this .memoList [this .memoIndex ] .markdown); } , // メモを追加 addMemo: function () { this .memoList.push( { markdown: marked( this .memoList [this .memoIndex ] .markdown) } ); } , // 作成したメモを一覧表示 displayMemo: function (text) { return text.split( /\n/ ) [ 0 ] ; } } } </script> 実行して、プレビューに入力内容がリアルタイムで markdown 書式で表示されていればOKです。 大見出しで「メモ」、中見出しで「ブログを書く」「メモ機能を実装する」、 小見出し で「メモを追加」「マークダウン記法でメモを表示する」というメモを追加しています。 プレビューには 小見出し で「マークダウン記法でメモを表示する」と表示しています。 おわりに 今回はVue.jsで Google アカウントのログイン機能と markdown 書式のメモ作成機能を紹介しました。今度はチャットができるようになるなど機能の拡張を行い、記事を作成していきたいと思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
こんにちは、開発エンジニアの amdaba_sk( ペンネ ーム未定)です。 ラク スの開発部ではこれまで社内で利用していなかった技術要素を自社の開発に適合するか検証し、ビジネス要求に対して迅速に応えられるようにそなえる 「 開 ( か ) 発の 未 ( み ) 来に 先 ( せん ) 手をうつプロジェクト(通称:かみせんプロジェクト)」 というプロジェクトがあります。 この記事はかみせんプロジェクト2019年度下期成果報告ブログの一つです。 前回の成果報告では 「機械学習プロジェクトの進め方」について検証した結果のまとめ をしました。今回は「じゃあ実際に 機械学習 をやることになったら、どんなライブラリ、サービスを使えばいいの?」といったところの検証結果をまとめようと思います。 対象読者は前回と違って、 機械学習 に興味のあるエンジニアの方です。 なお今までの記事はかみせんカテゴリからどうぞ。 tech-blog.rakus.co.jp 概要 今回の目標 対象ライブラリ・サービスの選定 サンプルプロジェクトについて 学習データについて 前処理について テキストのクリーニング 単語の分割 単語の正規化 ストップワードの除去 単語のベクトル表現 文書のベクトル表現 モデル学習について 学習のそのあと 実装を終えての所感 Python + scikit-learn Python + H2O JVM系 + Smile Apache Spark Amazon SageMaker AI Platform Azure Machine Learning まとめ 今後の展望 参考 概要 非常に単純な 機械学習 タスクを実施するプログラムをいくつかのライブラリ、サービスを用いて実装することで、それぞれのライブラリ、サービスの使いやすさを評価しました。 やはり Python の scikit-learn は デファクトスタンダード であり、ライブラリの備える API の充実ぶり、Web や書籍の情報量は他を圧倒するものがあります。ただ学習データの量や、連携するシステムの開発言語によっては Apache Spark も有用です。 クラウド のマネージドサービスは、単なる Notebook の実行環境としての使い方をしている限りにおいては結局のところ scikit-learn を用いることになりがちでありその利点を引き出すことはできないということが分かりました。マネージドサービスは付属する学習済みモデルを利用するサービス、AutoML による自動学習、アプリケーションへのデプロイの容易さといったところに利点があるのだと推測されます。 今回の目標 実際に 機械学習 を利用した機能を作ることになった場合にまずやることになるであろうライブラリ、サービスの選定フェーズに対して参考になりそうな情報をまとめたいと思い調査を行いました。 とはいえライブラリやサービスを選ぶうえでの条件、観点はさまざまであり案件ごとに重視するポイントも固有のものがあるはずです。さらに調査メンバーがそもそも 機械学習 に関してそこまで詳しくはないということを考えると、実務上そのまま使えそうなレベルの資料を作るのは難しすぎます。 そこでさしあたり単純なサンプルプロジェクトを設定して 機械学習 モデルの作成 スクリプト をいくつかのライブラリ、サービスを使って実装し、それぞれの使用感をまとめました。 対象ライブラリ・サービスの選定 Python + scikit-learn Python + H2O JVM 系 + Smile Apache Spark Amazon SageMaker AI Platform Azure Machine Learning JVM 系の言語としてはメンバーのモチベーション的な理由から Scala を採用しました。 また事前の調査から「 Python 3 + scikit-learn」が ディープラーニング を行わない場合の デファクトスタンダード のような存在であることが分かっていますので、他のライブラリはそれとの比較という観点で調査をすることにしました。 サンプルプロジェクトについて サンプルは弊社の事業を考えると日本語テキストを扱うものの方がよいだろうと思われました。その上で単純なものであればなんでもよかったので、思い付きのままにAIチャットサービスっぽいものを作ることにしました。 ここに一人のゲーマーが居る。彼または彼女を A としよう。 A は最近ハマっているとあるゲームについて、公式サポートに問合せをするべく Web サイトに訪問した。 サイトにはありがちなよくある質問一覧の他に、「チャットで問い合わせる」というリンクが設置されていた。リンクをクリックするとページ内にチャットウィンドウが立ち上がり「気軽にご質問ください!」という定型的なメッセージが表示された。ウィンドウの下部にはメッセージ入力欄があり、カーソルが誘うように明滅している。 A はしばしの黙考ののち、質問を入力し始めた―― ここで自然な会話のできるAIチャットボットを作れればすごいですが、難易度激ムズな気がするので以下のような動きをするものとしましょう。 訪問者のメッセージを受け付ける メッセージが既存の「よくある質問」のどれに相当するかを分類する 質問の候補をいくつか選択肢として表示する 選ばれた質問に対して事前に設定された回答を表示する (どことなく弊社のとあるサービスに激似ですが、気にしないことにします) 機械学習 が関わってくるのは上の 2 のステップです。比較的短文な日本語文章の多クラス分類問題であると定式化できます。 前回の記事でまとめた内容によれば、ここで 機械学習 を使わない MVP を検討するところではありますが、今回はその辺をすっ飛ばします。また上のイメージのような組み込み先システムの設計も先に考えておくべきところではありますが、これも簡単のためにすっ飛ばして前処理とモデル学習のステップを考えていくことにします。 学習データについて この分類問題では生のデータとしては例えば以下のようなものが得られると想像できます。 メッセージ よくある質問ID ログインできなくなりました。どうしたらいいですか? 1 ログインができず、ゲームがプレイできません。 1 ゲームを始めようとするとエラーが出てログアウトしてしまいます。 1 : : 機種変更しても同じセーブデータで遊べますか? 4 引き継ぎナンバーは一度発行すれば繰り返し使えますか? 4 動作が重いです。 5 ゲーム内で購入したアイテムの情報が反映されません。 6 : : ここで「メッセージ」が推測の入力となるデータ、「よくある質問ID」が推測したいクラスのラベルです。 会員制サイトなどで質問者の属性情報や前後の文脈など追加の情報が得られることもあるかと思いますが、ここでは匿名のユーザーによる単発の質問への対応という想定で上のようなデータをサンプルとして使うとしましょう。 とはいえそう都合よくチャットログデータは持ち合わせていませんでしたので、社内のいろいろな方に協力をお願いして自前で作りました。 クラス数: 96 データ 総数: 6051 クラスごとのデータ数: 平均: 63.0 最大: 199 最小: 16 (改めて統計情報を見てみると、クラス数めっちゃ多いしクラスごとの分布もめっちゃ偏っていてあまりいいデー タセット とは言えない気が…) 具体的なデータの中身は社外秘ということになったので公開できませんが、ゲームのサポートチャットではないということだけ言い添えておきます。 前処理について 元のデータは日本語テキストデータなので、何とかして数値データとして表現しなければなりません。 自然言語処理における前処理の種類とその威力 によれば、テキストデータの前処理は以下のものを行うようです。 テキストのクリーニング 単語の分割 単語の正規化 ストップワード の除去 単語のベクトル表現 文書のベクトル表現 それぞれについて具体的な方法など検討すべき点はあるものの、大まかには上の工程を行えば良さそうです。 テキストのクリーニング テキストのクリーニングは、テキスト内のノイズを除去する工程です。Web ページを スクレイピング して得たデータなどは HTML タグが含まれていたりしますが、これらは推測したいラベルと意味的な関わりの無いノイズとなります。ノイズを除去することでタスクの結果に及ぼす悪影響を抑えることができます。 今回はチャットメッセージの形で蓄積されたデータであり、ほぼ不要と思われるので特に何もしないことにしました。 単語の分割 テキストを 機械学習 の入力とする場合は、数値のベクトルに変換することがよく行われます。その際テキストを何らかの「最小単位」へと分割し、その最小単位が対応する数値ベクトルを使ってテキスト全体を数値ベクトルにするという方法を取ります。 この「最小単位」として何を使うかによって大きく二つの方法があるようです。 形態素 に分ける方法 MeCab や Janome 、 Kuromoji といった 形態素解析 器で文を 形態素 に分解します。 N-gram 法を使って分ける方法 テキストを文字ごとに分け、各文字の後ろN文字目までの組によって作られる「 N-gram 」を最小単位とする方法です。 今回の実装では 形態素 に分ける方式を取りました。理由としては 参考記事が 形態素 にしか触れていない 文法的に意味のある単位の方がそれっぽい 実装も容易 という点が挙げられます。 使用した 形態素解析 器は以下の通りです。 Python : Janome JVM : Kuromoji どちらもそれぞれの環境で導入が容易であり、使用方法も簡単であったためこれらを選びました。辞書は何を使うかも検討しなければいけないところですが、今回は簡便にどちらも IPA 辞書を使っています。 単語の正規化 単語の正規化では全角半角などの文字種の統一、表記揺れの吸収といった処理をすることで、意味的に同じ単語を同じものとして扱えるようにします。 文字種の統一 日本語文字は全角に 英数字記号は半角に 数字の置き換え 数字はすべて 0 に置き換え 単語の統一 形態素解析 から原形を使う ストップワード の除去 ストップワード は 自然言語処理 の際に一般的で役に立たない等の理由で処理対象外とする単語のことです。「てにをは」のような助詞や助動詞、「あれ、それ」などの代名詞といった単語はどのような意味のメッセージにも登場し、内容の推測には役に立たないため除去します。 ストップワード の除去には様々な方式があるようですが、ここでは日本語の ストップワード 辞書の一つである Slothlib を使い、これに含まれる語は除去することにしました。 単語のベクトル表現 これまでの工程できれいになった単語を、数値ベクトルに対応させます。ベクトル化するのにも様々な手法が存在するようですが、主なものは以下の2つのようです。 one-hot表現 分散表現 one-hot表現は各単語を単位ベクトルに対応させます。シンプルで分かりやすい方法です。 分散表現は Word2Vec みたいなやつですね。one-hot 表現からさらに加工して得るもののようです。 どちらを使うかはこの後の文書のベクトル表現の種類によって決まってきます。 文書のベクトル表現 前工程で決めた単語ごとの数値ベクトルを用いてテキスト全体の数値ベクトルへの対応を作ります。単語ベクトルと同じく主な手法は2種類に大別できます。 one-hot 表現からは文書に含まれる単語のベクトルを足し合わせた Bag of Words となります。さらに単語の出現頻度と逆文書頻度の積で単語を重みづけすれば TfIdfベクトル です。 分散表現での文書ベクトル表現は Word2Vec で得た単語ベクトルを足し合わせたり、 doc2vec を使ったりで得ることが出来ます。 今回のサンプル実装に先立っての事前調査で、scikit-learn に TfIdf ベクトルを計算するための API が用意されており、また仮に自前で実装したとしても何とかなりそうということで、Tfidf ベクトルを使うことにしました。 モデル学習について 分類 アルゴリズム は各ライブラリ、サービスごとに用意されたものを使うことになると想定されました。それぞれのライブラリ、サービスによっては実装されていない アルゴリズム もあるかもしれず、ここはあえて統一せずに使えるものを使うという方針にしました。 ただし選定の際のガイドとして scikit-learn のアルゴリズム選定チートシート を使用しました。 これによれば、今回のサンプルプロジェクトにおけるデー タセット 規模やタスクの性質から、線形 SVM 分類器、あるいは単純 Bayes 分類器が良いとのことです。そのため単純 Bayes 分類器が使える場合はこれを使うようにしています。 学習のそのあと 学習されたモデルはシステムへ組み込まれサービスとして運用されるのがまっとうなプロジェクトの進み先ですが、今回はサンプルプロジェクトなので評価指標の算出までで実装完了とします。 算出した評価指標は以下の通りです。 混同行列 正解率 適合率、再現率、F 値 加重平均 マクロ平均 実装を終えての所感 Python + scikit-learn scikit-learn は Python の代表的な 機械学習 の オープンソース ライブラリです。 改めて調べてみても、やはり デファクトスタンダード となっていると納得せざるを得ませんでした。 API が非常に充実している インターフェースの統一された API データ分割、ハイパーパラメータチューニング、性能評価、可視化もできる ドキュメントも充実している ググればサンプルがたくさん出てくる 特にインターフェースが統一され、かつ充実した API は非常に便利であり、 機械学習 を試したいならまずこれ触れば間違いはなさそうです。 Python + H2O H2O is an open source, in-memory, distributed, fast, and scalable machine learning and predictive analytics platform that allows you to build machine learning models on big data and provides easy productionalization of those models in an enterprise environment. H2O ドキュメントトップページ より引用 とあるように、H2O はライブラリというよりかはプラットフォームのようです。 Python からその機能を使うこともできるし、 アーキテクチャ を見れば Java や Scala , R などからも使うことが出来るとわかります。また scikit-learn のパイプラインに組み込むことが可能だったりと他ライブラリ、プラットフォームとの連携にも考慮されている点もいいですね。 また AutoML という機能もあり、自分でコードを書かずともデータを与えれば自動でいい感じに学習してくれるようです。 ただ今回は AutoML は使わず、自分で Python のコードを書いて実装しました。 scikit-learnと比較して 分類 アルゴリズム には単純 Bayes 分類器を選びました。出来上がったコードは自体は scikit-learn のものと比べ記述量や難易度は変わらない印象を受けました。 ただ H2O を使って実装してみた系の日本語記事が少なく、参考にできるのは公式ドキュメントのサンプルコードのみというのがつらいところ。 公式チュートリアル もありますが、中途半端なコードだったりで情報量が少なく、事前に scikit-learn などで実装した経験がないとなかなかしんどいかと。 また H2O はモデルの学習部分にフォーカスしているためか、テキストのベクトル化といった前処理に関して特に組み込みの API があるというわけではなさそうでした。そのため Tf-Idf ベクトル化は scikit-learn の API を使うなどする必要があります。 JVM 系 + Smile Smile is a fast and comprehensive machine learning engine. Smile は JVM 上で利用できる 機械学習 エンジンで、 Java や Scala など好きな JVM 言語で使うことが出来ます。組み込む先のシステムが JVM 言語で作られているのであれば、 機械学習 関連のコードとアプリケーションのコードで言語を揃えられるのが良いですね。 実行速度に優れ、 自然言語処理 、統計処理、データ可視化も Smile の API でできると謳われています。 scikit-learnと比較して ライブラリの特徴というよりかは言語的な特徴になりますが、 Scala での実装は説明的で分かりやすくなるように感じました。ただ Java で実装した場合、型表記が面倒か抽象的過ぎるかになりそうという印象でもあります。 Smile そのものに言及すると、 インターフェイス が独特で scikit-learn などとは差が大きく、結構慣れるのに時間がかかりそうです。使ってみた系の情報が少ない点もネックになるかなと思われます。 また分類 アルゴリズム は豊富に用意されているもののハイパーパラメータチューニングなどの便利 API などはなさそうで、そのあたりを自前で実装しなければならないあたり不便という印象がぬぐえません。 Apache Spark Apache Spark は巨大なデータに対して高速に分散処理を行うための フレームワーク です。 Java や Scala 、 Python などいろいろな言語での API が用意されています。 分散処理のための フレームワーク なので汎用的に使えるのですが、 機械学習 のための コンポーネント が組み込まれていることから 機械学習 ライブラリとしても使用することが出来ます。 先にも触れたようにいろいろな言語で使用することが出来ますが、今回は Scala から使ってみました。 scikit-learnと比較して Spark の 機械学習 コンポーネント が提供する API のインターフェースは scikit-learn のように統一されており、Smile より使いやすい印象です。 組み込みで前処理やハイパーパラメータチューニングができたりと機能も充実しており、使い勝手だけを見れば scikit-learn とも遜色ないように思えます。 今回 Windows 上での環境構築をしてみましたが、 インストーラ を起動しなければならないあたり面倒でした。 Linux や Mac であればコマンドでインストールできるのでその点は問題にはならないと思われます。 マネージドサービスで Spark を使えるものが多いため、そういったサービスで使う場合もよさそうです。 Amazon SageMaker Amazon SageMaker は AWS 上で提供されている完全マネージド型の 機械学習 サービスです。 Amazon SageMaker というサービスの下に Amazon SageMaker Studio Amazon SageMaker Ground Truth Amazon Augmented AI Amazon SageMaker Studio Notebooks ... といった機能が用意されています。 今回は自前で前処理等の部分を実装する想定だったため、 Amazon SageMaker Studio Notebooks を使って自分で実装した Python コードを実行しました。 scikit-learnと比較して 組み込み アルゴリズム について sagemaker というパッケージを読み込むことで組み込み アルゴリズム や Amazon SageMaker 独自の アルゴリズム やモデルを使用することが出来ます。 ただ提供されている アルゴリズム も限られていること、ドキュメントやサンプル実装の情報は存在するものの少ないことから、あえて組み込み アルゴリズム を利用するメリットは感じられませんでした。 Amazon SageMaker 上で scikit-learn コードを使用する方法もドキュメントに記載があることから、Notebook 上で自分でコードを書くのであれば scikit-learn を利用しておけばよいのではないかという印象です。 活用について 学習したモデルと AWS Lambda、 Amazon API Gateway を組み合わせることで、 HTTPS エンドポイントを作成することができます。 作成したモデルを用いて手早くアプリケーションを作成したい場合には有用そうです。 AI Platform AI Platform は Google Cloud 上の AI 関連サービスのブランドです。 2020/3/10時点でまだベータ版。そのためメニュー上でもかなり下の方に配置されています。 AI Platformというブランドの下に AI Platform Notebooks AI Platform Training AI Platform Prediction Data Labeling Service(ベータ版) Deep Learning VM Image Deep Learning Containers(ベータ版) ... とたくさんのサービスがぶら下がっています。 AI Platform Training ではト レーニン グデータ、検証データ、テストデータと分けて GCS( S3 の GCP 版)に格納することで組み込み アルゴリズム による学習も行える様子。 ただ今回は自前で前処理等の部分を実装する想定だったため、AI Platform Notebooks を使って実装したコードを実行しました。 scikit-learnと比較して 組み込みの アルゴリズム について AI Platform では独自の アルゴリズム などを提供する Python パッケージが用意されているわけではないようです。 そのため今回の実装では結局のところ scikit-learn での実装を AI Platform Notebook 上で実行できるようにしたものとなりました。 その際 GCP で用意されている scikit-learn は 2020/3/10 現在で 0.19.2 だったことでハマりました。最新の 0.22.x 系とは API の非互換の部分が多いので注意が必要です 1 。 環境について Notebooks 環境が用意されているので開発環境構築の手間はかからないのが良いです。 クラウド サービスらしくスペックの変更もすぐに実施できます。 ただデフォルトのスペックが n1-standard-4(4 vCPU / メモリ15GB) + ストレージ 100GB と少し強めなので、軽い処理の実験用につどスペック変更するのが手間でした。 活用について まだベータ版なので正式サービスに導入するには抵抗がありますが、サービスに組み込むための学習モデルの作成プラットフォームといった利用方法であれば問題なさそうです。 またハイスペックな インスタンス を利用することで短期間でのモデル作成が実現できそうとも感じました。 ただし、前処理やモデル生成の処理時間に困っていないのであれば、ローカルで scikit-learn を使うことに対するアドバンテージがないかもしれないというのも正直な感想です。 Azure Machine Learning Azure Machine Learning は Microsoft Azure 上で提供される 機械学習 サービスで、 機械学習 を含む DevOps、MLOps を実現する クラウド 環境です。 機械学習 モデルのト レーニン グ、デプロイ、自動化、管理、追跡というワークフローすべてにおいて使用できるツールが提供されています。 Azure Machine Learning デザイナー や 自動 ML という、コードを書かずにモデルの作成ができるツールもあります。 ただ今回は Azure Machine Learning 上の Notebook で自分で実装したコードを実行しました。 scikit-learnと比較して 組み込みの アルゴリズム について Azure Machine Learning では azureml という Python パッケージが用意されているものの、これは学習 アルゴリズム などを提供するものではないようです。 公式の チュートリアル を見ても、 Python でのサンプルは scikit-learn を使って線形回帰を行っています。 そのため今回の実装では結局のところ scikit-learn での実装を Azure Machine Learning の Notebook 上で実行できるようにしたものとなりました。 活用について AI Platform と同じくサービスに組み込むための学習モデルの作成プラットフォームといった利用方法ができそうです。 ただ Notebook の実行環境としてしか使わないのであれば、ローカルで scikit-learn を使うことに対するアドバンテージがないかもしれないというのも AI Platform と同様です。 まとめ 今回は 機械学習 のライブラリ、サービスの使用感を、単純なモデル学習 スクリプト を実装することで調べました。 単純な学習 スクリプト を自分で実装する限りにおいて、ライブラリやサービスの機能的な面で決定的な違いは無いように思えました。 ただし、普及具合を考えると scikit-learn がやはりおすすめということになります。 scikit-learn は 機械学習 ライブラリの デファクトスタンダード です。それ自体の充実した API 、ドキュメントもさることながら、日本語の解説記事も Web 上にあふれています。 Python の環境さえ作ればライブラリのインストールも容易であり、学習時やさくっと試したい時に最適でしょう。 今後の展望 今回は非常に単純な 機械学習 タスクに対し、自分で実装するという縛りの下でライブラリやサービスの使用感を調べました。 ですが、 Amazon SageMaker や AI Platform、Azure Machine Learning などを見ていると、Notebook 以外にもいろいろな 機械学習 関連機能、サービスがあります。 プロジェクトの要件にもよるかと思いますが、マネージドサービスを使う利点はそういったサービス群にこそあるのではないかと感じています。 今後機会があればそれらのサービスも触ってみたいものです。 参考 scikit-learn Machine Learning in Python H2O.ai Documentation h2oai/h2o-tutorials Smile - Statistical Machine Intelligence and Learning Engine Apache Spark Amazon SageMaker(機械学習モデルを大規模に構築、トレーニング、デプロイ)| AWS Amazon SageMaker Python SDK — sagemaker 1.51.0 documentation Google Cloud - AI & Machine Learning Products Google Cloud - AI Platform Notebooksベータ版 Azure Machine Learning 最新版を入れ直すことも可能かもしれないが未調査。 ↩
はじめに こんにちは。新卒1年目エンジニアのdd_fortです。 今回はDockerを勉強しようと思い、CentOS8でDockerの環境構築をしようとした話です。 Dockerが公式サポートされていないCentOS8にDockerを使用するときに躓いた点や気を付ける点を中心にインストール方法を紹介しようと思います。 目次 はじめに 目次 Docker とは リポジトリの追加 Docker のインストール Docker の起動 ネットワークの問題 回避策1:Docker 起動時のオプション 回避策2:firewalld の設定変更 回避策3:パケットフィルタリングツール変更 最後に Docker とは Docker社が開発している、コンテナ型の仮想環境を用いてアプリケーションを開発・配置・実行するためのプラットフォームです。 コンテナ型仮想環境 Doecerのメリット ホストOSを直接アクセスするためオーバーヘッドが少ない 可搬性が高くDockerをインストールすれば、ホストOS環境に依存せず動作する コード化されたファイルを共有することで開発環境のセットアップが容易になる リポジトリ の追加 まず、Docker の リポジトリ を追加します。 $ dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 以下のようになっていればOKです。 $ dnf repolist repo id repo の名前 状態 AppStream CentOS-8 - AppStream 5,120 BaseOS CentOS-8 - Base 2,126 extras CentOS-8 - Extras 13 docker-ce-stable Docker CE Stable - x86_64 63 Docker のインストール dnfコマンドを使用して dnf install docker のようなコマンドでインストールしようすると依存関係の問題でインストールできません。 回避策として今回は、 --nobest オプションを付けて実行します。 $ dnf install --nobest docker-ce containerd.io この状態のままだと、 dnf upgrade を実行すると Dockerを強制的にインストールしているためエラーが発生します。 そのため、CentOS7 の リポジトリ を利用してcontainerd.io をアップデートします。 $ dnf update https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.10-3.2.el7.x86_64.rpm 以下を実行してエラーが発生しなければOKです。 $ dnf -y update docker-ce Docker の起動 Docker を起動します。 $ systemctl start docker $ systemctl enable docker バージョン確認 $ docker --version Docker version 19.03.8, build afacb8b ネットワークの問題 しかし、CentOS8の場合はこのままではコンテナ内から名前解決ができずdnf 等が使えません。 回避策1:Docker 起動時のオプション コンテナの構築時や起動時に --network host を指定して実行することで解決することができます。 $ docker pull wordpress $ docker run --name wordpress-test --network host -d wordpress 上記は WordPress のコンテナを立てるときのコマンドです。 回避策2:firewalld の設定変更 iptables が動いている場合は、firewalld でNAPTするように設定を変更することで解決することができます。 $ firewall-cmd --add-masquerade --permanent $ firewall-cmd --reload 回避策3:パケットフィルタリングツール変更 根本的な原因としては、CentOS8からパケットフィルタリングツールが iptables から nftables に変わったことです。 (Docker が nftables を設定できないため) CentOS のパッケージフィルタリング構成 CentOS6 : iptables CentOS7 : firewalld(管 理I /F), iptables CentOS8 : firewalld(管 理I /F), nftables iptables を利用するように変更することで解決可能 firewalld を止める $ systemctl stop firewalld $ systemctl disable firewalld iptables をインストール $ dnf install iptables-services iptables を起動 $ systemctl enable iptables $ systemctl start iptables 最後に 私はCentOS8にDockerが公式非対応であること知らずに環境構築をはじめてしまったため、環境構築だけで時間が多く掛かってしまいました。 しかし、CentOS8とDockerの勉強になったのでよかったかなと思っています。 また、CentOS8ではpodman(Docker互換のコンテナエンジン)が使えるみたいなのでそれを試してみるのもいいかなと思いました。 この記事がCentOS8でDockerを使おうとしている人の助けとなれば幸いです。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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
id:radiocat です。カンバンは「チームの現状を良くしたい、何かを変えたい」という思いがカタチとして表れるツールであることを、この1年を通して感じています。 私は2019年4月から現在のチームのマネジメントに携わり、「チームのアウトプットを安定させる」ことを目指してこの1年間を取り組んできました。今回はその1年間をふりかえってみたいと思います。今年度、 配配メール / クルメル の開発チーム(略して「はいくる開発チーム」)は約半数の新しいメンバーが私と同時にJOINして新チーム体制がスタートしました。 いにしえの 開発プロセス の中に一筋のカンバン 配配メールは10年以上の歴史を持つメール配信の老舗サービスです。当時から稼働している機能がいまだに多数存在しているレガシーなシステムなので、開発スタイルもレガシーな流れを受け継いでいました。半年程度先までのタスクが担当者に アサイ ンされ、古来伝来の ガントチャート でリーダーが日々の状況をキャッチアップして 進捗管理 していました。 古来伝来のエクセル ガントチャート そんな開発スタイルの中で少し異質だったのがカンバンの存在でした。スケジュールは ガントチャート で管理しているはずですが、カンバンはどのような目的で使っているのか聞いてみると、各担当者がスケジュールを意識して個人レベルのタスク管理を徹底することで、スケジュールを維持するのが目的でした。 1年前のカンバン しかし改めて振り返ってみるとこのカンバンの運用には課題がいくつかありました。 タスク管理は個人任せ ガントチャート のマスタスケジュールからカンバンへの落とし込みは担当者に依存しており、目測を誤るとスケジュールが守れない 人ごとにレーン分け 全員の状況がわかるようになっていても、みんな自分のレーンに関心が集中しており、他のメンバーの進捗状況に目を向ける余裕はない 見えているのは1日分 1日の状況からは今後のスケジュールが予測しづらく、全体のスケジュールに関心を持つリーダーの不安は解消されない このような課題を抱えながら、はいくる開発チームは新体制で開発を続けていました。 カンバン駆動で漕ぎ着けた史上最大級のリリース 配配メールは2019年秋に『 Bridge 』という新しいプランをリリースしました。新しいプランは配配メールが配信ツールから マーケティング ・サービスへと進化するための高度な機能を提供するという、新チームが始まる前から企画されていた過去最大規模のプロジェクトでした。リリースを控え開発が終盤を迎えた頃、懸念していた課題が問題として顕在化していました。難易度の高い機能のいくつかで開発に大きな手戻りが発生し、スケジュールを リカバリ するため、チームは担当者のフォローやタスクの組み換えに追われていました。このタイミングでの 開発プロセス の変更にはリスクもありますが、このままの進め方では状況の改善が難しいと判断し、できる範囲で課題を改善することにしました。言わば「走りながらフォームを変えて制限時間内にゴールを目指す」作戦です。 1週間スプリントの導入 リリースが迫ってきている中でこれ以上の大きな手戻りは致命的なので、タスクを個人任せにせずチーム全員で助け合って進める必要がありました。しかし、それまでの進め方では熟練者ですら全体状況を見渡すのが難しくなっていました。そのため、スケジュールから逆算した1週間先の目指すべき状態を明確にし、チーム全体で1週間分の計画を立てて進めることにしました。そして、計画した1週間分のタスクを前出のカンバンのウラに貼り出しました。 カンバンのウラを計画に使用 この時点では特定の人に属人化したタスクが多数あったため、「人ごとのレーン分け」はいったんそのまま受け入れました。ただし、誰でもできるタスクは無理に誰かに割り当てるのではなく、できる人ができるタイミングで受け取ってどんどん進めていくことにしました。また、属人化したタスクも含めて毎朝全体の状態を全員でチェックして誰かのタスクが遅れそうならフォローしあってチーム全体で1週間後のゴールを目指すことにしました。 こうして、カンバンを軸とした、1週間の計画と全員で状況を確認する朝会が功を奏し、新プランはこの秋に無事リリースを迎えました。 www.hai2mail.jp 共用カンバンからチームのカンバンへ 「人ごとにレーン分け」の課題が残されたカンバンは、このあとチームを2つのグループに分けることで解消しました。新体制となって半年のまだまだ未成熟なチームでは、総勢8人という体制の中でお互いをフォローしあうには、全体を見渡すにしても助けを求めるにしてもサイズが大きすぎると考えました。4人でコンパクトに使うことになったカンバンは、人ごとにレーンを分けなくても全体の状況を見渡しつつ、個人のタスクが管理できるようになりました。複数人が個人タスクを扱っていた共用カンバンから、全員で一緒にタスクを扱うチームのカンバンに変わったのです。 個人の共用カンバンからチームのカンバンへ これにより、これまで概ね半年サイクルだったリリースサイクルを3ヶ月に短縮し、新プランをリリースしたあとも機能をアップデートし続けることができています。 可視化から 言語化 へ カンバンを使って開発タスクや課題が可視化されただけでなく、課題に向き合って気づきを得たことでその取り組みを 言語化 してアウトプットできるようにもなりました。以下は取り組みを推進したメンバーが 弊社主催のMeetup で発表したスライドです。 speakerdeck.com 課題の改善に取り組むだけでなく、それを振り返って改めて 言語化 することも大切なことです。チームにはまだまだ課題がありますが、このように取り組みを振り返って一つの区切りをつけることで、新たなステージへの次のチャレンジに向かうことができます。 何かを変えたいという思いからカンバンが生まれ変化が起きた 思えば、1年前チームが使っていたカンバンは今となっては使いにくいものでした。 しかし、それは言い換えれば「何かを変えたいけど変えれない」状態が使いにくいカンバンとして表れていたとも言えます。うまくいかないから止めるのではなく、うまくいかないことをいかにうまく活かすかが改善であると気づけたのがこの1年の学びです。 チームで計測したあるパフォーマンス指標に目を向けると、タスクが個人に依存して乱高下していたパフォーマンスは、カンバン駆動で改善に取り組んで以降は安定しています。 チームのパフォーマンス 前述のとおりチーム体制と開発する機能の難易度が変わったため高低差は一概に比較できませんが、この指標をもっと高い位置で安定させていくことが次のステージのチャレンジです。 配信ツールから マーケティング ・サービスへ メール配信は受信側に依存することが多い不確実性の高い技術です。新プランをリリースした現在の配配メールは、単にメールを届けるだけにとどまらず、お客様の マーケティング を支えるサービスとして活用されています。 マーケティング もまた不確実性の高いビジネスの手法です。 届くかわからない 読むかわからない クリックするかわからない サイトを再訪するかわからない 熱感が上がるかわからない 購入意志を持つかわからない 不確実性と向き合い、これらの「わからない」を「わかる」に変えていくことが、今後の私たちのチャレンジです。 取り組みを発信していきます 私たち「はいくる開発チーム」は、同じような課題に取り組んでいる中小企業のエンジニアチームが”楽”になることを目指して、自分たちの取り組みを発信しています。今後、以下のイベントでも取り組み事例をお伝えする予定です(新型コロナウィルスの影響によりイベントの開催は現時点では不透明ですが、何らかの形で事例はお伝えできると思います)。よろしければご参加ください。 2020.agilejapan.jp www.scrumosaka.org
はじめに 花粉がつらくなってきました... sts -250rrです。 開発エンジニアとして、チームに アサイ ンされて1年が経とうとしています。(速い) 私が担当している商材は主に Java で書かれているため、普段は JVM 上で動くアプリケーションに意識が行ってしまいがちで、そもそもアプリケーションがどうやって動いているなど ( Tomcat 上でアプリケーションを動かしているということは漠然と理解していますが...) OSレベルまで掘り下げして理解できていないように感じています。 今回は基礎に振り返るという意味で Linux のプロセスについてまとめていきます。 (本記事のコマンドはUnixOS系である MacOSX で実行しています。) はじめに プロセスって何? マルチプロセス プロセススケジューラ コンテキストスイッチ プロセスの状態 コマンドで確認 ゾンビプロセス まとめ 参考資料 プロセスって何? プロセスとは、「OS上で実行中のプログラム」を指します。 プロセスが動いているということは、コンピュータリソース(CPUやメモリなど)を消費している状態です。 例えば、 Linux コマンドは1つのプログラムであるため、コマンド実行時にプロセスを1つ作成します。 この間、プロセスはCPUやメモリを消費してプログラムを実行しています。 プロセスの特徴には以下のようなものがあります。 1つのCPU上で同時に処理するプロセスは1つだけ 複数のプロセスが実行可能な場合、個々のプロセスを適当な長さの時間ごとにCPU上で順番に処理する。 psコマンドを叩いてみると複数のプロセスが動いているように見えます。 (ターミナルを2枚開いた状態で1枚目はPostgresに接続、2枚目でpsコマンドを実行 することで2つのプログラムが動いている状態を用意しています。) $ ps -a PID TTY TIME CMD 17239 ttys001 0:00.02 login -pf sts 17240 ttys001 0:00.03 -bash 17642 ttys001 0:00.00 ps -a 15351 ttys017 0:00.03 login -pf sts 15352 ttys017 0:00.12 -bash 17237 ttys017 0:00.01 psql -U postgres -p 5432 PC上の操作でも複数の処理を同時に動かすことがあるように、 実際のWEBアプリケーションにおいても、 Apatch と Tomcat 、データベースが同時に動いて1つのサービスとして機能しています。 これを実現しているものが次項のマルチプロセスになります。 マルチプロセス マルチプロセスは複数のタスクを並行して実行する機能のことを指しますが、 Linux は厳密には同時並行で処理を行うことを実現してはいません。 Linux がやっていることは人が認識できない時間でプロセスを区切り、順番に処理することで複数のプロセスが同時に処理をしているように振る舞っています。 プロセススケジューラ 複数のプロセスが同時に処理をしているように振る舞うようにするため、 Linux はプロセススケジューラという機能を持っています。 例えば3つのプロセスを同時に処理する場合、プロセススケジューラによって以下のようにプロセス0 → 1 → 2 と処理を行っていきます。 また、プロセスの処理は以下のように並列で進んでいきます。 プロセスを短い間隔(タイムスライス)に区切り、1つのCPU上で動くプロセスを切り替えることで複数のプロセスが同時に処理しているように振る舞っています。 タイムスライスはそれぞれ等しいくらいの長さで区切られ、プロセスが増えるとタイムスライスの数が増えるため1つのプロセスが完了するまでに時間がかかります。 コンテキストスイッチ 1つのCPU上で動くプロセスがタイムスライスで切り替わることを コンテキストスイッチ と呼びます。 コンテキストスイッチ は、タイムスライスの時間が切れると容赦なく発生します。 例として、プロセス1 → 2 と順に実行する1つのプログラムがあったとします。プログラムの実行中にプロセス1が起動した場合、プロセス1 → 3 → 2 という順序で実行される可能性があります。 1プログラム以外の要因(他のプロセスが間に起動したなど)によって完了時間が伸びる可能性があるという点が コンテキストスイッチ によるプロセス特性の1つです。 プロセスの状態 ここまでで記載したように、1つのCPU上で動くプロセスは切り替わるため、プロセスは実行状態・スリープ状態などの複数の状態を持っています。 以下にプロセス状態の一例をあげます。 状態 意味 実行状態 CPU使用状態 実行待ち状態 CPUが割り当てられるのを待っている スリープ状態 イベントの発生を待っている(CPUは使っていない状態) ゾンビ状態 終了状態の受け取りを待っている プロセスの状態は以下のように遷移します。 プロセスはプロセススケジューラに基づき、CPUの実行権を得ると実行待ち状態から実行状態に遷移し、処理をすべて完了するまで、タイムスライス区切りで状態を遷移し続けます。 プロセスは終了状態を得ることで終了となります。 コマンドで確認 Mac 上でtopコマンドを実行してみると以下のような出力になりました。(上部10件まで) Processes: 418 total, 5 running, 413 sleeping, 1787 threads 19:16:35 Load Avg: 1.97, 3.00, 4.61 CPU usage: 7.7% user, 26.88% sys, 66.3% idle SharedLibs: 295M resident, 33M data, 29M linkedit. MemRegions: 109880 total, 3310M resident, 119M private, 1551M shared. PhysMem: 8606M used (2803M wired), 7777M unused. VM: 1959G vsize, 1882M framework vsize, 2689796(0) swapins, 2881396(0) swapouts. Networks: packets: 58205899/34G in, 48094735/17G out. Disks: 5133929/94G read, 2715156/54G written. PID COMMAND %CPU TIME #TH #WQ #PORT MEM PURG CMPRS PGRP PPID STATE BOOSTS %CPU_ME %CPU_OTHRS UID FAULTS COW MSGSENT MSGRECV SYSBSD SYSMACH CSW PAGEINS 3075 VBoxHeadless 59.5 01:52:54 39/2 5 996 908M 0B 47M 3075 3060 running *0[2] 0.00000 0.00000 501 272297 390 4904505+ 110938+ 80020677+ 12095394+ 56986420+ 760 0 kernel_task 28.9 01:47:26 172/4 0 0 105M 0B 0B 0 0 running 0[0] 0.00000 0.00000 0 173910+ 0 100809447+ 97799283+ 0 0 429621385+ 0 14655 firefox 15.0 04:38.25 61 4 775+ 443M+ 9384K 81M- 14655 1 sleeping *0[1192+] 0.00000 0.06446 501 1835132+ 15875 940954+ 304755+ 2763928+ 3000384+ 1945509+ 33761+ 15349 Terminal 6.4 00:11.47 9 4 261- 72M 33M+ 0B 15349 1 sleeping *0-[368+] 1.14998 0.42402 501 149598+ 491 55408+ 13334+ 134496+ 142201+ 63838+ 2824 2303 com.docker.h 5.6 02:01:48 15 0 38 2355M 0B 304M 2288 2300 sleeping *0[1] 0.00000 0.00000 501 725271 446 785 317 207705774+ 465 126169989+ 68 15698 top 4.3 00:01.43 1/1 0 25 5284K 0B 0B 15698 15352 running *0[1] 0.00000 0.00000 0 11303+ 109 489446+ 244672+ 39445+ 314731+ 1968+ 0 226 WindowServer 3.1 15:54.83 10 5 1377- 447M 19M 47M 226 1 sleeping *0[1] 0.38155 0.00856 88 1809748+ 8936 13705840+ 6222509+ 5598080+ 19197483+ 7974577+ 9094 188 hidd 1.9 02:39.48 5 3 241 3492K 0B 664K 188 1 sleeping *0[1] 0.00688 0.00000 261 83167+ 171 629366+ 489469+ 5409421+ 1849976+ 2190289+ 5218 265 airportd 1.1 03:49.45 12 10 1385+ 13M+ 0B 5144K- 265 1 sleeping *14180[212] 0.00000 0.93819 0 243908+ 268 5776050+ 2282663+ 11177993+ 5077848+ 4985570+ 2748 171 locationd 0.6 01:34.95 7 5 231+ 8600K+ 192K 1420K 171 1 sleeping 0[22641] 1.15398 0.00000 205 207357+ 338 357612+ 97377+ 3218875+ 737171+ 876386+ 12727 まだまだ情報量が多く見づらいので、Terminalとtopコマンドだけ抜粋します。 PID COMMAND %CPU TIME #TH #WQ #PORT MEM PURG CMPRS PGRP PPID STATE BOOSTS %CPU_ME %CPU_OTHRS UID FAULTS COW MSGSENT MSGRECV SYSBSD SYSMACH CSW PAGEINS 15349 Terminal 6.4 00:11.47 9 4 261- 72M 33M+ 0B 15349 1 sleeping *0-[368+] 1.14998 0.42402 501 149598+ 491 55408+ 13334+ 134496+ 142201+ 63838+ 2824 15698 top 4.3 00:01.43 1/1 0 25 5284K 0B 0B 15698 15352 running *0[1] 0.00000 0.00000 0 11303+ 109 489446+ 244672+ 39445+ 314731+ 1968+ 0 topコマンドは今正しく動いているプログラムであるため状態は実行中(running)となっています。 対して、Terminalはtopコマンドの実行後、入力待ち状態となったためスリープ状態(sleeping)となっています。 手元で実行していただくとわかるかと思いますが、topコマンド実行中にコマンドを終了させてみたり、新しく実行してみたりすると プロセスの生成や状態がみるみる変わっていく様子を見ることができます。 ゾンビプロセス IDE などで開発をしている中で、「既に8080ポートが使われている」などの理由でアプリ起動に失敗した経験が皆さんにもあるのではないでしょうか? そんな悪さをしているのがゾンビプロセスです。 ゾンビプロセスが生まれてしまう原因は様々ですが、 一定時間子プロセスからの返答がなかったことから、 タイムアウト として親プロセスを終了してしまい子プロセスが宙に浮いてしまう。 というものがよくある1つの例かと思います。 「親プロセスが終了したからといって子プロセスもあわせて勝手に終了するわけではない」と覚えておきましょう。(その逆もしかりです。) ゾンビプロセスができてしまったときは対象のプロセスを調べ、Killして対処しましょう。 まとめ 雑多になりましたが、 Linux のプロセスについてまとめていきました。 書籍やブログ記事などで知識を得ることはできますが、なにぶんOSの裏の動きは実際に手を動かしてみないとピンとこないものです。 コマンドは知っていても見方や期待される状態は知識のみでは補えない点も多いかと思います。 コーディングに疲れて一息つくときなどにpsコマンドやtopコマンドをボーっと眺めて、変化が起きる瞬間をウォッチしてみると面白い発見や知識がつながる瞬間があるかもしれません。 また、 Linux コマンドの一覧表を以下ブログにてまとめておりますので、ご参考ください。 よく使うLinuxコマンド一覧【最新版】 Linux の理解をより深めたい方へ以下関連おすすめブログ ・ ls コマンド 【使い方 まとめ】 ・ find コマンド 【使い方 まとめ】 ・ iptables まとめ【Linux ファイアウォール】 ・ sed コマンド【使い方 まとめ】 ・ vi コマンド【使い方まとめ】 ・ Linuxのファイル操作でよく使うLinuxコマンド ・ 初心者のためのawkコマンド ・ 実務で使える!基本的なシェル(Linux)コマンドの話 ~forとsed~ 参考資料 [試して理解]Linuxの仕組み ~実験と図解で学ぶOSとハードウェアの基礎知識~ エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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
こんにちは、takaramです。 メール配信サービス 配配メール / クルメル の開発チームでは昨年、 はいくる通信 と題し、メール配信に関する技術・ノウハウをご紹介しました。 今回はその続編として、 配信エラー解析 についてお話したいと思います。 配信エラー解析とは 配信エラー解析のしくみ 二種類のFromアドレス 配信エラーと到達率 まとめ 配信エラー解析とは 「自分の送ったメールが届かなかった」という経験はあるでしょうか?メールの送信に失敗する原因としては、例えば以下のようなものがあります。 メールアドレスが間違っている 相手の メールボックス が満杯になっている 相手に受信拒否されている サーバ間で通信に失敗した 特に メールマガジン などの一斉配信においては、配信エラーは珍しくありません。内容に興味がなくなった読者に受信拒否されたり、そもそも登録したアドレスが間違っていたりといったケースです。 エラー理由によって解決策は全く変わってくるため、メールが届かないとの問い合わせを読者から受けた際にはまず理由を把握する必要があります。また、配信エラーを放置し届かないメールを送り続けることはコストの無駄になってしまいます *1 。 そこで、我々の開発している配配メール/クルメルには 配信エラー解析 機能があります。 配配メールの配信エラー解析( 配配メールWebサイト より) 配信に失敗したアドレスとその理由がわかるほか、配信エラーが一定回数発生するとそのアドレスへの配信を自動で停止します。アドレス間違いなどの恒久的なエラーの場合は1回、メー ルボックス の容量オーバーなどの一時的なエラーの場合は3回の配信エラーで配信を停止します(回数は設定で変更可能)。 しかし、一斉に配信したうちのどのアドレスがなぜ配信エラーになったのか、どうやってわかるのでしょうか。 配信エラー解析のしくみ みなさんはこのようなメールを受け取ったことはありませんか? これは バウンスメール と呼ばれるもので、送信したメールが相手に届かなかったことを知らせるメールです。受信拒否などの場合は 送信先 サーバから、アドレスの@以降が間違っている、 送信先 サーバへの接続に失敗したなどの場合は送信元サーバからバウンスメールが送られます。 ではバウンスメールの宛先はどのアドレスかというと、もちろんメールのFrom(差出人)アドレスです。とはいっても、ここで言うFromアドレスは、みなさんが思い浮かべるものとは少し違っています。 二種類のFromアドレス メールの送受信の際、サーバやPCの間では以下のようなデータがやり取りされています。 Date: Mon, 9 Mar 2019 12:34:56 +0900 From: taro@example.com To: hanako@example.jp Subject: Test Mail こんにちは! 本文は「こんにちは!」の一行だけですが、件名や送信日時などが書かれた メールヘッダ が付け加えられています *2 。ここでFromに書かれているのが受信者が目にするFromアドレスで、 ヘッダFrom と呼ばれます。メルマガなら通常メールを作成した会社やブランドのアドレスになります。 一方、メールデータ(メールデータ+本文)を送信する際、送信元サーバはメールヘッダとは別に差出人と宛先のアドレスの情報を送信します。このFromアドレスを エンベロープ From といいます。ヘッダFromと違い、基本的にコンピュータ間の通信用にのみ使われるアドレスで、配配メールを使って送信したメールなら配配メールのサーバのアドレスになります。バウンスメールはこの エンベロープ From宛に送信されるのです。 メール送受信のイメージ 配配メールでは、一斉配信でも一通一通異なる エンベロープ Fromを利用していて、アドレスの中にメールIDや読者IDなどの情報が含まれています。そのため、受信したバウンスメールの宛先を見れば、誰が誰に送ったどのメールが配信エラーになったかがわかるのです。 ではエラー理由はなぜわかるかというと、バウンスメールの本文を解析しています。文面はバウンスメールを送信するサーバのソフトウェアによって異なりますが、そこに含まれるフレーズを元に判定をします。例えば「user unknown」というフレーズが含まれればアドレス間違い、「Rejected as spam 」が含まれれば受信拒否、といった形です。 配信エラーと到達率 ここまで配信エラー解析のしくみをご紹介しましたが、実はこの機能、 我々のサービスの質を守る ためにも重要なのです。 もし配信エラー解析機能がなく、利用者が配信エラーに気づかなければ、使われていないアドレスにもメールを送り続けることになります。これは 第1回の記事 で紹介した「 IPレピュテーション 」を下げる要因となります。 すなわち、受信サーバが配信サーバを「 適当なアドレスにメールを送りつけるスパムサーバ 」と認定し、メールを受け付けてくれなくなるのです。 メール配信サービスとしてメールをしっかりと届けるためにも、配信エラー解析は重要な機能なのです。 まとめ メール配信サービス 配配メール/クルメルの配信エラー解析のしくみ、ご理解いただけたでしょうか? 利用者が配信の状況を把握できるだけでなく、サービス全体の到達率を維持するためにも重要な機能となっているのです。 *1 : メール配信サービスの利用料金は、一般的に配信通数やアドレス登録数で決まります。 *2 : 実際は本文の 文字コード 、返信先アドレス、メッセージIDなどもっと多くの情報が書かれています。
こんにちは。 本日は ソフトウェアテスト の教科書 JSTQB の内容と実際に業務に反映した例をご紹介します JSTQB とは 日本における ソフトウェアテスト 技術資格認定の運営組織です。 ISTQB(International Software Testing Qualifications Board)という ソフトウェアテスト 技術者の国際的な資格認定団体がありますが、 JSTQB はその日本版にあたります。 いくつかテストに関する出版をされていますが、私が選んだ本は以下に掲載します。 ソフトウェアテスト教科書 JSTQB Foundation 第4版 シラバス2018対応 選んだ理由は下記の通りです。 ソフトウェアテスト を体系的に学べること 最新かつ網羅的なテスト技法を習得できること( シラバス 2018対応は2020年2月テストより実施) 資格試験の取得にもつながること テストの目的 そもそもテストの 目的 ってなんでしょうか。というところから本書では書かれています。 テスト期間、テスト 工数 (実施する時間)、メンバーのスキルレベル、開発担当者との関わり方などなど その条件によってもテストの 目的 は変わってきます。 要件を満たしている 妥当性の確認 ステークホルダー が判断できる情報提言 品質レベルのリスク低減 契約・法律・規約の検証 故障や欠陥を発見する 欠陥の作りこみを防ぐ 品質レベルの確認 評価 など、その目的は多岐にわたります。 製品を開発する上でもいくつかの観点がありますね。 機能追加の案件なんかは製品企画やお客様とお約束した要件を満たしているかチェックが欠かせません。 また、新しい機能を追加すればするほど、今まで当たり前のように動いていた機能は これからも当たり前のように動くことを保証しなければなりません。 言うのは簡単ですが、実際に実践するのは難しいです。 機能を追加すると思わぬところに影響を及ぼしたり、処理を追加しなければいけないところが漏れていたりと 大規模な製品ほど品質レベルを維持するのは難易度が高くなりますよね。 そんな難しい観点を網羅的にテストする一つのツールとして本書からヒントを得られればと思います。 ※実際の品質を保証するものではないので、利用状況に応じて善にも悪にもなるところは注意が必要です。 テストの7原則 目からウロコだったので、詳しくレポートします。 テストは欠陥がないことは示せない 確かにそうですね。 アプリケーションの ホワイトボックステスト をすべて網羅したとしても、 顧客が実際に動作させるOS/ブラウザの状況はすべて再現できるわけではありません。 どんなにテストしても「絶対に100%欠陥はありません!」ということを言い切るのは難しいですし、 正確な情報ではありません。 もちろんバグが限りなく0になるようにテストはすべきです。 全数テストは不可能 例えば、画面に文字を打てる入力欄があった時点で無理です。 ローマ字26文字×2(大文字、小文字)、ひらがなカタカナは濁音や半濁音を含まなくても50文字ずつ、漢字は・・・。 その組み合わせは実質無限であり、どこまでいっても全パターン網羅ではありません。 早期テストで時間とコストを節約 ソフトウェアテスト の特徴として、V字モデルがよく話に出ます。 要件定義に何か問題があった場合は、最も 下流 工程の システムテスト で見つかったり、 詳細設計や実装などの問題は 単体テスト を見つかったりと、 問題があったときに検知できるフェーズがV字のような形なので問われています。 テストにおけるコスト同じで、品質における問題が発見されるのが、 後の工程ほど上流工程に戻って修正するコストが多く発生します。 なるべく早期に問題を発見することでリリースまでの時間の節約につながります。 欠陥の偏在 テストにおける欠陥の8割はプログラムの2割部分に集中して存在する説があります。 見落とした観点と、見落としに関連する観点が依存しているからだと思いますが、 私の経験上も感覚が近いので、その通りかと思います。 殺虫剤の パラドックス 特定の虫に同じ殺虫剤を与え続けても、いずれ虫のほうが殺虫剤に対する免疫をつけるため、 どんどん効果が薄くなることを例にしています。 ソフトウェアテスト においても同様で、同一観点で何度テストしても問題の検知がし難いです。 テスターを変える、やり方を変える、観点を変えるなど何かしら前回のテストと差をつけて 取り組むことが必要です。 テストは状況次第 上述した目的から、テストの在り方が変わってきます。 24時間稼働のシステムであれば過負荷でもシステムダウンしないこと、 eコマースであれば外部に公開するのでセキュリティ面で問題ないか念入りにチェックします。 「バグゼロ」の落とし穴 ある観点ではバグがない状態でも、他の観点ではそうではないケースがあります。 入力欄が4桁しか入力できない問題を解決するために、5桁に変更しました。 「4桁しか入力できない」欠陥はなくなりますが、 代わりにデータベースの書き込み速度が遅くなる、といった新しい問題が発生しました。 その直し方が製品全体を見たときに本当に正しい形をよくよく考える必要があります テストの心理学 人それぞれ立場や理解が異なることから、協力体制を考えねばなりません。 心に刺さった一例を掲載します。 <一例> 開発担当者の心情として下記があげられます。 何度もテストしたから実装コードが正しいはずだ(確証バイアス) テスト担当者よりも製品の仕様をよく知っている(指摘を受けにくい) こうなってしまうと「開発担当者 vs テスト担当者」の対決構図になってしまいます。 「良い品質の製品を納品する」という共通認識のもと、協力体制で行わなければなりません。 テストで発見した指摘は下記の効果があります 欠陥情報の把握 品質向上 開発担当者の スキルアップ コスト削減 お互いの信頼のために、開発担当者は否定的に反応した理由を、テスト担当者は理解する努力が必要です。 相互の認識の確認と、顧客の問題を解決することが目的であるという認識のもと 製品の製造を進めていくことが大事ですね。 カバレッジ 上述した全件テストが不可能という話がありましたが、その中でもなるべく網羅的に テストがすることが求められています。 カバレッジ とはテストケースを利用して特定のアイテムを実行したときの度合いを示した式になります。 デシジョンカバレッジ(%)= (テストにより実行した判定結果の数 / テスト対象の判定結果の合計数) × 100 これをどれだけあげられるかが欠陥が限りなく0に近い製品を生めるかのポイントになります。 カバレッジ の実践 実際にある案件のテストケースレビューでこの観点を取り上げる場面がありました。 伝票のヘッダーに対して明細が1:2のテストをするとき、 「複数明細の伝票」という観点でテストケースが生成されていました。 しかしユーザーの操作は明細がいくつも作れるので、1:2だけのテストでは不十分です。 もちろんテストに対する 工数 も有限なので実施できるパターンはやはり限られます。 しかし カバレッジ を少しでもあげることは、欠陥の少ない製品に近づけるので 時間の許す限り、実践していくのが良いと判断し、メンバーに追加のテストを依頼しました。 テスト技法 一般的に紹介されていました。 個々の詳しい説明については割愛します。 ブラックボックステスト プログラム内部は参照せず、画面/ API などのUIから思いつく操作を実施する手法です。 ホワイトボックステスト  プログラム内部を参照し、分岐処理を網羅的に打鍵する手法です。 同値分割法  条件的に同じ判定になりうるパターンはグルーピングして一つのパターンとみなしてテストする手法です。  例: 6歳未満は無料 6歳から12歳以下は半額 13歳以上は定額 上記の条件であれば、「ありえない」「無料」「半額」「定額」の4つのパターンとみなしてテストします。 境界値分析  同値分割法の一種ですが、どの範囲で同値とみなすかを定めています。  例:20~34歳までの男性がアクセスするとM画面を表示する 2ポイント境界値分析法 3ポイント境界値分析法 デシジョンテーブル それぞれ;の因子を掛け合わせた観点をテストする手法です。 例: ある美術館の入館料は次の通りになっている   個人入館料 大人・高校生:2000円 中学生・小学生:1000円 幼児:600円 学校団体入館料 大人・高校生:1200円 中学生:720円 小学生:600円 幼児:360円   ※ただし、個人・団体を問わず県民の日は高校生以下無料 上記のケースだと全部で19ケース存在します。 ユースケース テスト ビジネスシナリオやシステムの使い方に基づいてテストケースを設計します。 結合テスト に近いです。 ユースケース 名、目的、アクター、事前条件、事後条件、基本フロー、代替フロー、例外フロー、備考など 実際に使われうるケースで要素を考えます。 状態遷移テスト その製品の操作状況によって実施するパターンを遷移していきます。 状態遷移テストの実践 応用して 経理 システムに反映すると下記のようになります。 実際に利用するとパターンが増えるので、より可視化が重要になります。 テストツール それぞれ紹介されていました。 実際の業務で利用されるものばかりです。 要求マネージメントツール  TestLink 欠陥マネージメントツール  Redmine , Bugzillia 構成管理ツール Git, Subversion 継続的 イテレーション ツール Jenkins レビューツール  Redmine 、 Trac 、ReviewBoard 静的解析ツール SpotBugs, Checkstyle テスト設計ツール PictMaster モデルベースドテストツール GraphWalker テスト実行ツール  Selenium , JUnit 要求 カバレッジ ツール OpenClover 性能テストツール  Jmeter 動的解析ツール Valgrind 数々の要素 上述したもの以外にもたくさんの要素がテストに関係しています。 経験ベースのテスト技法 これはエラー推測が重要になります。 アプリケーションの過去の動作状況 開発担当者が犯しやすい誤りの種類 他のアプリケーションで発生した故障 探索的テスト チェックベースドテスト マネージメント、レビューも良いテストを実現するために大きく左右します。 まとめ それぞれの状況に応じてベストプ ラク ティスは異なります。 テスト技法を用いて個々の状況に適したテストを選択できるかがポイントです。 おのおの限りがあるコストの中で重大な欠陥、故障を見つけることが必要になります。 そのために実行するテストの優先順位づけも重要です。 先人が行っていたテンプレートを鵜呑みにせず、テストケースの作成、 テストの実行時に 気づいた観点はすぐ実行することが 重大なシステム欠陥を防止する策になると思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
logy0704です。 今回は自分のコンテナ知識をアップデートするために調べたことを記事にしようと思います。 動作確認はDocker Desktop for Mac 2.1.0.5, Docker Engine 19.03.5で行なっています。 課題 解決策 結局どっち使えば良いの volume 名前付きvolumeと匿名volume -vと--mount まとめ 課題 基本的にコンテナを消すと中の状態を復元することはできません。 しかし、以下のようなケースでデータの永続化を行いたいことがあります。 データベースコンテナの状態を保存しておきたい。 別のコンテナにデータ移行したい。 解決策 コンテナのデータを永続化させる方法がdockerの機能として用意されています。 主な手段は次の二つです。 volume ホスト側のdocker管理 ディレクト リ以下をコンテナにマウントする。 bind mount ホスト側の ディレクト リをコンテナにマウントする。 結局どっち使えば良いの 公式ドキュメントではvolumeを推奨しています。 理由はいくつかありますが、代表的なものとしては、セキュリティ上の懸念があることが挙げられます。 bind mountの場合、コンテナからホスト側のdocker管理領域以外へのアクセスができるため、 コンテナへ侵入を許した際にホスト側にまで被害が拡大する可能性があります。 とはいえ、一部の ユースケース ではbind mountを使用したほうが望ましいケースもあります。 詳しくは公式ドキュメントを参照してください。 今回はvolumeについてもう少し見ていきます。 volume docker runコマンドに-v(--volume) or --mountを指定することで、volumeをマウントした状態のコンテナを作成することができます。 volume名とコンテナ側の ディレクト リを指定して実行してみます。 $ docker run -d --name sample -v my-vol:/app nginx:latest docker inspectコマンドでコンテナの情報を確認してみると、以下のようにvolumeがマウントされていることがわかります。 " Mounts " : [ { " Type " : " volume " , " Name " : " my-vol " , " Source " : " /var/lib/docker/volumes/my-vol/_data " , " Destination " : " /app " , " Driver " : " local " , " Mode " : " z " , " RW " : true , " Propagation " : "" } ] , docker volume lsコマンドで存在するボリュームの一覧を取得することができます。 $ docker volume ls DRIVER VOLUME NAME local my-vol コンテナ内にファイルを作成してから、コンテナを消してみます。 $ docker exec -it sample bash /# echo "hoge" > /app/hoge.txt $ docker rm -f sample $ docker volume ls DRIVER VOLUME NAME local my-vol コンテナが消えてもvolumeは消えていません。 再度、コンテナを作成し、先ほど作ったvolumeを指定してみます。 $ docker run -d --name sample -v my-vol:/app nginx:latest $ docker exec -it sample bash /# ls /app hoge.txt 先ほど作成したファイルを残すことに成功しました。 名前付きvolumeと匿名volume volumeには大きく次の2種類があります。 名前付きvolume その名の通り、名前を持ったvolumeです。先ほどのmy-volの部分が名前にあたります。 匿名volume 名前を指定しなかった場合に作られるvolumeです。docker側でユニークになるように ハッシュ値 の名前が振られます。 docker run -d --name sample2 -v /app nginx:latest " Mounts " : [ { " Type " : " volume " , " Name " : " a41506ad65ef324b213c6dd6cb246532a264193e8fe6fcda25829119817a0ec5 " , " Source " : " /var/lib/docker/volumes/a41506ad65ef324b213c6dd6cb246532a264193e8fe6fcda25829119817a0ec5/_data " , " Destination " : " /app " , " Driver " : " local " , " Mode " : "" , " RW " : true , " Propagation " : "" } ] , 名前付きvolumeのほうがなんのためのvolumeなのかわかりやすいため、基本的には名前を指定するほうが良いと思います。 -vと--mount volume作成には、-vと--mountの二つのオプションが使用できます。 厳密には挙動が異なる部分がありますが、ほとんど同じものです。 サンプルでは-vを使用しましたが、--mountのほうが新たに導入されたオプションで、直感的に記述できるため、こちらを使うのがおすすめです。 まとめ dockerコンテナのデータを永続化するにはvolumeを使う volumeを作成する際には名前をつけよう 使うオプションは--mountがおすすめ エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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年目のtakaramです。まもなく入社して丸一年となり、ほとんど経験のなかった SQL の力もついてきたと思っています。 しかし、パフォーマンス面も考慮した SQL となると、まだまだ知識が足りないと感じています。 特に、一対多の関連テーブルの一方を使って他方を絞り込む、といった SQL は、ネットを見てもEXISTSが速いという記事があったり相関サブクエリだから遅いという情報があったり……何が本当かよくわかりません。そこで、今回自分で調べてみることにしました。 なお、今回検証に用いたのは PostgreSQL 11.4 です。 検証 テスト用データ 1:2020年入社の社員がいる部署名を抽出 2:開発1課~9課の従業員を抽出 結論 検証 テスト用データ テスト用テーブルとして、従業員テーブル employees と部署テーブル departments を用意し、それぞれ5万件、1000件のレコードを挿入しました *1 。各テーブルはIDと名前のほか、従業員テーブルは所属部署IDと入社年を入れています。 これを使って2種類のデータを抽出する SQL を、それぞれIN, EXISTS, JOINの3パターンで作成し、 EXPLAIN ANALYZE で実行時間・コストを比較しました。 1:2020年入社の社員がいる部署名を抽出 まず、各パターンのクエリはこんな感じです。 -- INパターン SELECT name FROM departments WHERE id IN ( SELECT department_id FROM employees WHERE join_year = 2020 ); -- EXISTSパターン SELECT name FROM departments d WHERE EXISTS ( SELECT 1 FROM employees e WHERE e.department_id = d.id AND e.join_year = 2020 ); -- JOINパターン SELECT d.name FROM departments d INNER JOIN ( SELECT DISTINCT department_id FROM employees WHERE join_year = 2020 ) tmp ON tmp.department_id = d.id; それぞれの実行計画を出してみます。 IN QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------- Hash Join (cost=963.88..994.63 rows=1000 width=12) (actual time=5.690..6.153 rows=720 loops=1) Hash Cond: (departments.id = employees.department_id) -> Seq Scan on departments (cost=0.00..17.00 rows=1000 width=16) (actual time=0.023..0.178 rows=1000 loops=1) -> Hash (cost=954.66..954.66 rows=737 width=4) (actual time=5.651..5.651 rows=720 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 34kB -> HashAggregate (cost=947.29..954.66 rows=737 width=4) (actual time=5.376..5.500 rows=720 loops=1) Group Key: employees.department_id -> Seq Scan on employees (cost=0.00..944.00 rows=1318 width=4) (actual time=0.014..5.045 rows=1253 loops=1) Filter: (join_year = 2020) Rows Removed by Filter: 48747 Planning Time: 0.614 ms Execution Time: 6.336 ms EXISTS QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------- Hash Join (cost=963.88..994.63 rows=1000 width=12) (actual time=5.262..5.723 rows=720 loops=1) Hash Cond: (d.id = e.department_id) -> Seq Scan on departments d (cost=0.00..17.00 rows=1000 width=16) (actual time=0.006..0.171 rows=1000 loops=1) -> Hash (cost=954.66..954.66 rows=737 width=4) (actual time=5.237..5.237 rows=720 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 34kB -> HashAggregate (cost=947.29..954.66 rows=737 width=4) (actual time=4.957..5.084 rows=720 loops=1) Group Key: e.department_id -> Seq Scan on employees e (cost=0.00..944.00 rows=1318 width=4) (actual time=0.010..4.628 rows=1253 loops=1) Filter: (join_year = 2020) Rows Removed by Filter: 48747 Planning Time: 0.570 ms Execution Time: 5.902 ms JOIN QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------- Hash Join (cost=971.25..990.88 rows=737 width=12) (actual time=7.757..8.241 rows=720 loops=1) Hash Cond: (d.id = employees.department_id) -> Seq Scan on departments d (cost=0.00..17.00 rows=1000 width=16) (actual time=0.009..0.174 rows=1000 loops=1) -> Hash (cost=962.03..962.03 rows=737 width=4) (actual time=7.739..7.740 rows=720 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 34kB -> HashAggregate (cost=947.29..954.66 rows=737 width=4) (actual time=4.406..7.568 rows=720 loops=1) Group Key: employees.department_id -> Seq Scan on employees (cost=0.00..944.00 rows=1318 width=4) (actual time=0.007..4.041 rows=1253 loops=1) Filter: (join_year = 2020) Rows Removed by Filter: 48747 Planning Time: 0.127 ms Execution Time: 8.359 ms 予想外だったのですが、この3パターン、 ほとんど同じ実行計画になりました !INとEXISTSは全く同じ、JOINもコストの数値が若干異なるだけです。 2:開発1課~9課の従業員を抽出 こちらの SQL は以下のようにしました。 -- INパターン SELECT name FROM employees WHERE department_id IN ( SELECT id FROM departments WHERE name LIKE ' 開発_課 ' ); -- EXISTSパターン SELECT name FROM employees e WHERE EXISTS ( SELECT 1 FROM departments d WHERE e.department_id = d.id AND name LIKE ' 開発_課 ' ); -- JOINパターン SELECT name FROM employees e INNER JOIN ( SELECT DISTINCT id FROM departments WHERE name LIKE ' 開発_課 ' ) tmp ON e.department_id = tmp.id; では実行計画を見てみます。 IN QUERY PLAN -------------------------------------------------------------------------------------------------------------------- Hash Join (cost=19.51..970.32 rows=50 width=11) (actual time=0.283..16.909 rows=450 loops=1) Hash Cond: (employees.department_id = departments.id) -> Seq Scan on employees (cost=0.00..819.00 rows=50000 width=15) (actual time=0.007..7.910 rows=50000 loops=1) -> Hash (cost=19.50..19.50 rows=1 width=4) (actual time=0.104..0.105 rows=9 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 9kB -> Seq Scan on departments (cost=0.00..19.50 rows=1 width=4) (actual time=0.051..0.100 rows=9 loops=1) Filter: (name ~~ '開発_課'::text) Rows Removed by Filter: 991 Planning Time: 0.160 ms Execution Time: 16.985 ms EXISTS QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- Hash Join (cost=19.51..970.32 rows=50 width=11) (actual time=0.276..16.525 rows=450 loops=1) Hash Cond: (e.department_id = d.id) -> Seq Scan on employees e (cost=0.00..819.00 rows=50000 width=15) (actual time=0.006..7.664 rows=50000 loops=1) -> Hash (cost=19.50..19.50 rows=1 width=4) (actual time=0.103..0.104 rows=9 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 9kB -> Seq Scan on departments d (cost=0.00..19.50 rows=1 width=4) (actual time=0.050..0.099 rows=9 loops=1) Filter: (name ~~ '開発_課'::text) Rows Removed by Filter: 991 Planning Time: 0.161 ms Execution Time: 16.608 ms JOIN QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------ Hash Join (cost=19.54..970.35 rows=50 width=11) (actual time=0.392..16.747 rows=450 loops=1) Hash Cond: (e.department_id = departments.id) -> Seq Scan on employees e (cost=0.00..819.00 rows=50000 width=15) (actual time=0.009..7.770 rows=50000 loops=1) -> Hash (cost=19.53..19.53 rows=1 width=4) (actual time=0.161..0.161 rows=9 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 9kB -> Unique (cost=19.51..19.52 rows=1 width=4) (actual time=0.149..0.156 rows=9 loops=1) -> Sort (cost=19.51..19.52 rows=1 width=4) (actual time=0.148..0.150 rows=9 loops=1) Sort Key: departments.id Sort Method: quicksort Memory: 25kB -> Seq Scan on departments (cost=0.00..19.50 rows=1 width=4) (actual time=0.054..0.115 rows=9 loops=1) Filter: (name ~~ '開発_課'::text) Rows Removed by Filter: 991 Planning Time: 0.123 ms Execution Time: 16.836 ms こちらもパターン1同様、INとEXISTSは全く同じ実行計画です。JOINの方はDISTINCTのUnique, Sortが入っていますが、コストはわずかです。 結論 単純なクエリではIN, EXISTS, JOINとも、おおむね同様の実行計画となることがわかりました。 オプティマ イザが思った以上に賢かったですね! より複雑なクエリや、インデックスの有無によっては変わってくるかもしれませんが、この程度のシンプルなクエリならあまりパフォーマンスを気にする必要はないと考えていいでしょう。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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 : 参考までに、利用した SQL は こちら です
はじめに こんにちは。新卒2年目のEngawaです。 アプリ開発 を行うチームに配属された際に Android アプリの開発の入門とした簡単なアプリの開発をした時の所感を書こうと思います。 学習で参考にしたサイトはこちらになります 開発環境のインストール方法から エミュレーター の作成方法まで詳しく書かれているので、 Android Studio のバージョンで差異は若干ありますが簡単に設定することができます。 codeforfun.jp 所感 今まで開発はHMTLなどで見た目を整理して逐一確認をしていたのですが、ボタンの配置などは必要な物を引っ張るだけで簡単に作成できるのでHTML等を使用して見た目を作成するのよりはるかに楽でした。 下図の赤枠の部分から ドラッグ&ドロップ するだけ。 ただし、参考にしたサイトにも書いてありますが、ボタンなどを引っ張ってくると xml のコードがごちゃごちゃして見にくくなるので注意が必要です。 私自身はごちゃごちゃしていても(自分だけが見る場合なら)全然気にしないタイプなのですが、そうでない方は手入力での作成の方がスッキリ整えるので手入力をしていただければと思います。 実際に挙動を確認するのもシミュレーターがあるので私のように Android 端末持ってないけど、なんとなく アプリ開発 をやってみたいという方でも実機なしですぐに動作確認できます。 Android だと機種ごとで一部見た目が変わっているのですが、シミュレーターを使えば端末ごとにレイアウトがどうなっているかすぐに確認できるのでとても便利でした。 終わりに 使用する言語は主に Java なのでアプリを作成するなら Android アプリからはじめて見てもいいかもしれません。実際にリリースするなら費用も安いので。 参考にしたサイトには音楽プレーヤーの作り方もあるので時間があったら作っても見ようと思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
こんにちは、株式会社 ラク スで横断的にITエンジニアの育成や、技術推進、採用促進などを行っている開発管理課に所属している鈴木( @moomooya )です。 今回は dev.to で人気記事になっていた「 The 25 most recommended programming books of all-time. (史上最もおすすめされているプログラミング本【25選】)」を紹介したいと思います。 注:本記事は2020年2月18日に Pierre 氏がdev.toに投稿した The 25 most recommended programming books of all-time. を執筆者Pierre氏の了承のもと、日本語で紹介する記事です。 訳書の邦題について、訳書に改訂などがあった場合は出来る限り最新版の邦題で記載しています。 おすすめのプログラミング本を選んだ方法 「Best Programming Books」などのクエリで Google 検索しました。そのすべての結果をWeb Scraping API の Scraping Bee を利用して スクレイピング しました。 約150のリンクが収集でき、それらのページタイトルを以下のように集約しました。 特定の技術またはプラットフォームに焦点を当てたリスト 特定の年に焦点を当てたリスト 無料の本に焦点を当てたリスト Quora 1 および Reddit 2 のスレッド 最終的に70個のリストを抽出しました。 それらのリストに対して Python とBeatifulSoupを用いて、書名のリストを抽出しプログラミング本に対する約1300件のおすすめを抽出しました。 集計の結果はこちらの リポジトリ を参照してください。 注: 1300件 という数値は元記事中には記載ありませんが、参照先レポジトリ内の中間集計データより補完しています。 史上最もおすすめされているプログラミング本 25冊 注:元の記事では各プログラミング本の紹介が Amazon.com から転記されていましたが、こちらは Amazon.co.jp 版に置き換えています。 25. Jez Humble & David Farley, "Continuous Delivery" 邦題 『継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化』 おすすめスコア 8.8% amazon.co.jp より いつまで手動でデプロイしているんですか? 現代では継続的にソフトウェアをリリースすることが必須になっています。本書は、継続的なソフトウェアのデリバリーを実現するためのビルド、デプロイ、テスト、リリースの自動化についての本格的な解説書です。 24. Robert Sedgewick & Kevin Wayne, "Algorithms" 邦題 未訳? おすすめスコア 8.8% 鈴木注 おそらく訳書は出ていないと思われます。 ただし著者のRobert Sedgewick氏が執筆したプログラミング本として "セジウィック:アルゴリズムC" という類書は和訳されています。 セジウィック:アルゴリズムC 第1~4部 ―基礎・データ構造・整列・探索― 作者: ロバート セジウィック 近代科学社 Amazon 23. Cory Althoff, "The Self-Taught Programmer" 邦題 『独学プログラマー』 おすすめスコア 8.8% amazon.co.jp より 本書は「 Python だけ」を学ぶ本ではありません。 Python を使ってプログラミングを紹介していますが、伝えたい内容は Python に限らない「プログラミング全般」の知識です。 プログラマ になるためのスキルを独学できる本です。 Python プログラミングの基本を学べるだけでなく、 プログラマ として必要なスキル(シェル、 正規表現 、パッケージ管理、バージョン管理、データ構造、 アルゴリズム 、仕事の始め方・やり方)もひと通り学べるのが特徴です。 「プログラミングを始めたい」「できればその道でプロを目指してみたい」――そんな読者にオススメです。 本書の著者、コーリー・アルソフ(Cory Althoff)は、「独学 プログラマー 」です。本書は、彼が独学で、ゼロからプログラミングを学んだ体験に基づいて書かれました。 プログラミングを独学で身に付けるために、著者が Python を通して学んだエッセンスが書かれています。彼の独学 プログラマー としての学び方は、 Amazon.com での本書の評価を見るとわかるように、多くの人に支持されています。 ――訳者あとがきより 鈴木注 原著が Kindle Unlimitedで読めます!!! (2020年3月12日現在) 22. Steve McConnell, "Rapid Development" 邦題 『ラピッドデベロップメント―効率的な開発を目指して』 おすすめスコア 8.8% amazon.co.jp より ソフトウェア開発のスケジュールを正確に管理するための、全ての戦略、良い方法、価値のあるTipsを紹介。具体的な ケーススタディ も収録した実践テキスト。 21. Peter Seibel, "Coders at Work" 邦題 『Coders at Work プログラミングの技をめぐる探求』 おすすめスコア 10.2% amazon.co.jp より どうやってプログラミングを学んだ? 他人のコードをどうやって読む? ソフトウェアはどう設計する? バグを追跡する方法は? プログラミングの将来はどうなる? プログラミング言語 が果たす役割は? プログラマ であるピーター・サイベル氏が15人の偉大な プログラマー (コーダー)から その技を聞き出すインタビュー集を、『Joel on Software』訳者の 青木靖 氏が翻訳。 20. Eric Evans, "Domain-Driven Design" 邦題 『エリック・エヴァンスのドメイン駆動設計』 おすすめスコア 10.2% amazon.co.jp より ソフトウェア開発コミュニティでは、 ドメイン モデリング がソフトウェア設計の中心であることが広く認められてきています。 ドメイン モデルを通して、ソフトウェア開発者は豊富な機能を表現し、それをユーザの要求に本当の意味で応えるソフトウェアの実装に移すことができます。しかし、明らかに重要であるにもかかわらず、効果的な ドメイン モデリング をどのようにソフトウェア 開発プロセス に組み入れるかを説明する、実用的なリソースはほとんど存在しませんでした。 ドメイン 駆動設計はこの要求に応えるものです。これは具体的な技術についての本ではなく、読者に ドメイン 駆動設計への体系的なアプローチを提示するものです。設計のベストプ ラク ティスの応用的なセット、経験に基づくテクニック、さらに、複雑な ドメイン に直面するソフトウェアプロジェクトにおける開発を容易にする基本原則を紹介する一冊です。 19. Donald E. Knuth , "The Art of Computer Programming" 邦題 『The Art of Computer Programming 日本語版』 おすすめスコア 10.2% シリーズ一覧 The Art of Computer Programming Volume 1 Fundamental Algorithms Third Edition 日本語版 The Art of Computer Programming Volume 2 Seminumerical Algorithms Third Edition 日本語版 The Art of Computer Programming Volume 3 Sorting and Searching Second Edition 日本語版 The Art of Computer Programming Volume 4A Combinatorial Algorithms Part1 日本語版 amazon.co.jp より アルゴリズム のバイブル。 Knuth 先生の名著『The Art of Computer Programming』シリーズ 鈴木注 TeX はこの本を作るために生まれたらしいです。 シリーズ物となっており、原書では第4巻分冊が6冊目まで発行されて現在も刊行中です。 訳書としては第1巻~第3巻と第4巻分冊の1冊目までが和訳されています。 執筆予定は スタンフォード大学内のWebサイト で公開されています。第5巻は2025年予定だそうです。 18. Harold Abelson / Gerald Jay Sussman / Julie Sussman, "Structure and Interpretation of Computer Programs" 邦題 『計算機プログラムの構造と解釈 第2版』 おすすめスコア 13.2% amazon.co.jp より 言わずと知れた「計算機科学の古典的名著」復刊! プログラミング言語 LISP の方言である Scheme を使用し、抽象化、 再帰 、 インタプリタ 、 メタ言語 的抽象といった計算機科学における概念の真髄を丁寧に解説した古典的名著。また計算機科学教育に多大な影響を与えたことはもちろん、「 関数型言語 」の 聖典 のひとつとしても挙げられる。いわば、現代の計算機科学( コンピュータサイエンス )の礎であり、プログラミングの始原であり、すべてのITの原点といえる1冊。 17. Martin Fowler, "Patterns of Enterprise Application Architecture" 邦題 『エンタープライズ アプリケーションアーキテクチャパターン』 おすすめスコア 14.7% amazon.co.jp より エンタープライズ アーキテクチャ のレイヤ化とは? エンタープライズ アプリケーション開発は、多くの新しい技術の出現から利益を得てきました。 Java と.NETのようなマルチレイヤをなす オブジェクト指向 のプラットフォームは、今では一般的になっています。これらの新しいツールや技術は、強力なアプリケーションを構築することができます。しかし、これらの実装は容易ではありません。 オブジェクト開発を経験した技術者が、 アーキテクチャ を理解しないまま開発を行うために、 エンタープライズ アプリケーション開発では共通の失敗がしばしば生じます。本書は、 エンタープライズ アプリケーション開発者が直面するやっかいな課題に対する直接的な回答を示したものです。技術は変化( Smalltalk から Java 、.NET。CORBAまで)していても、共通の問題を解決するために同じ基礎的な設計の考え方を適用することができるのです。 本書は40以上のパターンを紹介しています。これらは、 エンタープライズ アプリケーションプラットフォームに適用可能な解決策です。前半は、 エンタープライズ アプリケーション開発についての短い チュートリアル です。 後半は、各パターンについて詳細に解説しています。各パターンは、 Java または C# でコード例を詳述し、使用法および実装について説明します。概念についても、豊富な UML ダイアグラムで例証します。 16. Jon Bentley, "Programming Pearls" 邦題 『珠玉のプログラミング 本質を見抜いたアルゴリズムとデータ構造』 おすすめスコア 16.1% amazon.co.jp より: 情報系の勉強をしたことのある人ならば、誰しもプログラムにおける アルゴリズム の概念に触れたことがあるだろう。 同じ動作をするプログラムでもエレガントな アルゴリズム を持つものと そうでないものの間には実行時間や堅牢性、リソースの利用量などにおいて 大きな隔たりがあり、時には劇的なほどパフォーマンスの差があることも珍しくはない。 一方でそのような アルゴリズム を創出することがいかに難しいかも周知のことである。 そのため現在では納期や効率に重点をおいたプログラミングが優先されることが多いが、 単純で美しいプログラムを書くことは何より重要なことである。 本書は著者を含めた プログラマー たちが扱った問題をベースに、 エレガントなプログラムを書く際のさまざまなアド バイス や手法について解説したものである。 大学での アルゴリズム 講義に登場してくる探索やソート、データ構造といった内容に触れており、 現実的な題材の下に要求の定義、リソースの活用の仕方、動作する環境などのさまざまな側面から どのように アルゴリズム を組むべきかといった、プログラムを組む上での原理原則を学べるように構成されている。 このように題材となっている内容は決して特殊ではなく、 プログラムを組んだ経験のある人ならば必ず触れたことのあるレベルなので、 高級言語 でのプログラムが書ける人ならば誰でも理解できる内容になっている。 随所に登場する設問や読書案内も読者が学習する上で役に立つだろう。 数理的な解析に重点を置く大学での アルゴリズム 講義の内容は 実際のプログラミングに生かしにくいが、本書では応用や実際のコード化といった面に 重点が置かれて説明がされているので実務上も大いに役立つ。 自分のプログラミングを原則的、一般的な見地からよりよいものにしていくために必ず役立つ本だ。(斎藤牧人) 15. Tom DeMarco & Tim Lister, "Peopleware" 邦題 『ピープルウエア 第3版』 おすすめスコア 17.6% amazon.co.jp より: ●ソフト開発の現場で多くの熱い共感を呼んだ名著! 開発プロジェクトで技術よりも何よりも大事なもの――それは「人」。一人一人の 人格の尊重、頭を使う人間にふさわしいオフィス、人材の選び方・育て方、結束した チームがもたらす効果、仕事は楽しくあるべきもの、仕事を生み出す組織づくり、 という6つの視点から「人」を中心としたプロジェクト開発の大切をユーモラスに 語っている。1987年の初版発行以来、多くのソフトウエア・エンジニアの共感を 呼んだ名著の改訂第3版。 ●プロジェクト管理に関わる新規書き下ろしを収録! 第3版では、時代の変化に対応し、以下の章が追加された。「リーダーシップについて 話そう」、「他者とうまくやっていく」、「 幼年期の終わり 」、「リスクとダンスを」、 「会議、ひとりごと、対話」、「E(悪い)メール」。 14. Thomas H. Cormen / Charles E. Leiserson / Ronald L. Rivest / Clifford Stein, "Introduction to Algorithms" 邦題 『アルゴリズムイントロダクション 第3版 総合版』 おすすめスコア 17.6% amazon.co.jp より: 原著は,計算機科学の基礎分野で世界的に著名な4人の専門家がMITでの教育用に著した計算機 アルゴリズム 論の包括的テキストであり,その第3版.前版までで既に アルゴリズム とデータ構造に関する世界標準教科書としての地位を確立しているが,より良い教科書を目指して再び全面的な記述の見直しがなされ,それを基に新たな章や節の追加なども含めて,大幅な改訂がなされている. 単に アルゴリズム をわかりやすく解説するだけでなく,最終的な アルゴリズム 設計に至るまでに,どのような概念が必要で,それがどのように解析に裏打ちされているのかを科学的に詳述している. さらに各節末には練習問題(全957題)が,また章末にも多様なレベルの問題が多数配置されており(全158題),学部や大学院の講義用教科書として,また技術系専門家のハンドブックあるいは アルゴリズム 大事典としても活用できる. 本書は,原著の第1~35章,および付録A~Dまでの完訳総合版である.また巻末の索引も圧巻で,和(英)‐英(和)という構成により,「数理用語辞典」としてもまことに有用である. 13. Charles Petzold , "Code" 邦題 『CODE: コードから見たコンピュータのからくり』 おすすめスコア 19.1% amazon.co.jp より Windows プログラミングの雄、チャールズ・ ペゾルド が10年来あたため続けてきたという注目の企画が、ついに1冊の本になった。 本書はコンピュータの動作を根本から解説するもので、データのやり取りを中心に、コンピュータの動作原理を解説している。序盤では 点字 やモールス信号などを題材とするコードの歴史と、実際に通信や演算を行うためのハードを詳細に扱っている。中盤からは、2進数の処理、リレーによる演算の方法、メモリやオートメーション、終盤ではプロセッサの構造から低級言語・ 高級言語 、OSなどにも触れている。 計算機工学、 論理回路 学などの高度な内容にも触れているため、理系の、できれば工学系の知識がある方が望ましい。とはいえ、読み物形式なので、順番に読んでいくことで予備知識がない方でも理解することはできるはずだ。懐中電灯、黒猫、シーソーなど、一見コンピュータと何の関係もなさそうな例をもとに、コンピュータのしくみを明かしているのは、本書の最大の魅力である。 先に述べた内容以外にも、ファイル形式や開発環境、グラフィックに関する解説など、まさしくコンピュータに関わるすべてに言及している。とくにプロセッサやOS、アプリケーションなどの歴史に関する部分は興味深い。数学的な部分にあまり興味がない方にとっても、読む価値は十分にあるだろう。コンピュータの動作や根本原理を知りたいという知的好奇心あふれる読者に、ぜひおすすめしたい1冊である。(斎藤牧人) 12. Steve Krug , "Don't Make Me Think" 邦題 『超明快 Webユーザビリティ ―ユーザーに「考えさせない」デザインの法則』 おすすめスコア 19.1% amazon.co.jp より: 明白で使いやすいサイトを実現するには? ↓ ユーザーに考えさせちゃダメ! Apple 、 Bloomberg 、 Lexus などを顧客としてきた、 ユーザビリティ コンサルタント の第一人者にして激安ユーザーテストの伝道師 ス ティー ブ・クルーグが説く、 ユーザーに「考えさせない」サイトの作り方。 20か国で翻訳、累計45万部超の世界的ベストセラー、ウェブ&モバイル ユーザビリティ の定番書『Don't Make Me Think』の日本語版です。 ちゃんと使ってもらえるサイトにしたいWeb担当者、コンバージョン率を上げたいEC担当者におすすめの一冊。 11. John Sonmez, "Soft Skills" 邦題 『SOFT SKILLS ソフトウェア開発者の人生マニュアル』 おすすめスコア 22% amazon.co.jp より ソフトウエア開発者専用に、「より良い人生」を送るためのノウハウ・スキルを網羅した、生き方バイブル本です。 プログラマー が良い人生を送るためには、技術習得法やキャリア構築法といったノウハウに加え、対人的な交渉・指導・意思疎通などをうまく行える能力や知恵、すなわちソフトスキルが不可欠です!本書では、キャリアの築き方、自分の売り込み方、技術習得法、生産性の高め方といった仕事で成功する方法だけでなく、財産の築き方、心身の鍛え方、恋愛で成功する方法など、「人生全般をより良く生きる方法」を具体的に説明します。 ■序文から抜粋 あなたがこの複雑な産業で活路を開こうとしている若いソフトウェア開発者なら、今手にしているこの本は、多くの知恵と優れたアド バイス を与えてくれるはずだ。 ロバート・C・マーティン(アンクル・ボブ) ■「解説」から抜粋 本書はソフトウェア技術者向けの書籍ではありますが、いわゆるテク ノロ ジー のことはほとんど書いてありません。しかし、「成功者」になるために必要なそれ以外の多くのことが書いてあります。(中略)今こそ私たちがもっと成功に貪欲になれるチャンスなのではないでしょうか。 ■「訳者あとがき」から抜粋 全体を読み通して感じたのは、人の弱さを十分に意識して書かれていること、率直であ ること、上からではなく同じ高さから話しかけてくることでした。(中略)校正のために読み返してみると、株や栄養や腹筋のことなど、「何かで読んだんだけどさあ」という枕で出てくるような話の多くを本書で覚えたことに気づきました。無意識のうちにいろいろな影響を受けているようです。この本、ただものではないですよ。 10. Gayle Laakmann McDowell, "Cracking the Coding Interview" 邦題 『世界で闘うプログラミング力を鍛える本 ~コーディング面接189問とその解法~』 おすすめスコア 22% amazon.co.jp より トップIT企業が出題するコーディング面接にチャレンジ! 人気のあるトップIT企業で行われるコーディング面接に合格し採用されるための攻略本として、グーグル等でエンジニアとして働き、かつ多くの採用プロセスに関わってきた著者によって本書は執筆されました。米国で大人気のコンピュータプログラミングに関するベストセラー書(Cracking the Coding Interview: 189 Programming Questions and Solutions)の日本語版です。 本書で取り上げるプログラミング問題はトップIT企業が求める能力が凝縮されている、面接で実際に使われたものです。そしてなによりも アルゴリズム を中心とした コンピュータサイエンス の基礎知識や活用法を楽しみながら学べる内容となっています。 前著「世界で闘うプログラミング力を鍛える150問」と比べ計算量に関する説明がより詳細になりました。新規の問題はもちろん、既存の問題についても時間計算量・空間計算量の記述が追加され、計算量の見積もりについての話題も参考になるでしょう。また木とグラフ・探索 アルゴリズム に関する説明も充実したものになり、純粋なプログラミング技術書としてのバランスと深みが増しました。 Big-O記法の解説章や発展課題、解き方のヒントの追加、また全ての問題がカテゴライズされより読みやすくなりました。 本書が支持されている理由は「面接」という限られた時間だけにフォーカスするのではなく、その前後における行動までアド バイス してくれている点です。「エンジニアとして日々をどう過ごすか」を考える上で非常に有益でしょう。 自分が プログラマ として現在「どんな分野」に関して、どの程度の「能力」があるのか。これからのキャリアアップに必要な知識やスキルを考える手がかりとして。あるいは本当に優秀な候補者を採用する面接を行うための一冊として。 本書にはそのヒントがたくさん含まれています。 9. Erich Gamma / Richard Helm / Ralph Johnson / John Vlissides, "Design Patterns" 邦題 『オブジェクト指向における再利用のためのデザインパターン』 おすすめスコア 25% amazon.co.jp より オブジェクト指向 における再利用のための デザインパターン 建築の発想を持ち込む オブジェクト指向 はソフトウエア開発の手法として誕生したが,ほかの分野にも広がりつつある。その一つとして注目すべきなのが「デザイン・パターン」の登場である。 デザイン・パターンとは, オブジェクト指向 でソフトウエア設計を行う際に利用するカタログ集である。例えば,経験豊かな プログラマ は以前に解いた問題と,これから新しく解こうとしている問題が類似していることに気づけば,以前に使った解法が応用できる。デザイン・パターンとはこうした解法をだれでも再利用できるように一般化(パターン化)したものの寄せ集めと考えればよい。 この考え方と実現手法は,一般に GoF ( Gang of Four :4人組)本と呼ばれる本書で有名になった。 GoF 本は建築家の クリストファー・アレグザンダー がまとめた都市計画のカタログ集『時を超えた建設の道』( 鹿島出版会 )に強い影響を受けている。この点はたいへん興味深い。 オブジェクト指向 の導入で,ソフトウエアの世界においても 建築学 のように,構造をパターン化できることを示しているからだ。 8. Michael Feathers, "Working Effectively with Legacy Code" 邦題 『レガシーコード改善ガイド』 おすすめスコア 26.4% amazon.co.jp より: あなたは、 Java や.netでレガシーコードを書いていませんか? 本書は、システム保守の現場でありがちな、構造が複雑で理解できないようなコードに対する分析手法・対処方法について解説します。つまり、コードを理解し、テストできるようにし、 リファクタリング を可能にし、機能を追加できるテクニックを紹介しています。レガシーコードとは、 メインフレーム のアプリケーションのことではなく、変更することが困難なコードを指しています。著者は、本書で「私にとって、テストがないコードはレガシーコードだ」「テストコードがあれば振舞いを変えても、すばやく変更、確認することができる。もし、テストコードがなければ振舞いを変更しても、それが正しいのか、悪いのか判断できない」「 ソースコード がきれいで、良い構造であれば十分か?そうではない。もし、テストコードなしで大幅な修正を加えるとしたら、信じられないほどのスキルと明確な理解が必要になる」と述べています。本書は Java 、C、 C++ でサンプルを記述していますが、記載されているテクニックは言語依存するものではないため、他の言語( Delphi 、 Visual Basic 、 COBOL 、 FORTRAN )でも使えます。 7. Robert Martin, "The Clean Coder" 邦題 『Clean Coder プロフェッショナルプログラマへの道』 おすすめスコア 27.9% amazon.co.jp より ソフトウェアのプロになるには本書が必要だ! ソフトウェアのプロとは? プロの行動とは? 衝突・厳しいスケジュール・理不尽なマネージャにどう対応すべきか? いつ・どのようなときに「ノー」と言うべきか? プロはプレッシャーにどう対応するのか? 6. Frederick P. Brooks Jr, "The Mythical Man-Month" 邦題 『人月の神話【新装版】』 おすすめスコア 27.9% amazon.co.jp より ソフトウェア開発の定番書『人月の神話』。「 ブルックスの法則 」で知られる ブルックス 教授によるこの名著が、同教授の35年ぶりの新刊「The Design Of Design」 (邦題「デザインのためのデザイン」)を記念し、従来の縦組みから横組みに変わり、 表紙カバーを新たに新登場。 IBM の大型コンピュータSystem/630、および オペレーティングシステム OS/360 の 開発チームを率いた著者が、プロジェクトで発生した問題点を詳細に分析し、 ソフトウェア開発にまつわる困難と展望について持論を展開したエッセイ集。 原著初版(1975年)の刊行から35年がたつ現在でも、大規模開発プロジェクトにおける ソフトウェア工学 の古典として読み継がれ、多くの読者を獲得している。 とくに、「遅れているソフトウェアプロジェクトへの要員追加は、 さらにプロジェクトを遅らせるだけだ」という ブルックスの法則 は名高い。 開発において「人員×月日」というスケジュール見積もりが適用されている問題を指摘し、 ソフトウェア産業 において広くいきわたっている「人月の神話」を明らかにした。 また、刊行20周年記念して発行された増訂版では、発表当時大きな議論を巻き起こした 「銀の弾はない」をはじめとして、初版刊行以降に発表された著者の代表的な論文4編を収録。 各方面から寄せられたさまざまな議論に対して、著者があらためて自身の見解を述べている。 5. Eric Freeman / Bert Bates / Kathy Sierra / Elisabeth Robson , "Head First Design Patterns" 邦題 『Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本』 おすすめスコア 29.4% amazon.co.jp より 初めて学ぶ方、過去に挫折した経験のある方、知識を確固たるものにしたい方を対象に、イラストや写真を使ってやさしく楽しく解説する人気のHead Firstシリーズの デザインパターン 編。 刺激的なレイアウト、思わず膝を叩く見事なたとえ、引き込まれる小話、楽しいクイズやパズルで飽きることなく読み進むことができます。 複雑難解な デザインパターン の概念が面白いほどよくわかる、目からウロコの画期的な書籍です。 4. Martin Fowler, "Refactoring" 邦題 『リファクタリング(第2版): 既存のコードを安全に改善する』 おすすめスコア 35% amazon.co.jp より ソフトウェア開発の名著、第2版登場! リファクタリング は、ソフトウェアの外部的な振る舞いを保ったままで、内部の構造を改善する作業を指します。本書は リファクタリング のガイドブックであり、 リファクタリング とは何か、なぜ リファクタリング をすべきか、どこを改善すべきか、実際の事例で構成され、ソフトウェア開発者にとって非常に役立つものとなっています。 本第2版では、約20年前のオリジナル原稿の構成は変わらないものの、大幅に書き換えられているほか、サンプルコードが Java から Java Scriptになるなど、現代的にアレンジされています。 3. Steve McConnell, "Code Complete" 邦題 『Code Complete 第2版 完全なプログラミングを目指して』 おすすめスコア 42% amazon.co.jp より 本書は効果的なコンスト ラク ションプ ラク ティスについての知識を集めた、実践的なプログラミング解説書です。ソフトウェア開発プ ラク ティスは目覚しい進歩を遂げていますが、一般の プログラマ にはなかなか浸透しません。本書は、業界の第一人者らの知識と、一般の商用プ ラク ティスとの橋渡しをします。10年前の第1版とコンセプトは同じですが、第2版は、全体を通じて オブジェクト指向 の考え方が反映されたものになっています。また、「 リファクタリング 」の章が追加され、サンプルコードは C++ 、 C# 、 Java 、 Visual Basic などにアップデートされています。本書は、ソフトウェア開発の総合ガイドを求めている経験豊富な プログラマ 、経験の浅い プログラマ を教育する技術指導者、正式なト レーニン グを受けたことのない独学 プログラマ 、これから社会に出る学生や新人 プログラマ などを特に対象としています。本書で説明されている研究成果や過去の経験は、高品質なソフトウェアを作成し、問題を少なく抑えて作業をより短期間で行うのに役立ちます。また、大きなプロジェクトを制御し、要求の変更に応じてソフトウェアの保守や修正を適切に行うのにも役立ちます。 2. Robert C. Martin, "Clean Code" 邦題 『Clean Code アジャイル ソフトウェア達人の技』 おすすめスコア 66% amazon.co.jp より コードを書き、読み、洗練する 本書の ケーススタディ を注意深く読むことで、コードを洗練していく過程で行うべき判断について学ぶことができます。プログラムが動作したからといって、プログラミングが終わったことにはならないのです。 1. David Thomas & Andrew Hunt, "The Pragmatic Programmer" 邦題 『新装版 達人プログラマー 職人から名匠への道』 おすすめスコア 67% amazon.co.jp より 経験に裏打ちされたヒントを満載し、実践可能なプログラムの作り方を教える。より効率的、そして生産的な プログラマー を目指す人に向け、具体的なアド バイス を、解決策のシステムを構成するよう関連付けて編集。 鈴木注 1位は 毎年少なくとも一つの言語を学習する で有名な『達人 プログラマー 』でした。原書では20周年版が去年2019年に出版されているので和訳版の改訂にも期待。 コメント欄から抜粋 私はいつも、Scott Rosenbergの「Dreaming in Code」を追加したいと思っています。 Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software (English Edition) 作者: Rosenberg, Scott Crown Amazon The Phoenix Projectにも言及した方がいいんじゃないか? ;) Phoenix Project: A Novel About It, Devops, And Helping Your Business Win 作者: Kim, Gene , Behr, Kevin , Spafford, George It Revolution Press Amazon 挙げられている中で『計算機プログラムの構造と解釈』や『Art of Computer Programming』 など何冊かは読むことには実用的な意味がありません 。 多くの人が推奨していますが、実際に読む人はほとんどいません。 それらは題材としてすでにマイナーな言語を使用し、現在では時代遅れまたは不要な概念を多く扱っています。 プログラミングの心理学 要求仕様の探検学―設計に先立つ品質の作り込み どちらもGerald Weinbergによるものですが、Martin Fowlerよりも質の高い本を書いています。 もう1人の素晴らしい著者はJuval Löwyで、彼の本をすべて推薦します。 特に彼の最新刊: Righting Software これらが次の上位25のリストに載っていても驚かないでしょう。 プログラミングの心理学 25周年記念版 作者: ジェラルド・M・ワインバーグ 日経BP Amazon 要求仕様の探検学―設計に先立つ品質の作り込み 作者: ゴーズ,D.C. , ワインバーグ,G.M. , Gause,Donald C. , Weinberg,Gerald M. , ヤナ川 志津子 , 純一郎, 黒田 共立出版 Amazon Righting Software (English Edition) 作者: Juval, Löwy Addison-Wesley Professional Amazon 素晴らしいリスト。 私もお勧めします: FreemanとPryceの『実践 テスト駆動開発 』 実践テスト駆動開発 (Object Oriented SELECTION) 作者: Steve Freeman , Nat Pryce 翔泳社 Amazon お見事! The Pragmatic Programmerを読んでいない人には絶対にお勧めします。 私が読んだ最初の技術書であり、今でも私のお気に入りです。 素敵なリスト。 Kent Beck の" Smalltalk Best Practice Patterns"が含まれていなかったことが信じられません。 これは信じられないほどの良書であり、 Smalltalk だけの話ではありません。 これはすべての人におすすめできます。 ケント・ベックのSmalltalkベストプラクティス・パターン―シンプル・デザインへの宝石集 作者: ケント ベック ピアソンエデュケーション Amazon 「Getting Real」も素晴らしい読み物です。 Getting Real: The smarter, faster, easier way to build a successful web application 作者: 37signals, 37signals , Fried, Jason , Heinemeier Hansson, David , Linderman, Matthew 37signals Amazon 所感 原書と訳書の表紙を並べて表示してみました。こういうの見比べるのが好きなんですよね……。 というわけでランキングのおさらいです。 1位『達人 プログラマー 』 2位『Clean Code』 3位『Code Complete』 4位『 リファクタリング 』 5位『Head First デザインパターン 』 6位『人月の神話』 7位『Clean Coder』 8位『レガシーコード改善ガイド』 9位『 オブジェクト指向 における再利用のための デザインパターン 』 10位『世界で闘うプログラミング力を鍛える本』 11位『SOFT SKILLS』 12位『超明快 Web ユーザビリティ ―ユーザーに「考えさせない」デザインの法則』 13位『CODE』 14位『 アルゴリズムイントロダクション 』 15位『ピープルウエア』 16位『珠玉のプログラミング』 17位『 エンタープライズ アプリケーション アーキテクチャパターン 』 18位『計算機プログラムの構造と解釈』 19位『The Art of Computer Programming 日本語版』 20位『エリック・ エヴァ ンスの ドメイン 駆動設計』 21位『Coders at Work』 22位『ラピッドデベロップメント』 23位『独学 プログラマー 』 24位『Algorithms』 25位『継続的デリバリー』 頻繁におすすめされるような有名なプログラミング本で、未読のものはあっても知らないものはそんなにないかな、と思っていたのですが意外と知らない本を発見できて嬉しかったです。日本語圏と 英語圏 でもまた違いがあったりするのでしょうか。 読みたいと思っていながら読めていなかったプログラミング本もたくさん思い出せたので、合間を見つけながらせっせと読んでいきたいと思います。 最後に改めて。了解をくれたPierreさん、ありがとう。 Pierre, thank you for your consent. エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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 Q&Aコミュニティサイト ↩ 匿名 掲示 板サイト ↩
はじめに こんにちは。新卒2年目のtaku_76です。といってもあと半月ほどで3年目になります。 今回は以前ある記事でコンテナ技術の習得は必須ということを見て、コンテナ技術について表面的なことしか知らないなーと思い、学習しています。まだ学習途中ですが、初めに学んだ基本的な内容をまとめておこうと思います。 はじめに Dockerとは コンテナ型の仮想化とは Dockerイメージとは Dockerコンテナの実行 helloworldイメージの実行と動作解説 nginxイメージの実行と動作解説 最後に Dockerとは Docker社が提供しているコンテナ型のアプリケーション実行環境です。 コンテナ型の仮想化とは OSの カーネル を利用してホストOS上にアプリケーション実行環境を作るものです。 以下のような構造となっています。 コンテナ型仮想化の構成 Docker Engineがコンテナ型仮想化のコアとなる部分で、コンテナの作成、起動、停止、削除などの操作はDocker Engineを介して行われます。使い方としましてはDockerのコマンドを実行することでDocker Engineに指示を出すことになります。 以下にコンテナ型仮想化のメリットとデメリットをまとめます。 メリット ホストOSの カーネル を使用するため、動作が早くリソースの使用率が少ない 同じDockerイメージからコンテナを起動することで、環境が変わっても問題なく動作させることができる デメリット コンテナはホストOSの カーネル を使用して動作するため、 カーネル を共有できないOSでコンテナを動作させることができない Dockerイメージとは 特定のコンテナを実行するのに必要なファイルをまとめた ファイルシステム です。一般的にOSのライブラリやアプリケーションなどがあり、Dockerイメージは自分で作成することもできます。構成としてはイメージのデータはレイヤーで構成され、読み取り専用なので編集することはできません。以下Dockerイメージの構成となります。 左がDockerイメージで右がDockerイメージから作成したコンテナの図になります。Dockerイメージは図のように階層構造でデータが管理されており、各層のレイヤーはコマンドを実行する際に階層が積み重ねられます。そして、Dockerイメージを元にコンテナを起動すると読み書き可能なコンテナレイヤーの層が作られます。コンテナレイヤー上にファイルの追加など行い、それをまたイメージとして保存することも可能です。注意として、過去のレイヤーで追加したファイルをコンレナレイヤーで削除し、新しいイメージを作成しても過去のレイヤーから対象のファイルが消えることはありません。よって、無駄なファイルがイメージに含まれないように作成することが大切です。 Dockerコンテナの実行 helloworldイメージの実行と動作解説 コンテナの実行は簡単です。以下のコマンドをターミナルで入力します。 $ docker run hello-world すると結果を以下のようになります。 Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 1b930d010525: Pull complete Digest: sha256:f9dfddf63636d84ef479d645ab5885156ae030f611a56f3a7ac7f2fdd86d7e4e Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/ ここで起こっていることを説明します。 1.まずローカルでDockerクライアントがDockerデーモンに接続してhello-worldのイメージを探します。 2.今回はローカルにhello-worldのイメージは存在しないので、インターネットを経由してDocker社が ホスティング しているDocker Hubのサーバに探しに行きます。 3.イメージが見つかればダウンロードしてローカルに保存します。 4.保存したイメージをもとにコンテナを起動して、コンテナに定義されたコマンドを実行します。 ※同じイメージがローカルに存在する場合は、2と3は省略されます。(ダウンロードがないので早く実行される) ちなみに、docker runコマンドは複数のコマンドをまとめて実行したもので、以下のコマンドをまとめています。 1.docker pull:イメージの取得 2.docker create:コンテナの作成 3.docker start:コンテナの起動 取得したイメージは以下のコマンドで確認することができます。 $ docker imeges 結果は以下のようになります。 REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest fce289e99eb9 14 months ago 1.84kB リポジトリ に含まれるイメージがTAGによって管理されているのですが、特にTAGを指定しなかった場合にはlatestTAGのイメージがダウンロードされます。 また、以下のコマンドでイメージの詳細情報を表示することができます。 $ docker inspect hello-world 結果は以下のようになります。 [ { "Id": "sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e", "RepoTags": [ "hello-world:latest" ], "RepoDigests": [ "hello-world@sha256:f9dfddf63636d84ef479d645ab5885156ae030f611a56f3a7ac7f2fdd86d7e4e" ], "Parent": "", "Comment": "", "Created": "2019-01-01T01:29:27.650294696Z", "Container": "8e2caa5a514bb6d8b4f2a2553e9067498d261a0fd83a96aeaaf303943dff6ff9", "ContainerConfig": { "Hostname": "8e2caa5a514b", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, (中略) Dockerコンテナの設定情報として、ホスト名や 環境変数 コンテナ起動時に実行されるコマンドやコマンド実行時の ディレクト リの情報などが表示されます。そのほかにも様々な情報が表示されていますので、詳細情報を確認する際によく使われるコマンドです。 そして、ローカルで取得したイメージの削除を行います。次のコマンドで削除することができます。 $ docker rmi hello-world 強制削除を行う場合は-fオプションを使用します。 ここまででhello-worldイメージを用いてDockerイメージの取得から削除までを解説しました。 nginxイメージの実行と動作解説 ここではnginxイメージを使用してデフォルトのnginxの画面が表示されるところまで進めます。 nginxコンテナを立ち上げるコマンドは以下になります。 $ docker run --name <コンテナ名> -d -p <ホスト側のポート番号>:<コンテナ側のポート番号> <イメージ名> コマンドの解説ですが、runについては説明済みですので割愛します。--name <コンテナ名>ですが、コンテナを識別したりコンテナの操作を行うために、起動するコンテナに名前をつけるオプションです。引数は任意の名前をとります。-dオプションはデタッチモードとしてコンテナの実行をバッググラウンドで行うためのオプションです。-pオプションは、コンテナのポートをコンテナ外に公開する設定です。ホストマシンが外部に公開するポート番号:コンテナに マッピング するポート番号という構成です。 それでは実際にnginxコンテナを起動してみます。 $ docker run --name nginx-test -d -p 8080:80 nginx 以下のような結果になります。 Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 68ced04f60ab: Pull complete 28252775b295: Pull complete a616aa3b0bf2: Pull complete Digest: sha256:2539d4344dd18e1df02be842ffc435f8e1f699cfc55516e2cf2cb16b7a9aea0b Status: Downloaded newer image for nginx:latest この状態で http://localhost:8080/ にアクセスするとデフォルトのnginxの画面が表示され、nginxコンテナが正常に起動できていることが確認できます。 こちらについて解説しますと、nginxコンテナの80番ポートをコンテナを実行している 仮想マシン の8080番ポートに対応させています。これによって 仮想マシン の8080番ポートにアクセスした場合、nginxコンテナのネットワークインターフェースの80番ポートに転送するように設定されます。そして80番でlistenしていたnginxがコンテンツを返し、ウェブブラウザの画面にデフォルトが表示されます。 ここで、コンテナの状態を確認するために以下のコマンドを実行します $ docker ps 以下のような結果になります。 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 14da03714d91 nginx "nginx -g 'daemon of…" 26 minutes ago Up 3 minutes 0.0.0.0:8080->80/tcp nginx-test STATUSを見ると、現在実行中であり起動して3分経過していることがわかります。 そして以下のコマンドでコンテナの実行を停止することができます。 $ docker stop nginx-test 状態を確認します。 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 14da03714d91 nginx "nginx -g 'daemon of…" 32 minutes ago Exited (0) 6 seconds ago nginx-test STATUSがExited状態となり停止していることが確認できます。 ここまでで、nginxコンテナの起動から停止までの解説をしました。 最後に 今回はDockerについて基本的なことや操作をまとめました。Dockerファイルについても記載したかったですが、次の記事を書く機会に自分の作成したいアプリケーション実行環境を構築した話を書きたいと思っています。
こんにちわ @kawanamiyuu です。早いものでもう開催から 1 ヶ月経ちましたが、先月開催された Object-Oriented Conference で登壇してきました。また、弊社はブロンズスポンサーとして協賛していました。 イベント概要 登壇資料 登壇に対する反応 登壇までのチャレンジ 〜登壇駆動登壇の実践〜 Appendix. イベント概要 日時:2020 年 2 月 16 日 (日) 会場: お茶の水女子大学 公式 HP: https://ooc.dev/ ハッシュタグ : #ooc_2020 ooc.dev 登壇資料 speakerdeck.com 登壇資料のサマリー 登壇に対する反応 since:2020-02-16_16:30:00_JST until:2020-02-16_16:50:00_JST #ooc_e 今回の発表の軸となる主張に共感いただけて良かったです。 「 アーキテクチャ の正体は依存関係の取り決め」、確かにだった。 アーキテクチャ テスト導入しているけど、自分の実装ミスに気付くだけでなく違反を起点に話し合うきっかけにもなっているのでもっと広まればいいなと思う。 #ooc_2020 #ooc_e — west-c (@west_c_) 2020年2月16日 アーキテクチャ は依存関係を適切に設計したいだけ →わかる。適切に設計したい理由はいろいろあるけど、「テストしやすいため」が個人的には一番。 #ooc_2020 #ooc_e — ウホーイ (@the_uhooi) 2020年2月16日 依存の向きにフォーカス、うん大好きさ #ooc_e — suzuki- hoge (@suzuki_ hoge ) 2020年2月16日 登壇までのチャレンジ 〜登壇駆動登壇の実践〜 今回の登壇にあたって 1 つチャレンジがありました。それは 登壇駆動登壇 の実践です。「 アドベントカレンダー (12月) → 社内ビアバッシュ (1月) → 自社主催のMeetup (2月) → 本番 (2月)」と アウトプットを重ねながら 言語化 に取り組む ことで発表の完成度を上げていきました。 今回は特にアウトプットのスパンが短かったためとても緊張感があり骨の折れる作業でしたが、プロポーザル作成時には整理しきれていなかったア イデア を 言語化 でき、満足のいく発表に仕上がりました。 何度か登壇を経験し人前で話すこと自体へのハードルはほとんど感じなくなった私ですが、「 言語化 」することが苦手な私にとっては今回の取り組みで得られた手応えは、今後の登壇に対するブレイクスルーになったと感じています。 Appendix. アーキテクチャ テストに興味がわいた方はこちらもぜひ↓ tech-blog.rakus.co.jp
こんにちは、株式会社 ラク スで横断的にITエンジニアの育成や、技術推進、採用促進などを行っている開発管理課に所属している鈴木( @moomooya )です。 ラク スの開発部ではこれまで社内で利用していなかった技術要素を自社の開発に適合するか検証し、ビジネス要求に対して迅速に応えられるようにそなえる 「 開 ( か ) 発の 未 ( み ) 来に 先 ( せん ) 手をうつプロジェクト(通称:かみせんプロジェクト)」 というプロジェクトがあります。 2019年度下期にこのプロジェクトで「 機械学習 プロジェクトの進め方」について検証したので紹介したいと思います。対象読者は非エンジニアを含む、 機械学習 を用いた機能を企画・設計する全ての関係者 となります。 今までの記事はかみせんカテゴリからどうぞ。 tech-blog.rakus.co.jp 今回の目標 参考書籍 あらためて機械学習とは 何ができるのか 機械学習プロジェクトのアンチパターン システムに組み込むまでの手順 プロジェクトの進め方 問題の明確化 システム設計 機能設計 アルゴリズム選定 前処理 モデルの作成 モニタリング 2つのモニタリング 活用事例 大日本住友製薬と英Exscientiaの創薬事例 講談社のTEZUKA2020プロジェクト 結論 社内勉強会の際に挙がった声と回答 今回の目標 「 人工知能が幻滅期に入った 」と言われるようになったので 1 、啓蒙活動期に向けて本格的な検証を開始しています。 プロダクトに 機械学習 を利用した機能を実装するにあたってエンジニ アサイ ドだけではなく、ビジネスサイドも含めて、どういった考えで取り組むべきなのかをまとめます。 いきなり難しい領域に取り組むのは無謀なので一番わかりやすい「 教師あり学習 」のみを想定します。 toB サービスの場合だとクライアントに説明しにくい処理を組み込むのはハードルが高すぎるということも考慮しています。 参考書籍 仕事ではじめる機械学習 作者: 有賀 康顕 , 中山 心太 , 西林 孝 オライリージャパン Amazon 特に第1部第1章は関係者全員に読ませましょう。 人工知能システムのプロジェクトがわかる本 企画・開発から運用・保守まで (AI & TECHNOLOGY) 作者: 本橋 洋介 翔泳社 Amazon プロジェクトを進めていくにあたっての ケーススタディ が豊富。 あらためて 機械学習 とは 何ができるのか 大まかにいって 入力データを分類すること 入力データから数値を予測すること の2つです 2 。 機械学習 プロジェクトの アンチパターン 「 機械学習 でなにかすごいことをしたい」という上司が現れて、何をすればいいかというところから頭をひねるようなプロジェクトになってしまい、うまくいかない結果に終わる危険性が高まります。 『仕事ではじめる 機械学習 』第1部第1章「 機械学習 プロジェクトのはじめ方」より 「言わずもがな」ですね。 システムに組み込むまでの手順 まずはデータがないと話がはじまりません。後から出てくるScikit-Learnの チートシート ではデータの特徴によって適切な アルゴリズム が選択できますが、 少なくとも50サンプル以上 が必要となります。他にも基準として「1万サンプルを超えるか」「10万サンプルを超えるか」という目安が 大規模なデータかどうか という基準として出てきます。 データを用意したら次に行うことはデータの前処理です。 機械学習 の世界では 「前処理8割」 という言葉があるようで、非常に重視されています。収集した生のデータはそのままだと扱いにくい書式や値であることが多く、それを学習 アルゴリズム への入力として都合の良い形に整形しましょう、というのが「前処理」になります。 取り組む前は次の学習モデル作成が大変なのかと思っていましたが、自前の アルゴリズム 構築を行わない場合は前処理工程の方が遥かに大変です。 前処理を行ったデータを入力として学習モデルの作成を行います。自前で学習 アルゴリズム の構築を行わない場合、公開されている アルゴリズム 選択 チートシート と 機械学習 ライブラリを用いて、学習 アルゴリズム の選定と実装ができるので比較的容易に学習モデルの作成は出来ます。 作成した学習モデルをシステムに組み込みます。組み込みの際には作成した学習モデルファイルを読み込んで利用します。併せてモニタリングに必要な数字も取得できるように作り込みましょう。 プロジェクトの進め方 問題の明確化 機械学習 を使っても 人間が真偽を判断できない問題は解くことが出来ません 3 。 注意しなければならないのは「人間が判断できるかどうか」を考えるときに所要時間はあまり意識する必要がないことです。例えば「何日もかければ正しいかどうか判断できる」というような実務上現実的ではない時間がかかる場合でも、 判断することができれば多くの場合問題ない です。なぜならプログラムの実行速度は人間の何倍もの速度があるため時間的な制約は問題にならないためです。 問題と対になる成果の指標も最初に決めておきましょう。 機械学習 を利用した機能の運用は高コストになりがちなので ビジネスに貢献出来ていることが必須 です。ビジネス観点でのKPIを定めておくべきです。 問題を人間が解けることの確認とKPIが定まったら、取り組む問題が本当に必要なのか、取り組む問題に 機械学習 が必要なのかを検証しましょう。『仕事ではじめる 機械学習 』でも大きく扱われているように 機械学習 を使わない方法を検討します。想定しているよりも機能や利用シーンが制限されたものでもよいのでプロトタイプを作成し、問題設定が想定通りかどうか検証しましょう。 検証の結果、 機械学習 を利用しなくても要件を満足することもある と思います。その時はオーバースペックなものを作らずに済んだと喜びましょう。 手段を目的と勘違いしてはいけません 。 システム設計 機能設計 データ収集方法 予測誤りのカバー方法 撤退ラインの定義 先述の通り、データ収集の仕組みが必要になります。数千サンプルのデータを継続的に収集することは手作業では割に合わない作業です。最初の1, 2回を検証を兼ねて手作業でやることは否定しませんが、それが3回、4回と続くようなら見直したほうが良いでしょう。 また、 機械学習 では誤った結果を出力する ことがあります。大体よく見かける予測正解率は80~90%程度なので5~10回に1度くらいと、 それなりの頻度で誤った結果を出力するため仕組みとして誤った結果を出力しても問題にならないような機能設計が必要 です。 そしてこのフェイズを超えたあたりからサンクコスト効果により、撤退判断が遅れやすくなります。なので、この時点で撤退条件を定義しておくとよいでしょう。 アルゴリズム 選定 機械学習 アルゴリズム の選定については自前で アルゴリズム を構築しない場合であれば以下で アルゴリズム 選定用の フローチャート が公開されています。 scikit-learn - Choosing the right estimator 機械学習アルゴリズム チート シート - Azure Machine Learning 昔は アルゴリズム から開発することが必要だったようですが、現在は優秀な アルゴリズム が簡単に利用することができます。感謝して使わせてもらいましょう。 前処理 いちばん大変な工程です。 機械学習 はデータからデータを導く アルゴリズム なので入力データが重要で、最終的な予測精度にも影響するようです。なのでたくさんのデータを集めて精度を上げるわけですが、 収集したデータが都合の良い書式であるとは限りません 。というよりも 都合が悪い形式であることを前提に取り組んだほうが良さそう です。データソースが1つであればまだマシでしょうが、複数のデータソースからデータを集めてきた場合にはさらに手間が増加するでしょう。 「前処理8割」という言葉があるらしいですが、以下のような様々な処理が必要になるようです。 データクリーニング(データクレンジングとも) 不要なサンプルの除去 ノイズとなる外れ値の除去 値がない項目の処理 値の整形 値の分布を正規化 よくあるのは0~1の範囲に変換 細分化しすぎている分類を集約 自然言語 での時制、語尾変化、類語の統一 これらを実現するための手法もいろいろ考案されているようで、前処理だけに絞った書籍も複数出版されています。 今回は前処理がこんなに奥深いテーマだということを知らなかったため深追いしませんでしたが、来年度の取り組みテーマとして前処理の掘り下げを予定しています。 モデルの作成 判断材料となるデータは場合によっては非常に大きなデータサイズになり得るため、そのままシステムに組み込むわけには行きません。そのため 機械学習 アルゴリズム を通して作成した 学習モデルのみを組み込み ます。学習モデルは誤解を恐れずに言うとデータを分析して抽出した条件式の詰め合わせのようなイメージです。 機械学習 アルゴリズム の選定や実装も先述の通り、既存の フローチャート やライブラリを利用することで比較的容易に実現することが出来ます。 ただし忘れてはいけないのは運用を開始しても、新たに収集したデータを元に定期的に学習モデルの再作成(再学習)が必要になります。 再学習を行わないと入力傾向の変化や未知の入力パターンに適応できなくなる ためで、この現象はコンセプトドリフトと呼ばれています。 モニタリング 2つのモニタリング 機械学習 の予測精度は入力傾向の変化によりいつかは劣化します。予測精度劣化の兆候を検知するためにもモニタリングは必要です。 予測結果が把握できている入力デー タセット を ベンチマーク デー タセット として利用します 。このデー タセット のことを「ゴールドスタンダード」というそうです。 もう一つはビジネス的な成果のモニタリングです。機能を作成してもビジネスに貢献できていなければ意味がありません。ビジネス観点の指標をモニタして成果が上がっていることを確認しましょう。 活用事例 では進め方はともかくどのような使い方をすればよいのでしょうか。 システム設計の項で触れた「誤った結果を出力しても問題にならないような機能設計」という観点を交えて見ていくと非常に参考になるケースを見つけました。 大日本住友製薬 と英Exscientiaの 創薬 事例 www.itmedia.co.jp こちらの 大日本住友製薬 と英Exscientiaが2020年1月30日に発表したAIを活用した新薬が 臨床試験 を始めた事例です。新薬開発の世界では膨大な組み合わせの中から候補となる組み合わせを専門家の知識と経験で見つけ出し、それを検証して製品化につなげるそうです。この事例では「膨大な組み合わせの中から候補となる組み合わせを見つけ出す」部分にAIを活用しているそうです。 膨大な組み合わせから候補を探すという「0を1にする」部分においては専門家が取り組んでもとても時間がかかる部分ですが、専門家よりもたとえ精度が劣ったとしても圧倒的な早さで候補を挙げてくれるとすれば大きな成果となるでしょう。もちろん専門家が取り組んだ場合に比べて使えない候補は多かったと考えられます。ただし使える候補か使えない候補か判断する部分に専門家(人間)が入ることで誤った結果を排除し、 臨床試験 に望むまでを4年半から12ヶ月未満と大幅に縮めることが出来たよい活用事例だと考えられます。 他にも同じような活用事例として、 三菱マテリアル も同様のプロセスで新素材の開発に活用しているようです。 三菱マテリアル は 機械学習 プロジェクトを始める際のビジネス フレームワーク となる 機械学習プロジェクトキャンバス を作成し公開してくれています。今回の取組中にも参考にさせてもらいました。 講談社 のTEZUKA2020プロジェクト www.itmedia.co.jp こちらは2020年2月26日に発表された 講談社 による「 手塚治虫 が新作を作ったらどんな漫画になるだろう」という課題にAIを活用したものです。 こちらも本質的なプロジェクトの進め方は上記の 創薬 プロジェクトと同様で、漫画の軸となるプロットとキャ ラク ターデザインの候補をAIが提示し、それを人間の判断により選び出し制作を行うという流れで取り組んだそうです。 結論 分類と数値予測 入力データの収集が必要 入力データはそのまま使えない、整形には手間とノウハウが必要 従来の方法では手間がかかりすぎることをやらせると良い 「人間ができること」を「間違えることもあるが」「高速に判断できる」 間違えたときのカバーする仕組みが大事 運用を始めると必ず劣化するので監視と再学習が必要 社内勉強会の際に挙がった声と回答 エンジニア、ビジネスサイドを交えた勉強会を社内で実施したときの声をいくつかピックアップします。 Q. プロジェクトに着手して試行錯誤しながらやることを決めると思うけど見積もりできなくない? A. 最初から正確な見積もりはできないのはそのとおりで、段階的に見積もることになります。数段階再見積もりを行うフェイズを計画時に設定し、段階的に精度を上げていくことになると思います。 Q. ユーザーに選択を求めるようなインタフェースで選択結果を正解として再学習すれば精度は上がる? A. 特定のケースに特化して学習してしまう「 過学習 」の状態にならないよう注意が必要です。 過学習 になってしまうと新しい入力パターンに弱くなってしまいます。 「お客さんの利用方法や行動をもとにアップセル候補の抽出に使えるかも。ベテランの勘を実装できれば」 「被検索などのデータから広報施策のタイミングとか見つけられそう」 「逆に 機械学習 ではないけど世間でAIと呼ばれている領域の知識も知りたい」 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com ちなみにハイプ・サイクルにおいて幻滅期は「衰退」「流行の終わり」といった勘違いをしやすい言葉ですが、まったくの逆で「 バズワード が定番技術に変わる準備期間」くらいの認識が正しいです。 ↩ 一般的な説明だと「分類」「回帰」「 クラスタリング 」「次元削減」と言われますが、最初の段階だと「分類」「数値予測」で話をした方が理解しやすいと思います。 ↩ 教師なし学習 とか、もっと言えば深層学習とかだとこの領域にも答えを見つけられるかもしれませんが、今回扱う 教師あり学習 の範囲では人間が判断出来ない問題は解けません。 ↩
こんにちは。 @penguin_no_045 です。 先日開催された[ PHPerKaigi 2020 で登壇させてもらいました。皆様どうもありがとうございます。 資料 speakerdeck.com 「PHPerがこれから「型」とお付き合いしていくために 」というタイトルでした。最近の PHP は型に関する進化が多いような気がしたのでテーマとして選定しました。 また、私の観測範囲内ですが、一時期別の言語ユーザーの間で 静的型付き言語 vs 動的型付き言語 だったり、動的型付き言語は 型推論 が入っているので云々といった話題が見られました。そこで、これからPHPerとして型と触れ合う機会が増える前に知識を整理しておこうと思い立ったわけです。 私はもともと静的型付き言語ユーザーだったので、型システムは身近なものでした。そこで軽い気持ちでプロポーザルをまとめたのですが、実際は今回のセッションの準備中に学んだ知識のほうが多かったです。 概念としてはずっと触れてきたものでも、改めて体系的に学ぶとあやふやな理解をしていた個所などはすぐに化けの皮が剥がれるものですね。 とくに型システムというテーマは不正確な情報がけっこう頻繁に流布されている印象があったので、自分もそうならないように気を付けました。諸先輩方からはどのように映ったのでしょうか? 発表内容について触れると、立て付けとしては型システム入門以前、といった感じになったと思います。そのため、今回のセッションでは簡単のために説明を省いた部分や、意図的にセッションのスコープから除外した領域が結構あります。 型システムに詳しい方は物足りなさを感じられたかもしれませんし、今回のセッションの中でも必要な概念が一部説明できないままの個所もあります。その部分についてはいつか語りたいなと思っています。 また、質疑などでより深い内容について質問をいただくことができ、その場で捕捉したいことを話すことができたのは幸いでした。改めて皆様ありがとうございました。 少しだけの補足 そんな質疑の中で1つ特に印象的だったものがありまして、「 PHP で型安全に配列を扱うには?」というものがありました。 これについてはよくある話で、私も「コレクションを定義するのが良い」と回答しましたが、そこにはたくさんの壁が待ち受けているということを説明できていませんでした。 実際にコレクションライブラリの自作にチャレンジして、 心が折れる 、というのは昔から繰り返されてきたものではないかと思います。( PHP に限らず) そういった際にこういった機能が使えると勝率が上がる、というものを紹介します。 ジェネリクス 現在の PHP でコレクションクラスを作るとしたらどのような シンタックス になるでしょうか。 簡単のためインターフェースなどの定義は省くと、こんなクラスになるでしょう。 class MyCollection { public function __construct () { } //実装は省略 } これでは何も得ることはできないですね。そこでほしくなるのが ジェネリクス です。 型を変数のように扱うことで、コレクションの「中身」の型を決めずにコレクション型を定義できるわけです。 class MyCollection[T] { /** * @var T[] */ private $elements; /** * MyCollection constructor. */ public function __construct () { } //実装は省略 // こうやって使う $foo = new MyCollection[MyType](); これができると、1つのコレクションの定義で色々な型を操作できます。 しかもこの型引数( [T] の T の部分)がインターフェースの場合、継承したクラスをなんでも入れられるという寸法です。便利ですね。 そして沼へ続く ただし、これを便利に使おうとすると大きな問題が発生します。次は引数の型が欲しくなるのです。 たとえば配列関数の中にある array_filter や array_map を実装するとします。するとこう書きたくなります。 // class MyCollection[T] 内 public function filter(closure[T, boolean] $filterFunc): MyCollection[T] { // .... } public function map[S](closure[T, S] $mapFunc): MyCollection[S] { // .... } もう少し便利な関数も追加しましょう。 flatMap というものをご存じでしょうか。これは関数型の考え方が入ったコレクションライブラリではよくある機能(というより基本的な機能)です。 どういう機能かというと、 map して flat にするのですが、ソースを見るとイメージしやすいと思います。 $mapFunc = function($elem){ return new MyCollection($elem, $elem + 1); } ); // 自分と、自分に1足したものをコレクションにして返す $example = new MyCollection(1, 2, 3); $mappedSample = $example- > map($mapFunc); // MyCollection( // MyCollection(1, 2), // MyCollection(2, 3), // MyCollection(3, 4) // ) $flatMappedSample = $example- > flatMap($mapFunc); // MyCollection(1, 2, 2, 3, 3, 4) と、こんな風にネストをはがしてくれるものです。これを実装しようとすると型引数はこのようになります。 // class MyCollection[T] 内 public function flatMap[S](closure[T, MyCollection[S]] $mapFunc): MyCollection[S] { // .... } ね?簡単でしょ?と言わんばかりですが、型を書くことでコレクションライブラリの実装ができそうな雰囲気は伝わりましたでしょうか?さらに言えば、ここまで定義してきた MyCollection をさらに抽象化して、ほかのコレクションや集合、 Optional のようなものを定義できそうにも思えないですか? 共通の概念のようなもの?それは モナド ・・・ これ以上は 関数型プログラミング の世界にも踏み込んでいくことになるので、追いかけるのはこのぐらいにしておきましょう。 まだまだ課題はある もちろんこのままでは型引数を書かないといけない、毎度毎度型を合わせないといけないなど実際に使用していくうえで面倒と感じる部分が多いままです。しかも、こういったときに頼りになる 型推論 は言語の仕組み上安全に実装できないなど、いろいろな壁が立ちはだかってくるでしょう。このような ジェネリクス を導入しただけでは解決できない課題はたくさんあります。そのほかにも様々な トレードオフ と戦わないといけない個所が山のようにありますが、面倒ではあるが安全に倒しきった形でのコレクションライブラリの実装は ジェネリクス があればできそうな予感がします。 まとめ さきほど「面倒」というキーワードが出てきましたが、静的型付き言語のデメリットとしてよく「実装が面倒」ということを上げられます。そうです。これをそのまま使うとその「面倒」を持ち込んでしまうのです。これをもっと便利に使えるようにしたい!となった場合は、より高度な型システムの概念が必要です。これから PHP がどのような方向へ進化していくのかはふんわりと見えている状態ではありますが、高度な型システムを獲得できるのは少なくともまだ先のこととなるでしょう。当分の間は将来の進化を期待しながら array と付き合っていくのが得策ではないか、と思います。 イベントの話といいつつ、ほとんど ジェネリクス の話になってしまいましたがここまでとします。 今回のイベントで関わってくださった皆様、本当にありがとうございました!
先日、弊社がスポンサーとなっている PHPer Kaigi 2020 に、私 Y-Kanoh も参加してまいりました。 PHPer Kaigiとは? 公式サイトによると、 PHP に携わる人が、技術的なノウハウと PHP 愛を共有するためのイベントです。 一般から公募された登壇者の トーク セッションや、アンカンファレンス、懇親会などのイベントが3日にわたり催されていました。 私は開催場所が東京だったこともあり、最終日である3日目のみ参戦させていただきました。 会場について すでに二日間開催されていたためなのか、会場に到着すると、すでに活気付いた雰囲気がありました。 朝早いはずですが、会場のいたるところにエンジニアが集まりコミュニケーションをとっています。 これは、なんといっても今回取り入れられた トレーディングカード が大きいと思います。 この トレーディングカード は、上記のように、入場と同時に他 ノベルティ と共に1人100枚配られます。 配られた100枚のカードには、その参加者の Twitter アイコンが印刷されており、会場内ではこれを他の参加者と交換することが推奨されます。 なにせ100枚もあるので、躊躇していては交換しきれません。参加者はお互いにこのカードを配りきることを目的に他の参加者とコミュニケーションをとる必要があり、他者へ話しかけるハードルがとても下がりました。 このカードを用意するためには、参加者全員分のアイコンを取り込み、カードを印刷する必要があると思います。運営スタッフ皆さまは大変だったと思いますが、このカードのおかげで、セッション外の時間でも始終盛り上がっていたと感じました。 私自身も、オープニングが終わったと同時にカードの交換を申し込まれ、他エンジニアの方と交流することができ、最終的には配られたカードの半分ぐらい交換することができました。 セッションについて 参加した日は、弊社のエンジニアが登壇した「PHPerがこれから「型」とお付き合いしていくために」や、「Zend VM における例外の実装」など、 PHP の内部構造についてのセッションが多かったように思います。 普段特に意識したことがない PHP のしくみや、仕様の理由などを考えることができました。 さらに PHP の 中間言語 の話まで聞くことができ、とても興味深かったです。 今回は用意されたセッションだけでなく、アンカンファレンスでの トーク にも参加しました。 おそらくほとんどの方がスライドなど用意していないかと思いますが、とても面白い トーク ばかりでした。 LT登壇について イベントの最後は恒例のLT大会です。 私も PHP の RFC の読み方をテーマに登壇させていただきました。 speakerdeck.com 自分の番になるまでは緊張していましたが、前に立った時には比較的落ち着いてお話しすることができました。 (調子に乗って喋りすぎたせいか、演台にタイマーとして使っていた スマートフォン を置き忘れ、私の後に登壇した MasaKu に取ってきてもらいました...) ちなみに、 トレーディングカード を配り切れていなくて焦っていた私は、LTで トレーディングカード 交換の宣伝もしてました(笑) 懇親会について 懇親会では、先に触れた トレーディングカード のおかげで、様々な方と交流することができました。 途中から始まった懇親会LT大会では、私自身が普段業務で関わっている ベトナム とのオフショア開発についての発表があったりと、とても楽しむことができました。 最後に 率直に、とても楽しいイベントでした! 1日のみの参加だったのが残念でしたが、 トレーディングカード や懇親会を通じてたくさんのエンジニアとコミュニケーションが取れ、有意義な1日だったと思います。 来年もぜひ、参加します!!
新卒2年目のyk_itgです。早いもので社会人2年目も残り1ヶ月となりました。 パフォーマンスチューニングの開発をする際に、indexはどのようなカラムに貼るのが良いのか気になったので、今回はそこで調べたことを書いてみます。 PostgreSQL のバージョン: 11.5 まずはindexを貼ってみる indexを使ってみる カーディナリティ カーディナリティを変えて比べてみる 参考資料 まずはindexを貼ってみる まずはindexを貼るためのテーブルを作っていきます。 テーブルには主キーのidの他に以下の2つのカラムを定義します。 sequence_number 一意な数字が入る2つのカラムを持つテーブルを用意します。 type 3種類の値(1, 2, 3)のいずれかが入る CREATE TABLE test ( id integer primary key, sequence_number integer , type integer ); そのテーブルに1つのtypeごとに100万件、計300万件のレコードを入れて、 INSERT INTO test(id, sequence_number, type ) SELECT i, i, ' 1 ' FROM generate_series( 1 , 1000000 ) AS i; INSERT INTO test(id, sequence_number, type ) SELECT i, i, ' 2 ' FROM generate_series( 1000001 , 2000000 ) AS i; INSERT INTO test(id, sequence_number, type ) SELECT i, i, ' 3 ' FROM generate_series( 2000001 , 3000000 ) AS i; sequence_numberとtypeの2つのカラムにindexを貼ります。 CREATE INDEX index_test_number ON test(sequence_number); CREATE INDEX index_test_type ON test( type ); indexが貼れたかどうかは、システムビューのpg_indexesで確認できます。 postgres=# SELECT * FROM pg_indexes WHERE tablename = 'test'; schemaname | tablename | indexname | tablespace | indexdef ------------+-----------+-------------------+------------+----------------------------------------------------------------------------- public | test | test_pkey | | CREATE UNIQUE INDEX test_pkey ON public.test USING btree (id) public | test | index_test_type | | CREATE INDEX index_test_type ON public.test USING btree (type) public | test | index_test_number | | CREATE INDEX index_test_number ON public.test USING btree (sequence_number) (3 rows) 主キーのカラムにはindexが元々 付いているようです。 indexを使ってみる 作ったindexがどのように使われているのか EXPLAIN ANALYZE で確認していきます。 比較をしやすいようにindexを貼る sequence_number と type を WHERE句に入れて、まずは何も貼っていない状態で試してみます。 実行前に ANALYZE を忘れずにしておきましょう。 また、indexを使ってもらえるようにenable_seqscanを off にし、見やすいようにパラレルクエリもoffにしておきます。 ANALYZE test; SET enable_seqscan TO 'off'; SET max_parallel_workers_per_gather TO '0'; EXPLAIN ANALYZE SELECT id, sequence_number, type FROM test WHERE type = 1 AND sequence_number = 1000000; ---------------------------------------------------------------------------------------------------------------------- Seq Scan on test (cost=10000000000.00..10000061217.00 rows=1 width=12) (actual time=87.978..235.451 rows=1 loops=1) Filter: ((type = 1) AND (sequence_number = 1000000)) Rows Removed by Filter: 2999999 Planning Time: 0.124 ms Execution Time: 235.470 ms (5 rows) 235.470 ms とそこそこ時間がかかっていることがわかります。 続いて2つのカラムにindexを貼った状態で試してみます。 EXPLAIN ANALYZE SELECT id, sequence_number, type FROM test WHERE type = 1 AND sequence_number = 1000000; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------- Index Scan using index_test_number on test (cost=0.43..8.45 rows=1 width=12) (actual time=0.022..0.023 rows=1 loops=1) Index Cond: (sequence_number = 1000000) Filter: (type = 1) Planning Time: 0.168 ms Execution Time: 0.044 ms (5 rows) 何も貼っていない状態と比べると、 235.470 ms と 0.044 ms で何倍も早くなっていることがわかりますね! ただ、sequence_numberだけでなく、typeにもindexを貼ったのに使われていません。 今度はtypeにだけindexを貼った状態で試してみます。 EXPLAIN ANALYZE SELECT id, sequence_number, type FROM test WHERE type = 1 AND sequence_number = 1000000; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------- Index Scan using index_test_type on test (cost=0.43..36304.43 rows=1 width=12) (actual time=184.283..184.284 rows=1 loops=1) Index Cond: (type = 1) Filter: (sequence_number = 1000000) Rows Removed by Filter: 999999 Planning Time: 0.526 ms Execution Time: 184.341 ms (6 rows) 今度はtypeのindexを使ってくれました! ただ、indexを貼ったのにも関わらず、 235.470 ms から 184.341 ms に代わっただけで、sequence_numberの方の 0.044 ms と比べるとほとんど効果が出ていないことがわかります。 この違いは何でしょうか。 カーディナリティ typeのindexの効果が出ない理由は、テーブルの行数に対してtypeのカーディナリティが低いためです。 カーディナリティ *1 とはそのカラムにある値の種類の絶対値(今回のtypeの場合は 3 )のことを指します。 テーブルの行数 300万 に対して、typeのカーディナリティは 3 しかなかったため、ほとんど効果が出なかったわけです。逆に一意でカーディナリティが高いsequence_numberのindexではかなり効果が出ています。 カーディナリティを変えて比べてみる 行数は300万のままで、typeのカーディナリティを変えて何パターンか実行してみました。 結果は以下のようになりました。※実行時間は同じ環境でも変動があります。 typeのカーディナリティ indexありの実行時間(ms) 2 324.752 3 223.665 4 193.7 10 66.488 100 6.953 1000 1.064 10000 0.073 100000 0.034 カーディナリティが1000を超えたあたりからだんだん効果が薄くなっていくのがわかります。 参考資料 カーディナリティについてまとめてみた https://qiita.com/soyanchu/items/034be19a2e3cb87b2efb カーディナリティ https://www.shift-the-oracle.com/words/cardinality.html 用語集 http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19217-02/glossary.htm 41.32. pg_indexes https://www.postgresql.jp/document/8.0/html/view-pg-indexes.html postgreSQL でインデックス一覧の表示 https://hacknote.jp/archives/2474/ エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 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 : 問合せの結果行数を指すこともあるそうです