リモートマシン上にお手軽インスタンスを立ち上げて、ハッピー開発ライフ

ついにM1 Macに乗り換えました。 しかし毎回darwin/arm64向けの何かを気にするのが大変です。 ということで、この機会にお家のamd64マシンを開発用に使うことにしました。

はじめに

多分M1は関係ありません。

そして解説記事ではなく備忘録なものなので、わかりにくいかもしれません。

リモートマシンについて

お家にあるamd64マシンは、M75q-1 Tinyというlenovoから出ている小さいデスクトップPCです。 新品実質3万円ぐらいという激安で購入できた時期があり、既に2年ぐらい自宅サーバーとして使い倒しています。 部屋のCO2濃度をモニタリングしてくれたりもしています。 スペックはこんな感じで、愛をこめてtinyという名前をつけています。

  • OS: Ubuntu 20.04
  • CPU: AMD Ryzen 5 PRO 3400GE
  • メモリ: 16GB
  • SSD 128GB(多分)

やりたかったこと

VSCodeにRemote - Containersという天才的な拡張機能が存在することをご存知でしょうか? Dockerfileと設定のjsonを書くだけで、VSCodeのガワをそのままにコンテナの中で開発することができるやつです。

このRemote - Containersが最近、更に便利になりました。 具体的には、リモートマシン上で動いているDockerデーモンをSSH経由で使うことができるようになりました。 つまり、M1 Macからtiny上で動くDockerデーモンを使うことで、「M1にも関わらずAMD64のCPU・イメージ上で開発」することができます。 これがやりたかったわけです。

実はこれは、いわゆるIntel Macの時代からやりたいと思っていました。 以下の理由があります。

  • Docker for Macが遅い
  • クライアント端末の性能は何でもよくなる(極論Macbook Air最小構成)
  • (つまり)M1に簡単に移行できる
  • Remote - Containers on Remote Machineのワクワク感

お手軽インスタンスを立ち上げる

やりたかったことを達成するには、普通にtinyにDockerをインストールすればよい気がしますが、実は個人的な問題があります。 tinyでは常時自宅サーバーが動いています。 以前このようなことを試したとき、開発中にメモリとCPUを食べすぎて、自宅サーバー用のリソースが足りなくなりました。 なので、CPUとメモリを制限したVMをtinyの中に立ち上げて、開発用マシンとすることにしました。 実際、インスタンスを自由に作ったり壊したりできると嬉しい場面もありますし。

こういう場合には、ESXiとかVirtualBoxを使うと思うんですが、それぞれ次の理由で却下されました。

  • ESXi

それだけのために使うのは仰々しいような…

メモリ足りなそう

ネットワーク関係が面倒そう

  • VirtualBox

何故か壊れました

そんなときに颯爽と現れたのが、Multipassです。 MultipassはVirtualBoxと違って動きました。 このようにCLIで簡単にインスタンスを作成/削除できます。

1
multipass launch --name ubuntu --mem 4G --disk 20G --cpus 4
1
multipass delete ubuntu

(ubuntuという名前で、メモリ4GB、ディスク20GB、4 vCPUのインスタンスを作成しています。デフォルトでUbuntu 20.04のイメージになります。)

そして、このようにホストからアクセスできるプライベートIPアドレスを振ってくれます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ multipass info ubuntu
Name:           ubuntu
State:          Running
IPv4:           10.138.112.156
                172.17.0.1
Release:        Ubuntu 20.04.4 LTS
Image hash:     c33e4d8f8fd5 (Ubuntu 20.04 LTS)
Load:           0.08 0.15 0.14
Disk usage:     7.6G out of 19.2G
Memory usage:   1.0G out of 3.8G
Mounts:         --

立ち上げたインスタンスのセットアップ

ということで、tinyにインスタンスを立ち上げることができました。 あとは立ち上げたインスタンスにDockerを入れて、SSHの公開鍵を追加すれば、使われる準備万端です。

インスタンス内のシェルに入ります。

1
multipass shell ubuntu

Docker公式の便利スクリプトでDockerをインストールします。

1
2
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

Dockerをrootless modeにするためにいくつかコマンドを実行します。

1
2
3
4
sudo apt-get install -y uidmap
dockerd-rootless-setuptool.sh install
echo "export PATH=/usr/bin:$PATH" >> ~/.bashrc
echo "export DOCKER_HOST=unix:///run/user/1000/docker.sock" >> ~/.bashrc

SSHクライアントの公開鍵を~/.ssh/authorized_keysに追加します。

1
vim ~/.ssh/authorized_keys

多段SSHの設定

あとはM1 Macから、立ち上げたインスタンスにSSH接続できるようにします。

立ち上げたインスタンスは、ホストOSからしか接続できないネットワークにいます。 つまり、 10.138.112.156 はtinyで動いているホストOSからのみアクセスできます。

1
2
3
4
$ multipass info ubuntu
(中略)
IPv4:           10.138.112.156
                172.17.0.1

したがって、tinyで動くUbuntuを踏み台にしてSSHをします。 次のような ~/.ssh/config を書いています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Host tiny.local
  (tiny接続情報)

Host ubuntu.tiny.local
  HostName 10.138.112.156
  User ubuntu
  Port 22
  UseKeychain yes
  IdentityFile ~/.ssh/id_rsa_ubuntu
  ProxyJump tiny.local

最後の ProxyJump を使うだけでほぼOKです。 これにより、次のコマンドで立てたインスタンスにSSH接続できるようになりました。

1
ssh ubuntu.tiny.local

やりたかったことをやる

実際に立てたインスタンス上でRemote - Containersをやりたいときは、次の手順を踏みます。

  1. Remote - SSHで立てたインスタンスに接続する。
  2. 作業したいリポジトリ・ディレクトリを開く。
  3. コマンドパレット(Command + Shift + P)を開いて、Remote-Containers: Open Folder in Container を選択

あとがき

実はこの記事も、立てたインスタンス上で動く(Hugoのイメージの)コンテナ上で、書きました。 天才的なことにポートフォワーディングもよしなにやってくれます。 コンテナ内で hugo serve してもM1 Macの方の localhost:1313 からプレビューが見れます。 とても素晴らしい体験で、ハッピー開発ライフを送っていけそうな気がしています。