くらしのマーケット開発ブログ

「くらしのマーケット」を運営する、みんなのマーケット株式会社のテックブログ。積極採用中です。

ReDashを利用して社内のデータを誰でも可視化できる環境を用意する話

こんにちはエンジニアののりすけです。

突然ですが、社内の同僚や上司に「このデータ出して」など、せっかく集中してコードを書いているのに 手が止まってしまったなんて経験はないですか?

「あのデータはこのテーブルに入っているのでSQL実行してくれれば。。。」

と思ってもDB接続権限ないか、とかそもそもSQLってなに状態だとどうしようもない。 かといってよく使うデータならバッチで出しとくかと思って定期的にバッチでデータ取得を行なって、ファイルサーバなど共有 できるとこに置いておく。大体次に言われるのは

「あのデータちょっと足りないから追加でこの項目と、あの項目もだして」 「。。。はい」

そんな経験を持つあなたに朗報です。 これ解決できます!(ちょっとした環境用意 + みんながSQLを少しだけ覚えるだけ)

この記事を読んでできること

  • Dockerを使ってReDash環境の構築ができるようになる

経緯

みんなのマーケットでは会社のデータを自分が必要なときに、必要な分だけ取得/分析ができる環境を ReDashというソフトウェアをもとに構築しています。

もともとは非エンジニアの方がSQLの勉強をできる環境を用意する目的で準備したのですが、今では各部門で業務に利用するなど、 自分で好きなデータを使って効率的に働くことができています。

具体的には、売上など会社の情報を表示するダッシュボードとして利用したり、仮説検証に必要な情報を 取得するなど様々な業務で利用できるようにしています。

ReDashとは

オープンソースで開発が進められている様々なデータソースを可視化するツールです。みんなのマーケットではデータソースとして PostgreSQLやAWS Athenaを利用しデータの取得/可視化を行なっています。

特徴として、対応するデータソースの多さが挙げられます。今回事例として取り上げるPostgreSQLなど各種RDBMSだけでなく、 AWS Athena, Google BigQuery, Google Analytics などの外部サービスにも幅広く対応しています。(すごい!)

参考URL:

構築手順

実際にReDashの環境を用意してみましょう。 今回は公式に用意されているdocker-composeファイルを利用してlocalに構築します。

Dockerについてはここでは説明を割愛しますが、すでに用意されてあるcomposeファイルを使ったりするので、 導入はかなり容易です。これを機にDockerを覚えても良いかと思います。(ってか覚えた方がすごく良いです、便利です)

1.ソースを取得します

$ git clone https://github.com/getredash/redash.git
$ cd redash

2.docker-composeファイルの中身を確認する

取得したソースにcomposeファイルの雛形が用意されています。 この記事を書いた時点ではdocker-compose.yml, docker-compose.production.ymlの2ファイルが 用意されています。

ファイル内のコメントにも記述されていますが、docker-compose.ymlは開発向けなので、ここでは docker-compose.production.ymlこちらのファイルを元に設定を記述していきます。

composeファイルをみるとわかるのですが、ReDash自体は複数のコンテナでサービスが構成されています。 それぞれ以下の役割を担っています。

  • server

    ReDashのWeb console画面を提供しています。基本的にこの画面を利用して ユーザはQueryの発行やデータの可視化を行います。ソースをみるとわかりますが、 Pythonで実装されおり、Application Serverとしてgunicornが使われています。

  • worker

    ReDashではデータの結果取得に時間がかかるようなQueryを扱うケースが多いため、実際のQueryを実行する処理 (実際にデータを取りに行ったりする処理)はこのWorkerコンテナが行なっています。 こちらもPythonで実装されceleryというPythonのjob queue処理を行うためのフレームワークが使われています。

    workerが処理を行う内容には主に、ユーザがアドホックに実行したQueryを処理するadhoc workersとスケジュールされた通りに Queryを実行するscheduled queries workersがあります。 この切り替えはcelery worker起動時に-Qオプションで制御され、1つのworkerで両方の機能を担うことも、 別々のworkerに分けることもできます。

  • redis

    celeryではQueueを貯めておく場所としてredisを利用することができます。このredis内にserverからjobがqueueに登録され、 workerが引き出して実行を行います

  • postgres

    PostgreSQl serverです。これは共有データを入れる方のDBではなく、ReDashのユーザ情報や作成したQuery/Dashboardの情報が格納されます。

  • nginx

    ReDash serverのリバースプロキシとして稼働するnginxです。

3.composeファイルを編集する

ファイルの冒頭コメントにも書かれているように推奨設定として以下が書かれています。今回はlocalで動かすため いくつかの設定を省いていますが、本番で設定と書いたものについては運用する際に必ず設定するようにしてください

  • cookie secretの設定 (本番で設定)

    server -> environment -> REDASH_COOKIE_SECRET にランダムな値を設定します

  • PostgreSQLのpasswordを設定 (本番で設定)

    postgres -> environment -> POSTGRES_PASSWORD で初回起動時にpasswordを作ることができますが、 composeファイル内にハードコードするのではなく.envファイルを使うなりして値を外部から注入できる形にする 方が良いと思います。

    .envファイル等docker関係の説明については割愛させていただきます。

  • Postgres containerの永続化

postgres:
  image: postgres:9.5.6-alpine
  # volumes:
  #   - /opt/postgres-data:/var/lib/postgresql/data
  restart: always

以下のように修正します

postgres:
    image: postgres:9.5.6-alpine
    volumes:
      - /opt/postgres-data:/var/lib/postgresql/data
    restart: always
  • Workerをadhocとscheduledで分ける (サーバの環境や利用者数に応じて設定を検討する)
worker:
    image: redash/redash:latest
    command: scheduler
    environment:
      PYTHONUNBUFFERED: 0
      REDASH_LOG_LEVEL: "INFO"
      REDASH_REDIS_URL: "redis://redis:6379/0"
      REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
      QUEUES: "queries,scheduled_queries,celery"
      WORKERS_COUNT: 2
    restart: always

これを以下のように修正

adhoc-worker:
  image: redash/redash:latest
  command: scheduler
  environment:
    PYTHONUNBUFFERED: 0
    REDASH_LOG_LEVEL: "INFO"
    REDASH_REDIS_URL: "redis://redis:6379/0"
    REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
    QUEUES: "queries,celery"
    WORKERS_COUNT: 2  # 運用する環境によって変更
  restart: always
scheduled-worker:
  image: redash/redash:latest
  command: scheduler
   environment:
    PYTHONUNBUFFERED: 0
    REDASH_LOG_LEVEL: "INFO"
    REDASH_REDIS_URL: "redis://redis:6379/0"
    REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
    QUEUES: "scheduled_queries,celery"
    WORKERS_COUNT: 2  # 運用する環境によって変更
  restart: always

特に、containerの永続化は忘れずに行うようにしてください。行わない場合、containerを破棄した際などにユーザ情報や作成したQueryの情報を失ってしまうため、こちらの設定は忘れないようにしてください。

また、localの環境にDBなどサンプルで利用するReDashのデータソースがない場合はcomposeファイルに合わせて記述しておくと、 別でインストール等手間は省けます

4.起動する

まず、ReDashのデータベースを作る必要があるため以下のコマンドを実行します

$ docker-compose -f docker-compose.production.yml run --rm server create_db

コマンドが終了したら各コンテナを起動します

$ docker-compose -f docker-compose.production.yml up -d

起動後、 localhost:80 にアクセスして以下の画面が出てくれば無事環境構築は完了です。

f:id:curama-tech:20180302120101p:plain

初回アクセス時にAdminユーザの情報を登録後以下のメイン画面が表示されます。

f:id:curama-tech:20180302120211p:plain

正しく構築が出来ているか確認するため以下の手順を実行します。

  1. 画面右上のメニューから管理ページへアクセスし、利用するデータソースを設定する

  2. QueryメニューのNew Queryを選択し、設定したデータソースが選択できることを確認する

  3. Queryを記述しExcecuteボタンを押してQueryの実行結果が表示されることを確認する

以上の3点が確認できれば正しく設定されていることがわかります。

使ってみる

ReDashにはデータを取得、可視化、共有をそれぞれ、Query, Visualizations, Dashboardという機能で 使うことが出来ます。

各機能の操作方法は以下のヘルプ参照してください。英語表記ですが操作手順については わかりやすく画像と、アニメーションでまとめられています。

https://help.redash.io/article/32-getting-started (英語)

また、細かい操作方法を知りたい場合は公式のヘルプが便利です

https://help.redash.io/ (英語)

使える人を増やそう

以上でReDashの構築と一通りの操作は終わりです。読んでいただいたように、簡単に構築ができるため、 ぜひ社内に構築して利用してください。

運用の注意点として、当たり前の話ですが、社内環境を構築する際には個人情報等の秘匿すべき情報は ReDashのデータソースとして扱わないようにする等の対応を忘れないようにしましょう。

また、みんなのマーケットで運用した経験から社内運用する上での重要な点をお伝えしておきます

SQLを読み書きできる人を増やす

SQLを使ってデータを取得するという特性上、環境を用意しただけでは、記事冒頭に書いた状況はどうしても発生してしまいます。 まずは数名規模 & SELECT文のみでも良いので勉強会などを通して使えるような状態でReDashを利用し始めると スムーズに運用を開始できると思います。

以上、社内のデータを誰でも可視化できる環境を用意する話でした。 また、一緒に働いてみたいといった方もぜひお待ちしてます! 次回は第一回に続きCTOが書く予定です。

TypeScriptをプロダクトで使って便利だった話

はじめに

みんなのマーケットでwebエンジニアとして働いている高橋です。

今回は弊社で採用しているTypeScriptについて簡単に紹介します。みんなのマーケットではNode.jsおよびAngular等のフロントのjsに対して、TypeScriptを全面的に用いています。本記事では実際にプロダクトでTypeScriptを使用してメリットと感じた点をいくつか挙げていきます。

TypeScriptとは

TypeScriptはJavaScriptにコンパイルすることができる、静的型付けプログラミング言語です。変換後のjsには、型情報は残りません。TypeScriptを用いることで、動的型付け言語であるJavaScriptに対して、実行前に静的型チェックを行うことができます。また、型定義機能以外にも、classやmodule機能の拡張なども行われています(こちらの日本語資料が詳しいです)。これだけでも十分便利なのですが、おまけ機能として、ES2015等からES5への変換も行うことができます。

メリット1: 変数・引数・戻り値等の型を明示できる

以下、簡単なコードを例にして、TypeScriptのメリットを紹介していきます。手元にTypeScriptの実行環境が無い方は、Microsoftが公開している↓のプレイグラウンドで、TypeScriptのコンパイルを試してみることができます。(Runを押すと結果を確認することができます。)

https://www.typescriptlang.org/play/index.html#src=function%20stringToNumber(s%3A%20string)%3A%20number%20%7B%0D%0A%20%20%20%20return%20parseInt(s%2C%2010)%3B%0D%0A%7D%0D%0A%0D%0Alet%20a%20%3D%20stringToNumber(1)%3B%0D%0Aalert(a)%3B%0D%0A

f:id:curama-tech:20180222115335p:plain

function stringToNumber(s: string): number {
    return parseInt(s, 10);
}

let a = stringToNumber(1); // 数値を渡しているためここでエラーが出る!
alert(a);

JavaScriptとTypeScriptの大きな違いとして、変数や関数に型定義を記述できる点があります。上記のコードでの: string: numberという記述がそれにあたります。TypeScriptのコンパイル時に、コンパイラは型チェックを行い、型の不整合があった場合にはエラーを発生させます。

例えば上記のコードにおいて、stringToNumber関数は引数に"100"のような文字列が来ることを想定しています。しかし、プレイグラウンド上でstringToNumber(1)の部分に赤線が引かれてある部分を見ていただければわかるように、ここでは型エラーが発生しています。なぜならば、この関数の引数として許容されるのはstring型であり、number型ではないからです。このように、動作前に型チェックを行うことによって、実行時に想定しない型が用いられてしまう可能性を軽減できます。

なお注意として、例えばstringToNumber("a")とした場合は、"a"はstring型のため、コンパイルは問題なく成功します。しかし実行時、関数からは NaNが返ってきてしまいます。NaNはnumber型ではありますが(https://github.com/Microsoft/TypeScript/blob/7b9ceb85fa4e19ade740faa2af2e00e62e16f7c9/lib/lib.es5.d.ts#L25 )、関数の戻り値に対してNaNを期待しない場合にバグを生む可能性があります。このような例については(少なくとも上記のコードでは)TypeScriptの機能で静的にチェックすることができません。ですので、TypeScriptを使用したからといって100%実行時エラーがなくなるわけでは無い点については注意が必要かと思います。

メリット2: 型がドキュメントの補助になる

メリット1から派生する恩恵として、例えばJavaScriptにて、

function hogeToFoo(hoge) {
  
  // ...なにか煩雑な処理...

  return fuga;
}

という関数があった時、「この関数には何を引数として渡せば良いのだろう?」と疑問に思うこともあるかと思います。そんな時、JSDoc等を用いてコメントベースで引数の情報をコードに残しても良いですが、例えばTypeScriptなら、

https://www.typescriptlang.org/play/index.html#src=interface%20Hoge%20%7B%0D%0A%20%20%20%20a%3A%20number%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20Fuga%20extends%20Hoge%20%7B%0D%0A%20%20%20%20b%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Afunction%20hogeToFoo(hoge%3A%20Hoge)%3A%20Fuga%20%7B%0D%0A%0D%0A%20%20%20%20%2F%2F%20...%E3%81%AA%E3%81%AB%E3%81%8B%E7%85%A9%E9%9B%91%E3%81%AA%E5%87%A6%E7%90%86...%0D%0A%20%20%20%20return%20%7B%20a%3A%20hoge.a%2C%20b%3A%20%22fuga%22%20%7D%3B%0D%0A%7D%0D%0A

interface Hoge {
    a: number;
}

interface Fuga extends Hoge {
    b: string;
}

function hogeToFoo(hoge: Hoge): Fuga {

    // ...なにか煩雑な処理...
    return { a: hoge.a, b: "fuga" };
}

として型情報を引数、戻り値に記述するだけで、この関数hogeToFooが、Hogeを引数に取りFugaを返す関数であることが一目瞭然となります。さらに、関数に適用する値についてはコンパイル時に静的チェックされるので、より実行時の安全が担保されます。

メリット3: 機能拡張・リファクタリングが容易

https://www.typescriptlang.org/ では、"JavaScript that scales."と標榜されています。この言葉が実感できる要素の一つとして、先述した静的型チェックの恩恵から生まれる、TypeScriptの機能拡張・リファクタリングの容易さがあるかと思います。例えば上記のコードにおいて、Hogeを拡張したいとします。すると、

interface Hoge {
    a: number;
    c: boolean; // プロパティを追加
}

interface Fuga extends Hoge {
    b: string;
}

function hogeToFoo(hoge: Hoge): Fuga {
    return { a: hoge.a, b: "fuga" };  // 型エラー!
}

Hogeに影響のある全ての部分において、このエラーがコンパイル時に発生します。つまり、Hogeの型付けを完璧に行っておけば、Hogeの拡張に対する影響範囲を漏れなく知ることができるということです。これで、開発時の心理的障壁を抑えることができます。

メリット4: ES2015以降のシンタックスが使える

TypeScriptにおいては、コンパイル時の設定でtarget: es5を指定することで、ES2015, ES2016, ... -> ES5の変換を行うことができます。例えば、オブジェクトのアサイン等を行うことができるスプレッドシンタックスを用いた場合は、

https://www.typescriptlang.org/play/index.html#src=const%20hoge%20%3D%20%7B%20a%3A%201%2C%20b%3A%202%20%7D%3B%0D%0Aconst%20fuga%20%3D%20%7B%0D%0A%20%20%20%20c%3A%203%2C%0D%0A%20%20%20%20...hoge%0D%0A%7D%3B%0D%0A%0D%0Aalert(fuga)%3B%0D%0A

const hoge = { a: 1, b: 2 };
const fuga = {
    c: 3,
    ...hoge
};

alert(fuga);

というコードが、

var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var hoge = { a: 1, b: 2 };
var fuga = __assign({ c: 3 }, hoge);
alert(fuga);

と変換されます。Babelを用いれば実現できる機能ではありますが、TypeScriptにビルトインされているため手軽に使える点がメリットかと思います。

さいごに

以上、実際にプロダクション環境でTypeScriptを使用してメリットと感じた点をいくつか挙げてみました。 TypeScriptを使ってコードを書いてみたいという方は、ぜひお声掛けください。 次回はnrskさんの予定です。

Prometheusを用いたSupervisor上のプロセス監視

はじめに

みんなのマーケットでSREチームに所属しています、千代田です。

今回は、弊社で導入している、Prometheusを用いたSupervisor上のプロセス監視について紹介します。
また、最後の方にSREチームとしての今後の課題について書きました。

Supervisorとは

Supervisor: A Process Control System
Supervisor is a client/server system that allows its users to monitor and
control a number of processes on UNIX-like operating systems.

くらしのマーケットでは複数のPythonアプリケーションをWSGIアプリケーションとして動かしており、
アプリケーションを実行するためのアプリケーションサーバとしてuWSGIを利用しています。
さらにブランチ別での環境構築システムや弊社独自のブルーグリーンデプロイを実現するために、
uWSGIプロセスの管理としてSupervisorを使用しています。

今回の監視を導入するに至った経緯

過去にSupervisor上のプロセスがfailしたことに気がつかず、翌日に発覚するといったことがありました。
その為、Supervisor上のプロセスの状態について検知するしくみが必要だと判断し、今回の監視を導入しました。

構成

監視はPrometheusnode_exporterで行っています。
アラート通知はAlertmanager経由でSlackにPOSTしています。

f:id:curama-tech:20180215132027p:plain

導入

Supervisorの動いているサーバを用意します(導入済みを前提としている為、割愛します)。

監視対象のサーバに最新のnode_exporterを導入します

もしDockerをすでに運用されている場合には、
サーバに直接的なオペレーションを行うよりも、コンテナのnode_exporterを使用することを推奨します。

wget https://github.com/prometheus/node_exporter/releases/download/v0.15.2/node_exporter-0.15.2.linux-amd64.tar.gz
tar xvzf node_exporter-0.15.2.linux-amd64.tar.gz
cd node_exporter-0.15.2.linux-amd64
mv node_exporter /usr/local/bin/node_exporter

Supervisordのコンフィグを設定します

inet_http_serverを設定する必要がある為、コンフィグに追記します。

[inet_http_server]
port = 127.0.0.1:30002

設定後はSupervisordを再起動する必要があります。

node_exporterを起動します

node_exporterはデフォルトでポート9100を使用する為、必要に応じてポートを変更します。

sudo /usr/local/bin/node_exporter --web.listen-address=:30001 --collector.supervisord --collector.supervisord.url=http://localhost:30002/RPC2

以上で、監視対象のサーバの設定は終わりです。

Prometheusで確認してみます

監視側のサーバにPrometheusを導入/設定する部分については割愛します。

Prometheusのコンソールにて、Expressionにnode_supervisord_upを入力して実行します。

f:id:curama-tech:20180215132031p:plain

Valueについてですが、1が起動している状態で、0が起動していない状態になります。
また、より詳細な判定を行いたい場合は、node_supervisord_stateを使用してください。
こちらのValueについては公式の説明を見た方がわかりやすい為、リンクのみとさせていただきます。

Prometheusのコンフィグを設定します

今回はAWS上で実行している為、ec2_sd_configsを使用しています。 必要に応じて変更してください。

global:
  scrape_interval:     15s 
  evaluation_interval: 15s 
rule_files:
  - '/etc/prometheus/alert.rules'
scrape_configs:
  - job_name: 'node'
    ec2_sd_configs:
      - region: ap-northeast-1
        port: 30001
      - source_labels: [__meta_ec2_tag_Name]
        target_label: name

alerting:
  alertmanagers:
  - scheme: http
    static_configs:
    - targets: ['AlermanagerのIP:9093']

Prometheusにアラートを追加します

下の例は、example_processがFORで指定した時間の間、停止していた場合にアラートを上げます。
また、ANNOTATIONSの中でアラート時に送信する内容を設定しています。
今回の場合は、$labels.nameでホスト名を、$valueでアラートが上がった際の値を取得しています。

ALERT ExampleProcessRunning
  IF node_supervisord_up{job='node',group="example_process",exported_name="example_process"} == 0
  FOR 5m
  LABELS { serverity = "warning" }
  ANNOTATIONS {
    firing_text = "Name: {{ $labels.name }}\n Value: {{ $value }}"
  }

AlertManagerで通知を行うようにします

今回は、Slackに通知を行います。
api_urlにはWebhookのendpointを設定してください。

global:

route:
  receiver: 'slack'
  group_by: ['name', 'alertname', 'severity']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 24h


receivers:
- name: 'slack'
  slack_configs:
    - api_url: 'https://hooks.slack.com/services/END_POINT'
      channel: '#channel'
      title: '[PROBLEM] {{ .GroupLabels.alertname }}'
      text: '{{ .CommonAnnotations.firing_text }}'

実際にアラートを送信します

supervisorctlでアプリケーションを停止させ、5分ほど待つとアラートが飛んできます。

supervisorctl stop example_process

f:id:curama-tech:20180215132034p:plain

以上で簡易的にですが、アラートを送ることができました!
必要に応じてsend_resolvedなどのパラメータを用いれば、改善したことを通知するアラート等を流すこともできます!

SREチームとしての課題

現在、くらしのマーケットでは各サーバで動いているアプリケーションをDockerへ移行していく為の準備を進めています。
その為、AWS ECS/EKS(サービス前ですが)などを用いての運用/監視の体制を作っていかなければいけません。
しかしながらDockerに関しての知見がまだまだ浅い為、こういった新しいものへの挑戦/解決方法が今後の課題となりそうです。

最後までお読みいただき、ありがとうございました!
また、一緒に働いてみたいといった方もぜひお待ちしてます!

次回は、Webエンジニアによる、TypeScriptの紹介記事です。

はじめに

みんなのマーケットでCTOをしている戸澤です。

このブログでは、みんなのマーケットの働き方やプロダクト開発で得た知見を共有していきます。 成功事例を載せるだけでなく失敗や苦慮していることも記事にして、読んだ方と議論してお互いに成功方法を探り、より知見を深める場にもしていきたいと思います。

最初となるこの記事では、会社の説明や技術スタックを紹介していきます。

会社

みんなのマーケットは、ハウスクリーニングや畳の張替えなど生活に関わるサービスを扱うプラットフォーム「くらしのマーケット」を運営するスタートアップです。 お掃除系のプラットフォームと思われることがありますがお掃除に限ったものではなく、リフォームや出張シェフ、出張DJなど幅広く生活に関係する203カテゴリのサービスを扱っています。

参考: カテゴリ一覧 - くらしのマーケット

その弊社は2011年1月に創業し、今年で8期目を迎えました。 東京・五反田に本社、宮崎に支社があります。 部署は4つあり、テクノロジー本部、マーケティング本部、コンサルティング本部、コーポレート本部です。

働き方

特徴は、ルールが少なく自由な職場環境であることです。自由な職場環境の一例として次のものがあります。

  • 18時以降ならアルコールOK(ビール、ほろよい、宮崎の焼酎等を完備)
  • ダンベル、チンニングスタンドで筋トレ可
  • ゲームOK
  • 自宅勤務OK
  • ネコが2匹いる

f:id:curama-tech:20180209122409j:plain

このような自由なルールがあるからといって、仕事時間にずっとゲームしたり自宅勤務ばかりで会社にまったく来ないといったメンバーはいません。 各メンバーのセルフマネジメントができているため、ルールが少なく自由な職場環境でも仕事が進められています。 また、数週間に一度のペースで、希望者どうしでたこ焼きを作って食べたり、キャンプにいったり、金継ぎ教室をオフィスで開いたりしています。

勤務時間も自由で、コアタイムなしのフレックス制を採用しています。そのため、出社時間は決まっていません。 たとえば早い人は9時に出社して18時に退社、遅い人は12時に出社して22時に退社するなどの各々の生活リズムにできるだけ合わせて働けるようになっています。

テクノロジー本部

テクノロジー本部のメンバーは、エンジニア(Web、スマホアプリ)、デザイナー、インフラ、ディレクターからなります。 普段は3チームに分かれて、新機能の開発、これまでリリースした機能の改善を進めています。 ディレクターだけでなく、エンジニア、デザイナー、インフラのメンバー全員が自分のメインの仕事をするだけでなく一緒にプロダクトのUXを考えて、どうすれば課題を最適に解決できるかを考えながら開発を進めています。

開発マシンは、全員MacBook Pro13"と27"モニタ(人によっては2台)を使っています(入社するときに必要なスペックを確認しています) 周辺機器も自由に使うことができ、REALFORCE, トラックボール、トラックパッドなどを導入しています。

また、朝会を開いてその日にやることの確認と問題の共有をして早期解決を目指しているほか、開発でわからないことがある場合は、すぐに聞ける雰囲気(集中モードのときは守られます)でペアプログラミングしながら教えてもらうなどしています。

技術スタック

創業当初より開発言語としてPythonを採用しています。 中途入社される方でPythonの開発経験がある方は少なかったですが、Pythonの特徴である可読性、習得のしやすさのおかげで早い段階で実際のシステム開発を進めることが実現できています。

初期はエンジニア1名で開発を進めていて、システム構成はPython2系 + Django + MySQL + Apache + さくらVPSでした。 初期の開発スピードの重視と仕様変更により、次第に初期システムはスパゲティコードになりました。 この状態では、変更に長い開発時間が必要で、変更するとリグレッションも頻発し、スタートアップのスピードが重視されるビジネス要件に耐えられなくなりました。 また、1名ではなんとか解読できていたコードも、エンジニアが2名、3名と増えていくにしたがって、解読困難になり、2015年秋にフルスクラッチでのシステムのリプレイスを開始しました。

リプレイスでは、アーキテクチャはマイクロサービスを、言語はPython3系, Node.js(TypeScript)を採用しました。 また、同時にプロビジョニングからデプロイまでのしくみも整備し、ユニットテスト、UIテスト、コーディング規約、コードレビューを徹底し、可読性の高いソースコードを維持しています。

アーキテクチャ

アーキテクチャとして、マイクロサービスを採用しています。 マイクロサービスをAPIとして、マイクロサービスがDBと通信します。Node.jsではそのAPIを使って、CRUDするしくみです。

バックエンド

マイクロサービスは、PythonのWebフレームワークのDjangoとFlaskで実装しています。 リプレイス当初はもともと開発経験の多いDjangoでスピード感をもって開発できるようにと採用しました。 現在は、マイクロサービスではDjangoほどリッチな機能は不要なので、新しいマイクロサービスからFlaskで実装しています。 また、最近ではHaskellのマイクロサービスも誕生しています。

マイクロサービスのAPIにCRUDし、クライアントにレスポンスを返すNode.jsでは、開発しやすくするために型定義が使えるTypeScriptで実装し、コンパイルして生成されたJavaScriptを実行しています。 また、Node.jsではノンブロッキングI/Oとイベントループのメリットを活かし、大量のクライアントからのリクエストとマイクロサービスへのリクエストを同時に処理できています。

フロントエンド

フロントエンドでは次のフレームワークや言語、ツールを採用しています。

  • Angular
  • webpack
  • Sass
  • Grunt
  • 画像やjsファイルの圧縮のモジュール

AngularもTypeScriptを使って実装しているため、型安全な開発ができています。

アプリ

SwiftとKotlinを開発言語に採用しています。 Kotlinは書きやすく開発効率が上がり、またNull Safetyなため、アプリのクラッシュが大幅に減りました。

インフラ

スケールが容易なAWSを採用しています。

導入しているツールは次のとおりです。

  • プロビジョニング、デプロイ: Ansible, whiptail
  • ログ: Logstash, Elasticsearech, Kibana
  • 監視: CloudWatch, CloudWatch Logs, Prometheus, Grafana
  • DB: PostgreSQL(RDS), Redis(ElastiCache)
  • プロセス管理: Supervisord, PM2
  • キャッシュ: Varnish
  • アプリケーションサーバ: uWSGI
  • Webサーバー: OpenResty
  • タスクキュー: Celery

Varnishは、マイクロサービスを使うにあたって、すべてのリクエストが毎回マイクロサービスまで到達してしまうと負荷が高くなることを軽減するために、マイクロサービスとNode.jsの間に配置し、マイクロサービスのレスポンスをキャッシュしています。

QA

導入しているサービスは次のとおりです。

  • CI: CircleCI, Drone.io
  • UIテスト: Appium, Selenium
  • ドキュメント: Sphinx, Qiita
  • バージョン管理: GitHub

開発環境

エディタは、PythonはPyCharmで、TypeScriptはVisual Studio Codeを使って開発するエンジニアが多いです。

社内ツールに、Tako(タコ)と呼ばれるブランチ毎のステージング環境を作るしくみがあり、ブランチ毎に動作確認を行っています。また、Ika(イカ)と呼ばれるツールでは、UIテストを自動化しています。

主な開発内容

テクノロジー本部では、くらしのマーケットに関わるすべてのプロダクトを開発しています。

  • くらしのマーケットのWeb
  • 店舗向けスマホアプリ
  • 一般ユーザー向けアプリ
  • 社内向け管理画面
  • 社内向け電話システム (IVR)

課題

チームが技術的課題として抱えているものです。

  • Angularの速度
  • バックエンドの速度向上
  • 増えるデータ量への対応(DB, ログ分析)

まとめ

  • 会社がみんなのマーケットで、やっているサービスがくらしのマーケット
  • セルフマネジメントを重視した自由な職場環境
  • マイクロサービスでNode.js(TypeScript)とPythonが主な開発言語
  • 速度向上、データ増加への対応が優先課題
  • 興味がある方はぜひお気軽に連絡してください (コーポレートサイト https://www.minma.jp/

技術スタックはかなり省略して書きましたが、詳細については今後の記事で紹介していきます。

次回は、インフラ担当による、supervisordのプロセス監視の紹介です。