GrafanaでZabbix/CloudWatchのメトリクス監視を一元化する

f:id:cluex-developers:20170213113941p:plain

Grafanaかっこいいですよね。いつまでも見ていられそうです。

今回は統合監視ツールZabbixとAWSのCloudWatchメトリクス群を Grafanaで一元的に可視化して見れるようにしてみます。

今回の背景

CloudWatchやEC2のメトリクスを見るのに、 AWSにコンソールで入っていちいち確認するの面倒じゃないですか。 加えてサービスごとにAWSアカウントが分かれているとなると更に不便じゃないですか。

・よりリアルタイムなデータを見たいものはZabbixのメトリクスを使用したい

・ELBにアタッチされてるホストの数も見たい

・オートスケーリングどんな感じか見たい

インスタンス毎のメトリクスも見たい

・ついでにサービス全体でどれくらいの負荷がかかってるかも見たい

・なんなら複数のAWSのアカウントを跨いで見れると尚良い

 

・・・ということで全部grafanaに突っ込みました。

 

構成

今回のメインはzabbix-server、Grafana及びCloudWatchあたりになります。

構成はこんな感じです。

f:id:cluex-developers:20170213111236p:plain

Grafanaは標準でCloudWatchをサポートしていて、 Zabbixもプラグインを入れることでデータソースとして使用することが可能です。

ちなみにGrafanaで標準サポートされているデータソースがこちら

・CloudWatch

・Elasticsearch

・Graphite

・InfluxDB

・OpenTSDB

・Prometheus

Zabbixも標準でサポートしてくれると嬉しいです。

grafanaをインストールする

インストールはOSはUbuntuベースで話を進めていきます。

Ubuntu以外に関してのGrafanaのインストールは、詳しくはこちらをご覧ください。

Grafana - Installation

$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_4.1.1-1484211277_amd64.deb
$ sudo dpkg -i grafana_4.1.1-1484211277_amd64.deb
$ sudo apt-get install grafana

   

grafanaにZabbixのプラグインをインストール

$ sudo grafana-cli plugins install alexanderzobnin-zabbix-app

GitHub - alexanderzobnin/grafana-zabbix: Zabbix plugin for Grafana dashboard

   

grafanaを起動

$ sudo service grafana-server start

 

Grafana自体にHTTPサーバーの機能も内臓されているので、これでGrafanaが立ち上がります。 デフォルトでは3000番ポートを使用するので、適宜リバースプロキシでnginxを前にかませるといいかと思います。そのままでも問題なく使えます。

 

では次にZabbixのデータを取り込んでいこうと思います。

Zabbix側でgrafana用のユーザーを作成

Zabbix側の手順としては

・grafana用のユーザーを作成する。

・grafana用ユーザーにREAD権限を付与。

といった流れになります。

 

ちなみにZabbix側でgrafanaユーザーに適切なパーミッションを与えていなかった結果、grafanaからAPI叩いてもデータが取れずに2時間ばかり無駄にしたのは私です。 パーミッションを与えてください。

 

Zabbixのパーミッションなどは本家にドキュメントがあります。

www.zabbix.com

grafanaでZabbixデータソースの設定

左上のロゴから、[Data Sources] → [Add data source]と辿っていきます。

設定はこんな感じです。

f:id:cluex-developers:20170213123526p:plain

 

Zabbixには標準でAPIが用意されているので

http://エンドポイント/zabbix/api_jsonrpc.php をURLのところに設定してあげます。

 

Zabbix API Detailsには先ほどZabbix側で作成したgrafana用ユーザーのUsernameとPasswordを設定してください。

今回は便宜上、Zabbix Serverが入っているEC2インスタンスにGrafanaも入れているのでVPCやSGなどは特に気にせず、GrafanaをプロキシとしてlocalhostでZabbixのエンドポイントを叩いています。

 

Zabbix用のダッシュボードを作成

ダッシュボードの作り方などはServerworksさんのこちらのブログが詳しいです。

blog.serverworks.co.jp

個人的な要件:EC2のメトリクス表示をオートスケーリングに対応させたい

Grafanaでダッシュボード作ったのはいいものの、 オートスケーリングでインスタンスが増減するたびにグラフの設定し直すのとかめちゃめちゃ面倒ですよね。

そもそもオートスケーリングしなくてもインスタンス多いとグラフ作るのが面倒。

でも1つのパネルで全インスタンスのCPU利用率とか表示させたい。

解決策:正規表現でフィルタリングができる。

f:id:cluex-developers:20170213111421p:plain

文字が黄色くなっている箇所ですが、Host名でフィルタをかけて、ホスト名がマッチするサーバー群のメトリクスを一発で表示することができます。

上の設定だけでこんなグラフがすぐに出来上がります。

f:id:cluex-developers:20170213111439p:plain

これだけでもパネル設定がかなりラクになりました。

そのほかの色々なグラフなどの作り方はgrafana-zabbix.orgが出しているデモの設定が参考になります。

http://play.grafana-zabbix.org/dashboard/db/grafana-zabbix-demo

GrafanaでCloudWatchをデータソースに設定する

続いてGrafanaにCloudWatchのデータを取り込んで、グラフを表示させていきます。

AWS IAMでgrafana用のユーザーを作成

CloudWarchReadOnlyAccess ポリシーをアタッチしたユーザーをIAMで作成します。 Grafanaの方で使用する認証情報はアクセスキー及びシークレットキーになります。

GrafanaでCloudWatchデータソースの設定

本家Grafanaのドキュメントではaws-cliのクレデンシャルファイルを使用した設定方法となっていますが、 設定画面から直接IAMユーザーのアクセスキーとシークレットキーを入れることも可能です。

Grafana - AWS CloudWatch

個人的にはサーバーの中に credential を置く必要ないので、GUIで設定しちゃうとがラクかなと思います。 GUIで設定しても、再度設定を開くとアクセスキーなどは見れないようになっています。

f:id:cluex-developers:20170213111502p:plain

これでCloudWatchの方からもメトリクスを表示が可能となりました。

複数のAWSアカウントを設定する際も同じ要領でIAMユーザーを作成し、データソースを追加していくことでGrafanaからデータを取り出せます。

   

ELBのHealthyHostCountをパネルに表示する

試しにCloudWatchからのメトリクスとしてELBにアタッチされているHealthyHostCountのメトリクスを表示させてみます。

設定としてはこんな感じです。 f:id:cluex-developers:20170213121249p:plain

これでZabbix及びCloudWatchからデータが取れるようになったので、あとはパネルを一通り作れば、完成です!

f:id:cluex-developers:20170213111537p:plain

     

【番外編】 AWSの請求情報をgrafanaで確認する

AWSの請求情報、ついでにGrafanaで見たくないですか? 私は見たいです。

例えばこんな感じ。 f:id:cluex-developers:20170213112148p:plain

Grafanaの公式サイトにあるテンプレートを使えばすぐに表示させることができます。

   

grafana.net

上記のものだとインポートした後にカスタマイズができないので、 インポートした後にカスタマイズしたいという方には、カスタマイズできるやつ置いておきます。

よかったら使ってください。

GitHub - yoshi42662/Grafana-AWS-Billing-Dashboard

We’re hiring!!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さいませ! エンジニアの方、ぜひ情報交換しましょう!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

男もすなるsshといふものを、我もしてみむとて解説するなり

こんにちは。エンジニアでインターンをしている村田です。 最近寒くてベッドから起き上がるのがつらいです。

最近デスクトップ環境をxubuntuにしたのですが使いやすくて感動してます。 軽くて、Ubuntuらしい便利な機能も多くありますので他の方にもぜひおすすめです!

話は変わりますが、コンピュータの用語で新しく知ったものを調べると なんでこんなにわかりづらい説明しかないんだ!! と思ったことってありませんか?

自分は結構多いです、この現象(笑)。 学習していくとそれが当たり前になってしまうので「このサイトにわかりやすく書いてあるじゃん」と思うのですが、最初は難しく感じてしまうものなんですよね。

今回は以前自分が概念を理解するまでに苦労してしまった「sshのしくみ」についてまとめていこうと思います。 (「わかりやすい」というのは個人的な感想です。)

ssh」略さずに言うと?

ssh」はsecure shellの略です。 日本語にすると安全なシェルということです。

ssh」っていつ使うの?

sshとは安全なシェルのことです!」と言われてもそれではなんのことかわからないと思いますので、もう少しかみ砕いて説明していきます。 このページをご覧になっている方は、例えば「サーバーにアクセスしようとしたけどsshってどういうことなんだ?」とか「GitHubを使ってみたいけどなんか登録してsshを使わなくちゃいけないらしい」といった方が多いかと思います。

それではsshとはどういう場面で必要なのでしょうか?

簡単な言葉で説明すると「他のコンピュータに対してアクセスして、何かしらの操作を加えたいとき」sshを使います。 例えば「サーバーに対してコマンドを実行して環境を作りたい」とか「GitHubなどのリモートリポジトリに対してブランチをマージさせたい」といったところで、sshというコマンドを使用したいわけです。

基本的にはssh user@host_nameで使います。 これは色々なところで書いてあるのでわかりやすいと思います。

そもそも、「安全なシェル」の「シェル」とは人間とコンピュータが気軽におしゃべりするための言語です。 ディレクトリを変えたり、 ファイルを削除したい!というときの「cd」とか「rm」はそれに当たりますよね。

遠くのコンピュータに命令したいときも当然同じようにおしゃべりできたら便利なわけですから、シェルを使いたいですよね。 ただし、暗号化なしで通信するとほかの人に見られる危険などがあるため安全なシェルすなわちsshを使いたいということなのです。 (実は以前は暗号化されていないtelnetというものを使っていました。基本的な機能しかないことを利用して、現在もサーバーを作る際にときどき用いることがあります。)

暗号化の仕組み

ssh公開鍵暗号方式を使用しています。(実は違うやり方でパスワード認証方式というのもありますが、あまり使われないように思われます。) 公開鍵暗号方式の通信とはサーバーに公開鍵を持たせて文書等を暗号化させて、受信する際には自分の持っている秘密鍵を使用して複合する通信のことです。

これを例えるとすると、 公開鍵を錠(lock)、秘密鍵はその錠の鍵(key)だと考えてください。

南京錠などを想像すればわかるように、lockのやり方がわかっても、keyが用意できなければ実際に中身にたどり着くことはできません。 リアルの世界とは違って暗号化の世界では錠の鍵穴を見て鍵を作り出すことができない(らしい)ので、自分の秘密鍵をばらさなければこれで安全に通信することが可能なわけです。

また、sshではより安全性を高めるために秘密鍵に対して鍵をかけるのが一般的です。 鍵に鍵をかけるというのは面白いですよね。

この鍵は暗号化も複合化も同じ共通鍵暗号方式を使用しています。

実際に使用していくコマンド

ここでは実践で使っていくコマンドを簡単に紹介していきます。 紹介できなかったオプションやコマンドがたくさんあると思うので、不足している部分に関しては最後の参照URL等をご覧ください。

また事前知識としてsshに関するファイルは~/.sshというディレクトリに入るのですが、そこのファイルの役割を紹介しておきます。

authorized_keys 公開鍵を登録しておくファイル。 config SSH接続に関する設定を書いておくファイル。 known_hosts 過去に接続したことがあるサーバーを書いておくファイル。

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

これはgithubで推奨されている鍵の作り方です。

-t rsaの部分はRSAという暗号化方式を使って作成するという意味です。 他にもDSAやECDSAといった種類がありますが一般的にはRSAを用いるのではないかと思います。

また-b 4096では鍵のビット数をしてしています。デフォルトでは2048になのでより強固に作成していると言えますね。

-C "your_email@example.com"の部分はコメントなので必要なければ省いて構いません。

途中でパスフレーズを聞かれると思いますが、これが先ほど書いたとおり秘密鍵に対するパスワードになっているので忘れないものを使用してください。 このコマンドを実行するとid_rsaid_rsa.pubが作成されます。もちろん前者が秘密鍵、後者が公開鍵です。

余談ですが、GitHubユーザーのSSH鍵6万個を調べてみたGitHubssh鍵に関して面白い考察がありました。

ssh-copy-id -i ~/.ssh/id_rsa.pub user@host_name

これはsshの公開鍵を登録したいサーバーに送るコマンドです。 iオプションで任意のファイルを指定して送ることができます。ここでは先程作成した~/.ssh/id_rsa.pubを指定しています。

ssh-agentとssh-add ~/.ssh/id_rsa

ssh-agentは認証エージェントとよばれるもので、sshをするときに必要なパスワードの入力をショートカットできるようにするコマンドです。 (ubuntuMacの人は自動的に起動しているのでこのコマンドは忘れてしまっても問題ありません。ただ、他のOSではssh-agent bashなどを打たないといけないかもしれません。)

ssh-addssh-agent秘密鍵を追加するメソッドです。 引数にとった秘密鍵を追加します。

参照URL

http://qiita.com/tag1216/items/5d06bad7468f731f590e http://qiita.com/suthio/items/2760e4cff0e185fe2db9 http://webos-goodies.jp/archives/50672669.html

またこちらは書籍ですが、私がsshの仕組みを少し理解できるようになったきっかけになった本です。 はじめてUNIXで仕事をする人が読む本

HTMLテンプレートエンジン「slim」の解析ツールslim-lintを導入してみた!

こんにちは、エンジニアの井戸田です。 弊社ではmamanokoという子育てをするママのためのメディアをRuby on Railsで運営しており、viewではHTMLテンプレートエンジンであるslimを使用しています。今までrubyの解析ツール rubocop や、scssの解析ツール scss-lint は導入しましたが、slimの解析ツールを導入していなかったということで、今回導入してみました。

f:id:cluex-developers:20161105201859j:plain

インストール

$ gem install slim_lint

又は Gemfile に下記を記入して bundle install をすることでインストールが可能です。

gem 'slim_lint', require: false

require: false を指定することで、 autorequire をしないようにしています。

実行方法

スキャンするディレクトリ、又は複数のディレクトリを指定することによって、コマンドラインから slim-lint を実行することが可能です。

$ slim-lint app/views

$ slim-lint app/**/*.slim

slim-lint -v/--version を指定するとバージョンを確認ができます。他にもいろいろなオプションがあるので、下記のURLから御覧ください。
ref) https://github.com/sds/slim-lint#usage

実装

slim-lintはデフォルトで下記のような設定になっています。
ref) https://github.com/sds/slim-lint/blob/master/config/default.yml

# Default application configuration that all configurations inherit from.
#
# This is an opinionated list of which hooks are valuable to run and what their
# out of the box settings should be.
# Whether to ignore frontmatter at the beginning of Slim documents for
# frameworks such as Jekyll/Middleman
skip_frontmatter: false

linters:
  CommentControlStatement:
    enabled: true

  ConsecutiveControlStatements:
    enabled: true
    max_consecutive: 2

  EmptyControlStatement:
    enabled: true

  RedundantDiv:
    enabled: true

  LineLength:
    enabled: true
    max: 80

  RuboCop:
    enabled: true
    # These cops are incredibly noisy since the Ruby we extract from Slim
    # templates isn't well-formatted, so we ignore them.
    # WARNING: If you define this list in your own .slim-lint.yml file, you'll
    # be overriding the list defined here.
    ignored_cops:
      - Lint/BlockAlignment
      - Lint/EndAlignment
      - Lint/Void
      - Metrics/LineLength
      - Style/AlignHash
      - Style/AlignParameters
      - Style/BlockNesting
      - Style/FileName
      - Style/FirstParameterIndentation
      - Style/FrozenStringLiteralComment
      - Style/IfUnlessModifier
      - Style/IndentationConsistency
      - Style/IndentationWidth
      - Style/MultilineArrayBraceLayout
      - Style/MultilineAssignmentLayout
      - Style/MultilineHashBraceLayout
      - Style/MultilineMethodCallBraceLayout
      - Style/MultilineMethodDefinitionBraceLayout
      - Style/MultilineMethodCallIndentation
      - Style/MultilineOperationIndentation
      - Style/Next
      - Style/TrailingBlankLines
      - Style/TrailingWhitespace
      - Style/WhileUntilModifier

  TagCase:
    enabled: true

  TrailingWhitespace:
    enabled: true

この設定の中で、不要・変更したい設定に対して、ホームディレクトリに .slim-lint.yml というファイルを作成し、記述することで無効化・変更します。

.slim-lint.yml の記述例

exclude:
  - 'repository_name/app/views/**/*.slim'

linters:
  CommentControlStatement:
    enabled: false

  LineLength:
    include: 'repository_name/app/views/**/*.slim'
    max: 80
    
  EmptyControlStatement:
    exclude: 'repository_name/app/views/**/*.slim'
オプション 説明
exclude slim-lintや各linterから除外したいファイルがある時に使用します。
include 指定したファイルで絞り込みを行います。
enable false とした時には、そのlinterが走ることはありません。そのlinterが不要な時に使用します。

linterの説明

CommentControlStatement

コメントの書き方についての制御するlinterです。

  • Bad
# commentout
  • Good
/ commentout

ConsecutiveControlStatements

連続の制御文に対して制御するlinterです。
ConsecutiveControlStatements には下記ようなオプションがあります。 制御構文が同じインデントで2行以上続くのでしたら、見やすさからviewに書かずに helper にその制御を移動させた方がいいと思います。

オプション 説明
max_consecutive 行に指定できる制御文の最大数
  • Bad
- code1
- code2
- code3
  • Better
ruby:
  code1
  code2
  code3
  • Good
- helper_method_name

EmptyControlStatement

空行に対して制御するlinterです。 こちらのlinterを true にすると空行がなくなり、場合によっては見づらくなる可能性があるので、注意する必要がありそうですね。

  • Bad
p pタグ
-
p pタグ
  • Good
p pタグ
p pタグ

RedundantDiv

div タグに対して制御するlinterです。
slimでは div.class_name と書かずに .class_name と記述するだけで divタグ になります。不要な場合に div と記述しないというのが、このlinterの役割です。

  • Bad
div.class_name
  • Good
    class・idを指定せず、ただの改行などの場合に使用する場合には div と記述する必要があります。
div
  • Good
.class_name

RuboCop

このlinterはテンプレート内に存在する Ruby コードをチェックする為のものです。
ホームディレクトリに作成された .rubocop.yml の内容が反映されるようです。
ref) https://github.com/bbatsov/rubocop

オプション 説明
ignored_cops 無視したいRubocopの設定を記述

TagCase

大文字のタグ名を報告するlinterです。 HTMLの標準では、小文字のタグ名を限定するということはなく、大文字でも対応できるのですが、XHTMLでは小文字の記述が必須なので、一貫性としてこのlinterが作られたようです。
個人的に大文字・小文字が混ざっているいると見づらい点や、小文字にすることで大文字より見やすくなる点があるので、小文字で揃えた方がいいと思います。

  • Bad
BODY
  p pタグ
  • Good
body
  p pタグ

いかがでしたでしょうか? slim-lint を導入することで、気をつけて同じフォーマットにしようとしていても、どうしても見逃してしまう部分や、行数を制限することで、1つのファイルだけで処理するのではなく、partial化させることにより見やすくなると思います。

最後まで読んでいただきありがとうございます。

We’re hiring!

フロントエンジニアの方で、サーバーサイドを触ってみたい方、ぜひ一度Cluexに遊びに来てください!!

www.wantedly.com

www.wantedly.com

AWSのCloudFrontからコンテンツをHTTP2 & gzipで高速に配信する

こんにちは、高橋です。

みなさん快適で楽しいAWSライフを送れていますか?

 

f:id:cluex-developers:20161105012605p:plain

   

実は2ヶ月ほど前、AWSからCloudFrontでHTTP2によるコンテンツの配信ができるようになったので早速導入して見ました。

Amazon CloudFront now supports HTTP/2

S3のバケットにあるCSSやJS、画像などのアセットをCloudFrontから配信するというパターンは AWSでインフラを構築するサービスではよくあると思います。

今回はこういったS3上にあるアセットをHTTP2とgzipを使用してCloudFrontから配信するための設定方法などを見ていきたいと思います。

設定そのものは3分くらいで終わります。

CloudFrontにHTTP2を適用する

先ずはAWSのコンソールを開き、CloudFrontを選択します。 f:id:cluex-developers:20161105002030p:plain

リンクまたはチェックボックスを押して「Distribution Settings」からディストリビューションの設定画面に飛びます。 f:id:cluex-developers:20161105002453p:plain

「General」タブの「Edit」ボタンから基本設定の変更ページへ飛びます。 f:id:cluex-developers:20161105011519p:plain

「Supported HTTP Versions」をHTTP2が含まれているものを選択して、右下の「Yes, Edit」ボタンを押します。 f:id:cluex-developers:20161105002830p:plain

ちなみに「Supported HTTP Versions」の右にある黒いボタンを押すと、下記のような説明が出てきます。

For viewer requests, choose the versions of the HTTP protocol that you want CloudFront to accept. In general, HTTP/2 is faster. CloudFront always forwards requests to the origin using HTTP/1.1.

日本語にすると

ユーザーからCloudFrontへのリクエストの際に使用するHTTPプロトコルを選択してください。一般的にHTTP2は速いっすよ。CloudFrontからオリジンへのリクエストはHTTP 1.1だけ使うでー。

といった感じでしょうか。

ユーザーからのリクエストはHTTP2を使用できますが、CloudFront内部でのリクエストではHTTP2はまだ未対応のようです。

   

とはいえ、ひとまずこれで設定完了です!楽勝ですね!

 

あとは設定が完了するまで時間を潰して、 設定が完了したらブラウザから確認できればOK!

Chromeなどのブラウザのデベロッパーツールから見てみると..... f:id:cluex-developers:20161105003848p:plain

レスポンスは304になっていますが、「Protocol」のところが「h2」になっていれば設定完了です!

   

CloudFrontでgzip圧縮による配信をする

では次にファイルをgzip圧縮して配信する設定を見ていきましょう。

   

先ほどのディストリビューションの設定画面に飛びます。 f:id:cluex-developers:20161105002453p:plain

今回は「Behaviors」タブを選択して、gzip配信にしたいものにチェックボックスを入れて「Edit」を押します。 f:id:cluex-developers:20161105102550p:plain

下にスクロールしていくと、一番下に「Compress Objects Automatically」という設定項目があるので、「Yes」を選択してから右下の「Yes, Edit」を押して設定完了!

f:id:cluex-developers:20161105013123p:plain

ちなみに「Compress Objects Automatically」の右にある黒いボタンを押すと、下記のような説明が出てきます。

Select whether you want CloudFront to automatically compress content for web requests that include Accept-Encoding: gzip in the request header. CloudFront compresses files of certain types for both Amazon S3 and custom origins.

日本語にすると

わいに頼んでくれたらリクエストヘッダにAccept-Encoding: gzipがあった時はCloudFrontの方でコンテンツの圧縮しとくで。S3とか別のオリジンのファイルでも圧縮できそうなら、わいが勝手にしとくやでー。

といった感じでしょうか。

   

圧縮倍率はCloudFrontの方でよしなに決めてくれるようです。

   

ということで設定が終わったのでブラウザから確認して見ましょう!

先ほどと同様にChromeなどでデベロッパーツールを開き、CloudFrontから配信されているファイルのレスポンスヘッダを確認します。

f:id:cluex-developers:20161105005957p:plain

「content-encoding: gzip」となっていますね!

これでCloudFrontにHTTP2とgzip配信の設定が完了しました!

手軽にできてすぐに効果が期待できるので、気になった方は試して見てください!

     

We're hiring!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さい!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

Mockを使って、Facebookログイン部分のRSpecを書いてみた!!

こんにちは、エンジニアの神山です。 最近、テストカバレッジを上げるためRSpecを書きまくっています。ちなみに最初は90%でしたが、苦闘の末95%まで上がりました。結構骨が折れましたね。

その中でも大変だったのがFacebookログイン部分のテストです。外部APIを使っており、そこの部分のテストの書き方が分からなくて悩んでいました。 色々と調べてみるとモックを使うとうまいことテスト出来るよという文献を見つけました。

f:id:cluex-developers:20161105203341p:plain

ということで今回は、外部API部分のテストの問題点、モックとは何か、またそれをどのようにテストに使うのかにフォーカスして記事を書きました。

外部API部分のテストの問題点

今回悩んだのは外部APIを使用している部分のテストをどのように書くかということです。

例えば、「ログインしようとしているユーザーのFacebookのアカウント情報を取得し、すでにDBに登録されていればログインさせる」ということです。 しかしこのテストを行うには、実際にFacebookAPIよりアカウント情報を取得しなくてはいけません。

しかしテストで外部APIを利用することには、幾つかのデメリットがあります。

  • 外部APIやその周辺で、予測や対応ができないエラーが起きることがある
  • 外部APIの使用に制限があったり、金銭などが発生する場合がある
  • 外部APIに紐付いているプロダクトにリアルに影響が起こる(Facebookの場合、実際に投稿がされてしまうなど)

では、どうすれば外部APIを利用せずに、外部API部分を使用している部分のテストを出来るのでしょうか。 そのようなときに使うのが、「モック」です。

モックとは何か。

モックを直感的に説明すると、「本物のふりをするニセモノのプログラム」です。下記の記事の言葉を使わせて頂きました。

qiita.com

例えばユーザーのFacebook情報を取得するとき、FacebookAPIにリクエストが発生したら予め用意したニセモノのプログラムを呼ぶようにすることが出来ます。 また「aが呼ばれたときに本当はbを返すのだけど、ここのテストだけはcを返したい」というときに使用したりもします。

つまりモックとは、外部APIなどテストを行うために必要だけれど使用できない、再現することが難しい場合に、その役割を担ってくれるものです。

ちなみにモックとスタブの違いは?

モックと似た言葉にスタブというものがあります。気になって調べてみたのですが、両者の違いは使用目的だそうです。基本的にどちらもニセモノを作り出すことには変わらないようです。

ただその違いを明確に理解するのはなかなか難しく、またあまり意識する必要もないという意見もあったので、今回は保留にしました。時間があるときに色々と調べてみようと思います。

ちなみに、分かりやすく書いてある記事があったので、載せておきます。

uehaj.hatenablog.com

jp.corp-sansan.com

モックの準備

Omniauth部分のテストに関しては以下に従っております。

github.com

まずモックにすべきところを整理しましょう。 今回はユーザーのFacebook情報を取得する際にFacebookAPIを用います。そしてFacebookに登録されている名前やEmailを取得します。 そのため、今回モックにするところはFacebookAPIにリクエストがあったときに返す部分です。 以下をHelperに記します。

def facebook_mock(name, email)
  OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new(
    provider: 'facebook',
    uid: 1234567890,
    info: {
      name: name,
      email: email
    },
    credentials: {
      token: 'hogepiyo1234'
    }
  )
end


OmniAuth.config.test_mode = true

たとえば、facebook_mock('foo', 'bar')とすれば、以下の値が得られます。

{ provider: 'facebook', uid: 1234567890, info: { name: 'foo', email: 'bar' } , credentials: { token: 'hogepiyo1234' } }

またOmniAuth.config.test_mode = trueはOmniauthを用いたテストの際に必要になるので、一緒に記述しておいてください。これを記述すると、FacebookAPIにリクエストが送られそうになると、それを中止してすぐにコールバックしてくれます。

RSpec

では、Facebookログイン部分の一連のテストを書いていきます。

まず、ユーザーを作成します。

let(:user) { User.create(name: 'hoge', email: 'hoge@hoge.com') }

テストの前にOmniAuth.config.mock_auth[:facebook]を初期化し、facebook_mockをセットします。

OmniAuth.config.mock_auth[:facebook] = nil
Rails.application.env_config['omniauth.auth'] = facebook_mock(
  name: user.name,
  email: user.email
)

準備はこれだけです。では実際にFacebookログインをしてみます。今回は分かり易くボタンを押してFacebookログインをする形式にしてみました。

click_link 'Facebookを利用してログインする'

さて、ボタンが押されると通常はFacebookAPIにリクエストが走りますが、テストではすぐにコールバックされます。 ただモックのおかげで、request.env['omniauth.auth']には下記の値が入っております。

{ provider: 'facebook', uid: 1234567890, info: { name: 'hoge', email: 'hoge@hoge.com' } , credentials: { token: 'hogepiyo1234' } }

そのため、request.env['omniauth.auth']['info']name: 'hoge', email: 'hoge@hoge.com'の組み合わせを取得できるようになります。あとはこれをDBと照合して、有効であるかどうかを確かめればテスト終了です。

テスト全体は以下のようになります。

discribe 'login via Facebook' do
  let(:user) { User.create(name: 'hoge', email: 'hoge@hoge.com') }


  before do
    OmniAuth.config.mock_auth[:facebook] = nil
    Rails.application.env_config['omniauth.auth'] = facebook_mock(
      email: user.email,
      name: user.name 
    )
    click_link 'Facebookを利用してログインする'
  end


  it 'should succeed' do
    expect(page.status_code).to eq 200
  end
end

ちなみに検証部分がステータスコードの判別しかないですが、ログインされているかどうかを簡単に確認できる指標が欲しいですね。 例えば、expect(current_user).to eq userのようなものがあれば。。

以上、Mockを使ったRspecの書き方でした。読んで頂きありがとうこざいました。 アドバイスなどありましたら是非お願いします!

We're hiring!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さい!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

参考文献

qiita.com

jp.corp-sansan.com

uehaj.hatenablog.com

github.com

Docker for Macが遅い問題をdocker-syncで解決する

こんにちは。エンジニアの志村です。 Docker for Mac便利ですね!

docs.docker.com

日本語でも様々な記事が出てきています。

私もVagrant + Dockerをメインに使用しておりましたが、ついにDocker for Macに乗り換えました。 弊社では、dev環境をDockerにしているのですが、Vagrant + Dockerの時は快適に開発が出来ました。 ただ、VMを使わなくなった途端にめちゃくちゃ動作が重くなりました。 docker-compose --service-port ●●という感じで、rails serverとwebpack-dev-serverを立ち上げるのですが、ブラウザからの読込が劇的に遅い…。seedデータ突っ込むのもめちゃくちゃ遅い… フォーラムでも話題になっていますね。

forums.docker.com

私の環境でいうと、Vagrant + Dockerの時よりも、体感10倍近く速度が落ちました…。

なぜ遅いのか

forums.docker.com

このDockerの中の人の回答を見ると、osxfsを用いてファイルシステムイベントを検知・監視していることが主な原因っぽいですね。 勿論、その他環境により差異はありますが、ファイルシステムを監視する方法を替えれば解決できそうです。

docker-sync

ファイル同期で速度早いと良く言われているのがrsyncですね。 docker-dev-osxという選択肢もありますが、これはDocker for Mac未対応… どうしようかなーとフォーラムを眺めているとdocker-syncなるものがありました。 フォーラムの中でも評価が良く、結構な人が使ってそうなのでdocker-syncを使用したいと思います。

docker-sync by EugenMayer

docker-syncはRubyベースで書かれています。

docker-syncの使い方

  1. docker-sync, fswatchをインストールします。
$ gem install docker-sync
$ brew install fswatch

ファイルの同期に関してはrsync, unisonが選択出来ます。 rsyncがホスト→ゲストの一方向の通信なのに対し、unisonはNFSのように双方向の通信が可能なようです。 今回はrsyncを使用します。 unisonに関してはまた記事を書ければと思います。

$ brew tap homebrew/dupes
$ brew install rsync

※ schema.rb, Gemfile.lock等、ゲスト(Docker)側で生成されるファイルに関してはdocker cpコマンドを使用するとホスト側に持ってくることが出来ます。

# Docker内のschema.rbをPC側に持ってくる
$ docker cp コンテナ:/var/www/db/schema.rb ./db/schema.rb
  1. docker-syncの設定 docker-syncはymlファイルに設定を記述します。 Railsを触っている身としては普段から使用しているのでありがたいです。

docker-syncはDocker Composeを使用します。 production環境やstaging環境でもDockerを使用している方は、docker-compose.ymlの他に、開発環境用のymlファイルを用意する必要があります。 私は開発環境のみなので、docker-compose.ymlを直接編集してしまいます。

公式のwikiに載っているので参考下さい。

github.com

docker-sync.yml

syncs:
  # docker-syncで使用するvolume名を記載
  web-sync:
    notify_terminal: true
    # ホスト側(PC)のパスを記載 今回はRailsのルートディレクトリを指定
    src: './'
    # ゲスト側(Docker)にマウントするパスを記載 Docker内の/var/wwwにマウントされる
    dest: '/var/www'

    # IPの設定
    sync_host_ip: '127.0.0.1'

    # rsyncのポート設定
    sync_host_port: 10871

    # rsyncさせないファイル類を記載
    sync_excludes: ['Gemfile.lock', 'Gemfile', 'config.rb', '.sass-cache/', 'sass/', 'sass-cache/', 'composer.json' , 'bower.json', 'package.json', 'Gruntfile*', 'bower_components/', 'node_modules/', '.gitignore', '.git/', '*.coffee', '*.scss', '*.sass']

    sync_excludes_type: 'Path'

    sync_args: '-v'

    # ファイル同期方法の選択 rsync or unison
    sync_strategy: 'rsync'

    # ゲスト側でのユーザー名の指定
    sync_user: 'test'

    sync_userid: '5000'

    # ゲスト側でのgroupの指定
    sync_group: 'testgroup'

    sync_groupid: '6000'

    # fswatchで変更を検知させないファイルやディレクトリ sync_excludesとかぶせとけばよいかと
    watch_excludes: ['.*/.git', '.*/node_modules', '.*/bower_components', '.*/sass-cache', '.*/.sass-cache', '.*/.sass-cache', '.coffee', '.scss', '.sass', '.gitignore']

    watch_args: '-v'

    watch_strategy: 'fswatch'   

docker-compose.yml

docker-compose.ymlにはversion1, version2と2種類の記述方法があります。 ここではversion2を使用します。

version: '2'
services:
  database:
    image: postgres:latest
    volumes_from:
      - datastore
    expose:
      - '5432'

  elasticsearch:
    image: elasticsearch:latest
    volumes_from:
      - datastore
    ports:
      - '9200:9200'

  redis:
    image: redis:latest
    ports:
      - '6379:6379'
    volumes_from:
      - datastore

  memcached:
    image: memcached:latest
    ports:
      - '11211:11211'
    volumes_from:
      - datastore

  datastore:
    build: docker/datastore

  web:
    build: .
    command: bash -c 'rm -f tmp/pids/server.pid && bundle exec foreman start && bundle exec sidekiq -C config/sidekiq.yml -L tmp/sidekiq.log -d'
    volumes:
      # 下記で宣言したvolume名を指定
      - web-sync:/var/www:rw
      - /var/www/client/node_modules
    ports:
      - '3000:3000'
      - '8080:8080'
    links:
      - database
      - elasticsearch
      - redis
      - memcached

# docker-syncで使用するvolume ここで宣言した名前がdocker-sync.ymlで使用される
volumes:
  web-sync:
    external: true

このような感じです。 環境によって差異があると思いますので、適宜調整をお願いします。 一番重要なのは、volumesのsync volumeの設定と、アプリケーション(Rails)側のコンテナでvolumesを指定する部分です。

docker-syncの起動

$ docker-sync start

もしくは

$ docker-sync-stack start

で起動します!

docker-sync-stack startの場合は、docker-sync startと同時にdocker-compose upも行われます。 私はpry-byebugを使用したいので、普段docker-compose run --service-port webで起動しているため、ターミナルを一枚docker-sync startに使用し、さらにdocker-compose run --service-port webを起動させるようにしています。

ベンチマークはとっていないのですが、Vagrant + DockerをNFSで同期させたのと同じ、もしくはそれ以上に早くなりました! GUIで様々な設定が出来、VMとかも必要ないのでDocker for Macは便利ですね。 是非docker-syncを使って快適な開発環境を構築してみては如何でしょうか?

We’re hiring!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さいませ!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

今年2回目の開発合宿に行って来ました!@土浦・筑波

こんにちは、エンジニアの高橋です。

最近都内は急に肌寒くなってきましたが、みなさんお元気でしょうか?

今回は茨城県の土浦・筑波エリアで開催!

先日、2泊3日で茨城県の土浦・筑波の方に開発合宿に行ってきました!

台風が近づいている中での開催となり、心配な所もありましたが無事に終わったので、 今回はその様子をまとめようと思います。

 

2月の開発合宿に続いて、今年2回目の開催となります。 Cluexでは合宿を利用して 普段なかなか手のつけられない技術的課題や技術的負債への取り組みや、 理念会議といったメンバー全員でじっくり話し合う場を設けるなど といったことをしています。

ちなみに前回(2016年2月)の日光での合宿の様子はこちら cluex-developers.hateblo.jp

古民家ゲストハウス「jicca」

f:id:cluex-developers:20161014105046j:plain

今回の宿泊先には2泊3日の全日貸切で、筑波山の近くにある 茨城県石岡市のゲストハウス「jicca」さんを使わせてもらいました。

jicca-gh.com

ここのゲストハウスはクラウドファンディングを活用して、 もともと実家の母屋だったものをゲストハウスに改装したそうです。

 

ここの近辺は豊かな自然が溢れていて、とても落ち着いている長閑な場所で、

jiccaは最近は雑誌やラジオなどで度々話題になっているそうです。

 

こちらを運営されている比企(ひき)さんという方は、もともと大手企業で活躍された後に 現在はこちらの「jicca」を運営されつつ個人で中小企業診断士として中小企業コンサルをされている方で、とても暖かい方でした。

   

今回の合宿でのTRY

合宿に参加したのはエンジニア4人・ディレクター3人の合計7名。

合宿中はエンジニア陣は2人で1チーム、ディレクター3人で1チームの全部で3チームに別れて目標を立てました。 合宿の目標はそれぞれのチームであらかじめ決めておき、合宿当日はすぐに作業が開始できる様にしておきました。

ディレクターチーム 売上を上げるためのコンテンツマーケティングの運用方針を決める

今後の事業成長戦略を各々が考えて発表

Web appチーム Rspecのテストカバレッジを95%達成

インフラ&ネイティブアプリチーム CoffeeScriptからES2015(ES6)への完全移行

     

開発合宿1日目

台風が近づいてる影響で初日は曇り空。

午前9時、都内某所に集まり出発です!

都内から土浦までは高速道路を使用して、車でおよそ1時間半ほどの距離にあります。

f:id:cluex-developers:20161014110311j:plain

新卒3人組

前日も遅くまでオフィスに残っていたみたいで非常に眠そうですね。

   

土浦までの道中は高速道路も渋滞なく順調に進み、

守谷SAで途中休憩を挟んでほどなくして土浦に到着です。

   

宿に着く前に、激安ジャングルで物資調達です。

車内では眠そうだった彼も元気になったみたいです。

f:id:cluex-developers:20161014111650j:plain

 

そして11時過ぎにjiccaへ到着。

 

到着してからまずは全体で各チームの合宿中の目標を共有し、 終わり次第すぐに各自作業に取り掛かります。

   

ディレクターチームは主に話し合いが多いので1階にて作業。

エンジニアの方は机と椅子が用意されている2階に上がり作業を開始。

 

ディレクターチーム

f:id:cluex-developers:20161014132340j:plain

 

エンジニアチーム

f:id:cluex-developers:20161014132912j:plain

   

   

 

1日目の夜は夕食と温泉を兼ねて、

車で10分ほど移動して「やさと温泉 ゆりの郷」へ行きました。

www.yurinosato.jp

   

この近辺は日帰り温泉が500~1000円くらいで点在しているので、 その中から一番近場の場所を選びました。

  f:id:cluex-developers:20161014133300j:plain

  f:id:cluex-developers:20161014133340j:plain

「jicca」のオーナーの比企さんも一緒に温泉にお供して頂きました。

  f:id:cluex-developers:20161014133657j:plain

     

茨城県は「常陸牛」や「ローズポーク」が有名なようです。

こちらの常陸牛は柔らかくてとても美味しいお肉でした。

f:id:cluex-developers:20161014134306j:plain

f:id:cluex-developers:20161014184004j:plain

     

夕食後

 

本日はアルコールは一切なしで、 夕食を食べた後はすぐ宿に戻って作業再開です。

 

f:id:cluex-developers:20161014184229j:plain

   

ディレクターチームは順調に進んでいるようで、午前12時頃には就寝。

   

エンジニアの方はまだまだ続きます。

f:id:cluex-developers:20161014135638j:plain

f:id:cluex-developers:20161014135809j:plain

午前4時

まだ続きます。

f:id:cluex-developers:20161014170527j:plain

     

初日が終わり、全員が寝静まったのは午前6時頃。

長い長い1日目が終了です。

     

開発合宿2日目

f:id:cluex-developers:20161014141004j:plain

台風は進路を変えていなくなってしまい、 晴れ晴れとした天気になりました。

   

午前9時、起床。

全員で集まり1日のスケジュール確認です。

f:id:cluex-developers:20161014140301j:plain

 

午前中は引き続き各チームごとで作業。

 

昼食は手打ち蕎麦が有名とのことで、宿の近くの蕎麦屋さん「かまたや」へ

創業60年以上の老舗蕎麦屋だそうです。

f:id:cluex-developers:20161014141622j:plain

www.ibarakiguide.jp

   

蕎麦はもちろん、天ぷらがめちゃめちゃ美味しいです。

f:id:cluex-developers:20161014152519j:plain

     

宿に戻り、今回の合宿のもうひとつの一大イベントに入ります。

 

Cluex理念会議

f:id:cluex-developers:20161014142338j:plain

 

2日目は昼食後から6時間ほどに渡り、

ディレクター・エンジニア含めて全員で「理念会議」といったものを行います。

 

前回の合宿に続き、

今回も個人個人の将来・会社の将来、自分がこれからどうしていきたいか・どうなりたいのかといったことを発表して、

それをどうすれば実現できるのかということをみんなで話し合っていきます。

   

・・・5時間経過後

   

f:id:cluex-developers:20161014142542j:plain

 

ということで、写真にはあまり変化がないですが、

一人一人にフォーカスして個人の思想やその背景にある思い、

そしてそれぞれの将来に関してじっくり話し合ってきました。

   

f:id:cluex-developers:20161014171731j:plain

 

f:id:cluex-developers:20161014171853j:plain

   

理念会議が終わった頃には日がすっかり暮れてしまいました。

これからやや遅めの夕食です。

   

2日目の夕食はBBQ!

f:id:cluex-developers:20161014145526j:plain

「jicca」の方で茨城県産の食材をたくさんご用意して頂いて、みんなでBBQです。

今回のBBQに望むためのチーム編成はこちら

     

 

調理部隊

f:id:cluex-developers:20161014143738j:plain

f:id:cluex-developers:20161014144102j:plain

     

バーベキュー屋の店員

火起こし部隊

f:id:cluex-developers:20161014143842j:plain

       

おじさんたち

火起こし部隊の後ろで待機です。 

f:id:cluex-developers:20161014150015j:plain

       

そして・・・・

     

いざBBQ!

f:id:cluex-developers:20161014144402j:plain

f:id:cluex-developers:20161014144448j:plain

f:id:cluex-developers:20161014145426j:plain

f:id:cluex-developers:20161014144559j:plain

     

普段はこういった機会がなかなか無いので、

みんなでお酒を飲みながら語り合って、貴重な時間を過ごせました。

   

BBQを堪能し、ほどなくして2日目も終了です。

今日はみんな疲れが出てきたようで、午前12時にはみんな就寝したみたいです。

     

開発合宿3日目 (最終日)

3日目も天気に恵まれた、気持ちのいい朝を迎えました。

午前9時に起床し、10時にチェックアウトです。

f:id:cluex-developers:20161014104553j:plain

   

3日目は土浦市街の方で貸会議を借りました。夕方まではこちらで各チーム作業を行い、最後に経過発表です。

お借りしたのは、「ワークヒル土浦」の特別会議室。社内の会議などであれば法人でも利用ができます。

10時~17時の利用で4000円ほどと、なかなかのお手頃価格です。

土浦市勤労者総合福祉センター「ワークヒル土浦」

   

人数のわりに、とても立派な会議室でした。

f:id:cluex-developers:20161014151114j:plain

こちらで一通り作業をした後、

各チームからの合宿目標の結果発表と振り返りを行いました。

       

今回の合宿で取り組んでいた、 「ES2015(ES6)の導入」や「Rspecカバレッジ」に関しては、追ってブログにまとめたいと思いますので、しばしお待ちください。

       

観光編

JAXA(筑波宇宙センター)に入ってきました!

f:id:cluex-developers:20161014175051j:plain

     

合宿最終日、会議室で結果発表などを終えた後、

20分ほど車を走らせて茨城県つくば市にある「JAXA 筑波宇宙センター」に行ってきました。

筑波宇宙センター | ファン!ファン!JAXA!

   

日本の宇宙産業を牽引するJAXA

アメリカではNASAスペースシャトルが引退し、SpaceXなどの民間企業へのシフトが進んでいますが、そんな宇宙を相手にする仕事はロマンがあってすごいなあと個人的には思うところです。

最先端の技術たちを駆使して宇宙という過酷な環境に挑むって本当にすごいです。

教育にも最先端の技術をどんどん導入していきたいです。

     

という訳で、JAXA見学です!

   

ISSの日本実験棟「きぼう」

f:id:cluex-developers:20161014153413j:plain

中にも入れちゃいます。

f:id:cluex-developers:20161014175208j:plain

   

宇宙服 (1着あたり10億円するそうです)

f:id:cluex-developers:20161014153511j:plain

興味津々です。

f:id:cluex-developers:20161014174004j:plain

f:id:cluex-developers:20161014153844j:plain

   

その他にも衛星やロケット、エンジンなどが実物大で見れます。

f:id:cluex-developers:20161014154253j:plain

f:id:cluex-developers:20161014174904j:plain

f:id:cluex-developers:20161014175003j:plain

       

施設の方(JAXAを引退された方?)に、

宇宙に関して、衛星や衛星に使用している技術など

色々なお話を付きっきりで伺うことができました。

 

f:id:cluex-developers:20161014154645j:plain

ありがとうございました。

f:id:cluex-developers:20161014181714j:plain

         

合宿のまとめ

前回に引き続き、今回も有意義な合宿となりました。

 

今回の合宿での反省点や改善点は次回以降にまた活かしていきたいと思いますが、

これから開発合宿をやろうと考えている方にも、気をつけるポイントなどをあげておこうと思います。

 

Keep

・メンバーのチームワーク強化に繋がった。

・宿泊代がお手頃。

・旅のお供に温泉は大事。

・ご飯が美味しい。

・宿にネット環境が整っている。

・自然溢れる環境なのでとてもリフレッシュできた。

・近くに娯楽施設がないので、逆に集中できた。

   

Problem

・床に座って作業するときは座椅子があると良かった。

・作業に当てられる時間が意外と短い。合宿中にやることの選択と集中が大事。

   

開発合宿のポイント

合宿にあたって気をつけていたことは、

・合宿の目標には数値的な結果が見えやすい目標を。

→ 加えて、合宿の開始と同時に、すぐに作業に取り掛かれる様に準備しておくと時間を無駄にせずに済みます。

→ 開発合宿の醍醐味として合宿中に立てた目標を達成することがとても大事だと思うので、どうしても終わらなそうであれば合宿前にある程度の下準備をしておくといいかと思います。

 

・お酒は最終日の前夜のみに抑える。

→ ダメになりやすい人は控えましょう。お酒を飲むとせっかくの目標もどうでもよくなっちゃいます。

 

・ネット環境の確認。

wifiの有無はもちろん、速度面なども事前に確認しておくと安心です。

→ もしもの時に、モバイwifiを各自で持参しておくと何かと便利ですが、圏外になることもしばしばあります。

 

・電源の確保。

→ 作業する場所で電源が使用できるかはもちろんですが、延長ケーブルを持参すると便利なこともよくあるので、事前に確認しておくと安心です。

 

・お昼に温泉は入らない。

→ 温泉に入ると気持ちが満たされてせっかくの目標もどうでもよくなっちゃいます。ダメになりやすい人は控えましょう。

 

・近隣の施設や観光地を調べておく。

→ 特に宿泊施設内もしくは近場にコンビニがあるか確認しておくと安心です。24時間営業ではないコンビニもあるので気をつけておくといいかと思います。

 

・予定は詰め込み過ぎない。

→ 食事処の予約や会議室の予約など、時間的な余裕を持って対応できる様にしておくといいかと思います。

→ 合宿中にやることも基本的には1~2つに絞ると雑念なく集中できるのではないかと思います。

 

番外編

懐かしきスマブラ!!

実は遡ること2日目のBBQの後、懐かしきスーパーファミコン任天堂64が出現!

jiccaの方で貸していただきました!

   

プロジェクターも用意して頂いて懐かしい気分に浸りつつ、ガチバトル。

f:id:cluex-developers:20161014155218j:plain

f:id:cluex-developers:20161014175817j:plain

   

jiccaで飼われている猫です。

猫かわいいですね、猫。

f:id:cluex-developers:20161014180231j:plain f:id:cluex-developers:20161014180047j:plain

いかがでしたでしょうか?

日々の業務では対応できない経営課題や技術課題に取り組むために、新しい技術の試験の場に、新しい事業の創出の場に、開発合宿やってみてもいいかもしれません。

   

個人的にはリフレッシュもできてES6の導入も無事に進み、温泉に入れて美味しいご飯を食べれていい合宿でした。 これからまた更に事業スピードあげていきたいところです。

       

We're hiring!!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています!

気になった方はご連絡ください!!

ビジネスサイド

www.wantedly.com

www.wantedly.com

エンジニア

www.wantedly.com

cluex.co.jp