iPadを開発機にするまでの道のり~コンテナを用いた開発環境の構築~

# はじめに

業務ではmacOSを使っているのですが、プライベートでは休日にカフェを転々としながら作業をするため、バッテリーの持ちがよく、軽い開発機が欲しくなりました。

私がしたい事としては、開発で利用するマシンへのSSH接続とブラウジングの二つ。
iOSアプリやAndroidアプリといったモバイル機の開発はせず、SSHしてつないだマシン上でDockerやKubernetesでごにょごにょすることがほとんどです。
なので、軽くてめっちゃバッテリーのもつブラウジングできるSSH用端末が欲しかったわけです。
ブラウジングできるという要件は調べ物のため以外に、開発で利用するマシンでHTTPポート等を公開した場合に、そこへブラウザでアクセスして確認するためです。

そこで私はiPad Proを選択しました。

# iPadを開発機に使うということ

iPadで開発をしたいと考える人は世の中に他にもいるようで、ヤフってみるとそれなりに出てきます。iPadだけで[Pythonを使った開発ができるPythonistaのようなアプリ](https://apps.apple.com/jp/app/pythonista-3/id1085978097)や、[code-serverを使った開発環境構築](https://qiita.com/upaldus/items/73b2a8f0a54e1fbc5361)をしている人など、みなさん色々試行錯誤されているようです。

その中でも特に影響を受けたのがFatihさんの ["Using the iPad Pro as my development machine"](https://arslan.io/2019/01/07/using-the-ipad-pro-as-my-development-machine/) という記事で、コンテナで開発環境を作り、そこへSSHするというものでした(現在はコンテナを用いた開発環境は使っていないようですが)。

それをベースにコンテナを使い"容易に使い捨て可能な開発環境の構築"、"開発環境のアップデートの自動化"、"データの永続化"を実現しています。ここでは細かな手法は書ききれないため、ざっくりとどのように開発環境を構築したかを記載していきます。もしこんな記事でも見ていただき、需要があれば細かな実装に関する詳細についても別記事で紹介しようと思います。

# 構築した環境

構築した環境は以下の図のようなものです。
VPN経由でVMへアクセスし、そのVPNを使いSSH接続した後、開発に利用するアプリケーションがインストールされたコンテナへdocker execして開発を行います。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/df425557-4869-0c14-d2b4-7ca8ad793273.png)

詳細を以下で説明してきます。

# 開発環境として利用しているVPS

私はプライベートで利用する開発環境はVPS上へ構築しています。
個人的に気に入っているVPSは[Vultr](http://vultr.com)で理由は以下の3つです。

1. シンプルな料金設定でプリペイドで利用料を払うことができる
2. 結構安い
3. 日本リージョンが選択できる

# 開発環境への接続

### 開発環境へのセキュアなアクセス

プライベートの内容しか含まれないと言っても、通信内容を見られるのは気持ちがよくありません。

例えば開発環境で動かしたWebサービスへアクセスするためにHTTPのような暗号化されていない通信が経路の途中で見られてしまうため、そのためにポート解放するのは避けたい。
SSHポートフォワーディングを利用することでセキュアにアクセスできるようになりますが、都度ポートフォワードするのは面倒。

そのため開発環境への接続はVPNを利用しています。
そうすることで開発環境側のポート解放はVPNで用いているポートのみに絞ることができます。

VPNにはwireguardを利用しており、これもコンテナで起動しています。[wireguadにはカーネルモジュールとして実装していないgolangの実装](https://git.zx2c4.com/wireguard-go/about/)もあり、それを利用するとコンテナとしてwireguardが簡単に起動できます。wireguardにはiOS用のクライアントもあるためとっても素敵です。
wireguardは任意のUDPポートを1つだけ公開するだけで済むところも素敵です。

[こんな感じ](https://github.com/uesyn/dotfiles/tree/master/workspace/wireguard)でコンテナ化しています。

### SSHクライアント

iOSにはいくつかのSSHクライアントが存在しますが、fatihさんと同様[Blink](https://www.blink.sh)というアプリを利用しています。有料無料問わずいくつか試しましたが、Blinkを利用することに決めました。

完全に個人的な感想となってしまいますが、
1. 他のクライアントよりも描画が早い
2. 描画が崩れがない(他のクライアントではコンテナへexecした上でtmuxを起動した時に崩れてしまうものもあった)
3. Moshも使える
4. OSSで公開されているため、更新が止まってもなんとか頑張れる


という理由でこれを選択しました。

# 開発環境に利用するコンテナイメージ

開発環境もコンテナ化してあります。コンテナ大好きなので。
ベースイメージはdebianを利用し、自分用の`devbox-base`というベースイメージを作成しています。
このイメージにはバージョンが変わっても大きく挙動が変わりそうになりものを入れています。
作成しているベースイメージは[こんな感じ](https://github.com/uesyn/dotfiles/blob/master/workspace/base/Dockerfile)です。

開発に利用する言語やコマンドでバージョンの更新が開発に影響しそうなものは、`app-get`という雑なインストールスクリプトを書いてインストールし、最終的に開発に利用するコンテナイメージを作成しています。
あまりにも雑なスクリプトなので、今後[asdf](https://asdf-vm.com/#/)や[linuxbrew](https://docs.brew.sh/Homebrew-on-Linux)への移行を検討中です。
以下はそのイメージを作成するDockerfileをわかりやすくしたものです。

```Dockerfile
FROM uesyn/devbox-base:latest as app-get
RUN git clone https://github.com/uesyn/dotfiles.git /dotfiles
ENV PATH="/dotfiles/bin:/usr/local/bin:$PATH"

FROM app-get as kubectl
ENV KUBECTL_VERSION v1.17.0
RUN app-get install kubectl@${KUBECTL_VERSION}

FROM devbox-base:latest
COPY --from=kubectl /usr/local/bin /usr/local/bin
COPY entrypoint-devbox /entrypoint
ENTRYPOINT ["/entrypoint"]
```

実際のDockerfileは[こちら](https://github.com/uesyn/dotfiles/blob/master/workspace/devbox/Dockerfile)です。

# コンテナイメージの更新

手動でイメージを更新するのは面倒なので、github actionsを利用して自動更新するようにしています。

- ベースイメージは1週間に1回
- 開発環境は定期的に利用するアプリのバージョンの更新を確認
  - 新しいものがあれば自動的にPR
  - mergeすると新しい開発環境用のコンテナイメージがビルドされる

上記のように自動化することで、開発に利用するアプリの更新を自分で確認しに行く必要がなくなり、更新を入れるか入れないかを自分で判断できます。github actions便利ですね。
アプリの更新の有無は先ほどの自作スクリプトにバージョンリストを取得する実装をしているので、それを利用しています。

以下はアプリに更新があった時の実際のPRです。

<img width="1032" alt="スクリーンショット 2019-12-16 16.56.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/b873c302-c6ba-f94c-d7ed-54334ae9de2b.png">

実際のgithub actionsのworkflowは[こちら](https://github.com/uesyn/dotfiles/tree/master/.github/workflows)を参照ください。

# データの永続化

VM上に開発環境を構築し、不要になったら削除するようにできるようにしているので、削除したくないデータは永続化する必要があります。
開発しているソースコードはgithub等に上げておけば問題ないのですが、例えば私の場合メモは@mattnさんの[memo](https://github.com/mattn/memo)というアプリを利用しているため、このデータはどのマシンでも共有で永続化しておきたいので、Dropboxを使っています。memoすごくシンプルにササッメモがとれてすごく素敵です。

DropboxにはLinux用クライアントもあり、それもコンテナとして起動しています。
Linux用のDropboxクライアントは何もいじらないとHOMEディレクトリへDropboxというディレクトリを作成し、その中身をsyncしてくれるようです。
なので開発用コンテナのHOMEディレクトリと同じ領域をDropboxコンテナのHOMEディレクトリとしてマウントさせ、永続化したいデータはそこに保存するようにしています。

# 最後に

プライベートで利用するため、結構雑な感じで作っていますが個人的に満足いく環境が作れました。
今のところiPadと上記の環境で困っていることはありません。
この上で開発するアプリよりも環境構築の方が楽しいという状態になっていますが、まぁいっか！
本当は細かい環境構築のこだわりポイントがあるのですが、それは別の機会に！