お手軽 VSCodeOnline on VPS

# 手間無く快適な環境が欲しい
人間だれしも快適な環境に心惹かれるものです
例えば、何処でもブラウザからアクセスできるクラウドなVisual Studio Codeとかね

ただ、人間は同様に面倒くさがりです
面倒事を避けつつ可能なら課金も避けながら簡単に構築できる方法を模索します

<details><summary>~~注意 : おそらく一部の人にとってこれよりも良い方法があります。~~ 現在有効ではないかもしれないので、無視してください。</summary>
Visual Stuido Codespaces Self-hosted
Visual Stuido Codespacesはこの記事で紹介しているcode-serverの公式版みたいな物です。Azureではなく自前のVPSでもホストすることが出来ます。
詳しくは自分のブログの[こちらの記事](https://www.okin-jp.net/archives/2901)をご確認下さい。
</details>


# とりあえず概要
## 出来るもの
ほぼ無料でVPSとそれに乗った[Visual Studio Code](https://code.visualstudio.com/)[^1]がブラウザから使えるようになります
最近発表されたVSCode Onlineみたいなものですね

## GCPの無料枠とは
Google Cloud PlatformにはAlways Freeと呼ばれる無料枠があります。
>1 つの非プリエンプティブ f1-micro VM インスタンス（1 か月あたり）。次の米国リージョンのいずれかで利用できます。
- オレゴン: `us-west1`
- アイオワ: `us-central1`
- サウスカロライナ: `us-east1`
30 GB の標準永続ディスク ストレージ（1 か月あたり）
5 GB のスナップショット ストレージ（1 か月あたり）。次のリージョンでのみ利用できます。
- オレゴン: `us-west1`
- アイオワ: `us-central1`
- サウスカロライナ: `us-east1`
- 台湾: `asia-east1`
- ベルギー: `europe-west1`
ネットワーク トラフィックの制限:
- プレミアム階層を使用する必要があります。スタンダード階層は使用できません。
- トラフィックは北米の GCP リージョンから送信する必要があります。
- オセアニアと中国のリージョンを除き、まとめて送信できる下りトラフィックは 1 GB までです。
[出典 : Google Compute Engine の料金](https://cloud.google.com/compute/pricing?hl=ja#freeusage)

簡単に言うとアメリカの一部リージョンで共有vCPU1コア, メモリ0.6GB, ストレージ30GBまでの仮想マシンをほぼ無料で使えるということです
例外は1年に一回程度発生する1円(無料分の計算の問題)とネットワークトラフィックの制限ですね
ネットワークトラフィックについては、初回読み込み時にそれなりに通信するので課金が発生する可能性があります

## Oracle Cloudの無料枠とは
Oracle CloudにはFree Tierと呼ばれる無料枠があります

> 常時無償のクラウド・サービスにはどのようなものがありますか?
データベース
自律型データ・ウェアハウスと自律型トランザクション処理のどちらかを選択。合計2つのデータベース（それぞれ1 OCPUと20GBのストレージを搭載）。
コンピュート
2つの仮想マシン（それぞれ1/8 OCPUと1GBのメモリーを搭載）。
ストレージ
2つのブロック・ボリューム（合計100GB）。10GBのオブジェクト・ストレージ。10GBのアーカイブ・ストレージ。
その他のサービス
ロード・バランサ（1インスタンス、10Mbpsの帯域幅）。監視（5億個の取込みデータポイント、10億個の取出しデータポイント）。通知（1か月当たり100万の通知配信オプション、1か月当たり1,000件の電子メール）。アウトバウンド・データ転送（10TB/月）。
[出展：Oracle Cloud Free Tier | Oracle 日本](https://www.oracle.com/jp/cloud/free/)

簡単に言うとvCPU1/8コア, メモリ1GB, ストレージ50GBまでの仮想マシンを2台無料で使えるということです
こっちは東京にリージョンがありますし完全に無料なので枠が残っていればこっちの方が良いと思います
(2019/12/23現在 東京リージョンはリソースが枯渇しているのでほかのリージョンに建てることになります)

## code-serverとは
[code-server](https://github.com/cdr/code-server)は、[coder.com](https://coder.com)で提供されているクラウドVSCodeサービス(？)の一部をオンプレ、その他VPSで実行できるよう提供されているオープンソースのサーバーです
実行するとWebサーバーが立ち上がり一部制限があるものの概ねVSCodeがつかえます

クラウドVSCodeサービス自体について詳しくまとまっている記事はこちらです
https://qiita.com/MegaBlackLabel/items/bbec97db769667962bce

# 用意するもの
以下のうちいずれか
- Goolgeアカウントと[Google Cloud Platform](https://cloud.google.com/?hl=ja)の登録(要クレカ)
- [Oracle Cloud](https://www.oracle.com/jp/cloud/platform/overview/)のアカウントの登録(要携帯電話番号・クレカ)
- 任意のx86_64なVPS(arm系はだめです)

これだけです

当方の環境はGCPのUbuntu18.04とOracle Cloudの18.04で試していますが、基本的にはCentOS7等でも同じ手順で問題ないはずです

# やることリスト
1. GCPで無料枠(f1-micro) or OracleCloudで無料枠のVPSを建てる
2. [code-server](https://github.com/cdr/code-server)を設定する
3. そのあとにやるべきこと

## 1.VPSを建てる
**一般的なVPSを利用中で、VPSを使い回す方はこの手順をスキップできます**

### a.GCPのVPSの立て方

無料で使うに当たってやることが多いので、参考にしたQiitaの記事を張ります
https://qiita.com/riku-shiru/items/a870edd9dc0b132e092c
ざっくり言うと
今回はこの記事のRailsアプリケーションの代わりにcode-serverを動かします

### b.Oracle CloudのVPSの立て方
無料で利用する手順は難しくありませんが，Firewall周りの設定はiptablesを使うので後日別記事にします

## 2.code-serverを設定する

[code-server](https://github.com/cdr/code-server)はDockerコンテナとして立ち上げることも出来ますが、ビルド済みのバイナリを使って起動することも出来ます
リソースに余裕がない環境や既存のVisual Stadio Codeの拡張機能と共有したい場合などはバイナリで直接起動してしまった方が便利です

ビルド済みのバイナリはGitHubのreleaseにあるのでダウンロードしてきましょう。https://github.com/cdr/code-server/releases/latest に有ります
この記事を執筆したときの最新版は2.1692-vsc1.39.2だったので以下のようになります。ついでにtar.gzで圧縮されているのでtarで解凍します

```bash:bash
wget https://github.com/cdr/code-server/releases/download/2.1692-vsc1.39.2/code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz
tar -zxvf code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz
```

code-server1.1156-vsc1.33.1-linux-x64の中身を確認します

```bash:bash
$ ls -l code-server2.1692-vsc1.39.2-linux-x86_64
total 178368
-rw-r--r-- 1 okin okin      1133 Nov 15 08:31 LICENSE.txt
-rw-rw-r-- 1 okin okin      7789 Nov 15 08:31 README.md
-rw-r--r-- 1 okin okin    139147 Nov 15 08:31 ThirdPartyNotices.txt
-rwxr-xr-x 1 okin okin 182491117 Nov 15 08:31 code-server
```
code-serverがバイナリなので、いくつかのオプションを与えて起動してみます

```bash:bash
cd code-server2.1692-vsc1.39.2-linux-x86_64
chmod +x code-server 
./code-server --port=8443 --cert
```

実行してみるとおそらくこんな感じのログが標準出力されます

```bash:bash
info  Server listening on http://localhost:8443
info    - Password is 6583c222c481a2cbdcdb0fae
info      - To use your own password, set the PASSWORD environment variable
info      - To disable use `--auth none`
info    - Using generated certificate and key for HTTPS
```

Password is という形で起動時に自動生成されるログイン用のパスワードが表示されるのでそれを確認し、`https://<VPSのIPアドレス>:8443`を開きます

* 証明書が信頼できない旨が表示されますがhttpsでは通信できているので大丈夫です
(chromeで開けないときはそのページで`thisisunsafe`とキーボードで打つと進める事があります)

* パスワードは環境変数PASSWORDに指定することも出来ますし，リバースプロキシ等で認証を掛けるなら--auth noneというオプションで向こうにすることも出来ます
GCPやOracle Cloudの場合はクラウド側のファイアウォールでも8443を許可する必要があります

この状態でもscreen等で動かしておけば利用できます

## 3.systemdで自動起動させる
自動起動の方法は星の数ほどありますが，今回はUbuntuやCentOS7等で利用されているsystemdを使って自動起動を構成します
このときcode-serverはHome以下ではなく/usr/local以下などに移動しておいた方がいいでしょう
code-serverはシングルバイナリなのでそのままコピーするだけで大丈夫です

```bash:bash
sudo cp code-server /usr/local/bin
```

### systemdでのUnit定義の例(code-serverを/usr/local/binにコピーした場合)
好きなテキストエディタでUnit定義を書きます

```:/etc/systemd/system/code-server.service
[Unit]
Description=code-server
After=network.target

[Service]
Environment="PASSWORD=パスワード"
ExecStart=/usr/local/bin/code-server --port=8443
Restart=on-failure
User=実行させたいユーザー名

[Install]
WantedBy=multi-user.target
```

ここまで完了したらsystemdでstart出来るか確かめます

```bash:bash
sudo systemctl enable code-server
sudo systemctl start code-server
```
動作確認します

```bash:bash
sudo systemctl status code-server
● code-server.service - code-server
   Loaded: loaded (/etc/systemd/system/code-server.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2019-12-23 23:57:34 JST; 27s ago
 Main PID: 21694 (code-server)
    Tasks: 39 (limit: 1105)
   CGroup: /system.slice/code-server.service
           ├─21694 /usr/local/bin/code-server --port=8443 --cert
           ├─21727 /usr/local/bin/code-server --port=8443 --cert
           ├─21760 /usr/local/bin/code-server /src/build/code-server2.1692-vsc1.39.2-linux-x86_64-bu
           └─21775 /bin/bash

Dec 23 23:57:34 orcl systemd[1]: Started code-server.
Dec 23 23:57:36 orcl code-server[21694]: info  Server listening on https://localhost:8443
Dec 23 23:57:36 orcl code-server[21694]: info    - Using custom password for authentication
Dec 23 23:57:36 orcl code-server[21694]: info    - Using generated certificate and key for HTTPS
```
これで自動起動まで設定できました


## 4.そのあとにやると良いこと

* 適当なドメインでLet's encryptを使って証明書を取得する
    * "信頼できない証明書"をやめるため
* nginxやapacheでリバースプロキシを設定し，認証を掛ける
    * VirtualHostや認証，キャッシュなどの制御をするため
*  既にあるローカルのVisual Studio Codeと拡張機能を共有する
*  Setting Sync等で既存のVisual Studio Codeと設定を同期する

証明書とリバースプロキシについては，[https-portal](https://github.com/SteveLTN/https-portal#hybrid-setup-with-non-dockerized-apps)を使って証明書取得とリバースプロキシ設定，認証まで自動化してしまうと非常に楽です
dockerで動きますがdocker外のアプリケーションも`dockerhost`という名称で指定することができます
もしdockerとdocker-composeの環境が整っているのであれば`docker-compose.yml`を書いて,`docker-compose up`するだけです[^2]

```docker:docker-compose.yml
https-portal:
  image: steveltn/https-portal:1
  ports:
    - '80:80'
    - '443:443'
  environment:
    DOMAINS: 'username:password@example.com -> https://dockerhost:8443'
    STAGE: 'production'
```

systemdで自動起動させるときは[DockerComposeで管理されたコンテナをsystemdで動かすときのコツ](https://qiita.com/little-forest/items/82870224493fa72eab8d)などの記事が参考になります

拡張機能の共有については2つのやり方があります

1. code-server側の拡張機能ディレクトリ`~/.local/share/code-server/extensions/`を既存のvscodeの拡張機能ディレクトリのシンボリックリンクにする
2. code-serverに実行オプション`--extra-extensions-dir 既存のvscodeの拡張機能ディレクトリのフルパス`を付与する

# 最後に

最後にとても重要な情報です
code-serverは簡単にブラウザでVSCodeが動いて快適ですが，Terminalなども動くので乗っ取り等に気をつけてください

# おまけ
code-serverのオプション一覧です

```bash:bash
$ code-server --help
code-server 2.1692-vsc1.39.2

Usage: code-server [options][paths...]

Options
  --locale <locale>              The locale to use (e.g. en-US or zh-TW).
  --user-data-dir <dir>          Specifies the directory that user data is kept in. Can be used to
                                 open multiple distinct instances of Code.
  -v --version                   Print version.
  -h --help                      Print usage.
  --telemetry                    Shows all telemetry events which VS code collects.
  --extra-builtin-extensions-dir Path to an extra builtin extension directory.
  --extra-extensions-dir         Path to an extra user extension directory.
  --base-path                    Base path of the URL at which code-server is hosted (used for
                                 login redirects).
  --cert                         Path to certificate. If the path is omitted, both this and
                                 --cert-key will be generated.
  --cert-key                     Path to the certificate's key if one was provided.
  --format                       Format for the version. Allowed value is 'json'.
  --host                         Host for the server.
  --auth                         The type of authentication to use. Allowed values are 'password',
                                 'none'.
  --open                         Open in the browser on startup.
  --port                         Port for the main server.
  --socket                       Listen on a socket instead of host:port.

Extensions Management
  --extensions-dir <dir>                            Set the root path for extensions.
  --list-extensions                                 List the installed extensions.
  --show-versions                                   Show versions of installed extensions, when
                                                    using --list-extension.
  --category                                        Filters installed extensions by provided
                                                    category, when using --list-extension.
  --install-extension <extension-id | path-to-vsix> Installs or updates the extension. Use
                                                    `--force` argument to avoid prompts.
  --uninstall-extension <extension-id>              Uninstalls an extension.
  --enable-proposed-api <extension-id>              Enables proposed API features for extensions.
                                                    Can receive one or more extension IDs to
                                                    enable individually.

Troubleshooting
  --verbose                          Print verbose output (implies --wait).
  --log <level>                      Log level to use. Default is 'info'. Allowed values are
                                     'critical', 'error', 'warn', 'info', 'debug', 'trace',
                                     'off'.
  -s --status                        Print process usage and diagnostics information.
  --disable-extensions               Disable all installed extensions.
  --disable-extension <extension-id> Disable an extension.
  --max-memory                       Max memory size for a window (in Mbytes).
```


***

[^1]: 正確には[code-server](https://github.com/cdr/code-server)
[^2]: Firewallの設定を忘れないでください