remote.it を使ってAKS(Azure Kubernetes Service)上のサービスに接続してみた！

# はじめに
本記事は [QiitaEngineerFesta 2022 「remote.it を使って○○に接続してみた！」](https://qiita.com/official-events/8a2ab08f6fa0f093ae39) の参加記事です。

https://qiita.com/official-campaigns/engineer-festa/2022

本キャンペーンで初めて [remote.it](https://ja.remote.it/) というサービスを知りました。
公式サイトや既存のQiitaの記事などを見るに、インターネットからのインバウンドが開いてない環境に対しセキュアにアクセス経路を確立するためのサービスのようです。

使い道を考えたときに、普段よく触ってる Kubernetes と組み合わせて活用できないかと思いつきました。

Azure上のAKS(Azure Kubernetes Service)で起動したアプリに対し、安全な経路で接続できると便利だなー、と…。
![remoteit.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/d7060bde-95ba-a438-4de7-955df92d9e67.png)

いろいろ触ってみたところなんとか実現できそうだったので、その手順を紹介します。

# おことわり
Kubernetes上でのremote.itの起動を試すにあたり、Kubernetesのお作法からするとあまり推奨できない手法を取っています。
大事な環境ではこの記事の内容を真似しないようご注意ください。

# 環境
AKSのKubernetesバージョン 1.22.6 で試しています。
CNIの違いは今回の試した内容ではあまり関係ないと思いますが kubenet を選択しました。

# remote.it アカウント作成
何はともあれ remote.it のアカウント作成が必要です。
[remote.it](https://ja.remote.it/) の右上の `はじめる` からアカウント作成画面に入ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/e05cbf5f-5a13-2cb2-1b11-7cc75aa21121.png)

メールアドレスとパスワードを入力してアカウント作成します。
`CREATE ACCOUNT` ボタンを押すと入力したメールアドレス宛に確認のメールが送信されます。メール内のリンクをクリックすると登録が完了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/0f52d779-26a5-d663-1459-a19d7b21f30b.png)

登録が完了しログインすると。以下のような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/11f4acc2-9a50-55cc-db22-3dee0d7f2e34.png)

# サンプルアプリケーションの準備
AKS上でサンプルアプリケーションを起動しておきます。サンプルにはnginxのPodを利用しました。

```sample.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: sample
---
apiVersion: v1
kind: Service
metadata:
  name: samplesvc
  namespace: sample
spec:
  selector:
    app: sampleapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sampleapp
  namespace: sample
  labels:
    app: sampleapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sampleapp
  template:
    metadata:
      labels:
        app: sampleapp
    spec:
      containers:
        - name: sampleapp
          image: nginx:latest
          ports:
            - containerPort: 80
```

```bash
kubectl apply -f sample.yaml
```

`sample` という名前のNamespaceを作成し、その配下にServiceとDeploymentを作成しています。
```bash
kubectl get pod,svc -n sample
```

```
NAME                             READY   STATUS    RESTARTS   AGE
pod/sampleapp-64c4fc65fb-q4wh6   1/1     Running   0          3m3s

NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/samplesvc   ClusterIP   10.0.19.155   <none>        80/TCP    3m3s
```
Serviceのタイプは　`ClusterIP` となっており、**AKSクラスタ外に公開していない** 状態です。

# remote.it 用コンテナイメージのビルド
AKS上でremote.itを実行するため、コンテナイメージをビルドします。

Dockerfileは以下のとおりです。(手抜き…)
```Dockerfile
FROM ubuntu:20.04

RUN apt-get update && apt-get install -y init sudo iproute2 curl

RUN sh -c "$(curl -L https://downloads.remote.it/remoteit/install_agent.sh)"

CMD ["/sbin/init"]
```

Ubuntu(Debian)用のremote.itはsystemdで起動します。
コンテナ上でsystemdを実行するため、`init` パッケージをインストールし CMDで `/sbin/init` を指定しています。
その他必要なパッケージと、remote.itのエージェントをインストールしています。

これをビルドし、AKSからPullできる適当なコンテナレジストリーに格納しておきます。
```bash
docker build -t <hogeregistry.example.com>/remoteit:latest .
```
```bash
docker push <hogeregistry.example.com>/remoteit:latest
```
　
# remote.it コンテナ起動
AKS上に前項でビルドしたremote.itコンテナをデプロイします。

まずはremote.itの **Registration Code** を取得する必要があります。
remote.itにログインした画面の左メニュー上部の `+`ボタンをクリック、`Linux & Raspberry Pi`を選択し、右側に表示されるインストールコマンドを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/b1c49151-32ea-7022-f127-76363a505d66.png)

`R3_REGISTRATION_CODE=～` の箇所の値をコピーします。上記スクリーンショットの白塗り部分です。

Registration Codeが準備できたら、PodをAKS上へデプロイしましょう。
マニフェストは以下のとおり。`remoteit` というNamespace配下にPodをデプロイします。
マニフェスト内の `"{{ REGISTRATION_CODE }}"` の箇所を上記で取得した値に置き換えてください。

```remoteit.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: remoteit
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: entrypoint
  namespace: remoteit
data:
  entrypoint.sh: |-
    #!/bin/bash
    apt-get update && apt-get -y install curl
    sh -c "$(curl -L https://downloads.remote.it/remoteit/install_agent.sh)"
---
apiVersion: v1
kind: Pod
metadata:
  name: remoteit
  namespace: remoteit
spec:
  initContainers:
  - name: init
    image: ubuntu:20.04
    command:
    - /bin/entrypoint.sh
    env:
    - name: R3_REGISTRATION_CODE
      value: "{{ REGISTRATION_CODE }}"
    volumeMounts:
    - mountPath: /bin/entrypoint.sh
      name: entrypoint
      readOnly: true
      subPath: entrypoint.sh
    - mountPath: /etc/remoteit
      name: remoteit-config
  containers:
  - name: remoteit
    image: <hogeregistry.example.com>/remoteit/remoteit:latest
    imagePullPolicy: Always
    volumeMounts:
    - mountPath: /etc/remoteit
      name: remoteit-config
    securityContext:
      privileged: true
  volumes:
  - name: entrypoint
    configMap:
      defaultMode: 0700
      name: entrypoint
  - name: remoteit-config
    emptyDir: {}
```

```bash
kubectl apply -f remoteit.yaml
```

remote.itのデーモンは、`/etc/remoteit/`配下の設定ファイルを読み込んで起動するようです。
上記マニフェストでは、
1. ConfigMapにinitContainerで実行するスクリプトを格納
2. initContainerでConfigMapに格納したスクリプトを実行し、環境変数の値を利用し /etc/remoteit/ 配下に設定ファイルを生成
3. initContainerで生成した設定ファイルを利用し、ビルドしたイメージを起動

という流れでremote.itを起動しています。
イメージビルドの項でも触れましたが、remote.itデーモンはsystemdで起動します。
Pod上でsystemdを起動するため、`privileged: true` を追加し特権でコンテナを実行しています。
セキュリティ上問題を生む可能性もあるので、利用には十分注意しましょう。

# remote.itの設定
Podが起動して少しすると、remote.itの `Devices` メニューに **remoteit** というデバイスが追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/1bf3c8c9-ea86-6988-0efa-0ea0717adba0.png)

remoteit をクリックし、次の画面でServiceの追加(`+`ボタン)をクリック、右のメニューに以下のように入力していきます。

- SERVICE TYPE: **HTTP**
- SERVICE NAME: **sampleapp HTTP**
- SERIVCE PORT: **80**
- SERVICE HOST ADDRESS: **samplesvc.sample**
- Enable Service: **チェックを付ける**

入力が終わったら **SAVE** ボタンをクリックし保存しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/815af67b-2a9e-557d-5ce4-558b2f871a9e.png)

Kubernetesクラスター内からは、
`<Service名>.<Namespace名>`
という名前で他のアプリケーションにアクセスすることができます。
上記で入力した `samplesvc.sample` は、
`sample` Namespaceの `samplesvc` Serviceを示します。

以上で設定は完了です。次の項でremote.itを利用してサンプルアプリにアクセスしてみましょう。

# remote.itでアクセス経路の開通
先ほど保存した`sampleapp HTTP`サービスの **CONNECT + LAUNCH** ボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/d917f83c-c63a-26bf-bd78-e7462599bee9.png)

クリック後少し待つと、remote.itから発行されたランダムな文字列のサブドメイン付きURLが表示されます。
**LAUNCH** をクリックしてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/b2672838-88b2-1cc3-4e43-a4c9b363a24f.png)


AKS上のnginxにアクセスできました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/98ccda34-853e-795f-abe4-355dff081798.png)

図にすると以下のようなイメージです。
![remoteit.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/56bdafef-d0dd-e665-2721-eb63da353a1b.png)

なお、この発行されたURLは[パブリックに公開されます](https://support.remote.it/hc/en-us/articles/360059414391-My-Service-is-online-but-the-connection-failed-)。**URLを知っていれば誰でもアクセスできてしまう** ので注意しましょう。

Serviceの設定をHTTPではなく `TCP・ポート80` として公開すれば、**最初にアクセスした人のアクセス元IP以外からは受け付けない** という状態になります。
ただ、アクセス先ポート番号が80番ではなくなってしまうため、アクセス元の環境によっては支障が出るかもしれません。自身の環境によって使い分けるのが良いでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/ddf5f00c-ffbd-b76f-25ad-43711ca931b1.png)

# おわりに
remote.it、初めて使ってみましたが便利そうなサービスです。

コンテナ上で動かすことはおそらく標準の使い方ではないと思いますが、
一般的なLinuxディストリビューションのマシンがあればスクリプトを実行するだけで導入でき、お手軽に利用開始できます。（Arch LinuxやOpenWRTのパッケージまで公開してるのは珍しい）

https://ja.remote.it/download?products=device-package

個人利用であれば無料のプランもあります。

https://ja.remote.it/pricing

これからの季節、お盆の帰省の際などに自宅の機器にアクセスするといった用途で導入するのもいいかもしれませんね。