Gitpod をローカルのk8sにインストールしてみた（再チャレンジ編）

## はじめに

[Gitpod をローカルのk8sにインストールしてみたらできなかった件](https://qiita.com/kiikura-biz/items/1a89a57012a2beb5a28b)の続編になります。続編のため一応母艦はmacOSではありますが実質的にLinuxでの挑戦となっています。

bindマウントでのsharedプロパゲーションに関してはhyperkit VM側で回避できるかもとも思ったのですが目的から逸脱しそうな感じだったので早々に諦めてしまいました。

元々味見してみるためにローカルで動かすのが目標でしたので「ドメインなし」「SSL証明書なし」でローカルにインストールする流れでいきたいと思います。

なお、前回タイムリーにGitLab連携ができなくなっていた部分に関しても修正されていましたのでGitLabに連携する形で試してみます。



## インストール構成

今回ベースをLinuxにしてみるということで、手元にあったVirtualBox(vagrant)上のUbuntu 18_04 にKubernetesを構築してインストールを試みます。ただしブラウザーに関しては母艦のMacBook上のものを使いたかったのでDNS（dnsmasq）はMacBook側で設定し、SSHのポートフォワーディングを使ってUbuntu側のgitpodにアクセスする構成とします。

- ホストOS: macOS Catalina
- VirtualBox 6.1.16/Vagrant 2.2.9
- ゲストOS: Ubuntu 18_04
- Kubernetes: v1.20.0 (k3s)
- Gitpod: 0.6.0

Kubernetesクラスタ構築にはRancher社のk3sを使ってみました。一回つかってみたかったのとローカルへのインストールですしフットプリントは小さい方がいいかなという理由です。この選択によりあとでハマるのですが・・・。

- Kubernetes: ゲストOS上でk3sにより構築
- ドメイン: ホストOS上でdnsmasqでローカルホストDNS構築しgitpod.io-localドメインでアクセス
- HTTPS証明書: 前回HTTPでもアクセスできそうだったのでSSLは使わずHTTPで試す
- MySQL, Docker Registry: 運用では外部サービスの利用が推奨となっているがChartで自動構築されるのでそのまま利用
  - HTTPS証明書が準備できない場合は外部のhttpsでアクセス可能なDocker Registryを用いた方がよいです。
- MinIO: クラウド環境の設定を追加しなくても動作可能に修正されていたのでそのまま使う

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695122/e691f94e-da64-8ae1-bdd7-8f4b8ee2f103.png)


## 1. 前提ソフトのインストール(ホスト側）

### 1.1 Dnsmasqのインストール

前回と同じですので省略します。前回同様でホスト側のDNS設定を127.0.0.1に指定してdnsmasqを参照するようにします。


### 1.2 VirtualBox+ VagrantをインストールしてUbuntuを起動する

本筋部分ではないのでさらっとですが以下です。

```shell-session
$ brew cask install virtualbox
$ brew install vagrant
$ vagrant init bento/ubuntu-18.04
$ vagrant up
```

sshでポートフォワードしつつ接続します。ここではゲストOSのIPアドレスが`10.0.2.15`であると仮定します。

なおプロンプトが`$`の場合はホストOS(macOS)、プロンプトが`$$`の場合はゲストOS(Ubuntu)として表記してます。

```
$ sudo ssh -p 2222 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR -o IdentitiesOnly=yes -i ~/.vagrant.d/insecure_private_key -L 80:10.0.2.15:80 -g vagrant@127.0.0.1
```

最初にsudo のパスワード（macOS側の管理者パスワード）、次にvagrantによるsshログインのパスワードが聞かれるのでそれぞれ入力します。vagrantユーザのパスワードはおそらく`vagrant`です。


## 2. 前提ソフトのインストール(ゲスト側）

### 2.1 k3sのインストール（兼Kubernnetesインストール）

ゲスト側でおもむろにインストールスクリプトを動かす。

```terminal
$$ curl -sfL https://get.k3s.io | sh -
[INFO]  Finding release for channel stable
[INFO]  Using v1.20.0+k3s2 as release
[INFO]  Downloading hash https://github.com/rancher/k3s/releases/download/v1.20.0+k3s2/sha256sum-amd64.txt
[INFO]  Downloading binary https://github.com/rancher/k3s/releases/download/v1.20.0+k3s2/k3s
[INFO]  Verifying binary download
[INFO]  Installing k3s to /usr/local/bin/k3s
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Creating /usr/local/bin/ctr symlink to k3s
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s

```

以上、これだけでkubectlが叩ける状態になります・・・びっくりするほど簡単ですね。


#### k3sのcontainerdで正規の証明書じゃなくてもregistryにアクセスできるようにする

これはドメインも証明書も正規のものではない今回のローカル構成による独自手順です。

gitpodのインストールでは構築される各Podと連携するregistryポッドの証明書が生成されるのでPod内からregistryへのアクセスはできるのですがgitpodで実際にユーザが使うワークスペース用のPodを起動するときにノードがこのregistryからイメージをpullできる必要があります。そこで今回は証明書検証をスキップしてアクセスできるように設定します。

ちなみに正規の証明書がある場合は証明書の設定されたproxyポッド経由でregistryにアクセスするように構成されるのでこの手順は不要です。あくまでもローカルで強引に動かす場合の設定です。

ゲストOSの`/etc/rancher/k3s/registries.yaml `を修正します。

```
configs:
  "registry.gitpod.io-local":
    tls:
      insecure_skip_verify: true
```

k3sを再起動しておきましょう。

```
$$ systemctl restart k3s
```




### 2.2 Helm インストール

Linuxへのインストールはスクリプトが公式っぽいのでスクリプトでいれます。


```
$$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
$$ chmod 700 get_helm.sh
$$ ./get_helm.sh
```

HelmとKubernetesの疎通ができているか確認します。

```
$$ helm list
Error: Kubernetes cluster unreachable: an error on the server ("") has prevented the request from succeeding
```

どうやらk3sでKubernetesを構築した場合configが`/etc/rancher/k3s/k3s.yaml`に配置されるのですがHelm側が認知していない感じです。

Helmが参照するconfigをKUBECONFIG環境変数で調整します(k3sで構築しなければ不要な気がします）

```
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
```

必要に応じて.bashrc等に追記しておきましょう。

## 3. GitLabとの連携

https://www.gitpod.io/docs/gitlab-integration/

の手順どおりにGitLabへの認証連携を設定します。

GitLabの画面右上のメニューからSettings --> Applicationから連携アプリを追加します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695122/82922051-c7ec-3a4b-2ced-721d5b991257.png)

`Application ID`と`Secret`を覚えておきます。



## 4. Gitpodのインストール

### 4.1 HelmでのGitpodインストール準備

前回の記事ではHelmのリポジトリからインストールしましたが、だいぶこまめにハマるのでGitHubの開発リポジトリのビルドからインストールします。

GitHubリポジトリ内のドキュメントにブランチビルドのインストール方法が書いてあります。

https://github.com/gitpod-io/gitpod/blob/master/docs/self-hosted/install/install-on-kubernetes.md#install-branch-build

WerftのCIジョブをみてビルド番号をみつけて、その成果物のDockerイメージからChart一式を引っこ抜くようです。

https://werft.gitpod-dev.com/job/gitpod-build-master.361


リリース系の場合は`gcr.io/gitpod-io/self-hosted/installer:0.6.0`で
ブランチビルド系の場合は`eu.gcr.io/gitpod-core-dev/build/installer:master.361`のような感じになるようです。

じつはこの記事を書くために試行錯誤してる最中にv0.6.0がでたのでそれを使います。

```
$ docker run --entrypoint cp -v $PWD:/workspace gcr.io/gitpod-io/self-hosted/installer:0.6.0 -R /dist/helm/ /workspace
```

カレントディレクトリ配下に次のようなディレクトリ一式ができていると思います。

```
.
   |-helm
   |---gitpod
   |-----charts
   |-----config
   |-------db
   |---------init
   |-------proxy
   |---------debug
   |-----secrets
   |-------messagebus
   |-----templates
```


これらをvagrantのゲストOS側にコピーします。


### 4.2 valuesファイルの変更

ゲストOS側でコピーした一式のフォルダのhelm/gitpodに移動して設定上書き用のファイルvalue-over.yamlを作成します。

```
$$ cd helm/gitpod
$$ vi value-over.yaml
```

同じディレクトリにvalues.yamlファイルがあるので参考にしつつvalues-over.yamlを次のようにしました。


```yaml
hostname: gitpod.io-local
certificatesSecret:
  # 今回はhttpでホストするので空文字指定
  secretName:
authProviders:
- id: Public-GitHub
  type: GitHub
  description: ""
  host: github.com
  icon: ""
  oauth:
    callBackUrl: http://gitpod.io-local/auth/github/callback
    clientId: <GitHubのクライアントID>
    clientSecret: <GitHubのクライアントシークレット>
    settingsUrl: https://github.com/settings/connections/applications/07c7d6d4b7f6ae70af91
- id: Public-GitLab
  type: GitLab
  description: ""
  host: gitlab.com
  icon: ""
  oauth:
    callBackUrl: http://gitpod.io-local/auth/gitlab/callback
    clientId: ＜GitLabのアプリケーションID＞
    clientSecret: ＜GitLabのシークレット＞
    settingsUrl: https://gitlab.com/profile/applications
components:
  wsDaemon:
    containerRuntime:
      containerd:
        # containerdソケットを指定。利用するディストリビューションに応じて適切に
        socket: /run/k3s/containerd/containerd.sock
      nodeRoots:
      - /var/lib
      # コンテナのイメージが格納されるパスを指定
      - /run/k3s/containerd/io.containerd.runtime.v2.task/k8s.io
```

このvalues-over.yamlファイルを利用してクラスタにインストールします。

```
$$ helm install  gitpod -f values-over.yaml .
NAME: gitpod
LAST DEPLOYED: Thu Jan 21 17:18:27 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
```
サービスやポッドが起動しているか確認します。

```
$$ kubectl get svc
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                 AGE
kubernetes        ClusterIP      10.43.0.1       <none>        443/TCP                                 1d
registry-facade   ClusterIP      10.43.79.189    <none>        3000/TCP                                21h
ws-manager        ClusterIP      10.43.115.21    <none>        8080/TCP                                21h
theia-server      ClusterIP      10.43.18.240    <none>        80/TCP                                  21h
messagebus        ClusterIP      10.43.150.23    <none>        5672/TCP,25672/TCP,4369/TCP,15672/TCP   21h
image-builder     ClusterIP      10.43.19.170    <none>        8080/TCP                                21h
server            ClusterIP      10.43.174.199   <none>        3000/TCP,9500/TCP                       21h
dashboard         ClusterIP      10.43.17.212    <none>        3001/TCP                                21h
mysql             ClusterIP      10.43.157.89    <none>        3306/TCP                                21h
blobserve         ClusterIP      10.43.119.114   <none>        4000/TCP                                21h
db                ClusterIP      10.43.55.222    <none>        3306/TCP                                21h
minio             ClusterIP      10.43.245.25    <none>        9000/TCP                                21h
registry          ClusterIP      10.43.139.178   <none>        443/TCP                                 21h
ws-proxy          ClusterIP      10.43.84.175    <none>        8080/TCP                                21h
proxy             LoadBalancer   10.43.189.32    10.0.2.15     80:30176/TCP,443:31198/TCP              21h

$$ kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
registry-facade-bfx4n               1/1     Running   0          21h
dashboard-5fffd64d9c-4khzw          1/1     Running   0          21h
proxy-57885ffb6c-5z45f              1/1     Running   0          21h
blobserve-84ff95cb56-wdvfn          1/1     Running   0          21h
ws-manager-74d5897964-bzf9t         1/1     Running   0          21h
theia-server-7b9fc949d6-q7j87       1/1     Running   0          21h
svclb-proxy-zsqkk                   2/2     Running   0          21h
image-builder-7ccb77458c-8l6mb      2/2     Running   0          21h
ws-scheduler-8488f68555-956d7       1/1     Running   0          21h
node-daemon-6zr28                   1/1     Running   0          21h
ws-daemon-4j22v                     1/1     Running   0          21h
ws-proxy-64cfddd46-td4d5            1/1     Running   0          21h
minio-6d7bd56fcc-pfff7              1/1     Running   0          21h
messagebus-58fdd99865-2zh5w         1/1     Running   0          21h
mysql-7cbb9c9586-stpxw              1/1     Running   0          21h
server-5b945dcfbd-csdkd             1/1     Running   0          21h
ws-manager-bridge-ffff794f6-cztpw   1/1     Running   4          21h
registry-77ccc846c-bvg8t            1/1     Running   0          21h
```

### 4.3 Dnsmasqの設定変更

registryサービスのClusterIPも10.97.219.208でふられていますので

ホスト側(macOS)側の/usr/local/etc/dnsmasq.confを次のように更新してDnsmasqを再起動します。

```dnsmasq.conf
address=/gitpod.io-local/192.168.11.2
address=/registry.gitpod.io-local/10.43.139.178
```

## 5. 動作確認

ホスト側（macOS)でブラウザを起動して http://gitpod.io-local/ にアクセスします。

画面右上の「Login」からGitLabを選択してGitLabでログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695122/7eb72570-40d2-4739-9674-85678e5de2b3.png)


お試し用に適当なGitLabプロジェクトのURLをコピーしておいてブラウザ上でURLに以下のように打ち込みます。

`http://gitpod.io-local/#<GitLabプロジェクトのURL>`

初回はワークスペース用のコンテナイメージを作成するので数十分くらい待ちますが・・・うまくいけば・・・


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695122/be076851-9245-bd9f-de5c-be669295f6ab.png)


こんな感じでワークスペースの開発画面が表示されます！ :satisfied:


## トラブルシュートメモ

##### ws-manager-bridgeポッドが起動しない

```
$$ kubectl logs ws-manager-bridge-xxxxxx
```

でログを確認してみて`no exchange 'gitpod.ws.local' in vhost '/'`のようなエラーが発生する場合、messagebusポッドのRabbitMQを再コンフィグします。

```
$$ kubectl exec messagebus-58fdd99865-5vv5q --stdin --tty -- /bin/bash
bash-5.0$ /usr/local/bin/configure.sh 
Waiting for pid file '/var/run/rabbitmq-pid/rabbitmq.pid' to appear
pid is 303
Waiting for erlang distribution on node 'rabbit@messagebus-58fdd99865-5vv5q' while OS process '303' is running
Waiting for applications 'rabbit_and_plugins' to start on node 'rabbit@messagebus-58fdd99865-5vv5q'
Applications 'rabbit_and_plugins' are running on node 'rabbit@messagebus-58fdd99865-5vv5q'
exchange declared
exchange declared
exchange declared
*** Not found: /api/bindings/%2F/e/gitpod.ws.local/q/gitpod.ws

exchange declared
All configuration done.

```

##### proxyサービスがPendingのまま

サービスを確認したときにproxyポッドもしくはサービスが上がってこない場合

```
$$ kubectl get svc
```
で確認してみます。proxyサービスが`Pending`になっている場合、

External IPがTraefikで使われているためバインドできない可能性があります。

k3sだとデフォルトでtraefikがインストールされるのでtraefikがExternal IPをバインドしていて使えないようです。

最初からtraefikを使わないモードでインストールすれば良かったのですが、ここではtraefikを削除して対応します。

```
$$ kubectl -n kube-system delete helmcharts.helm.cattle.io traefik
```


##### ワークスペースのコンテナが起動してこない

`ws-<ワークスペースID>`のようなポッドはできているのだが起動しきらない現象で２つパターンがありました。

１つはワークスペースが空のままで、Readiness Probeが失敗するケース。gitpodで使われているKubernetesクライアントのバージョンだと今回利用したv1.20.0+k3s2だとKubernetesのバージョン(v1.20.0)が新し過ぎて一部の呼び出しがエラーになるようです。ただv0.6.0のリリース直前にクライアントライブラリのバージョンがアップされたので一応v1.20.0でも動くようになりました。

https://www.gitpod.io/docs/self-hosted/latest/install/install-on-kubernetes/

にもKubernetesのバージョンが明記されるようになったのでv0.6.0時点だと1.15 <= x <= 1.17にした方が無難そうです。


もう１つは以下のようにポッドを起動する適切なノードが見つからないエラーです。

```
Warning  FailedScheduling  4m46s  workspace-scheduler  no suitable node found: zero nodes available
```

gitpodをインストールしたときにノードラベルが設定されて、それを目印にワークスペース用のポッドを起動するようです。一方でgitpod 0.5.0の時点で`helm uninstall`したときにノードのラベルが残ったままになるという不具合があり、同一のKubernetesクラスタで試行錯誤したので古いバージョンのノードラベルが残ったままになっていました。結果として目印が見つからない状態になっていたようです。

v0.5.0のノードラベルを消して、
```
$$ kubectl label node <ノード名> gitpod.io/theia.0.5.0-
$$ kubectl label node <ノード名> gitpod.io/ws-sync-
```

v0.6.0の新しいノードラベルを設定します。(もしくはいったんアンインストールしてインストールし直すでも可）

```
kubectl label node <ノード名> gitpod.io/theia.0.6.0=available
kubectl label node <ノード名> gitpod.io/ws-daemon=available
```

※余談ですがv0.6.0からワークスペースを準備するコンポーネントがws-syncがws-daemonに名称変更されています。


## まとめ

さて、ドキュメントの構成と変えているせいもありますが今回だいぶ苦労してしまいました・・・。

学びとしては以下あたりです。

- GitHubのリポジトリをみるのがよさげ。
- Kubernetesのバージョンは気にしよう。
- ディストリビューションも気にしよう。デフォルトの設定はkubeadmかminikubeあたりがよさげです。
- registryはクラスタ外のものを利用したほうがよさげ。

というかトラブルシュートしたおかげでだいぶお勉強になりました。



### 参考文献
この記事は以下の情報を参考にして執筆しました。
- [Install Gitpod Self-Hosted on Kubernetes](https://www.gitpod.io/docs/self-hosted/latest/install/install-on-kubernetes/)
- [GitHub Gitpodリポジトリ](https://github.com/gitpod-io/gitpod)

また、
- GitLab は、GitLab B.V. の米国およびその他の国における登録商標もしくは商標です。
- GitHub は、GitHub Inc. の米国およびその他の国における登録商標もしくは商標です。
- その他の記載されている会社名、製品名、サービス名、ロゴ等は各社の商標または登録商標です。