RAKUS Developers Blog

株式会社ラクスのエンジニアブログ

戦闘力53万風のマイクロサービス

f:id:Y-Kanoh:20180111033033j:plain

こんにちは!エンジニアのY-Kanohです。

弊社のエンジニアは、業務終了前にその日の稼働報告を社内システムに入力することになっています。 しかしながら、この入力を忘れるメンバー(主に私)が多く、チームのリーダーに指摘されてから、数日前の仕事状況を思い出して記入することが度々ありました...。(すみません。)

そこで、チームで導入されているチャットツール、「MatterMost」に稼働報告を忘れている人へ通知をするようにしてみました。

Mattermost Private Cloud Messaging

しかし、ただ作るだけではすぐ終わってしまったので、かねてから興味があったDockerを使ってマイクロサービスとして作り直しています。

今日はそのお話です。

概要

大雑把に言えば、作成したBotシステムは下図のようなイメージです。

f:id:Y-Kanoh:20180111024417p:plain

プログラムは毎朝始業直前に動かすようセットします。 稼働状況は社内のDBに登録されているので、まず社内DBを参照し、昨日の入力を忘れているメンバーを見つけ、チャットに通知します。

MatterMostには外部連携機能があり、外部システムからの投稿が可能です。*1

設計

概念図

最初は、「動けばいい」の信念のもと、上記図のBotシステムをそのまま1コンテナにしただけの設計でした。

マイクロサービスを目指すにあたり、そのコンテナ内容を機能ごとに分割し、以下のような設計で実装しています。*2

f:id:Y-Kanoh:20180111024243p:plain

作成したコンテナは以下の7つです。

  1. 管理用BFFコンテナ
  2. Bot用BFFコンテナ
  3. API Gatewayコンテナ
  4. API Gateway情報DBコンテナ
  5. 社内DBアクセス用コンテナ
  6. チーム情報DBアクセス用コンテナ
  7. チーム情報DBコンテナ

※社内DBには稼働情報、チームDBにはチームメンバー情報やチームが使用しているチャット情報が格納されています。

これらのコンテナ群は、大きくBFFAPI Gatewayバックエンドの三種類に分けることができます。

BFF

BFFとは、Backends for Frontendsの略で、つまりフロントエンドのためのバックエンドです。*3 *4

BFFは、クライアントの種類ごとに、クライアントとバックエンドの間で、データ加工や画面作成などの処理を行います。

BFFを使用することで、バックエンドから受け取ったデータを、各BFFが対象とするデバイスやユーザ向けに適した形に加工して表示させることができます。

API Gateway

機能を各コンテナに分けるうえで、一つ問題がありました。

コンテナ同士はやり取りする相手のコンテナ接続情報を知っておく必要があります。そのため、やり取りするコンテナを増やすたびに、接続先情報をそれぞれ定義する必要があるため、 コンテナ依存関係の定義が複雑になってしまいます。

そこで、今回はバックエンドのコンテナは、互いに通信せず、必ずAPI Gatewayを介してやり取りするようにしました。 これにより、以下のメリットが見込まれます。

  • APIを一元管理
  • コンテナ間の依存関係を簡略化
  • 共通処理を行う場合ここで実行可能
  • BFFからのサービス呼び出しを簡略化

バックエンドのAPIは、必ずAPI Gatewayを介して呼び出されます。Gatewayに来たリクエストは、Gatewayで適切なコンテナへルーティングされるため、Dockerコンテナで面倒なコンテナ同士の依存関係を定義する必要はなくなります。

また、今回は特に実装していませんが、バックエンドへの認証機能や、ログの収集なども、ここで一括して行うことができます。

今回、API Gatewayには、OSSとして開発が進められているKongを使用します。

Kong - Open-Source API Management and Microservice Management

バックエンド

BFF、API Gatewayの導入により、バックエンドはその他の機能とかなり疎結合になります。

フロント側に適したデータ形式に加工する必要が少ないため、設計の制約が減ります。

そこで、バックエンドのAPIは、RESTの原則に基づいた実装にしました。

docker-compose.yml

docker-compose.ymlは以下の通りです。

前述したAPI Gatewayにより、各コンテナはkongコンテナをlinkさせるだけでバックエンドのAPIが使用できます。

version: '2'
#==============================================================================
# Volumeの定義
#==============================================================================
volumes:
  team_vol:
    driver: 'local'
  kong_vol:
    driver: 'local'

#==============================================================================
# servicesの定義
#==============================================================================
services:
  #############################################################################
  ## API Gateway
  #############################################################################
  # API GatewayのDB
  kong-database:
    image: postgres:9.4
    environment:
      - POSTGRES_USER=kong
      - POSTGRES_DB=kong
    volumes:
            - kong_vol:/var/lib/postgresql/data

  # API Gatewayの初期設定を行うコンテナ
  kong-migration:
    image: kong
    depends_on:
      - kong-database
    environment:
      - KONG_DATABASE=postgres
      - KONG_PG_HOST=kong-database
    command: kong migrations up

  # API Gatewayコンテナ
  kong:
    image: kong:latest
    depends_on:
      - kong-database
      - kong-migration
    environment:
      - KONG_DATABASE=postgres
      - KONG_PG_HOST=kong-database
      - KONG_PG_DATABASE=kong
    ports:
     - "8000:8000"
     - "8443:8443"
     - "8001:8001"
     - "8444:8444"

  #############################################################################
  ## 社内DBアクセス機能
  #############################################################################
  qcp_watcher:
    links:
      - kong
    build:
      context: "./BEService/QcpWatcher"
      dockerfile: "Dockerfile"
    ports:
      - 49513:80
    extra_hosts:
      - qcp:192.168.99.100  #社内DBのホストを指定

  #############################################################################
  ## Team情報管理機能
  #############################################################################
  # Team情報管理DB
  team_db:
    build: "./BEService/Team/DB"
    ports:
      - 54321:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_DB: postgres
    volumes:
            - team_vol:/var/lib/postgresql/data

  # TeamDBアクセスコンテナ
  team:
    links:
      - kong
      - team_db
    build: "./BEService/Team"
    ports:
      - 49514:80


  #############################################################################
  ## Bot用コンテナ(MatterMost連携コンテナ)
  #############################################################################
  mm_bff:
    links:
      - kong
    build: "./FEService/MmBff"
    ports:
      - 49515:80

  #############################################################################
  ## 管理者用コンテナ
  #############################################################################
  admin_bff:
    links:
      - kong
    build: "./FEService/AdminBff"
    ports:
      - 49516:80

実際に作成したBot

毎朝こんな感じに通知してくれるようになりました。

f:id:Y-Kanoh:20180111025537p:plain

より通知を目立たせるために、戦闘力が53万の方にご協力いただいています

f:id:Y-Kanoh:20180108181755p:plain

この方にはbotをマイクロサービスにする前から協力いただいています。最初はアイコン画像だけでしたが、メンバーからの要望により、口調もあの方になりました。

最初は黄色いネズミのアイコンだったのですが、こちらのほうが断然反応していただけました。

f:id:Y-Kanoh:20180108181351p:plain

全員稼働報告が入力できていた場合は、ちゃんと褒めて(?)いただけます。ありがたや。

今後の拡張

Botを機能ごとに分割することで、追加機能の実装もやりやすくなりました。

また、コンテナによって機能ごとにプログラムが隔離されているため、興味がある技術を試しに使ってみることもできます。

そのため、今後は業務に役立ちそうなものを 趣味 自己学習として、拡張できればと思います。

Google Homeにプレゼンさせてみた。

こんにちは。楽楽精算開発チームの岡本です。

会社で購入したGoogleHomeで自由に遊べる権利を頂いたので、色々と遊ばせてもらいました。
先日その内容を社内でプレゼンする機会があったのですが、せっかくなのでGoogleHome自身にプレゼンしてもらいました。

今回は、その際に実施した設定を説明しようと思います。
※設定方法についてはwest-cさんが既に書かれているので、設定した内容だけ書いていきます。

今回やりたいこと

  1. スマートフォンでプレゼン開始を指示する。
  2. ディスプレイにスライドが表示される。
  3. GoogleHomeがスライドの内容を喋り始める。
  4. スライドの内容を喋り終えると、次のスライドに切り替わる。
  5. スライドが終了するまで2~4を繰り返す。

準備するもの

Wan側

  • Action on Google
    • Googleアシスタント内で呼出し可能なチャットアプリ*1を構築できるサービスです。
    • 構築したチャットアプリから更に別のWebサービスを呼び出すこともできるので、この仕組を用いればGoogleアシスタントから直接Webサービスを呼び出せます。
    • 今回はDialogflowを呼び出すように構築しています。
  • Dialogflow
    • 自然言語の解析エンジンです。受取った音声データを解析してテキストに変換してくれます。
    • また、Firebaseホスティングを用いたスクリプト実行も可能なので、変換したテキストを他のWebサービスに連携させたりもできます。
  • Firebase
    • いわゆるBaasです。
    • 今回はホスティング機能とDB機能を使用します。

Lan側

  • RaspberryPi
    • 超小型のコンピューターです。
    • 今回は以下の機能を実装しています。
      • ディスプレイへのスライド切替え通知
      • GoogleHomeへのメッセージ通知
  • GoogleHome
    • いわゆるスマートスピーカー。今回は喋らせるだけなのでスマートな機能は使いません。

ざっくりとした処理の流れ

f:id:okana-yg:20171223153335j:plain

  1. スマートフォンGoogleアシスタントからAction on Googleで構築したチャットアプリを呼び出す。
  2. チャットアプリで入力した音声データをDialogflowに連携する。
  3. Dialogflowの解析結果テキストをFirebaseのDBに保存する。
  4. 保存したテキストをRaspberryPiに通知する。
    (通知されたテキストがプレゼン開始キーワードの場合は、プレゼンを開始する。)
  5. スライドの表示/切り替えをディスプレイに通知する。
  6. 喋らせるメッセージをGoogleHomeに通知する。

Action on Googleの設定

Action on Googleで新規プロジェクトを作成しActionApp informationを設定します。

Action

ADD ACTIONSからDialogflowを選択します。
f:id:okana-yg:20171224003323j:plain

App information

重要なのはAssistant app nameの項目だけです。
Assistant app nameで設定した名前でGoogleアシスタントでチャットアプリを呼び出すことになります。
今回は「発表アプリ」にしたので、スマホに「OK Google 発表アプリにつないで」と話しかけるとチャットアプリが起動します。
それ以外の項目については適当に設定してしまって問題ないです。
f:id:okana-yg:20171224001105p:plain

以上でAction on Googleの設定は完了です。

Dialogflowの設定

Action on GoogleAction設定から、Dialogflow画面に遷移できるので、新規プロジェクトを作成しIntentsFulfillmentを設定します。

Intentsの設定

チャットアプリで入力した音声データに対して、どのように振舞うのかを設定します。今回は以下の2つを設定します。
f:id:okana-yg:20171224132107j:plain

  • Default Welcome Intent
    チャットアプリが起動した時の振舞いを設定します。
    今回は、アプリ起動時にチャットアプリが「発表アプリを起動しました。」と応答するようにText responseを設定しました。
    f:id:okana-yg:20171224132545j:plain
  • プレゼン開始
    チャットアプリにプレゼン開始を指示した時の振舞いを設定します。今回は以下のような振舞いを想定しています。
    1.チャットアプリに「プレゼン開始」と音声入力する。
    2.「プレゼン開始」のキーワードをFirebaseに保存する。
    3.チャットアプリが「プレゼンを開始しました。」と応答する。

    「プレゼン開始」の音声入力でsaveDataというアクションが実行されるように設定します。
    saveDataの内容についてはFulfillmentで設定します。
    f:id:okana-yg:20171224140841j:plain
    アクション実行後、チャットアプリが「プレゼンを開始しました。」と応答するように設定します。
    f:id:okana-yg:20171224144012j:plain

Fulfillmentの設定

Inline EditorENABLEDに変更し、index.jspackage.jsonを以下のように設定します。

/** index.js **/

'use strict';

const firebase = require("firebase");
const functions = require("firebase-functions");
const DialogflowApp = require("actions-on-google").DialogflowApp;

// Firebaseへの接続情報
var config = {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
    databaseURL: "XXXXXXXXXXXXXXXXXXXXXX",
    projectId: "XXXXXXXXXXXXXXXXXXXXXX",
    storageBucket: "XXXXXXXXXXXXXXXXXXXXXX",
    messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
    var app = new DialogflowApp({request, response});
    let actionMap = new Map();
    // saveDataアクションを登録
    actionMap.set("saveData", function(app) {
        // Firebaseにキーワードを保存する
        firebase.database().ref("/googlehome").set({word:"プレゼンを開始します"});
    });
    app.handleRequest(actionMap);
});

Firebaseへの接続情報Firebaseの画面でウェブアプリに Firebase を追加をクリックして表示されるコードを使用します。

/** package.json **/
{
  "name": "dialogflowFirebaseFulfillment",
  "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "~6.0"
  },
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "dependencies": {
    "actions-on-google": "^1.5.x",
    "firebase": "^4.8.0",
    "firebase-admin": "^4.2.1",
    "firebase-functions": "^0.5.7",
    "apiai": "^4.0.3"
  }
}

設定後、DEPLOYをクリックしFirebaseスクリプトをデプロイします。

以上でDialogflowの設定は完了です。

Firebaseの設定

FirebaseDatabaseにデータ保存場所とアクセスルールを設定します。

データ保存場所の設定

Database > データから保存場所を作成します。今回は/googlehome/wordに作成します。
f:id:okana-yg:20171224151113j:plain
作成した保存場所には、PUTでデータを保存出来ます。

curl -X PUT \
  https://XXXXXX.firebaseio.com/googlehome/word.json \
  -H 'content-type: application/json' \
  -d '"保存キーワード"'

保存したデータはGETで取得できます。

curl -X GET \
  https://XXXXXX.firebaseio.com/googlehome/word.json \
  -H 'content-type: application/json'

アクセススールの設定

Database > ルールからDBへのアクセスルールを設定します。
今回は設定簡易化のため/googlehome配下のデータには認証無しでアクセスできるように設定しています。

{
  "rules": {
    "googlehome": {
      ".read": true,
      ".write": true
    },
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

以上でFirebaseの設定は完了です。

RaspberryPiの設定

以下の内容をスクリプト化し、RaspberryPiのNode.js上で実行します。

  • GoogleHomeへのメッセージ通知
  • ディスプレイへのスライド表示通知
  • FirebaseのDB更新通知の受信

※RaspberryPiに初期インストールされているNode.jsはバージョンが古いので、最新の安定バージョンに更新する必要があります。

GoogleHomeへのメッセージ通知

GoogleHome任意のメッセージをプッシュするNode.js用ライブラリが公開されているので、ありがたく使わせていただきます。
google-home-notifier

  • ライブラリのインストール
$ npm init
$ npm install google-home-notifier
  • 動作確認
/** testMessage.js **/
const googlehome = require("google-home-notifier");
const language = "ja";

// デバイス設定(Google-Homeで始まる全デバイスにメッセージが通知される。)
googlehome.device("Google-Home", language); 
// 通知するメッセージ
googlehome.notify("こんにちは。私はグーグルホームです。", function(res) {
    console.log(res);
});

※test.jsの文字コードはUTF8で作成する。

$ node testMessage.js
Device "Google-Home-XXXXXXXXXXXXXXXXXXXXXX" at 192.168.1.11:8009 <- 応答したGoogleHomeのデバイス名
Device notified

ディスプレイへのスライド表示通知

スライドの表示にはfbiというコマンドを使います。

  • コマンドのインストール
$ sudo apt-get install fbi
  • 動作確認
$ sudo fbi -nocomments -noverbose -a -T 1 -d {出力場所} {画像ファイルのパス}

※コンソール出力の場合は、{出力場所}に/dev/fb0を、HDMI出力の場合は/dev/fb1を設定します。

FirebaseのDB更新通知の受信

Firebaseのドキュメントを元に設定します。

  • ライブラリのインストール
$ npm install firebase
  • 動作確認
/** testFirebase.js **/

// Firebaseへの接続情報(Dialogflowの項目で設定したものと同じ)
var config = {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
    databaseURL: "XXXXXXXXXXXXXXXXXXXXXX",
    projectId: "XXXXXXXXXXXXXXXXXXXXXX",
    storageBucket: "XXXXXXXXXXXXXXXXXXXXXX",
    messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);

const path = "/googlehome";
const key = "word";
const db = firebase.database();
// 更新通知を受信した時の処理を記述
db.ref(path).on("value", function(changedSnapshot) {
    // 更新された値をログに表示
    console.log("取得キーワード:" + changedSnapshot.child(key).val());
});
$ node testFirebase.js
# Firebaseに「テスト」をPUTしてDBを更新する
取得キーワード:テスト

index.js作成

実行ファイルとしてindex.jsを作成します。

/** index.js **/

const firebase = require("firebase");
const exec = require("child_process").exec;
const googlehome = require("google-home-notifier");

// メッセージを通知するGoogleHomeの設定
const language = "ja";
const deviceName = "Google-Home-XXXXXXXXXXXXXXXXXXXXXX";
googlehome.device(deviceName, language); 

// Firebaseへの接続情報を設定
var config = {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
    databaseURL: "XXXXXXXXXXXXXXXXXXXXXX",
    projectId: "XXXXXXXXXXXXXXXXXXXXXX",
    storageBucket: "XXXXXXXXXXXXXXXXXXXXXX",
    messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);


// GoogleHomeへメッセージを通知
var notifyGoogleHome = function(word) {
    googlehome.notify(word, function(res) { console.log(res); });
};
var totalSpeakTIme = 0;
var speak = function(word, speakTime) {
    // 前回メッセージの終了後、メッセージを通知
    setTimeout(function() { notifyGoogleHome(word); }, totalSpeakTIme);
    // 今回メッセージの秒数分カウントアップ
    totalSpeakTIme += (speakTime * 1000);
}

// ディスプレイへスライドの表示を通知
var notifyDisplay = function(imgPath) {
    // fbiコマンドを使ってディスプレイに画像を表示
    exec("fbi -nocomments -noverbose -a -T 1 -1 -d /dev/fb0 " + imgPath, function(err, stdout, stderr){
        if (err) {
            console.log(err);
        }
    });
};
var totalDisplayTIme = 0;
var show = function(imgPath, speakTime) {
    // 前回スライドの終了後、表示を通知
    setTimeout(function() { notifyDisplay(imgPath); }, totalDisplayTIme);
    // 今回スライドの表示秒数分カウントアップ
    totalDisplayTIme += (speakTime * 1000);
}

// Firebaseの更新通知を受取った時の処理
const path = "/googlehome";
const key = "word";
const db = firebase.database();
db.ref(path).on("value", function(changedSnapshot) {
    // 更新された値を取得
    const value = changedSnapshot.child(key).val();
    if (value === "プレゼンを開始します") {  // 開始キーワードの場合はプレゼン開始
        // 登録された値をFirebaseから消しておく
        db.ref(path).set({[key]: ""});
        // プレゼン中は何もしない
        if (totalDisplayTIme > 0 || totalSpeakTIme > 0) {
            return;
        }

        // プレゼン開始メッセージをGoogleHomeに喋らせる
        speak("承知しました。" + value, 10);
        // 1枚目のスライドを表示する
        show("./img/001.jpg", 15);

        // 以下、プレゼン用スクリプトを記述
    }
});

ファイルが作成できたら、実行します。

$ sudo node index.js

fbiコマンドの実行時にroot権限が必要なのでsudo付きで実行します。

以上でRaspberryPiの設定は完了です。

実際にやってみる

設定完了後、スマホに対して「発表アプリにつないで」「プレゼン開始」と指示すると、index.jsに書かれた内容でGoogleHomeがプレゼンをしてくれます。

長いので冒頭部分だけですが、実際にこんな感じでGoogleHomeにプレゼンしてもらいました。


参考

Google Home開発入門 / google-home-notifier解説
Raspberry Pi でTFT液晶モジュールにいろいろ表示する


*1:Googleアシスタントと会話できるアプリ

開発をアジャイルに!スクラムトレーニング から始める最初の一歩

こんにちは。楽楽精算開発チームの堀内です。

先日、ryuzeeさんこと吉羽さんに社内でスクラムトレーニングを実施して頂きました。最初から最後までアジャイルで、全てが楽しく、得るものが多いトレーニングでした。
今日はその紹介です。

前置き

ラクスではプロダクト開発の改善活動の一部にアジャイルの考え方を取り入れて進めています。
実のところ、この改善活動の後ろで我々を支援してくれるのが吉羽さんです。
始まりとしては、プロダクトをもっと良くするにはどうするのが良いかと頭を抱えている頃に、チームメンバーの1人が「改善やチームビルディングに詳しい吉羽さん」に相談してみるのが良いのではないか、と言ってくれたことでした。
「なるほど!」と思い会社に掛け合ってみたところ、タイミングも良かったこともあり、すんなりとOKしてくれました。
早速吉羽さんに相談してみると「目的、ゴールを明確にして、アクションを決めたら一覧にして優先順位を決めて並び替えて、、、」とアドバイスを受けました。アクションを実施するためにチームを組んで少しずつ進めていると、良い感じで改善が進み始めました。そのことを吉羽さんに伝えると、「それがスクラムと言うんです」と。
それを言われて正直なところ「へぇ」という印象でした。スクラムというものが良くわかっていなかったんです。でも、その言葉をキッカケにスクラムアジャイル今でも色褪せることのない良書とされるものを読み漁り「なるほどっ!」と思うようになりました。
改善活動も持続的に続けることができており、今では開発チーム全体に改善の文化が根付いてきたように思います。
そんなアジャイルのやり方を開発以外の方々にも知ってもらいたい、そう思って企画したのが吉羽さんによるスクラムトレーニングです。

f:id:yhoriuchi:20171220134510j:plain
トレーニングの様子

トレーニングの流れ

実施して頂いたのは半日コースのトレーニングでした。 全体の流れは下記。

全体説明

トレーニング内容の全体を最初に説明して頂きましたが、中でも最も印象に残っているのは次の言葉でした。
「16時に終わります。終わることをコミットしますので、安心してください。16時から通常業務に戻れます。質問は常に受け付けます。好きなだけ聞いてください。ただし、質問が多ければ予定しているトレーニングの内容が最後までいかないことになります。」
捉え方によっては、最後までトレーニングの内容を受けたければ質問はほどほどに、とも思えますが、アウトプットは途中の状況に応じて変わるということを示してくれていたと感じます。アジャイルで言う「スコープの調整」ということなんだろうなと。
つまり、16時に終わることをコミット(タイムボックスを固定)し、トレーニングの内容(スコープ)は質問の量(状況)に応じて調整する、という事なんだなと。

スクラムの概要説明

これは教科書的というか、ryuzee.comのブログに書かれているような内容で、馴染みのない人にも分かりやすく説明して頂きました。

紙飛行機を使ってスクラムを体験

今回の醍醐味とも言うべき、ゲームを通じてのスクラム体験です。
5〜6人に分かれて、チームで飛ばした紙飛行機の数を競い合うもので、以下のように進めました。 

  1. 見積もり、計画、紙飛行機製作、振り返りを1セットとして4回繰り返す(つまり4スプリント実施)
  2. 紙飛行機を作る上でいくつかのルールが決められている
  3. 決められた距離以上飛ばして始めて成果となる
  4. 最後に全体の振り返り

f:id:yhoriuchi:20171220142527j:plain

各スプリントが始まる前に、何機製作できそうか、各チームが目標となる数字を公表します。
1回目は大体の見積もりは当たっていませんでした。
2回目の見積もり精度はかなり改善されました。
やってみないと分からなかったことが、1度実施して振り返ることで見積もりも製作も上手くなり、改善したということなんだと思います。
3回目は紙飛行機を製作する上で決められていたルールを1つだけ取り除くことができます。これはどのチームも同じルールを外しました。そのルールがボトルネックとなる「制約」と全員が考えていたんですね。3回目はどのチームも良い数字を叩き出します。
4回目は3回目に外した制約がもと通りになり、1、2回目と同じ制約のもとで紙飛行機を製作します。
途中で発生するルール変更が開発で言うところの「ビジネスの状況変化」を表すわけなんですが、ルールを変えることで成果も変わることが体感できました。
制約を外すことで成果が変わることが体感できたことは良かったのですが、それ以上に重要なことは「ルールに書かれていないことは禁止していない」と言うことでした。
例えば、製作に必要なハサミは1つだけというルールはなく、複数必要なら予備があるので使っても良かった、他のチームの紙飛行機を分析してはいけないルールは無い、などです。他にもいくつかあったのですが、決められたルールに縛られると新たな考えを「発想」する事が出来なくなるんだと気づく事ができました。
実際の開発の現場に置き換えるなら、ボトルネックと認識されている「制約」を外すことも重要ですが、「制約」になっていないことに目を向けて工夫することも重要、ということになります。

f:id:yhoriuchi:20171220134517j:plain
チームと結果

最後に

トレーニングは予定通り16時に終わりました。全体を通じて要所要所で質問もありました。紙飛行機のワークショップでは時間が若干押しているようでした。でもきっちり16時に終わりました。
トレーニング内容のスコープが吉羽さんにより調整されたのか、予め想定されていたバッファに収まったのかは分かりません。
ただ、私たちの学びや満足が成果だとすると、私たちは間違いなく成果を得ることができましたし、予定通り16時から通常業務に戻ることができたため、全て計画通りでした。
アジャイルの原理原則を全体を通じて体感できる有意義なトレーニングでした。
 

その後

後日、参加者にアンケートを取らせて頂いたところ、皆が満足感を得られていたことが改めてわかりました。
また、開発以外の方々とスクラムの考えを共通認識として持つことができたため、ちょっとした会話の中でも「価値」「サイズを小さくする」といったキーワードが聞こえ始めています。
今では改善活動だけでなく、開発をアジャイルで始めることができるようになりました。まだまだ始まったばかりですが、大きな一歩を踏み出しています。

募集

お陰様で順調に伸び続けているラクスは、アジャイルを取り込むことで更に改善し、飛躍していきます。
この変化の真っ只中で成長を実感されたい方、ぜひ一緒に働きましょう!楽しさとやりがいは保証します!

中小企業向けクラウドサービス(ASP) | 株式会社ラクス
f:id:yhoriuchi:20171220144108p:plain

Google Chromeの旧バージョンをインストールする手順

はじめに

こんにちは。ラクスのstrongWhiteです。 今回はGoogle Chrome (以下、Chrome) の旧バージョンをインストールする手順について書きます。

ここ最近、会社でSeleniumを動かす機会があり、そのときにChromeの旧バージョンが必要になったので、インストールする手順をいろいろ調べていました。 これから調べたインストール手順を書いていきますが、私自身、実際に手順を試していません。 なぜ試さなかったかというと、これから書く手順はレジストリに手を加える必要があり、PCに重大な不具合を引き起こす可能性があるからです。上記を承知した上で試したい方は以下を読み進めてください。

旧バージョンは公式にはない

まず前提として、Chromeの旧バージョンは公式では提供されていません。 Chromeは、セキュリティアップデートなどで最新のバージョンが利用可能になったときに自動更新できる仕組みになっており、常に最新のバージョンを保てるようになっているからです。旧バージョンを利用すること自体、想定されていないということですね。 そのため、旧バージョンをインストールするには有志が公開している非公式なサイトからインストーラを入手する必要があります。

ただインストールするだけじゃダメ

前述の通りChromeは自動更新する仕組みになっているので、ただインストールするだけではダメです。自動更新を停止させる設定を行わなければいけません。 自動更新の停止設定はChrome側からは行えず、ターミナルなどで外部から設定しないとけません。 Windowsだとレジストリを変更する必要があり、Macだとdefaultsコマンドを実行しなくてはいけません。 レジストリは不用意に変更するとPCに重大な不具合が起こる可能性があります。ひとつひとつの操作が理解できた上で慎重に行うことを強く強くオススメします。

自動更新を復活させる方法

自動更新は停止させたあとでも以下の方法で復活させることができます。 Windowsの場合はレジストリエディタからHKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Updateに、DWORD値でDisableAutoUpdateChecksCheckboxValueという値を作成し、0に設定すればOK。 Macの場合はdefaults write com.google.Keystone.Agent checkInterval 1を実行すればOKです。 これらはGoogle Update 関連のタスクを有効にする設定です。

インストールの手順

これまでのセクションを読んで察することができるかもしれませんが、Chromeの旧バージョンをインストールする手順は公式がサポートしている手順ではないのでご自身の責任でお試しください。 以下、調べた手順。

Windows手順

  1. すでにChromeがインストールされている場合はアンインストールする
  2. 旧バージョンのインストーラを取得する
  3. ネットワーク接続を切断する
  4. インストールする
  5. レジストリエディタからHKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Updateに、DWORD値でDisableAutoUpdateChecksCheckboxValueという値を作成し、1に設定する
  6. ネットワーク接続を有効にする

Mac手順

  1. 1~4までは上と同じ
  2. ターミナルを起動し、defaults write com.google.Keystone.Agent checkInterval 0 と実行する
  3. ネットワーク接続を有効にする

※手順を補足すると、インストール前にネットワーク接続を切断しているのは、インストール後にChromeが自動更新しないようにするためです。その他、Windows手順の5とMac手順の2では、Google Update 関連のタスクを無効にする設定を行っています。

おわりに

今回は Chromeの旧バージョンをインストールする手順を書きました。 冒頭でなぜ実際に手順を試していないと書いたのか、ここまで読んでいただいた方は意味がわかったかと思いますが、公式でサポートされている手順ではなく、かつPCが壊れる危険性を孕んでいますので、何度も言いますが試す際は自己責任でお願いします(汗)

参考

社内勉強会の運営で実践してみた12+1のアイデア

id:radiocat です。大阪本店で社内勉強会の運営をしています。 今回は「IT勉強会/コミュニティ運営 Advent Calendar 2017」の21日目の記事として投稿します。

qiita.com

社内の勉強会イベント

大阪では大きく2つの勉強会イベントが開催されています。

ビアバッシュ

大阪の開発部で毎月実施している勉強会です。参加自由ですが会社予算で飲み物と軽食を出してもらっています。私は主にこの運営に関わっています。先日はクリスマス版で実施しました。

f:id:radiocat:20171220020316p:plain:w600

もくもく勉強会

有志の呼びかけで毎週木曜日に実施する勉強会です。月末の木曜日はLT形式で発表会をします。特に予算や制約はなく、やりたいメンバーが集まってゆるくやっています。こちらは運営メンバーが以前このブログに投稿していますので詳細はそちらをご覧ください。

tech-blog.rakus.co.jp

これらの運営に関わって実際にやってみた事についてまとめてみました。なお、やってみて思ったほどうまくいかなかった事も知見を得たという点では価値があると思うので惜しみなくご紹介します。


コンテンツ編

テーマを決める

ビアバッシュでは毎月テーマを決めて発表者を募集しています。同じ開発部のメンバー同士とはいえチームによって文化が違ったり、使っている技術が違っていたりするため、ある程度テーマを絞ったほうが発表者が発表しやすく参加者も参加しやすいだろうと考えて運営側でテーマを決めています。ただし、あくまで参加しやすい場を作る事が目的なので、多少テーマから外れた発表であっても指摘することはありません。

過去に実施したテーマをいくつかご紹介します。

テーマ 概要
各サービスのお仕事発表会 各サービスで取り組んでいる課題やチャレンジしていることを共有してもらいました。
各サービスのトラブル特集 その名の通りトラブル事例の共有ですが、ビアバッシュでやってみると業務報告とは違った本音や苦労話が聞けました。
読書特集 技術書の紹介や効率よく読むための工夫などの読書術について共有してもらいました。
QAセッション大会 他のサービスの担当者に聴いてみたいことを事前に募集して回答してもらいました。
爆LT大会 時間の許す限りみんなでLTをやります。

自己紹介LT

オフショア先のベトナム子会社からの出張者や中途入社、新卒の配属などで新しいメンバーが参加する場合、LT形式の自己紹介をしてもらっています。本当にただプレゼン資料を使って自己紹介するだけです。入社していきなりLTするのは敷居が高いですが、自己紹介ならば敷居は高くないと考えてお願いしています。新メンバー全員に必ずお願いする事も重要であったりします。結果的に新しいメンバーの事を知る良い機会になっています。次回以降で発表してもらう時の心理的敷居が下がることも密かに期待しています。

f:id:radiocat:20170117192432j:plain:w500

開発外の部署の人にもプレゼンしてもらう

たまには開発部外の発表も聴いてみたいと考えて募集したところ、営業、人事、総務、経理、情シス、インフラチームといった他部門の方々からたくさん応募いただくビックイベントになりました。二部構成にして第二部は開発部のLTも行ったところ、開発部外の人も開発部の発表が聞けて新鮮だったとコメントをもらえました。ただ、ここまで大々的にやるといつもの会場に人数が収まりきらないなど、運営側はそれなりに大変です。

f:id:radiocat:20171220203253j:plain:w500

ハッカソンしてみる

ビアバッシュの特別回としてハッカソンもやってみました。ビアバッシュ2回分の枠を使い、1回目にアイデアソンとチーム分けを行い、次の回までにハッカソンして発表してもらいました。私も参加して同僚とチームを組んでIoT的な鳩時計を会議室で鳴らす仕組みを作ってみたりしました。

qiita.com

普段の仕事とは違った開発にチャレンジできて刺激的なイベントですが、参加者も運営側もビアバッシュよりは時間を使うことになるのでそれなりの覚悟で臨む必要はあります。ただ、この取り組みが評価されて社内で表彰していただきました。

f:id:radiocat:20160707195051j:plain:w500

質問タイムを削る

これは結果的にバッドノウハウでした。色々アイデアを考えて実行すると思った以上に発表者が集まったり内容が濃くなりすぎて全体の時間が長くなりすぎることがあります。そこで質問時間を削るという苦渋の決断をしました。結果的に時間は短縮できましたが、後ほどアンケートを取ったところ反応はあまり良くなかったです。質問タイムを設けても質問が出ない事はありますが、質問タイムが無いとその場の雰囲気としてはやはり物足りないのだと思います。質問タイムは無くすのではなく時間を区切るなどで場をうまくコントロールするほうが効果的です。

会場セッティング編

会場を社外にしてみる

開発部では四半期に一度、部全体で懇親会を実施しています。その機会を利用させてもらい、懇親会の会場でビアバッシュを行いました。今年の7月にはカフェラボというグランフロント大阪内の施設を借りて実施しました。

kc-i.jp

また、昨年は結婚式の二次会などで使う水槽のあるオシャレなお店を借りて、前出のハッカソンイベントの発表会を実施しました。

f:id:radiocat:20171220004306p:plain:w300

自社のカフェスペースよりも巨大なスクリーンでマイクも使ったりなど、ちょっとしたビックイベント感が演出できました。

課題としては準備の過程でネット環境が無かったりプロジェクタがDVIしか対応していないなどが分かって事前の調整が普段以上に大変でした。部の懇親会の場を使わせてもらうので、懇親したい人もいる事を考えると懇親会の全ての時間をプレゼン発表に割り当てるのも目的が変わってしまうため、タイムスケジュールの調整も難しいところはあります。

サテライト会場で同時中継する

前出の開発部外からも参加者を招いてビアバッシュを実施したところ、人数が集まり過ぎてしまい会場に入り切らないという問題が発生しました。その対策として社内の会議室をサテライト会場にしてGoogleハングアウトを使って同時放映する方式を試してみました。しかし、映像が途切れたり中継の声が聞き取れなくて隣の本会場から笑い声だけが直接聞こえてくるなど、残念ながらこの試みは不評でした。

f:id:radiocat:20171220191825j:plain:w600

ただ、ベトナム出張中のメンバーからの要望で同じ仕組みを使ってベトナムオフィスと中継した時は大きな問題はなく、最低限の内容はベトナムに伝わって楽しめているようでした。明らかに現場で見ることができないと分かったうえでの中継であれば、参加者もその前提で見るため多少の映像や音声の途切れなどは気にならないのかもしれません。

食べ物や飲み物を工夫してみる

ビアバッシュの定番といえばビールとピザですが、毎回ピザも飽きるんじゃないかという仮説のもと、色々な食べ物にチャレンジしてみました。勉強会なのに食べ物にうるさいあたり、大阪っぽさが滲み出ていますがお察しください。

注意点といては予算が限られているのと、配達してもらえるか会社の近くで受け取りができる必要があり、選択肢は限られます。

実際に手配してみた食べ物を一部紹介します。

マクドナルド

f:id:radiocat:20171220003654p:plain:w300

ひとりあたり100円台のバーガー各種3つとチキンを手配しましたが若手はともかく中堅以上にはややキツかったようです。あと、一定金額以上になると配達の注文手続きがかなりやっかいなのが運営泣かせです。

たこ焼き

f:id:radiocat:20171220003841p:plain:w500

会社の近くで受け取りできるお店がソース無しタイプの会津屋のたこ焼きだったので、ピザを離れるにしても一足飛びにチャレンジしすぎた感はありました。

お弁当

f:id:radiocat:20171220205206p:plain:w500

事前に人数を確定させて人数分のお弁当を手配しました。ハンバーグ弁当、焼肉弁当などの注文も1人ずつ受付ました。ドタキャンや飛び入り参加に対応できない難しさはあります。

スパークリングワイン

f:id:radiocat:20171220205800p:plain:w500

12月の回ではビールの代わりにスパークリングワインを出してクリスマスを演出してみました。過去に社内で営業目標の達成イベントが行われた時に使ったシャンパングラスを発掘してきて活用しました。

以上のように食べ物・飲み物も色々チャレンジしてみましたが、アンケートを取ってみたところ何だかんだピザの評判が良くて結局のところピザで充分という結論になっています。

f:id:radiocat:20171220193943p:plain:w500

ツール編

スマホ三脚

f:id:radiocat:20171220192104j:plain:w300

前出のGoogleハングアウトを使った同時中継で使用しました。この三脚にスマホを固定してGoogleハングアウトアプリを使うだけなので、撮影の手間がかからず高価なテレビ会議システムなどを使わずに海外にも同時中継ができる点は運営の強い味方です。

Chromecast

Chromecastを購入してプロジェクタのHDMI端子につないでPC側はコードレスでプレゼンできるようにしました。ChromeがインストールされたPCなら画面をそのままキャストできるのはもちろん、Android端末であれば手持ちのスマホだけでプレゼンができて便利です。注意点は弊社のネット環境は社内向け環境と自由に外部接続出来る環境が分離されており、Chromecastは後者の環境に接続するため社内環境のデモなどにChromecastを使うことができません。お金をかければもっと高度な実現方法はあると思いますが、手軽に利便性を上げる事が出来る点ではこれも運営の強い味方です。

掲示板と立て看板

ビアバッシュは開発部内のイベントとして実施するため事前に出欠をとりますが、もくもく勉強会・発表会はその日参加できる人だけでゆるく実施しているので事前に出欠はとっていません。自由でゆるくやれるメリットはありますが、参加したいと思っていてもイベント自体を忘れていて参加できなかったという事が起こります。そのためカフェスペースの掲示板に予定を貼り出したり、開催当日に立て看板を設置して周知するようにしました。アナログな手法ですが、以前よりもイベントの存在に気づいて社内で声をかけてもらったりするケースが増えており、意外と効果が出ているように感じています。

f:id:radiocat:20171220004819p:plain:w500

f:id:radiocat:20171220004739p:plain:w500

スライド共有

プレゼンで使った資料は共有フォルダに置いて参加できなかった人にも共有していますが、ファイルを置いてパスを共有してもなかなか見ないのが人情です。SlideShareなどのパブリックなサービスを使えばスライド共有できますが、内輪だけで楽しみたいケースもあるため全部をパブリックにしてしまうと逆に発表者のネタを縛ってしまう可能性があります。このハードルが意外と大きいのではないかという仮説のもと、社内にスライド共有の仕組みを導入しました。

f:id:radiocat:20171220195647p:plain

現在はまだ試験運用中ですが、KnowledgeというOSSの情報共有ツールを使ってみています。

information-knowledge.support-project.org

おまけ

クリスマスを楽しむ

昨年の12月のもくもく発表会ではクリスマス版としてLT後に参加者同士でプレゼント交換をしました。今年もちょうどこの記事を投稿している当日に実施する予定です。ちなみに去年はプレゼント交換しようとした時に「自分の持ってきたプレゼントが自分に回ってこないように、全員にランダムにプレゼントを割り当てるプログラムってどう書くの?」という話題になりLTで使ったプロジェクタがそのまま使われて即席でモブプロが始まるというハプニングもありました。アドベントカレンダーといえば今やクリスマスまでの期間を楽しむエンジニアのイベントの1つですが、プレゼント交換は弊社大阪のエンジニアにとってのもう1つのクリスマスイベントとして定着していきそうです。

f:id:radiocat:20161215210659j:plain:w500


おわりに

大阪開発部では楽しみながらエンジニアのスキルやノウハウをあげていけるように、運営メンバー同士で様々なアイデアを出し合って取り組んでいます。まだまだ色々なアイデアを試してみたいと思っていますが、社外の方との共同開催やゲスト参加なども大歓迎ですので、このブログを見て一緒に勉強会をやってみたいと感じた方がいらっしゃいましたら、是非 id:radiocat までご連絡ください。

yum installで学ぶ! yum の仕組み

はじめに

エンジニアのnorthmkyです。 ラクスに新卒で入社し、今年で2年目になります。
業務ではシステムの運用保守チームに所属しているので断然サーバ周りの作業が多いということで今回はその中でもyumコマンドについてまとめてみました。

おそらくこれを読めば

  • yumrpmとの違いって?とりあえずyum使ってるけど...
  • yum install [package]って打つだけでなんでDL&Installできるの?

という疑問は少し解消されるかと思います。
まだまだ浅い知識なので間違っているところがあれば指摘いただけると嬉しいです。

yumとは

まずものすごく大雑把にいいますとyumは下記になります。

  1. パッケージ管理システム
  2. rpmというパッケージ管理システムをラッパーしている
    • yum = rpm + 「リポジトリ」による自動更新機能 + 依存関係の管理(検出だけじゃない)

yumRedHatディストリビューションのパッケージ管理システムです。
パッケージというのは「ソフトウェアを構成するファイルらをまとめたもの」で、設定ファイルやドキュメント、プログラム本体、プログラム本体が動くためのライブラリなどがそれにあたります。1

Linuxでは「パッケージ読み解き、ソフトウェアを正しく動作させるようにする」「パッケージの情報を管理する」ことでソフトウェアを使える/管理する仕組みを採用していて、今回取り上げるyumLinuxディストリビューションのなかでRedHat系のパッケージ管理システムとなります。

このyumですが実際裏側ではrpmというパッケージ管理システムを使っています。
rpmではできなかった点をyumができるようにしている」という立ち位置になっています。

この出来なかった点が冒頭に述べた

  1. リポジトリ」による自動更新機能
  2. 依存関係の管理(検出だけじゃない)

です。

リポジトリとは

意味のあるまとまりでパッケージを複数格納している場所のことです。
この場所には下記が格納されています。

  • 複数のパッケージ(./*.rpm)
  • そのリポジトリのメタ情報ら(./repodata)

パッケージが複数存在してもその場所はリポジトリとは言えません。メタ情報があってリポジトリとなります。2
yumではこのリポジトリの情報を元にパッケージを検索したり、installを行います。

yum/rpm/リポジトリの関係性

yumrpmリポジトリの配置場所との関係性は下記のようになっています。

f:id:northmky:20171213093542j:plain

yum/etc/yum.repos.d/*.repoに記載されているリポジトリ場所にあるパッケージらに対してrpm経由でDLしたり、インストールしたり、必要であればインストール時に依存関係のあるパッケージを検出して一緒にインストールしたりします。
逆をいえば、/etc/yum.repos.d/*.repo以外のレポジトリは情報がないのでyumは取得することができませんので注意してください。

では、実際によく打つコマンドを例にしてどのようになっているか見てみます。

yum installを見てみる

「このコマンド(ソフトウェア)、インストールしてないからインストールしよう」というのはよくあると思うので
今回はyum isntall [package]を通してどんなことが行われているかを見ていきたいと思います。

f:id:northmky:20171213100704j:plain

大枠の流れは上記のようになっています。

今回はphpをインストールしてみたいと思います。 (OS:CentOS6)

[root@localhost ~]# yum install php

こちらを実行すると、下記のようによく見るインストールしますか?という対話モードになります。 この中身を見ていきましょう。

[root@localhost ~]# yum install php
読み込んだプラグイン:fastestmirror, security
インストール処理の設定をしています
Loading mirror speeds from cached hostfile /* 1.ミラーサイトの中で一番地理的に近いミラーサーバを各レポジトリごとに探す */
 * base: ftp.jaist.ac.jp
 * epel: ftp.jaist.ac.jp
 * extras: ftp.jaist.ac.jp
 * remi-safe: mirrors.mediatemple.net
 * updates: ftp.jaist.ac.jp
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> Package php.x86_64 0:5.3.3-49.el6 will be インストール
--> 依存性の処理をしています: php-common(x86-64) = 5.3.3-49.el6 のパッケージ: php-5.3.3-49.el6.x86_64
--> 依存性の処理をしています: php-cli(x86-64) = 5.3.3-49.el6 のパッケージ: php-5.3.3-49.el6.x86_64
--> トランザクションの確認を実行しています。
---> Package php-cli.x86_64 0:5.3.3-49.el6 will be インストール
---> Package php-common.x86_64 0:5.3.3-49.el6 will be インストール
--> 依存性解決を終了しました。

依存性を解決しました  /* 2.必要なパッケージ情報を取得する */
=============================================================================================================================
 パッケージ                     アーキテクチャ             バージョン                         リポジトリー              容量
=============================================================================================================================
インストールしています:
 php                            x86_64                     5.3.3-49.el6                       base                     1.1 M
依存性関連でのインストールをします。:
 php-cli                        x86_64                     5.3.3-49.el6                       base                     2.2 M
 php-common                     x86_64                     5.3.3-49.el6                       base                     530 k

トランザクションの要約 
=============================================================================================================================
インストール         3 パッケージ

総ダウンロード容量: 3.8 M
インストール済み容量: 13 M
これでいいですか? [y/N]

1. ミラーサイトの中で一番地理的に近いミラーサーバを各レポジトリごとに探す

結果としてリポジトリの場所(URL)のほとんどが.jpドメインつきなため、一番地理的に近いミラーサイトをレスポンスを計測して選んだというのがわかります。

これはyumが、前述した/etc/yum.repos.d/*.repoリポジトリの情報からインストールするリポジトリの場所を決定しています。ではphpのパッケージが存在する 「base」というリポジトリの情報が記載されているCentOS-Base.repoを見てみます。

CentOS-Base.repo
  1 # CentOS-Base.repo
  2 #
  3 # The mirror system uses the connecting IP address of the client and the
  4 # update status of each mirror to pick mirrors that are updated to and
  5 # geographically close to the client.  You should use this for CentOS updates
  6 # unless you are manually picking other mirrors.
  7 #
  8 # If the mirrorlist= does not work for you, as a fall back you can try the 
  9 # remarked out baseurl= line instead.
 10 #
 11 #
 12 
 13 [base] /* 1.リポジトリ名 */
 14 name=CentOS-$releasever - Base
 15 mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra /* 2.ミラーサイトリスト */
 16 #baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
 17 gpgcheck=1
 18 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
 19 
 20 #released updates 
 21 [updates]
 22 name=CentOS-$releasever - Updates
 23 mirrorlist=http://mirrorlist.centos.org/?
 ...
 ...

*.repoの中には基本的にはリポジトリ情報が複数記載されています。
mirrorlistに設定されているURLを実際に叩くと、

[root@localhost ~]# curl 'http://mirrorlist.centos.org/?release=6&arch=x86_64&repo=os&infra='
http://ftp.yz.yamagata-u.ac.jp/pub/linux/centos/6.9/os/x86_64/
http://ftp.iij.ad.jp/pub/linux/centos/6.9/os/x86_64/
http://mirror.fairway.ne.jp/centos/6.9/os/x86_64/
http://ftp.tsukuba.wide.ad.jp/Linux/centos/6.9/os/x86_64/
http://ftp.riken.jp/Linux/centos/6.9/os/x86_64/
http://ftp.nara.wide.ad.jp/pub/Linux/centos/6.9/os/x86_64/
http://ftp.jaist.ac.jp/pub/Linux/CentOS/6.9/os/x86_64/
http://www.ftp.ne.jp/Linux/packages/CentOS/6.9/os/x86_64/
http://mirror.0x.sg/centos/6.9/os/x86_64/
http://mirror.nus.edu.sg/centos/6.9/os/x86_64/
[root@localhost ~]

ミラーサイトのリストが返ってきます。
このリストからyumは一番近い場所を選定して、リポジトリ内のパッケージを取得する、ということをしているようです。 今回はftp.jaist.ac.jpでした。

2. 必要なパッケージ情報を取得する

phpパッケージを対象にしただけですが、実際には依存してる他パッケージもインストールの準備をしています。 パッケージが依存しているソフトウェアは、パッケージ(.rpm)のSPECファイルというパッケージのメタ情報(ソースバージョンなど)を記載したファイルに記載してあります3 この情報をRPMデータベース(/var/lib/rpm)4と呼ばれるパッケージの情報を保存しておくDBに登録し、install前に検索をかけ、依存関係の有無を見ています。

このような処理が走ったあとでDL&installが行われます。

おわりに

yumについてまとめてみました。
yumとはに記載した

  1. パッケージ管理システム
  2. rpmというパッケージ管理システムをラッパーしている

が伝えることができていれば幸いです。

ネットではチートシートや「rpmよりyumをまずは使うべし」などのハウツーの記事は多いのですが、
改めてどういった仕組み、処理でインストールが行われているかの記事はあまり見なかったので試行錯誤しながら執筆しました。
少しでも知識の助けになれば幸いです。(そして私自身の正しい知識の定着のためにもご指摘頂けると非常に助かります...!!)

GoogleHomeアプリを開発しよう!Dialogflowチュートリアル

こんにちは、 west-c です。

スマートスピーカー、国内でも各社から販売され盛り上がっていますね。 私も弊社開発チーム内での GoogleHome モニター選考に当選し*1、自宅で使い方を模索中です。

GoogleHome(厳密には GoogleHome 内蔵のGoogleアシスタント)は、ユーザが自由にアプリを開発したり公開したりすることができます。 簡単な対話アプリであればノンプログラミングで作成することも可能です。 今回は、自分で作成したスクリプトと組み合わせたアプリの作成手順を説明したいと思います。

作成するアプリの説明

今回は「指定した路線の遅延有無を教えてくれるアプリ」を題材に説明します。以下のような会話が行えることを目標としましょう。

ユーザ:「OK Google, XX線の遅延情報を教えて」
GoogleHome:
(遅延が発生している場合)「XX線で遅延発生中です。」
(遅延が発生していない場合)「現在XX線では遅延は発生していません。」

このアプリの流れを図にすると以下のようになります。 f:id:west-c:20171128171848p:plain

図内に登場する単語を簡単に説明します。

  • Action on GoogleGoogle アシスタント対応のアプリ開発を行えるプラットフォームです。Action on Googleで作成するアプリの単位をプロジェクトと呼びます。
  • Dialogflow:自然言語対話のプラットフォームです。Dialogflowを使うことで、GUIで簡単に会話形アプリを作成することができます。Dialogflowで作成するアプリの単位をエージェントと呼びます。
  • 遅延情報取得プログラム:今回作成するスクリプトです。遅延情報の取得処理はこのスクリプトで行います。Dialogflow 上でFulfillmentとして設定することで Dialogflow と連携することができます。

以下から詳細な手順を説明します。

Action on Googleでプロジェクトを作成する

GoogleHomeと連携しているGoogleアカウントでAction on Googleのコンソールにアクセスし、Add/import project から新しいプロジェクト TrainDelayInfo を作成しましょう。
作成したプロジェクトをクリックし、ADD ACTIONSから Dialogflow のBUILDを選択します。 f:id:west-c:20171127160038p:plain

CREATE ACTIONS ON DIALOGFLOWをクリックすると、Dialogflowのコンソールに遷移します。

Dialogflowでエージェントを作成する

ここからDialogflow上での設定になります。 コンソールには以下のような画面が表示されるので、CREATEをクリックします。 f:id:west-c:20171127161851p:plain

今回 Dialogflow で設定することは大きく以下の3点です。 順番に設定していきましょう。

  • Entitiesの作成
  • Intentsの作成
  • Fulfillmentの実装

Entitiesを作成する

Entitiesでは、ユーザからの入力として受け付ける単語を定義します。例えば、Dialogflowで提供しているEntityに@sys.colorがありますが、これは色名の単語(赤色・青色・黄色など)が定義されています。 「赤」という入力を「赤色」と判断するなど、単語の揺れを補正することも可能です。

今回は電車の路線名を入力として受け取る必要があるため、路線名を定義する @train-route というEntityを新しく作成します。 Entityの作成は Entities> CREATE ENTITYから行います。

f:id:west-c:20171127170046p:plain

左側(reference value)には「パラメータの単語」を、右側(synonym)には「その単語の同義語」を入力します。 日本語の場合、ひらがなで認識される可能性もあるため synonym にひらがなも入力すると良いでしょう。 上記の場合、ユーザが「おだきゅうせん」や「小田急」と発言しても、「小田急線」として認識されます。

作成が完了したらSAVEで保存しましょう。

Intentsを作成する

Intentsではユーザの会話によってどの処理を実行するのかを定義します。

今回は「路線名の遅延情報を教えて」という会話に反応するIntentを作成するため、Intents > Default Welcome Intentで以下のように編集しました。

f:id:west-c:20171127172330p:plain

  • User says:このIntentとして認識される例文を入力します。追加で「路線名は遅延している?」という呼びかけでも認識するようにしました。黄色背景部分(路線名)はパラメータとなる部分です。
  • Action:上のテキストボックスにはアクション名を指定します。後述のFulfillmentの実装で利用します。2つ目のテーブルではパラメータを指定します。
    • REQUIRED:必須有無を選択できます。必須の場合、このパラメータが入力されないと次の Intent には遷移しません。
    • PARAMETER NAME:パラメータ名です。Fulfillmentにてパラメータを識別するために利用します。
    • ENTITY:このパラメータのEntityです。今回は先ほど作成したEntityの@train-routeを指定しています。
    • IS LIST:複数の値を受け取る場合に指定します。
    • PROMPTS:パラメータの入力を促す文章を定義します。REQUIREDにチェックしたパラメータについて、ユーザからの入力が無かった場合やEntityに存在しない単語を入力された場合に使われます。
  • Response:このIntentでのユーザへの返答を定義します。今回はFulfillmentを使って返答するため、デフォルトで設定されているものは削除します。

作成が完了したらSAVEで保存しましょう。

Fulfillmentを実装する

IntentとEntityを使って路線名は取得できましたので、いよいよFulfillmentで遅延情報を確認するスクリプトを書いていきましょう。

今回は、Fulfillmentの実装に Firebase というサービスを利用します。 DialogflowはFirebaseと連携しているため、Dialogflow上でFulfillmentの実装・デプロイをすることができます。

Fulfillment > Inline Editor > ENABLED で表示されるエディタにスクリプトを記述しましょう。 f:id:west-c:20171127175826p:plain

今回実装したコードは以下のようなものです。 遅延情報を取得する処理は割愛しますが、API等を利用するなどして遅延有無をisDelayフラグにセットする想定です。

'use strict';

process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').DialogflowApp;
const functions = require('firebase-functions');

// Intentのアクション名を指定
const NAME_ACTION = 'confirm_delay';

// パラメータのPARAMETER NAMEを指定
const ROUTE_ARGUMENT = 'train-route';

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
    const app = new App({request, response});

    function confirmDelay (app) {
        // 路線名を取得する
        let route = app.getArgument(ROUTE_ARGUMENT);

        let isDelay = false;

        /* 指定した路線の電車遅延情報を取得する処理をここに書く */

        if (isDelay) {
            app.tell(route + 'で遅延発生中です。');
        } else {            
            app.tell('現在' + route + 'では遅延は発生していません。');
        }
    }
    let actionMap = new Map();
    actionMap.set(NAME_ACTION, confirmDelay);

    app.handleRequest(actionMap);
});

下部のDEPLOYボタンを押せばFirebaseへのデプロイが完了します。

IntentからFulfillmentを呼び出すためには、Intents > Default Welcome Intent > Fulfillment > Use webhook にチェックを付けてください。 f:id:west-c:20171127180755p:plain

動作確認をする

これで設定は概ね完了したので、動作確認をしてみます。 右側のテキストエリアに会話文を入力することで、Dialogflowでの動作確認を行えます。 早速「おだきゅうの遅延情報を教えて」と入力してみましょう。

f:id:west-c:20171127181406p:plain

USER SAYSにはユーザが入力した文章が表示されます。 作成したEntity@train-routeにて、「おだきゅう」は「小田急線」の同義語と定義したため、パラメータtrain-routeには小田急線がセットされています。 このパラメータがセットされた状態でFulfillmentが呼び出され、レスポンスとして「現在小田急線では遅延は発生していません」という文章が返ってきました。

GoogleHome実機でのテストは、Integrations > Google Assistant > TESTGoogleアシスタントと連携することで実施できます。

おわりに

以上でGoogleHome上で動作するアプリを作成することができました。 Dialogflow自体はSlackやTwitterとも連携しているため、今回説明した内容を応用すればアプリ開発Bot開発の幅が広がりそうですね。 ほぼすべての設定をWeb上で実施でき敷居も低いと思いますので、皆さんもぜひお試しください。

お知らせ

ラクスでは去年に引き続き、ラクス Advent Calendar 2017を実施いたします! Qiitaにカレンダーを公開していますのでお楽しみに。 qiita.com

参考

*1:選考方法:じゃんけん

Copyright © RAKUS Co., Ltd. All rights reserved.