4
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法(2/3)

Last updated at Posted at 2019-04-12
  1. ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法(1/3)
    概要編
  2. ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法(2/3)
    クラウド側環境構築編 <-- 今回
  3. ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法(3/3)
    ロボット側環境構築編

はじめに

前回は、VPNやWebRTCを使ったインターネット越しにカメラ映像を視聴するシステムの概要とネットワーク構成について書きました。今回は、Azure上にクラウド側環境を構築していきます。

なおLet's EncryptでTLS証明書を取得しますので、お名前.com等のドメインレジストラからドメインを一つ調達しておいてください。

各種バージョン

  • クライアント側
バージョン
OS macOS Sierra 10.12.6
azure-cli 2.0.61
kubectl 1.14.0
docker 18.09.2
git 2.4.13
envsubst 0.19.8.1
  • クラウド側
バージョン
AKS 1.12.6

環境変数の設定

クラウド側環境を構築する際に必要になる設定値を、環境変数として定義しておきます。 DOMAINEMAIL は、自分が所有するものに適切に変更してください。それ以外の環境変数は、特に理由がなければ変更しなくても大丈夫です(既存のAzure上で同じ名前のリソースがある場合など)。

個別に変更する必要がある環境変数

TERMINAL1
kurento-robot-camera-k8s$ export DOMAIN="example.com"
kurento-robot-camera-k8s$ export EMAIL="nobody@example.com"

利用するAzureリージョン

TERMINAL1
kurento-robot-camera-k8s$ export REGION="japaneast"

Azureのリソース名

TERMINAL1
kurento-robot-camera-k8s$ export DNS_ZONE_RG="dns-zone"
kurento-robot-camera-k8s$ export SYSTEM_RG="kurento"
kurento-robot-camera-k8s$ export VNET_NAME="kurentovnet"
kurento-robot-camera-k8s$ export PODSUBNET_NAME="kurentosubnet"
kurento-robot-camera-k8s$ export GW_PUBLICIP="gwpublicip"
kurento-robot-camera-k8s$ export GW_NAME="kurentogw"
kurento-robot-camera-k8s$ export AKS_NAME="kurentoaks"

AKSノードのスペック(ノード数は3以上)

TERMINAL1
kurento-robot-camera-k8s$ export NODE_COUNT=3
kurento-robot-camera-k8s$ export NODE_VM_SIZE="Standard_D2s_v3"
kurento-robot-camera-k8s$ export NODE_OSDISK_SIZE_GB=64
kurento-robot-camera-k8s$ export K8S_VERSION="1.12.6"

2019/04/04時点で、東日本リージョンのAKSは 1.12.6 が最新版でした。

自動生成するユーザー名やパスワード

TERMINAL1
kurento-robot-camera-k8s$ export COTURN_CREDENTIAL="$(cat /dev/urandom | \
  LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 8):$(cat /dev/urandom | \
  LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 16)"; echo ${COTURN_CREDENTIAL}
kurento-robot-camera-k8s$ export BASIC_USERNAME="$(cat /dev/urandom | \
  LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 8)"; echo ${BASIC_USERNAME}
kurento-robot-camera-k8s$ export BASIC_PASSWORD="$(cat /dev/urandom | \
  LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 16)"; echo ${BASIC_PASSWORD}

上記のコマンドはBSD系のMacOS用です。Linuxの場合、 $(cat /dev/urandom 2>/dev/null | head -n 40 | tr -cd 'a-zA-Z0-9' | head -c 8) のようなコマンドで代替できると思います。

Azureにログイン

az コマンドを用いてAzureにログインします。会社でAzureを利用している場合など、 "規定のディレクトリ" 以外にテナントが存在する場合は、適切なテナントを指定してください。

TERMINAL1_規定のディレクトリにログイン
kurento-robot-camera-k8s$ az login
TERMINAL1_テナントを指定してログイン(テナント名を適切に変更)
kurento-robot-camera-k8s$ export TENANT="example.onmicrosoft.com"
kurento-robot-camera-k8s$ az login --tenant ${TENANT}

ソースコードの取得

Githubより、今回構築するシステムのソースコードを取得します。

TERMINAL1
$ git clone https://github.com/nmatsui/kurento-robot-camera-k8s.git
$ cd kurento-robot-camera-k8s/

Azure Virtual NetworkとAzure VPN Gatewayの構築

Azure Virtual Networkを用いて、今回のシステムに必要な仮想ネットワークを構築します。またその仮想ネットワーク上にAzure VPN Gatewayを構成し、IKEv2でPoint-to-siteのVPNトンネルを敷設できるようにします。
fig3.png

今回のシステム用のリソースグループ作成

TERMINAL1
kurento-robot-camera-k8s$ az group create --name ${SYSTEM_RG} --location ${REGION}

Azure Virtual Networkを作成

TERMINAL1
kurento-robot-camera-k8s$ az network vnet create \
  --name ${VNET_NAME} \
  --resource-group ${SYSTEM_RG} \
  --location ${REGION} \
  --address-prefix 10.168.0.0/16 \
  --subnet-name ${PODSUBNET_NAME} \
  --subnet-prefix 10.168.0.0/24

VPN Gateway用subnetを追加

TERMINAL1
kurento-robot-camera-k8s$ az network vnet subnet create \
  --vnet-name ${VNET_NAME} \
  --name GatewaySubnet \
  --resource-group ${SYSTEM_RG} \
  --address-prefix 10.168.255.0/27

Azureの場合、VPN Gateway用subnetの名前は GatewaySubnet に固定されています。

VPN Gateway用Public IPの取得

TERMINAL1
kurento-robot-camera-k8s$ az network public-ip create \
  --name ${GW_PUBLICIP} \
  --resource-group ${SYSTEM_RG} \
  --allocation-method Dynamic

VPN Gatewayを作成

TERMINAL1
kurento-robot-camera-k8s$ az network vnet-gateway create \
  --name ${GW_NAME} \
  --resource-group ${SYSTEM_RG} \
  --location ${REGION} \
  --public-ip-address ${GW_PUBLICIP} \
  --vnet ${VNET_NAME} \
  --gateway-type Vpn \
  --sku VpnGw1 \
  --vpn-type RouteBased \
  --address-prefixes 10.255.0.0/24 \
  --client-protocol IkeV2 \
  --no-wait

VPN Gatewayの作成にはある程度時間がかかるようです。この記事を書いた際は、20分ぐらいかかりました。(公式マニュアルによれば、45分以上かかる場合もあるそうな)。

VPN Gatewayの確認

TERMINAL1
kurento-robot-camera-k8s$ az network vnet-gateway show \
  --name ${GW_NAME} \
  --resource-group ${SYSTEM_RG} \
  --query provisioningState \
  --output tsv

構築に成功すれば、 Succeeded と表示されます。

AKS起動

先程作成したAzure Virtual Network上に、Azure AKSを起動します。
fig4.png
実はAzure AKSは、何も指定しなければAKS専用のAzure Virtual Networkを自動的に構成し、その上にKubernetesのデフォルトのネットワークドライバを用いてK8S用の仮想ネットワークを重畳します。K8S単体で動作させるだけならば、これで問題ありません。
しかし今回は、POD間通信で用いられるK8S内部のネットワークをAzure VPN Gateway経由でロボットのLANにまで延伸したいので、K8SのネットワークドライバとしてAzure Virtual Networkを明示的に指定してAKSを起動します。

pod用subnetのIDを取得

TERMINAL1
kurento-robot-camera-k8s$ export PODSUBNET_ID=$(az network vnet subnet list \
  --resource-group ${SYSTEM_RG} \
  --vnet-name ${VNET_NAME} \
  --query "[?name=='${PODSUBNET_NAME}'].id" \
  --output tsv)

network pluginとsubnetを指定してAKSを起動

TERMINAL1
kurento-robot-camera-k8s$ az aks create \
  --name ${AKS_NAME} \
  --resource-group ${SYSTEM_RG} \
  --location ${REGION} \
  --node-count ${NODE_COUNT} \
  --node-vm-size ${NODE_VM_SIZE} \
  --node-osdisk-size ${NODE_OSDISK_SIZE_GB} \
  --network-plugin azure \
  --vnet-subnet-id ${PODSUBNET_ID} \
  --docker-bridge-address 172.17.0.1/16 \
  --dns-service-ip 10.2.0.10 \
  --service-cidr 10.2.0.0/24 \
  --no-ssh-key \
  --kubernetes-version ${K8S_VERSION} \
  --no-wait

VPN Gatewayと同様に、AKSの起動にも時間がかかります。この記事を書いた際は、やはり20分ぐらいかかりました。

AKSの確認

TERMINAL1
kurento-robot-camera-k8s$ az aks show \
  --name ${AKS_NAME} \
  --resource-group ${SYSTEM_RG} \
  --query provisioningState \
  --output tsv

構築に成功すれば、 Succeeded と表示されます。

Credentialの取得

TERMINAL1
kurento-robot-camera-k8s$ az aks get-credentials \
  --name ${AKS_NAME} \
  --resource-group ${SYSTEM_RG} \
  --overwrite-existing

AKSのnode確認

TERMINAL1
kurento-robot-camera-k8s$ kubectl get nodes
NAME                       STATUS   ROLES   AGE     VERSION
aks-nodepool1-78167327-0   Ready    agent   8m40s   v1.12.6
aks-nodepool1-78167327-1   Ready    agent   10m     v1.12.6
aks-nodepool1-78167327-2   Ready    agent   10m     v1.12.6

AKS上にSTUN & TURN Server起動

WebRTCでNAT越えをする際には、ブラウザやKurentoのコンテナが "インターネット側から見て" どのようなPublic IPとPortで到達できるのかを知る必要があります。その際に用いられるのが、STUN Serverです。ブラウザやKurentoが用いるLAN内のPrivate IPとPortが、最終的にどのようにNATされインターネット側から見えるのかをSTUN Serverが観測できれば、その観測されたPublic IPとPortを用いてブラウザとKurentoがP2Pで接続できます。
一方、NATやFirewallの構成次第ですが、WebRTCで必要となるPublic IPやPortの情報をSTUN Serverが観測できないことがあります。このような場合でも、ブラウザやKurentoがPulbic IPを持つTURN Serverに接続できるなら、TURN ServerにWebRTCのパケットを中継してもらうことで、映像を流すことは可能になります。

今回はこのSTUN & TURN Serverとして、coturnというOSSを利用します。
fig5.png

K8Sの制限により、今回構築するTURN ServerはUDPのみListenします。そのため、80/tcpや443/tcpしか通さないような制約が厳しいネットワークでは、TURN Serverにさえ接続できません。仕様上、TURN ServerはTCPでもTLSでも動作しますので、TCPとTLSをListenするTURN Serverを別途起動すれば、この問題を解決できます。

coturnのLoadBalancer Serviceを起動

coturnはその仕様上、coturn自身がNATされた状況で動作する場合、どのPublic IPからNATされてくるのかを知っておく必要があります。そこで、coturnのPodへルーティングするLoadBalancer Serviceを最初に起動します。

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f coturn/coturn-service.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get services -l app=coturn
NAME     TYPE           CLUSTER-IP   EXTERNAL-IP      PORT(S)                                                                                                                                                                        AGE
coturn   LoadBalancer   10.2.0.139   xx.xx.xx.xx      80:32704/UDP,49150:30983/UDP,49151:32439/UDP,49152:32325/UDP,49153:32277/UDP,49154:32220/UDP,49155:31336/UDP,49156:31185/UDP,49157:30756/UDP,49158:31146/UDP,49159:31975/UDP   1m

coturnのPublic IPを確認

TERMINAL1
kurento-robot-camera-k8s$ COTURN_PUBLIC_IP=$(kubectl get services \
  -l app=coturn \
  -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}'); \
echo ${COTURN_PUBLIC_IP}

coturnの設定ファイルを生成

テンプレートをもとに、NAT元のPublic IPやTURN Serverのcredentialを変更してcoturnの設定ファイル turnserver.conf を生成します。

TERMINAL1
kurento-robot-camera-k8s$ cp coturn/turnserver.conf.template coturn/turnserver.conf
kurento-robot-camera-k8s$ sed -i '' -e "s/^external-ip=.*$/external-ip=${COTURN_PUBLIC_IP}/" \
  coturn/turnserver.conf
kurento-robot-camera-k8s$ sed -i '' -e "s/^user=.*$/user=${COTURN_CREDENTIAL}/" \
  coturn/turnserver.conf
kurento-robot-camera-k8s$ sed -i '' -e "s/^realm=.*$/realm=coturn.${DOMAIN}/" \
  coturn/turnserver.conf

coturnの設定ファイルをsecretに登録

coturnのPodから参照できるように、生成した設定ファイルを turnserver-conf という名前のSecretとしてAKSに登録します。

TERMINAL1
kurento-robot-camera-k8s$ kubectl create secret generic turnserver-conf \
  --from-file=coturn/turnserver.conf

coturnのPodを起動

最後に、coturnのPodを起動します。

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f coturn/coturn-deployment.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get pods -l app=coturn
NAME                      READY   STATUS    RESTARTS   AGE
coturn-7ff4c894cb-dr5bh   1/1     Running   0          108s

AKS上にKurento起動

Kurentoの公式Docker imageを用いて、AKS上にKurentoを起動します。Serviceを経由してNATを増やす意味は無いので、WebRTCのシグナリング時にはPODの生Private IPをそのまま使うべきです。そのため、Headless ServiceとStatefulSetを用いて、Signaling&APPコンテナからPodに直接到達できるようにします。
fig6.png

KurentoのHeadless Serviceを起動

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f kurento/kurento-service.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get services -l app=kurento
NAME      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kurento   ClusterIP   None         <none>        8888/TCP   27s

KurentoのWebRTC用設定ファイルを生成

テンプレートをもとに、STUN ServerとTURN Serverの設定を変更してKurentoのWebRTC用設定ファイル WebRtcEndpoint.conf.ini を生成します。

TERMINAL1
kurento-robot-camera-k8s$ cp kurento/WebRtcEndpoint.conf.ini.template kurento/WebRtcEndpoint.conf.ini
kurento-robot-camera-k8s$ sed -i '' -e "s/^stunServerAddress=.*$/stunServerAddress=${COTURN_PUBLIC_IP}/" \
  kurento/WebRtcEndpoint.conf.ini
kurento-robot-camera-k8s$ sed -i '' -e "s/^turnURL=.*$/turnURL=${COTURN_CREDENTIAL}@${COTURN_PUBLIC_IP}:80/" \
  kurento/WebRtcEndpoint.conf.ini

KurentoのWebRTC用設定ファイルをsecretに登録

KurentoのPodから参照できるように、生成した設定ファイルを kurento-webrtcendpoint-conf という名前のSecretとしてAKSに登録します。

TERMINAL1
kurento-robot-camera-k8s$ kubectl create secret generic kurento-webrtcendpoint-conf \
  --from-file=kurento/WebRtcEndpoint.conf.ini

KurentoのPodを起動

最後に、KurentoのPodを起動します。

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f kurento/kurento-statefulset.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get pods -l app=kurento -o wide
NAME        READY   STATUS    RESTARTS   AGE    IP             NODE                       NOMINATED NODE
kurento-0   1/1     Running   0          110s   10.168.0.132   aks-nodepool1-78167327-1   <none>

Kurento PodのPrivate IPが、生成したAzure Virtual NetworkのSubnet内で割り振られていることが確認できます。

AKS上にAPI Gatewayと認証認可サービス構築

ブラウザがロボットのカメラ映像を視聴する際、最初にKurentoとWebRTCで接続するためのJavascriptを持ったWebページを表示します。このHTTP GET Requestの処理はSignaling&APPコンテナが担当しますが、インターネットからアクセスされる以上、TLSによる経路の暗号化とユーザー認証は必須となります。
このような横断的な機能を各Signaling&APPコンテナに作り込むのは無駄ですので、TLSの終端と認証認可を行うAPI Gatewayをフロントに立て、各Signaling&APPコンテナから責務を切り離すことにします。
fig7.png

DNSの設定

まずはSignaling&APPコンテナをFQDNで名前解決できるように、自分のドメインのname serverをAzure DNSに構築します。

DNS用リソースグループ作成

TERMINAL1
kurento-robot-camera-k8s$ az group create --name ${DNS_ZONE_RG} --location ${REGION}

DNS Zone作成

TERMINAL1
kurento-robot-camera-k8s$ az network dns zone create \
  --resource-group ${DNS_ZONE_RG} \
  --name "${DOMAIN}"

name server確認

TERMINAL1
kurento-robot-camera-k8s$ az network dns zone show \
  --resource-group ${DNS_ZONE_RG} \
  --name "${DOMAIN}" \
  --query nameServers

このコマンドを実行すると、次のようなname serverのリストが帰ってきます。これらをドメインレジストラに登録してください。

コマンド実行結果
[
  "ns1-XX.azure-dns.com.",
  "ns2-XX.azure-dns.net.",
  "ns3-XX.azure-dns.org.",
  "ns4-XX.azure-dns.info."
]

TLS証明書の取得

自分のドメインで名前解決ができるようになれば、Let's EncryptからTLS証明書を取得できます。後々複数のサブドメインで異なるアプリを立ち上げますので、DNSチャレンジを用いてワイルドカード証明書を取得します。

certbotコンテナを用いてDNSチャレンジを生成

最初に設定したDOMAINやEMAIL等を変数展開しつつ、DNSチャレンジを行うdockerコマンドを生成します。

TERMINAL1
kurento-robot-camera-k8s$ echo "docker run -it \
-v $(pwd)/secrets:/etc/letsencrypt certbot/certbot certonly \
--manual \
--domain *.${DOMAIN} \
--email ${EMAIL} \
--no-eff-email \
--agree-tos \
--manual-public-ip-logging-ok \
--preferred-challenges dns-01 \
--server https://acme-v02.api.letsencrypt.org/directory"

別のターミナルを開き、生成したdockerコマンドを実行すると、自分のドメインのTXTレコードに設定すべき値を表示して入力待ちになります。例えば次のような感じです。

TERMINAL2
$ docker run -it -v /tmp/kurento-robot-camera-k8s/secrets:/etc/letsencrypt certbot/certbot certonly --manual --domain ........
....
-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.cloudconductor.jp with the following value:

ABCDEFGHIJKLMNO-PQRST-UVWXYZabcdefghijklmno

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue

TXTレコード登録

TERMINAL1で、表示された名前と値を持つTXTレコードをDNS Zoneに作成します。 DNS_TXT の値は、実際に表示された値に書き換えてください。

TERMINAL1
kurento-robot-camera-k8s$ export DNS_TXT="ABCDEFGHIJKLMNO-PQRST-UVWXYZabcdefghijklmno"
kurento-robot-camera-k8s$ az network dns record-set txt add-record \
  --resource-group ${DNS_ZONE_RG} \
  --zone-name "${DOMAIN}" \
  --record-set-name "_acme-challenge" \
  --value "${DNS_TXT}"

次のコマンドを実行し、TXTレコードが生成されていることを確認しましょう。

TERMINAL1
kurento-robot-camera-k8s$ az network dns record-set txt list \
  --resource-group ${DNS_ZONE_RG} \
  --zone-name "${DOMAIN}"

TLS証明書の取得

TXTレコードが登録されていれば、TERMINAL2でエンターキーを押してTLS証明書を取得します。TERMINAL2で次のように表示されれば成功です。

TERMINAL2
.....
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
.....

実際のTLS証明書は、 secrets ディレクトリの下に格納されています。 privkey.pemfullchain.pem が生成されていることを確認してください。

TERMINAL1
kurento-robot-camera-k8s$ ls -al secrets/live/${DOMAIN}

不要なTXTレコードの削除

TLS証明書が取得できれば、先程登録したTXTレコードは削除してしまいましょう。

TERMINAL1
kurento-robot-camera-k8s$ az network dns record-set txt remove-record \
  --resource-group ${DNS_ZONE_RG} \
  --zone-name "${DOMAIN}" \
  --record-set-name "_acme-challenge" \
  --value "${DNS_TXT}"

API Gateway ServiceとPodを起動

今回は、OSSのK8S API Gatewayであるambassadorを用いて、TLS終端とsubdomainによるサービスルーティングを行います。

ambassadorのLoadBalancer Serviceを起動

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f ambassador/ambassador-service.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get services -l app=ambassador
NAME         TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)                      AGE
ambassador   LoadBalancer   10.2.0.19    xx.xx.xx.xx   443:30517/TCP,80:31893/TCP   2m50s

TLS証明書をsecretに登録

TERMINAL1
kurento-robot-camera-k8s$ kubectl create secret tls ambassador-certs \
  --cert=$(pwd)/secrets/live/${DOMAIN}/fullchain.pem \
  --key=$(pwd)/secrets/live/${DOMAIN}/privkey.pem

ambassadorのPodを起動

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f ambassador/ambassador-deployment.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get pods -l app=ambassador
NAME                         READY   STATUS    RESTARTS   AGE
ambassador-5c79db557-289pd   1/1     Running   0          2m29s

認証認可サービスの起動

ambassadorは、別のサービスと連携することでHTTPリクエストの認証を行うことができます。今回は、シンプルなBasic認証を行う自作のサービスと連携させます。

認証認可サービスのCluster IP Serviceを起動

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f auth/auth-service.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get services -l app=auth
NAME   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
auth   ClusterIP   10.2.0.237   <none>        3000/TCP   33s

認証認可サービスの設定ファイルを生成

環境変数として定義したBasic認証のCredentialを元に、設定ファイルを生成します。

TERMINAL1
kurento-robot-camera-k8s$ cat secrets/auth-tokens.json.template | \
  envsubst > secrets/auth-tokens.json

認証認可設定ファイルをsecretに登録

認証認可サービスのPodから参照できるように、生成した設定ファイルを auth-tokens という名前のSecretとしてAKSに登録します。

TERMINAL1
kurento-robot-camera-k8s$ kubectl create secret generic auth-tokens \
  --from-file=secrets/auth-tokens.json

認証認可サービスのPodを起動

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f auth/auth-deployment.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get pods -l app=auth
NAME                    READY   STATUS    RESTARTS   AGE
auth-67ff6bd94b-xt5hf   1/1     Running   0          44s

WebRTC検証用のSignaling&APPコンテナ(loopback)を起動

実際にロボットに接続する前に、WebRTCが正しく動作するかを確認します。Azure AKS上のcoturnやKurentoを利用して、PCのカメラ映像を自分自身へループバックするアプリを作りましたので、それを動作させてみましょう。
fig8.png

loopbackコンテナのCluster IP Serviceを起動

looopbackコンテナのServiceには、次のようなambassadorのMapping定義が記述されています。

loopback-service.yaml
kind: Service
metadata:
  name: loopback
  labels:
    app: loopback
  annotations:
    getambassador.io/config: |
      ---
      apiVersion: ambassador/v0
      kind:  Mapping
      name:  loopback-mapping
      prefix: /
      host: "^loopback\\..+$"
      host_regex: true
      service: http://loopback:3000
      use_websocket: true
spec:
  type: ClusterIP
  selector:
    app: loopback
  ports:
  - port: 3000
    targetPort: loopback
    name: loopback

ambassadorはこのannotationを検知して、 loopback.${DOMAIN} というsubdomainの場合はこのloopback ServiceにHTTP Requestを転送してくれます。

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f loopback/loopback-service.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get services -l app=loopback
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
loopback   ClusterIP   10.2.0.125   <none>        3000/TCP   20s

loopbackコンテナの設定情報をSecretに登録

このloopbackコンテナは、起動前に接続するSTUN ServerやTURN Serverの情報を loopback-secrets という名前のSecretに設定しておく必要があります。詳細はソースコードを確認してください。

TERMINAL1
kurento-robot-camera-k8s$ kubectl create secret generic loopback-secrets \
  --from-literal=STUN_LIST=${COTURN_PUBLIC_IP}:80 \
  --from-literal=TURN_LIST=${COTURN_CREDENTIAL}@${COTURN_PUBLIC_IP}:80

loopbackコンテナのPodを起動する

TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f loopback/loopback-deployment.yaml
TERMINAL1
kurento-robot-camera-k8s$ kubectl get pods -l app=loopback
NAME                        READY   STATUS    RESTARTS   AGE
loopback-5f5798bbfd-xv2b9   1/1     Running   0          2m8s

DNSにloopbackアプリ用のAレコードを登録する

TERMINAL1
kurento-robot-camera-k8s$ AMBASSADOR_PUBLIC_IP=$(kubectl get services \
  -l app=ambassador \
  -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}'); \
echo ${AMBASSADOR_PUBLIC_IP}
TERMINAL1
kurento-robot-camera-k8s$ az network dns record-set a add-record \
  --resource-group ${DNS_ZONE_RG} \
  --zone-name "${DOMAIN}" \
  --record-set-name "loopback" \
  --ipv4-address "${AMBASSADOR_PUBLIC_IP}"
TERMINAL1
kurento-robot-camera-k8s$ nslookup loopback.${DOMAIN}

WebRTCの動作確認

では最後に、このloopbackコンテナを使ってWebRTCの動作確認を行います。

loopback Web画面を表示

ChromeやFirefox等のモダンブラウザを立ち上げ、 https://loopback.${DOMAIN} にアクセスしてください。API Gatewayと認証認可サービス、及びloopbackコンテナが正しく起動していれば、Basic認証の画面が表示されます。
fig9.png
secrets/auth-tokens.json に定義されている usernamepassword を使ってログインしてください。loopbackコンテナのWeb画面が表示されます。
fig10.png

シグナリング

Start ボタンをクリックすると、loopbackコンテナを中心に、WebSocketを用いてブラウザとKurentoがSDPを交換します(この際に、coturnを使ってNAT越えのための情報を収集します)。

WebSocketで交換しているSDPの内容等は Console に出力されますので、興味があれば確認してください。

fig11.png

WebRTCによるブラウザ - Kurento間の経路確立

STUN & TURN Serverの情報をもとに最適な経路が確定すると、自動的にWebRTCによってSRTPによるMedia Streamが開設されます。
fig12.png

この図は、TURNサーバを経由せずにブラウザとKurentoが直結した場合です。

Web画面の左側の Local Stream はカメラ映像そのもので、右側に表示される Remote Stream はAzure AKS上のKurentoを経由して表示されています。

接続経路の確認

ブラウザの機能を用いて、ブラウザとKurentoが最終的にどのように接続されたのかを確認することができます。TURNサーバ経由で接続されてしまった場合は、TURNサーバのCPUを消費しますので、注意が必要です。

意味
host ローカルネットワークのIPとPortで接続
prflx UDPホールパンチング中に発見された接続候補のIPとPortを元に接続
srlfx STUN Serverを利用して入手したIPとPortを元に接続
relay TURN Serverを中継して接続

Chromeの場合

chrome://webrtc-internals/ を開くと、結局どのような経路で接続されているのかがわかります。様々な経路候補の中から、最終的に選択された経路が 太字Conn-xxxx-x-x という項目でまとめられています。
そのうち、googLocalCandidateType がブラウザ側から見た接続経路、 googRemoteCandidateType がKurento側から見た接続経路になります。

Firefoxの場合

about:webrtc を開くと、経路候補が表になって表示されます。最終的に選択された経路は、テーブル右端の選択がtrueとなっている経路です。 ローカル通信情報 がブラウザ側から見た接続経路、 リモート通信情報 がKurento側から見た接続経路になります。
fig13.png
この記事を書いたネットワーク環境では、UDPホールパンチングで発見した経路を使い、ブラウザとKurentoが直結したようです。

次回は

ここまででクラウド側は構築できました。次回はロボット側を構築し、Raspberry PiやROSロボットのカメラ映像をインターネット越しに視聴したいと思います。

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?