自宅サーバーで Docker + Nginx + code-serverを動かそうとした話

# はじめに
兼ねてから目をつけていた[code-server](https://github.com/cdr/code-server)を動かして、**どこでもコーディング環境**を作ってみました。

ドメインを貼ってる自宅サーバーに、nginx経由でcode-serverにアクセスする流れを作ろうとしたら、ポートのプロキシ(?)でつまづいた話です。
こんなやり方をする方はなかなかいないと思いますが、備忘録のために。

## そもそもcode-serverとは？

ざっくり言うと、みなさんおなじみ**VSCode**をブラウザから開けるオンラインエディタです。
https://github.com/cdr/code-server
GitHubを開いた方は見て分かる通り、Docker上で動きます。一時期はSaaSとして、[Coder](https://coder.com)というものが無料で使えた時期もあるようなのですが、いまはエンタープライズ向けになっているようです。

ちなみにMicrosoftが昨年11月に、**Visual Studio Online**というオンラインエディタを公開していますね。（[Announcing Visual Studio Online Public Preview](https://devblogs.microsoft.com/visualstudio/announcing-visual-studio-online-public-preview/)）
ただ本家はAzureの月額課金が必要なので、ケチ臭い学生の僕としては無課金で使いたいところ。

というわけで、今回自宅で稼働させているサーバ機に**code-server**を導入してみました。

## 動作環境
* macOS 10.15.2
* Docker for Mac
* Nginx

動作環境が`macOS`と`Docker for mac`を使っている点に注意してください。
**code-server**自体はCentOSなどでも動かせますが、今回掲載しているコードが一部`Docker for mac`でしか動作しないので、その辺はよしなに解釈してください。

# 1. とりあえずcode-serverを動かす

## 1-1. docker-compose.ymlを作る

動作確認をするため、とりあえずcode-serverをdockerから起動します。と言ってもimageがあるので、そのまま使うだけです。
手軽に起動するために`docker-compose.yml`を作りました。そのままdockerコマンド叩いても使えます。

```yml:docker-compose.yml
version: "3"
services:
  code:
    image: codercom/code-server
    restart: unless-stopped
    command: code-server
    environment:
      PASSWORD: "<任意のパスワード>"
    ports:
      - 8080:8080
    volumes:
      - ./projects:/home/coder/project
      - ./code-server:/home/coder/.local/share/code-server
```

`./projects`が、code-serverのワークディレクトリです。`./code-server`は、vscodeの設定や拡張機能なんかが入ります。
今回はカレントディレクトリにディレクトリ作ってマウントしていますが、docker内部にvolume作っても良いかもしれないです。

環境変数`PASSWORD`は、ログインの時に必要なパスワードになります。
パスワードを設定しない場合は、

```yml
command: code-server --auth none
```
とするとパスワード認証なしで使えます。


## 1-2. 起動する

魔法の呪文。

```sh
$ docker-compose up -d
```

あとはブラウザから、 `http://localhost:8080`を開くと……

<img width="90%" alt="スクリーンショット 2020-01-29 0.23.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/622a2bf0-fc4a-58e3-9877-3f0841336890.png">

ここで先ほど設定したパスワードを入力。

<img width="90%" alt="スクリーンショット 2020-01-28 23.19.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/5ad23ecf-2ef4-4bdb-70cb-68126374525b.png">

はい、動いてる。終了。

# 2. 外からアクセスする

今回はただ動かすだけではなく、外からアクセスするためにもうひと工夫します。
図で表すとこんな感じですね。nginxもDockerで動かしています。ちょーわかりやすい。

<img width="90%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/bd1ddee7-fd84-e686-8b0b-a7c2f833a89b.png">

**一点だけ気にしなければならない**のが、ポート開放です。
自宅のモデムは大人の事情でNTTの`IPv6 over IPv4`になっているのですが、実はこれ80番ポート含め自由な番号を開放することができなくなっています。

とはいえ全くポート開放できないわけではないのです。
ブラウザで開くときに `example.com:99999`のようにポートを指定すれば良いだけ。
かっこ悪いけど、自分で使うためだけの自宅サーバーなので気にしない。かっこ悪いけど。

## 2-1. nginxを設定する
サーバー上でnginxをロードバランサとして動かしているので、そこに設定を加えます。
当初の設定はこんな感じに。

```conf:code.conf
# SSL関係の部分など、一部端折ってます。

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
  listen 80;
  server_name code.example.com;

  location / {
    proxy_set_header Host $host;

    # WebSocket
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_pass http://host.docker.internal:8080;
  }
}
```

code-serverは`WebSocket`を使っているので、そのための設定を記載しています。
こんなもんで動くやろ。いざレッツチャレンジ。

<img width="90%" alt="スクリーンショット 2020-01-28 23.55.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/042b12f9-e1cf-a4cd-fb18-cbcb011df828.png">

なんやて工藤🤔

`code.example.com:9999`で開いたのですが、しばらくロードした後で、上のような画面に。
よく見ると、`code.example.com/login`にリダイレクトされているようです。

**せっかく指定したポート番号が消えとる!!!**

## 2-2. 解決策

```diff:code.conf
# SSL関係の部分など、一部端折ってます。

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
  listen 80;
  server_name code.example.com;

  location / {
-   proxy_set_header Host $host;
+   proxy_set_header Host $http_host;     

    # WebSocket
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_pass http://host.docker.internal:8080;
  }
}
```

これだけの話でした。いや、これだけを探すのにかなり苦労したんだけども。

`$http_host`を用いることで、リクエスト元のポート番号をそのままヘッダーに付けてくれるので、リダイレクトのときにもポート番号を引き継いでくれるようです。
<img width="90%" alt="スクリーンショット 2020-01-29 0.42.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/a6cbe83a-f776-046a-cbf8-9fe9ed754d7b.png">

無事、アクセスできました🎉

# 番外編：iPadで使ってみた

Safariでアクセスして「ホーム画面に追加」をすることで、PWAにして使えるとのことだったので、試してみました。

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/99dcb3d7-b3e5-dfca-0634-d677a92a8a53.jpeg">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/b34f4aed-bccd-6747-4f27-2d7c4588253e.jpeg">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/40b23c19-be3a-f19a-9e50-8e70e0d795f0.jpeg">

不具合があったり、そもそもインターフェースがタッチパネル向けじゃなかったりと多少使いにくさはありますが、移動時間にちょっといじったりするのにはとても良さそうです。

ちなみにスクリーンショットでgitを使っていますが、これは以下のような感じでvolumeをマウントしてます。`:ro`は、ReadOnlyってことですね。

```diff:docker-copmose.yml
...
volumes:
  - ./projects:/home/coder/project
  - ./code-server:/home/coder/.local/share/code-server
+ - ~/.gitconfig:/home/coder/.gitconfig:ro
+ - ~/.ssh:/home/coder/.ssh:ro  
```

# おわりに

今回いろいろ調べてる間に見つけたんですが、本家VSCodeでも[ブラウザ実行できるようになったらしい](https://code.visualstudio.com/updates/v1_40)ですね。
とは言ってもまだテスト環境としてということみたいですが。

オンラインエディタはブラウザで実行できるので、iPadみたいなタブレット端末やマシンスペックの低い端末でも開発できるメリットがあります。
ネットに繋がなきゃいけない制約はありますが、もうすぐ5Gも一般利用できるようになりますし、もっと身近な存在になっていくんじゃないですかね。