コネヒト開発者ブログ

コネヒト開発者ブログ

開発環境改善としてDockerを導入した話

こんにちは。 5月よりコネヒトにjoinしたインフラエンジニアの永井(shnagai)です。

コネヒトにjoinして最初のタスクで、開発環境改善として一部のサービスにDockerを導入しました。 今回は、開発環境改善の道筋とそこから得られたDocker周りの知見について共有します。

開発環境で解決すべき課題

まずは、開発環境改善を行うにあたり、現状の環境の課題を明確にしました。 vagrantで開発環境を組んでいるとありがちな課題がコネヒトの環境にもありました。

1. 開発環境が不安定

vagrant環境が壊れる。。 突然VMが不調になりそれを直すために1~2時間持っていかれたり、 Aさんの環境でしか起きない再現不能の不具合が起きたり、 本来割くべき開発への時間が開発環境を直すことに費やされていました。。

2. アプリケーション増加によるホストOSの負荷問題

macで4,5,6個VM立てると重すぎて。。 サービス数が増える度に、VMの数が増えていきホストマシンのmacのリソースが限界を迎えていました。

3.本番/開発環境の乖離

初回に作成したVagrantのboxファイルを固定して使っていた関係で、 boxの中身はブラックボックスになりメンテナンスもほぼされていない状態でした。 本番との乖離は認識しているけど、追いつかせるのが面倒だし何が正しいかもいまいち不明瞭でした。

理想の開発環境

技術選定するにあたり、開発環境の理想の姿を定義しました。

  • 開発者はコマンド一つくらいのレベルで、さっとローカル環境を立ち上げる事が出来る
  • 誰でも同じ環境が立てられる(開発者/デザイナー関係なく)
  • 設定ファイルがgit管理されておりブラックボックスがない(ミドルウェア,OS,プラグインのバージョンも設定ファイルで管理)
  • 本番も同じ設定ファイルから作られたイメージがデプロイされ、開発環境と本番環境に全く差異のない状態

この理想の状態を実現出来るツールは、今のところDocker一択という話になり、Dockerの導入を前提として開発環境の改善を進める事にしました。

Docker導入の進め方

「新しいツールを入れる時は小さく始める」という原則に基づき、下記のような流れで実際の導入を進めました。

まずは、手元で動く環境を作る
↓
特定のメンバ(1,2名)にパイロット協力してもらい膿を出し切る
※パイロットメンバの協力が絶対不可欠
※いきなり全体展開は絶対ダメ(開発止まる。。。)
↓
ハンズオン形式で全メンバに概要説明した後Docker環境を手元に作ってもらう
↓
Docker関連の問題が出た時はすぐにキャッチアップして潰していく

アーキテクチャ

f:id:nagais:20170705162026p:plain

技術的TIPS

ここからは、Docker for Mac + docker-composerで開発環境を組む上で得られた知見をTIPS集的に書きます。

docker-composeで楽にコンテナ起動

docker-composeというと、複数のコンテナを一括で立ち上げれるという事がよく言われるメリットですが、 今回は、コンテナ操作のコマンドを容易に出来るという点に注目してdocker-composeを導入しました。

例えば、コンテナを立ち上げる時に

docker-composeだとymlに定義することで下記のコマンドでコンテナが簡単に起動出来ます。

※ -dはバックグラウンド起動

$ docker-compose up -d 
version: '3'
services:
  sample1:
    container_name: sample1-web
    build: .
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html:cached
    environment:
      LANG: ja_JP.UTF-8
      TZ: Asia/Tokyo
    command: bash -c "cd /var/www/html && sh docker-init-setup.sh && /usr/sbin/httpd -DFOREGROUND"
networks:
  default:
    external:
      name: sample_link

Dockerコマンドだと、毎回ポートマッピングやらコンテナ/イメージ名を意識しなければなりません(面倒くさい)

$ docker run -d -p 8080:80 -name sample1-web [イメージ名] 

日々使う開発環境では、コンテナを直感的に操作出来る事は重要だと思うのでこれはメリットだなと感じています。

コンテナ同士の通信

ホスト-コンテナ間の通信はポートマッピングしたポートで通信可能ですが、コンテナ間で直接通信したケースもあります。 そんな時は、docker-networkでユーザ定義NWを作り、そのNWにコンテナを参加させてあげます。

docker-networkの作成

## network作成
$docker network create --driver bridge sample_link

## 確認
$docker network ls|grep mamari_link
54528e6eacf6        sample_link                 bridge              local

コンテナ起動時に、networkを明示的に指定してあげる事で、同一docker-networkに存在するコンテナ同士はコンテナ名で通信する事が可能になります。

docker-composeの場合は、ymlに下記のように定義

networks:
  default:
    external:
      name: sample_link

dockerコマンドの場合は、下記のように –netオプションを使用

docker run -d --net=sample_net

Docker for Mac遅すぎる問題

Docker for Macは、MacのHyperkitという仮想マシンレイヤ(linux)を使って、シームレスにDockerをMac上で動かすものですが、このHyperKit経由でのディレクトリマウントが遅い問題が出ました。

これは、Docker for Mac の17.05以降でサポートされた、VOLUMEのcachedオプションで回避しました。

たいてい開発環境だと、ホストでコードをいじりつつコンテナ上のwebアプリで動作確認をするというパターンかと思います。cachedと聞くと時間差がありそうで少し不安になりますが、今のところ同期が遅いというような声は聞こえてきていませんので問題なく動いています。(Docker for Mac Edgeの17.05でたまに同期されない問題が出たので、17.06 stableに上げたところ解決しました)

    volumes:
      - .:/var/www/html:cached
今だと、Docker for Macの17.06がstableなので、そちらを使うとcachcedオプションが使えます!!

DockerコンテナからVirtualMachineへの通信がたまに途切れる問題

これが、一番頭を悩ませた問題なのですが、コンテナからVM上に立っているredisに対して通信をしていたのですが、たまにredisと通信が出来なくなるような事象が発生しました。

実際に、redis-cliを使って計測した結果が下記です。

アベレージは、0.72sなのにたまに45sの通信が発生。これにあたるとアプリが異常に遅くなるという事象でした。。

$ docker exec redis redis-cli --latency -h 192.168.33.200 -p 6379
min: 0, max: 45, avg: 0.72 (11446 samples)

redisをコンテナで立てて、同じ試験をした結果抜群の安定感を確認。

$ docker exec redis redis-cli --latency -h docker-redis -p 6379
min: 0, max: 3, avg: 0.21 (10061 samples)

最終的な原因まではわからなかったのですが、dockerコンテナからVMへの通信は、ホストマシン間で複数のブリッジNWを経由して行われるので、不安定になることがありそうです。素直にDockerコンテナに集約してあげた方がいいかもしれません。

さいごに

Dockerfileはインフラの持ち物でなく、みんなで育てていきたい

という気持ちが開発者に伝わり、自分で新サービスdockerで作り始めるメンバがいたり、すぐにDockerfileの修正点指摘してくれたりコード化されたメリットを早くも感じ始めています。

Vagrant構成で苦しんでいる方がいれば、そんなに障壁は高くないのでまずは1サービスから小さくDocker導入を進めてみればいかがでしょうか?