フルリモート時代に対応するための開発環境におけるクラウドコンピューティング

はじめに

タイトルはカッコよくしてみましたが、話はよくあるEC2で開発環境を作るってやつです。 ここ半年ぐらい運用しているのですが、大きなトラブルなく運用できています。 特に最近の外出できない事態において、本当に半年前にやっておいて良かったなぁと実感しています。

利点としては以下のようなことが挙げられます。

  1. コンピューティングリソースはクラウドで割り当てるので、新規の開発者に物理マシンを支給する必要がない(少なくとも高価なものはないです。支給の必要がある場合にはうちの会社ではChromeOSを支給しています)
  2. 全てのソースコードクラウド上で管理されるので、物理的な事故によるセキュリティの心配がなくなる
  3. 何かトラブルがあった時に熟練した開発者が物理的に隣にいなくても、リモートから調査できる

おまけ tmuxセッションでペアプロもできる

これについて書いていきますが、基本的にはWebアプリケーション開発における話だと思ってください。

コンピューティングリソースはクラウドで割り当てるので、新規の開発者に物理マシンを支給する必要がない

これに関しては割安かどうかは会社によるかもしれません。うちはt3a.xlargeを各自に割り当てています。 シンガポールリージョンで1時間0.1888USDなので、単純計算で一日付けっ放しにしたとすると1ヶ月で、 0.1888 * 24 * 30 = 135.936USD ですね。

ただ普通のプロダクトでt3.xlargeは必要ないかなぁという気もします。 僕たちの開発環境はk8sをプロダクションとほぼ同様の環境で(リソース的に同様ということではなく、構成は同様という意味で)動かしているので、通常の開発環境のWebアプリケーションよりはリソースを使っています。

僕の使ってるローカルのマシンはもう相当ガタがきているので、これをストレスなく動かせる自信がないです。これもクラウドにして良かったなーと思う一つの理由ですね。

あとはスクリプトを組んで、ちゃんと使ってない時間は落とすようにすれば経費は大分削減できると思います。 単純計算で一日に8時間労働するとすると、1/3になるので。(週末や土日は計算がめんどいの省く)

全てのソースコードクラウド上で管理されるので、物理的な事故によるセキュリティの心配がなくなる

外出時にマシンを落としたりした時にリモートでロックかけなきゃ!みたいな話はよく聞きますが、ローカルにデータは落とさないという制約さえ守っていればもうその心配はなくなります。 ただ公開鍵を開発環境のマシンから削除すればいいだけです。 自分自身でも開発していて、特にアプリケーションのソースコードをローカルに落とす必要は感じないです。

何かトラブルがあった時に熟練した開発者が物理的に隣にいなくても、リモートから調査できる

フルリモートにおいて、これはすごく助かっています。 日々成長、変化していくプロダクションコードにおいて「xxを最新にあげたら動かなくなった!」みたいな悲鳴はよく聞きます。 その度に詳しい開発者が隣に行って調査して解決する、みたいなのはよく見られる光景です。

物理的に近い距離にいればそんなに苦ではないですが、フルリモートではそれも難しいです。何らかのオンラインミーティングサービスを使えばスクリーンシェア機能を通じてある程度近いことはできると思いますが、それでも簡単ではないでしょう。

この環境を整備しておけば、sshで他人の開発環境に入って容易に調査することができます。 前述のおまけのところに書きましたが、tmuxセッションを使ってペアプロもできます。

前置きが長くなりましたが、構築のコツや運用などまとめておこうかなーと思います。

構築

stepサーバを立てる

めちゃめちゃ安いサーバでいいと思うので、各開発環境へのステップサーバとして立ち上げておくと便利です。 このサーバを各開発環境へのセットアップスクリプトの流し込みや、/etc/hosts を通じてのローカルマシンからリモートマシンへのアクセスのアドレスの解決に使います。(詳しくは後述します)

セットアップに関しては、僕たちはansibleを使ってスクリプトを組んでいます。新しい開発者が入ってきたら、スクリプトを流して待つだけで新しい開発環境が出来上がります。

EC2インスタンスを立てる

stepサーバと同じVPCを設定して、private ipをstepサーバから解決できるようにしておきます。

それ以外は特別なことはないのですが、開発マシンに一様に割り当てるIAMロールのセットアップをしておくと良いです。 それによって、そのロールの権限を変えることでAWSリソースへのアクセスを制限できるので、各開発者がリモートマシンに自身のAWSのcredentialをセットアップする必要がありません。

僕たちはAWS SSMのparameter storeを使って環境変数などを管理していますが、各開発環境からのアクセスもロールを通じてこの値を取ることができます。

AWS Systems Manager Parameter Store - AWS Systems Manager

環境変数の管理方法は色々あると思いますが、プロダクションでAWSを使っているなら、このあたりもAWSに寄せておくと便利に使えていいですね。

stepサーバのhostsの登録

ansibleを使っているなら、スクリプトを流すために開発環境のアドレスをansibleのhostsファイルに書いておくと思います。

[dev_machines]
joe_re1.dev ansible_host=172.0.0.1
joe_re2.dev ansible_host=172.0.0.2
joe_re3.dev ansible_host=172.0.0.3

おそらく誰がやってもこんな感じでしょう。ホスト名はなんでもいいのでsshの時に使いたい名前を指定してください。ansible_host=の右辺はEC2のprivate ipアドレスになります。

ssh時にここで設定したマシン名を使えるようにする為、これを元にstepサーバの/etc/hostsを生成します。これもansibleスクリプトで行なっているので、向き先が自分(127.0.0.1)のスクリプトを各開発マシンに実行するものとは別に用意しています。

冪等性を保つため、最初のステップ(set default host)でからのファイルを作成、2つめのstep(Add the inventory into /etc/hosts)でansibleのhostsから/etc/hostsの更新を行なっています。

  - name: set default host
    copy:
      src: ../resources/dev_step_settings/hosts
      dest: /etc/hosts

  - name: Add the inventory into /etc/hosts
    lineinfile:
      dest: /etc/hosts
      regexp: '.*{{ item }}$'
      line: "{{ hostvars[item]['ansible_host'] }} {{item}}"
      state: present
    when: hostvars[item]['ansible_host'] is defined
    with_items:
      - "{{ groups['dev_machines'] }}"

あんまりこれを詳しく説明しだすとansibleの説明になってしまうので省きます。気になる方はこちらが参考になると思います。

Using Variables — Ansible Documentation

シェルなどでも簡単に書ける処理だとは思うので、どんなプロビジョニングツールを使っていても大丈夫だと思います。

開発者からsshの公開鍵をもらう

もらった後にstepサーバと開発環境サーバの両方に公開鍵を登録しておきます。 ansibleではauthorized_keyを使って簡単に書けます。

authorized_key – Adds or removes an SSH authorized key — Ansible Documentation

セットアップスクリプトを開発環境サーバに流す

基本は待つだけです。自分たちがやっているのは、細かいところを省けばgithubリポジトリのクローン、docker containerのビルド、k8s(microk8s)のセットアップ、開発に必要な環境変数の設定あたりです。

前述のように、awsリソースへのアクセスが必要なものはロールを通じて行います。

開発者側の設定

ssh config

以下のようなssh configの設定を開発者に追加してもらいます。

stepサーバへの設定

Host dev-step-server
  Hostname ec2-xxxxxxxxxxx.ap-southeast-1.compute.amazonaws.com
  User ubuntu
  Port 22
  IdentityFile ~/.ssh/id_rsa

開発マシンへの設定

Host joe_re.dev
  Hostname joe_re.dev
  User ubuntu
  Port 22
  ProxyCommand ssh dev-step-server -W %h:%p

stepサーバへの設定の部分は特に説明する部分はないですね。

開発マシンへの設定の方もインフラエンジニアの方なら定番かもしれませんが、ProxyCommandを使って、接続先のホストの解決をdev-step-serverに任せています。 -Wオプション以下の%hに本来の接続先のホストが、%pにポートが入ります。なのでこの場合、joe_re.dev:22となります。

joe_re.devの名前はstepサーバのhostsに登録済みなので、これを解決してリモートサーバにアクセスができます。

/etc/hosts

chrome.local ドメインのアクセスをlocalhostに自動でしてくれます。 メンバーの何人かはChromeOSを使っていますが、現在のChromeOSは/etc/hostsの設定を行うことができないように制限されているので基本的には.localドメインで開発をしています。 なのでChromeを使う限りにおいては /etc/hostsをいじる必要はありません。ただし、手元のシェルから何かしたいなーとか他のブラウザからアクセスしたいなーとかの場合は設定する必要があります。

port forwardingで開発環境のサーバにアクセス

Mac, Linux, WIndows

この手の話ではど定番のやつですね。他の方法があるなら知りたいです。

自分たちはk8sを開発環境で動かしているので、-Lオプションを使って、リモートの開発サーバをさらに経由してk8sのアドレスにアクセスしています。

ssh -F ~/.ssh/config -L 9001:10.152.183.218:443 joe_re.dev

Windowsだとsshするのにちょっとコツがいるのかもしれませんが、開発環境としては自分は使っていないこともあって、把握していないです。

ChromeOS

ChromeOSは少し特殊な設定が必要です。最新のChromeOSはターミナルのssh clientとブラウザと完全に独立しているので、ターミナルでport forwardingしてもブラウザからは開発環境にアクセスすることができません。 なのでChromeのエクステンションでこれを解決します。 chrome.google.com

設定はこんな感じです。 f:id:joe-re:20200518072625p:plain

このエクステンション上でProxyCommandをうまく噛ませる方法が分からずに、ここで今まで避けてたPublicDNSが登場しています。 悔しい。。どなたか設定方法ご存知の方は教えてください。

構築はここまでで完了です。

運用

sshを通じた開発

開発環境は開発者個人に割り当てられているものなので、配布したあとは自由にセットアップしてもらっています。仮に壊してもansible scriptをapplyするだけですぐに元に戻せます。

なのでvimemacsなどのターミナルフレンドリーなエディタを使っている人であれば、そのまま開発環境上にセットアップするだけです。 ホストのeditorを使う場合はVSCのRemoteSSHプラグインが便利です。

code.visualstudio.com

正直使う前はssh経由でホスト上のエディタで開発するとかエクスペリエンスそんなによくないんだろうな〜と思っていたのですが、使ってみるとめっちゃ快適です。 ほぼローカルと遜色なく開発できます。

トラブルシュート

冒頭に述べた話ですが、何か自分では解決できそうにない未知のエラーに遭遇した場合に、ごめんちょっと見てーってslackで問いかければ、詳しい人がリモートから直接調査することができます。

フルリモートの環境において、あれ見た?このコマンドの結果はどう?みたいなやり取りを省けるので便利です。

全ての開発環境に何か変更を加えたいとき

長く運用していると、あるタイミングで全ての開発環境に一斉に実行して欲しいコマンドがあったりします。アプリケーション側であれば、大体dockerfileに記述するので、最新化してリビルドしてってslackに書けばそれで終わるのですが、k8sのバージョンをあげた場合などに必要になったりすることが多いです。

そんな時はansible scriptを全台に向けて流すだけなので簡単です。次回以降のセットアップにも必要になるならそのまま残しておきましょう。

おわりに

そんなこんなで割と快適な環境で開発できています。 フルリモートを意識すると、物理的なマシンの管理にリソースを割かなくて済むのは非常にありがたいと思います。 まだまだ改善できるところもあると思うので、良いアイデアなどお持ちの方はぜひ教えてください。