---
title: VMware Spring Cloud® Data Flow for Kubernetes 1.4をAKSにインストールするメモ
tags: ["Spring Boot", "Spring Cloud", "Spring Cloud Stream", "Spring Cloud Data Flow", "Kubernetes", "Azure", "AKS", "cert-manager", "Dex"]
categories: ["Programming", "Java", "org", "springframework", "cloud", "dataflow", "kubernetes"]
date: 2022-01-04T15:04:49Z
updated: 2022-01-04T17:21:44Z
---

[VMware Spring Cloud® Data Flow for Kubernetes 1.4](https://docs.vmware.com/en/VMware-Spring-Cloud-Data-Flow-for-Kubernetes/1.4/scdf-k8s/GUID-index.html) をAKSにインストールします。

**目次**
<!-- toc -->

### Cloud Shellの準備

インストール作業はCloud Shellで行います。ヘッダーのCloud Shellアイコンをクリックします。

![image](https://user-images.githubusercontent.com/106908/147896982-85fe2f58-5246-4af9-a832-a0ea97e55ca0.png)

次のようなターミナルが現れればOKです。

![image](https://user-images.githubusercontent.com/106908/147897042-1e43235c-3be5-49e7-862f-ce1fce56aca3.png)

予め次のバージョンのCLIがインストールされていました。

```
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.0", GitCommit:"ab69524f795c42094a6630298ff53f3c3ebab7f4", GitTreeState:"clean", BuildDate:"2021-12-07T18:16:20Z", GoVersion:"go1.17.3", Compiler:"gc", Platform:"linux/amd64"}

$ az version
{
  "azure-cli": "2.31.0",
  "azure-cli-core": "2.31.0",
  "azure-cli-telemetry": "1.0.6",
  "extensions": {
    "ai-examples": "0.2.5",
    "ssh": "1.0.0"
  }

$ helm version
version.BuildInfo{Version:"v3.4.0", GitCommit:"7090a89efc8a18f3d8178bf47d2462450349a004", GitTreeState:"clean", GoVersion:"go1.14.10"}

$ java -version
openjdk version "11.0.9" 2020-10-20
OpenJDK Runtime Environment Microsoft.13779 (build 11.0.9+8-20200922)
OpenJDK 64-Bit Server VM Microsoft.13779 (build 11.0.9+8-20200922, mixed mode)
```

`.bashrc`を編集して`PATH`の追加と`kubectl`の補完を設定します。

```
mkdir -p $HOME/clouddrive/bin
echo 'export PATH=$PATH:$HOME/clouddrive/bin' >> $HOME/.bashrc
echo 'source <(kubectl completion bash)' >> $HOME/.bashrc
source $HOME/.bashrc
```

以降で使用するCLIをインストールします。

```
wget -O $HOME/clouddrive/bin/pivnet https://github.com/pivotal-cf/pivnet-cli/releases/download/v3.0.1/pivnet-linux-amd64-3.0.1
chmod +x $HOME/clouddrive/bin/pivnet

wget -O $HOME/clouddrive/bin/yq https://github.com/mikefarah/yq/releases/download/v4.16.2/yq_linux_amd64
chmod +x $HOME/clouddrive/bin/yq

curl -L https://carvel.dev/install.sh | K14SIO_INSTALL_BIN_DIR=$HOME/clouddrive/bin bash
```

### AKSのK8sクラスタの作成

次のコマンドでリソースグループを作成します。

```
az group create --name scdf --location japaneast
```

次のコマンドでAKSのK8sクラスタを作成します。以降でIngressを使用するため、予め`http_application_routing`アドオンを有効にします。

```
az aks create \
  --resource-group scdf \
  --name scdf \
  --node-count 1 \
  --enable-addons http_application_routing \
  --enable-cluster-autoscaler \
  --min-count 1 \
  --max-count 5 \
  --node-vm-size standard_d4s_v3 \
  --network-plugin kubenet \
  --network-policy calico \
  --load-balancer-sku standard \
  --zones 1 2 3 \
  --generate-ssh-keys
```

次の環境が作成されました。
![image](https://user-images.githubusercontent.com/106908/147898169-f74bedc5-2a8d-4b8e-8bac-e86a766db760.png)

次のコマンドでkubeconfigを取得します。

```
az aks get-credentials --resource-group scdf --name scdf
```

クラスタにアクセスして状態を確認します。

```
$ kubectl cluster-info
Kubernetes control plane is running at https://scdf-scdf-85c083-c8066356.hcp.japaneast.azmk8s.io:443
addon-http-application-routing-default-http-backend is running at https://scdf-scdf-85c083-c8066356.hcp.japaneast.azmk8s.io:443/api/v1/namespaces/kube-system/services/addon-http-application-routing-default-http-backend/proxy
addon-http-application-routing-nginx-ingress is running at http://20.78.28.70:80 http://20.78.28.70:443
CoreDNS is running at https://scdf-scdf-85c083-c8066356.hcp.japaneast.azmk8s.io:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://scdf-scdf-85c083-c8066356.hcp.japaneast.azmk8s.io:443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

$ kubectl get pod -A
NAMESPACE         NAME                                                              READY   STATUS    RESTARTS   AGE
calico-system     calico-kube-controllers-78f868dcc4-5rrpx                          1/1     Running   1          4m46s
calico-system     calico-node-2p5nw                                                 1/1     Running   0          4m46s
calico-system     calico-typha-7767dcb544-55klx                                     1/1     Running   0          4m47s
kube-system       addon-http-application-routing-default-http-backend-6f6f7c5lzbr   1/1     Running   0          6m45s
kube-system       addon-http-application-routing-external-dns-7d7bfd959-wxxrb       1/1     Running   0          6m45s
kube-system       addon-http-application-routing-nginx-ingress-controller-846snpz   1/1     Running   0          6m45s
kube-system       coredns-845757d86-gnws4                                           1/1     Running   0          6m46s
kube-system       coredns-845757d86-lr964                                           1/1     Running   0          4m9s
kube-system       coredns-autoscaler-5f85dc856b-xrbbd                               1/1     Running   0          6m41s
kube-system       csi-azuredisk-node-9r6bp                                          3/3     Running   0          5m4s
kube-system       csi-azurefile-node-clkw5                                          3/3     Running   0          5m4s
kube-system       kube-proxy-dc667                                                  1/1     Running   0          5m4s
kube-system       metrics-server-6bc97b47f7-j2wg7                                   1/1     Running   0          6m45s
kube-system       tunnelfront-59847f757b-nds4w                                      1/1     Running   0          6m39s
tigera-operator   tigera-operator-56bf9877cf-gntz7                                  1/1     Running   0          6m42s

$ kubectl get svc -A
NAMESPACE       NAME                                                  TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
calico-system   calico-kube-controllers-metrics                       ClusterIP      10.0.80.168    <none>        9094/TCP                     4m41s
calico-system   calico-typha                                          ClusterIP      10.0.51.237    <none>        5473/TCP                     5m23s
default         kubernetes                                            ClusterIP      10.0.0.1       <none>        443/TCP                      7m43s
kube-system     addon-http-application-routing-default-http-backend   ClusterIP      10.0.60.95     <none>        80/TCP                       7m21s
kube-system     addon-http-application-routing-nginx-ingress          LoadBalancer   10.0.221.197   20.78.28.70   80:32132/TCP,443:32549/TCP   7m21s
kube-system     kube-dns                                              ClusterIP      10.0.0.10      <none>        53/UDP,53/TCP                7m22s
kube-system     metrics-server                                        ClusterIP      10.0.85.10     <none>        443/TCP                      7m21s
```

有効にした`http_application_routing`アドオンの実体はnginx-ingress + external-dnsであることがわかります。

### http_application_routingアドオンの動作確認

#### Ingressリソースの作成

`http_application_routing`アドオンを有効にすると自動でDNS Zoneが作成され、
そのIngressリソースに指定したhost名に対するAレコードがExternal DNSによって作成されます。

Ingressのサブドメイン、すなわちDNS Zone名は次のコマンドで確認できます。

```
INGRESS_SUBDOMAIN=$(kubectl get pod -n kube-system -l app=addon-http-application-routing-external-dns -o jsonpath='{.items[0].spec.containers[0].args}' | jq . | grep domain-filter | awk -F '"' '{print $2}' | awk -F '=' '{print $2}')
```

今回の環境では次の値でした。

```
$ echo ${INGRESS_SUBDOMAIN}
d341de814c76477e80c7.japaneast.aksapp.io
```

動作確認してみます。 https://docs.microsoft.com/ja-jp/azure/aks/http-application-routing のサンプルを試します。

```yaml
cat <<EOF > demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: aks-helloworld  
spec:
  replicas: 1
  selector:
    matchLabels:
      app: aks-helloworld
  template:
    metadata:
      labels:
        app: aks-helloworld
    spec:
      containers:
      - name: aks-helloworld
        image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
        ports:
        - containerPort: 80
        env:
        - name: TITLE
          value: "Welcome to Azure Kubernetes Service (AKS)"
---
apiVersion: v1
kind: Service
metadata:
  name: aks-helloworld  
spec:
  type: ClusterIP
  ports:
  - port: 80
  selector:
    app: aks-helloworld
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: aks-helloworld
  annotations:
    kubernetes.io/ingress.class: addon-http-application-routing
spec:
  rules:
  - host: aks-helloworld.${INGRESS_SUBDOMAIN}
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: aks-helloworld
            port: 
              number: 80
EOF

kubectl apply -f demo.yaml
```

次のコマンド作成されるリソースの状況を確認します。Ingressの`ADDRESS`が設定されるまで待ちます。ホスト名ごとに初回は5分くらいかかるかもしれません。

```
$ kubectl get pod,service,ingress
NAME                                  READY   STATUS    RESTARTS   AGE
pod/aks-helloworld-796448fcc4-zs4nb   1/1     Running   0          3m19s

NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/aks-helloworld   ClusterIP   10.0.48.45   <none>        80/TCP    3m20s
service/kubernetes       ClusterIP   10.0.0.1     <none>        443/TCP   39m

NAME                                       CLASS    HOSTS                                                     ADDRESS       PORTS   AGE
ingress.networking.k8s.io/aks-helloworld   <none>   aks-helloworld.d341de814c76477e80c7.japaneast.aksapp.io   20.78.28.70   80      3m
```

ブラウザでIngressのhost名にアクセスし、次の画面が表示されればOKです。

![image](https://user-images.githubusercontent.com/106908/147899307-8680fb2a-0efd-4b49-ad7c-fad7f287f92f.png)

DNSのレコードのデフォルトのTTLが300秒なので、ホスト名が解決できない場合は`dig`コマンドでTTLの残り時間を確認してみてください。
TTLが0になってもアクセスできない場合は [トラブルシューティングのページ](https://docs.microsoft.com/ja-jp/azure/aks/http-application-routing#troubleshoot) を確認してください。 

#### cert-managerによるIngressのTLS対応

HTTPS対応をしたいため、次のコマンドでcert-managerをインストールします。

```
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml
```

次のコマンドでClusterIssuerを作成します。 AKSはパブリックIPを使用するので、Let's EncryptのHTTP-01チャレンジが利用できます。 

```yaml
cat <<EOF > cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: user@yourdomain.com
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
        ingress:
          class: addon-http-application-routing
EOF
kubectl apply -f cluster-issuer.yaml
```

先ほど作成したIngressを次のコマンドでTLS対応します。

```yaml
cat <<EOF > ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: aks-helloworld
  annotations:    
    kubernetes.io/ingress.class: addon-http-application-routing
    cert-manager.io/cluster-issuer: letsencrypt
    ingress.kubernetes.io/force-ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"    
spec:
  tls:
  - secretName: aks-helloworld-tls
    hosts:
    - aks-helloworld.${INGRESS_SUBDOMAIN} 
  rules:
  - host: aks-helloworld.${INGRESS_SUBDOMAIN}
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: aks-helloworld
            port: 
              number: 80
EOF
kubectl apply -f ingress.yaml
```

次のコマンドでTLS証明書が発行されたことを確認します。Certificateリソースの`READY`が`True`になり、`aks-helloworld-tls`というSecretが作成されるまで待ちます。

```
$ kubectl get ingress,certificate,secret
NAME                                       CLASS    HOSTS                                                     ADDRESS       PORTS     AGE
ingress.networking.k8s.io/aks-helloworld   <none>   aks-helloworld.d341de814c76477e80c7.japaneast.aksapp.io   20.78.28.70   80, 443   11m

NAME                                             READY   SECRET               AGE
certificate.cert-manager.io/aks-helloworld-tls   True    aks-helloworld-tls   33s

NAME                         TYPE                                  DATA   AGE
secret/aks-helloworld-tls    kubernetes.io/tls                     2      5s
secret/default-token-smdqj   kubernetes.io/service-account-token   3      112m
```

5分以上たっても`READY`が`True`にならない場合は該当のCertificateリソースを削除してみてください。自動的に再作成されます。

`READY`が`True`になればブラウザでIngressのhost名にアクセスし、Let's Encryptによって発行されたTLS証明書が使用されていることを確認します。
![image](https://user-images.githubusercontent.com/106908/147901791-c1b70150-cbbe-4935-a4d6-8e5e59410449.png)

動作確認が終われば作成したリソースを削除します。

```
kubectl delete -f demo.yaml
```

### VMware Spring Cloud Data Flow for Kubernetesのインストール

いよいよ [Spring Cloud Data Flow](https://docs.vmware.com/en/VMware-Spring-Cloud-Data-Flow-for-Kubernetes/index.html) をインストールします。

#### manifestのダウンロード

[Tanzu Network](https://network.tanzu.vmware.com)のアカウントがない場合は作成してください。
manifestをCLIからダウンロードするために、APIトークンを取得します。

https://network.tanzu.vmware.com/users/dashboard/edit-profile

下の図の"REQUEST NEW REFRESH TOKEN"をクリックし、API TOKENを取得してください。

![image](https://user-images.githubusercontent.com/106908/147899475-bbe0f6cf-1dfa-4d92-9f40-3a423f7d0cc3.png)



`pivnet` CLIを使い、次のコマンドでログインしてください。

```
TANZU_NETWORK_API_TOKEN=***************-r
pivnet login --api-token=${TANZU_NETWORK_API_TOKEN}
```

次のコマンドでmanifestをダウンロードします。

```
mkdir -p $HOME/scdf
cd $HOME/scdf
pivnet download-product-files -p p-scdf-for-kubernetes -r 1.4.0 --glob='data-flow-*.tgz'
```

次のファイルがダウンロードされます。

```
$ ls -lh $HOME/scdf
total 608M
-rw-r--r-- 1 toshiaki toshiaki 166K Jan  3 04:25 data-flow-1.4.0.tgz
-rw-r--r-- 1 toshiaki toshiaki 502M Jan  3 04:25 data-flow-images-1.4.0.tgz
```

#### Data Flow Serverのインストール

次のコマンドでmanifestのtgzファイルを展開します。
```
cd $HOME/scdf
tar xzvf data-flow-1.4.0.tgz
```

次のコマンドでData Flow Server用のIngressのマニフェストを今回の環境用に更新します。

```yaml
INGRESS_SUBDOMAIN=$(kubectl get pod -n kube-system -l app=addon-http-application-routing-external-dns -o jsonpath='{.items[0].spec.containers[0].args}' | jq . | grep domain-filter | awk -F '"' '{print $2}' | awk -F '=' '{print $2}')
cat <<EOF > $HOME/scdf/spring-cloud-data-flow/apps/ingress/kustomize/overlays/dev/ingress-patch.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: scdf-ingress
  annotations:    
    kubernetes.io/ingress.class: addon-http-application-routing
    cert-manager.io/cluster-issuer: letsencrypt
    ingress.kubernetes.io/force-ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"    
spec:
  tls:
  - secretName: data-flow-tls
    hosts:
    - data-flow.${INGRESS_SUBDOMAIN} 
  rules:
  - host: data-flow.${INGRESS_SUBDOMAIN}
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: scdf-server
            port:
              number: 80        
EOF
```

次のコマンドでImage Pull Secretを作成します。

```
TANZUNET_USERNAME=********
TANZUNET_PASSWORD=********
kubectl create secret docker-registry scdf-image-regcred \
  --docker-server=registry.pivotal.io \
  --docker-username=${TANZUNET_USERNAME} \
  --docker-password=${TANZUNET_PASSWORD} \
  --dry-run=client -oyaml \
| kubectl apply -f-
```

次のコマンドでSpring Cloud Dataflowをインストールします。

```
$HOME/scdf/spring-cloud-data-flow/bin/install-dev.sh \
  --database postgresql \
  --broker rabbitmq \
  --monitoring prometheus
```

次のコマンドでSpring Cloud Dataflow用のIngressをインストールします。

```
kubectl kustomize $HOME/scdf/spring-cloud-data-flow/apps/ingress/kustomize/overlays/dev/ | kapp deploy -y -a ingress -f -
```

作成されたリソースを確認します。5分以上たっても`data-flow-tls`という名前のCertificateリソースの`READY`が`TRUE`にならない場合は、このリソースを一度削除し、再作成されるのを待ってください。

```
$ kubectl get pod,pvc,ingress,certificate
NAME                                                         READY   STATUS    RESTARTS   AGE
pod/alertmanager-prometheus-kube-prometheus-alertmanager-0   2/2     Running   0          23m
pod/grafana-f58496fc5-stmrm                                  1/1     Running   0          23m
pod/postgresql-0                                             1/1     Running   0          25m
pod/prometheus-kube-prometheus-operator-658bdb4fb-gjt88      1/1     Running   0          23m
pod/prometheus-kube-state-metrics-b57ff7797-vr5z5            1/1     Running   0          23m
pod/prometheus-node-exporter-xwnw8                           1/1     Running   0          23m
pod/prometheus-prometheus-kube-prometheus-prometheus-0       3/3     Running   1          23m
pod/rabbitmq-0                                               1/1     Running   0          24m
pod/scdf-prometheus-proxy-78d6f6d647-srf2c                   1/1     Running   0          12m
pod/scdf-server-7fc8d6766f-ph8rl                             1/1     Running   0          14m
pod/skipper-5645f4c7c7-ndx7j                                 1/1     Running   0          15m

NAME                                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/data-postgresql-0   Bound    pvc-f314498d-65e3-443b-a257-a947a0a7c914   8Gi        RWO            default        25m
persistentvolumeclaim/data-rabbitmq-0     Bound    pvc-75c1620f-b62c-445f-9a5c-2d3c22f66988   8Gi        RWO            default        24m
persistentvolumeclaim/grafana             Bound    pvc-4b6edcf3-b9ee-4e3a-8700-97afe0914d30   10Gi       RWO            default        23m

NAME                                     CLASS    HOSTS                                                ADDRESS       PORTS     AGE
ingress.networking.k8s.io/scdf-ingress   <none>   data-flow.d341de814c76477e80c7.japaneast.aksapp.io   20.78.28.70   80, 443   7m33s

NAME                                        READY   SECRET          AGE
certificate.cert-manager.io/data-flow-tls   True    data-flow-tls   7m33s
```

ブラウザで`https://<Ingressのhost名>/dashboard`にアクセスし、次の画面が表示されればOKです。

![image](https://user-images.githubusercontent.com/106908/147903449-39f3889d-639c-44ff-9dd7-391e8e19e333.png)

#### Grafana用のIngress作成

Grafana用のIngressは用意されていないので、次のコマンドで新規作成します。

```
INGRESS_SUBDOMAIN=$(kubectl get pod -n kube-system -l app=addon-http-application-routing-external-dns -o jsonpath='{.items[0].spec.containers[0].args}' | jq . | grep domain-filter | awk -F '"' '{print $2}' | awk -F '=' '{print $2}')
cat <<EOF > $HOME/scdf/spring-cloud-data-flow/services/dev/monitoring/grafana-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: grafana-ingress
  annotations:    
    kubernetes.io/ingress.class: addon-http-application-routing
    cert-manager.io/cluster-issuer: letsencrypt
    ingress.kubernetes.io/force-ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"    
spec:
  tls:
  - secretName: grafana-tls
    hosts:
    - grafana.${INGRESS_SUBDOMAIN} 
  rules:
  - host: grafana.${INGRESS_SUBDOMAIN}
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: grafana
            port:
              number: 3000        
EOF
```

再度、次のコマンドでインストールします。

```
$HOME/scdf/spring-cloud-data-flow/bin/install-dev.sh \
  --database postgresql \
  --broker rabbitmq \
  --monitoring prometheus
```

作成されたリソースを確認します。5分以上たっても`grafana-tls`という名前のCertificateリソースの`READY`が`TRUE`にならない場合は、このリソースを一度削除し、再作成されるのを待ってください。

```
$ kubectl get ingress,certificate
NAME                                        CLASS    HOSTS                                                ADDRESS       PORTS     AGE
ingress.networking.k8s.io/grafana-ingress   <none>   grafana.d341de814c76477e80c7.japaneast.aksapp.io     20.78.28.70   80, 443   19m
ingress.networking.k8s.io/scdf-ingress      <none>   data-flow.d341de814c76477e80c7.japaneast.aksapp.io   20.78.28.70   80, 443   139m

NAME                                        READY   SECRET          AGE
certificate.cert-manager.io/data-flow-tls   True    data-flow-tls   139m
certificate.cert-manager.io/grafana-tls     True    grafana-tls     73s
```

ブラウザでIngressのhost名にアクセスし、次の画面が表示されればOKです。

![image](https://user-images.githubusercontent.com/106908/147910880-b93d0a3f-861c-446d-8e5a-2ab5f8d4e237.png)

デフォルトのユーザー名 / パスワードは`admin` / `CHANGEME`です。 

```
sed -i "s|http://localhost:3000|https://grafana.${INGRESS_SUBDOMAIN}|" ${HOME}/scdf/spring-cloud-data-flow/apps/data-flow/kustomize/overlays/dev/application-monitoring.yaml
```
#### Pre-packaged Applicationsの登録

Data Flow Serverに [Pre-packaged Applications](https://dataflow.spring.io/docs/applications/pre-packaged/) を登録します。

"Applications" -> "ADD APPLICATION(S)"をクリックし、"import application starters from dataflow.spring.io"をクリックし、"Stream application starters for RabbitMQ/Docker"を選択します。
その後、"IMPORT APPLICATION(S)"をクリックしてPre-packaged Applicationsの定義をインポートします。

![image](https://user-images.githubusercontent.com/106908/147910169-f6aca0ad-2f62-4e4a-851a-5b58bf22b426.png)

"Applications"に戻ると登録されたアプリケーション一覧が表示されます。

![image](https://user-images.githubusercontent.com/106908/147910329-e9a837d7-f85b-4c72-9d89-422bfad08d5f.png)


#### Spring Cloud Dataflow Shellの動作確認

ShellでもData Flow Serverにアクセスしてみます。次のコマンドでShellのjarファイルをダウンロードします。

```
wget -O $HOME/scdf/spring-cloud-dataflow-shell-2.9.1.jar https://repo.spring.io/release/org/springframework/cloud/spring-cloud-dataflow-shell/2.9.1/spring-cloud-dataflow-shell-2.9.1.jar
```

次のコマンドでShellを起動します。

```
INGRESS_SUBDOMAIN=$(kubectl get pod -n kube-system -l app=addon-http-application-routing-external-dns -o jsonpath='{.items[0].spec.containers[0].args}' | jq . | grep domain-filter | awk -F '"' '{print $2}' | awk -F '=' '{print $2}')

java -jar ${HOME}/scdf/spring-cloud-dataflow-shell-2.9.1.jar --dataflow.uri=https://data-flow.${INGRESS_SUBDOMAIN}
```

次のような出力が表示されます。

```
  ____                              ____ _                __
 / ___| _ __  _ __(_)_ __   __ _   / ___| | ___  _   _  __| |
 \___ \| '_ \| '__| | '_ \ / _` | | |   | |/ _ \| | | |/ _` |
  ___) | |_) | |  | | | | | (_| | | |___| | (_) | |_| | (_| |
 |____/| .__/|_|  |_|_| |_|\__, |  \____|_|\___/ \__,_|\__,_|
  ____ |_|    _          __|___/                 __________
 |  _ \  __ _| |_ __ _  |  ___| | _____      __  \ \ \ \ \ \
 | | | |/ _` | __/ _` | | |_  | |/ _ \ \ /\ / /   \ \ \ \ \ \
 | |_| | (_| | || (_| | |  _| | | (_) \ V  V /    / / / / / /
 |____/ \__,_|\__\__,_| |_|   |_|\___/ \_/\_/    /_/_/_/_/_/

2.9.1

Welcome to the Spring Cloud Data Flow shell. For assistance hit TAB or type "help".
Successfully targeted https://data-flow.d341de814c76477e80c7.japaneast.aksapp.io
dataflow:>
```

`app list`コマンドを実行して、登録したアプリケーション一覧を確認してください。

```
dataflow:>app list
╔═══╤═══════════════╤═════════════════════╤═══════════════╤════╗
║app│    source     │      processor      │     sink      │task║
╠═══╪═══════════════╪═════════════════════╪═══════════════╪════╣
║   │syslog         │groovy               │router         │    ║
║   │twitter-message│filter               │twitter-update │    ║
║   │websocket      │twitter-trend        │tcp            │    ║
║   │rabbit         │image-recognition    │cassandra      │    ║
║   │s3             │http-request         │throughput     │    ║
║   │zeromq         │splitter             │redis          │    ║
║   │ftp            │semantic-segmentation│ftp            │    ║
║   │tcp            │bridge               │rsocket        │    ║
║   │geode          │object-detection     │mqtt           │    ║
║   │cdc-debezium   │aggregator           │rabbit         │    ║
║   │jms            │script               │websocket      │    ║
║   │jdbc           │transform            │jdbc           │    ║
║   │twitter-search │header-enricher      │geode          │    ║
║   │mqtt           │                     │zeromq         │    ║
║   │mail           │                     │s3             │    ║
║   │time           │                     │file           │    ║
║   │load-generator │                     │wavefront      │    ║
║   │sftp           │                     │sftp           │    ║
║   │file           │                     │mongodb        │    ║
║   │twitter-stream │                     │analytics      │    ║
║   │http           │                     │elasticsearch  │    ║
║   │mongodb        │                     │log            │    ║
║   │               │                     │pgcopy         │    ║
║   │               │                     │twitter-message│    ║
╚═══╧═══════════════╧═════════════════════╧═══════════════╧════╝
```

`exit`でShellから抜けてください。

### 簡単なストリームの作成

[こちらのドキュメント](https://docs.vmware.com/en/VMware-Spring-Cloud-Data-Flow-for-Kubernetes/1.4/scdf-k8s/GUID-getting-started-pipeline.html) のように`http | splitter | log`という簡単なストリームを作成します。

"Streams" -> "Streams"をクリックし、テキストエリアに`http | splitter --expression=payload.split(' ') | log`を入力してください。
あるいは左のパレットからアプリケーションをドラッグ&ドロップし、線をつなげてください。

![image](https://user-images.githubusercontent.com/106908/147912627-80d540a4-a3e3-41fd-a038-3854e77fc075.png)

`--expression`の値は下の図のようにダイアログからも設定できます。

![image](https://user-images.githubusercontent.com/106908/148086843-aa1f5ca7-3cf0-4458-9375-fbde06e3a582.png)

![image](https://user-images.githubusercontent.com/106908/147912651-2a6bba76-c626-45ef-ba6e-b841e24bce7d.png)

"CREATE STREAM(S)"ボタンをクリックし、ダイアログ上のフォームの"Name"に`words`を入力し、"CREATE THE STREAM(S)"ボタンを押してください。

![image](https://user-images.githubusercontent.com/106908/147912735-3a363ecc-fc3c-44e0-86be-1a212696e18a.png)

Streamが作成されました。この段階では定義のみで、デプロイはされていません。

![image](https://user-images.githubusercontent.com/106908/147912777-6aaa6fee-51cb-4214-a30e-6d3a4e5c2606.png)

メニューから"Deploy"を選択します。

![image](https://user-images.githubusercontent.com/106908/147912813-81398cd7-c45a-44fb-b3bd-b0a2f6a4413c.png)

パラメータを設定します。ここでは`http` sourceにLoad Balancerをアタッチするために"Deployment Platform"の"EDIT"ボタンをクリックしてください。

![image](https://user-images.githubusercontent.com/106908/147912853-7f6d35d9-f373-4354-92d4-e5f76c7356a2.png)

`create-load-balancer`にチェックを入れ、"UPDATE"ボタンをクリックしてください。

![image](https://user-images.githubusercontent.com/106908/147913004-14be162c-bc5d-4332-ac38-7ecda4be9487.png)

"DEPLOY STREAM"ボタンをクリックしてください。"Status"が`DEPLOYING`に変わります。

![image](https://user-images.githubusercontent.com/106908/147913064-70a20e7e-cc72-4119-ad09-c144278d06cb.png)

数分すると"Status"が`DEPLOYED`に変わります。

![image](https://user-images.githubusercontent.com/106908/147913253-116661cd-37da-4102-982e-0f5140076ea5.png)

Stream名をクリックすると詳細を確認できます。

![image](https://user-images.githubusercontent.com/106908/147913285-70611d26-0f03-4fdf-ad0f-57d5198bdc9b.png)

"Streams" -> "Runtime"をクリックすると実際にデプロイされているインススタンスの状態を確認できます。"VIEW DETAILS"をクリックすればそれぞれの詳細を確認できます。

![image](https://user-images.githubusercontent.com/106908/147913535-54faa4a5-1d1a-4b7f-93ea-3792ae195d36.png)

`http` sourceにアタッチされたLoad BalancerのIPは詳細の`service.external.ip`から確認できます。

![image](https://user-images.githubusercontent.com/106908/147913546-25616966-d0fa-46a8-9681-5007561d96df.png)

このIPに対して次のようにHTTPリクエストを送ります。

```
curl http://20.210.16.33:8080 -d "This is a test" -H "Content-Type: text/plain" -sv
```

次のようにHTTP Statusが`202`を返せばOKです。

```
*   Trying 20.210.16.33...
* TCP_NODELAY set
* Connected to 20.210.16.33 (20.210.16.33) port 8080 (#0)
> POST / HTTP/1.1
> Host: 20.210.16.33:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: text/plain
> Content-Length: 14
> 
* upload completely sent off: 14 out of 14 bytes
< HTTP/1.1 202 
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1 ; mode=block
< Referrer-Policy: no-referrer
< Content-Length: 0
< Date: Mon, 03 Jan 2022 09:00:36 GMT
< 
* Connection #0 to host 20.210.16.33 left intact
* Closing connection 0
```

Streamの詳細に戻って、`log` sinkの"VIEW LOG"ボタンをクリックしてください。

![image](https://user-images.githubusercontent.com/106908/147913821-c8f57bc1-111c-4587-b36a-b412e517b069.png)

次のように、リクエストで送ったメッセージが空白を区切りに分割されて1行ずつ出力されていることがわかります。

![image](https://user-images.githubusercontent.com/106908/147913837-a4da8831-7ccf-4601-ba55-3b07a1ab8d28.png)

"GRAFANA DASHBOARD"ボタンをクリックしてください。

![image](https://user-images.githubusercontent.com/106908/147916358-10d7db38-3985-4991-a3d9-ae79f442dc46.png)

このStreamのメトリクスをダッシュボードで確認できます。

![image](https://user-images.githubusercontent.com/106908/147914161-6f4a51c2-cbf0-45b8-889d-6065b0a2423d.png)


### Data Flow ServerのOAuth2連携

Data Flow Serverはデフォルトでは認証がかかっていないので、OAuth2連携の設定を行います。
ここではOAuth2 Serverとして [Dex](https://dexidp.io) を使用します。

#### Dexのインストール

Dexは https://github.com/dexidp/helm-charts を使ってインストールします。
次の設定ファイルを作成します。

```yaml
INGRESS_SUBDOMAIN=$(kubectl get pod -n kube-system -l app=addon-http-application-routing-external-dns -o jsonpath='{.items[0].spec.containers[0].args}' | jq . | grep domain-filter | awk -F '"' '{print $2}' | awk -F '=' '{print $2}')

cat <<EOF > $HOME/scdf/dex-values.yaml
config:
  issuer: https://dex.${INGRESS_SUBDOMAIN}
  expiry:
    signingKeys: "10m"
    idTokens: "5m"
  logger:
    level: "debug"
    format: "json"
  storage:
    type: kubernetes
    config:
      inCluster: true     
  enablePasswordDB: true
  oauth2:
    skipApprovalScreen: true
    passwordConnector: local
  staticClients:
  - id: dataflow-server
    secret: scdf
    name: Spring Cloud Data Flow Server
    redirectURIs:
    - https://data-flow.${INGRESS_SUBDOMAIN}/login/oauth2/code/dex
  staticPasswords:
  - email: admin@example.com
    # bcrypt hash of the string "CHANGEME" 
    # \$ echo CHANGEME | htpasswd -BinC 10 admin | cut -d: -f2
    hash: \$2y\$10\$s4LWe9GLP9IUgh.yeX/lB.Z1Hu8atkGcZmP6/1FxDxI6ENlkkczWa
    username: admin
    userID: admin
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: addon-http-application-routing
    cert-manager.io/cluster-issuer: letsencrypt
    ingress.kubernetes.io/force-ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
  hosts:
  - host: dex.${INGRESS_SUBDOMAIN}
    paths:
    - path: /
      pathType: Prefix
      backend:
        service:
          name: dex
          port:
            number: 5556
  tls:
  - secretName: dex-tls
    hosts:
    - dex.${INGRESS_SUBDOMAIN}
EOF
```

次のコマンドでインストールします。

```
helm repo add dex https://charts.dexidp.io
helm upgrade dex dex/dex -f $HOME/scdf/dex-values.yaml -n dex --create-namespace --install --debug --wait   
```

作成されたリソースを確認します。5分以上たっても`dex-tls`という名前のCertificateリソースの`READY`が`TRUE`にならない場合は、このリソースを一度削除し、再作成されるのを待ってください。


```
$ kubectl get pod,certificate,ingress,order -n dex
NAME                       READY   STATUS    RESTARTS   AGE
pod/dex-76f5bcf845-g5svr   1/1     Running   0          6m33s

NAME                                  READY   SECRET    AGE
certificate.cert-manager.io/dex-tls   True    dex-tls   113s

NAME                            CLASS    HOSTS                                          ADDRESS       PORTS     AGE
ingress.networking.k8s.io/dex   <none>   dex.d341de814c76477e80c7.japaneast.aksapp.io   20.78.28.70   80, 443   6m33s

NAME                                                  STATE   AGE
order.acme.cert-manager.io/dex-tls-lh9sh-2368157721   valid   112s
```

ブラウザで`https://dex.${INGRESS_SUBDOMAIN}/auth`にアクセスして次の画面が表示されればOKです。

![image](https://user-images.githubusercontent.com/106908/147946160-0ff421dc-309e-49c4-9c60-76247233ec76.png)

#### Data Flow Serverのアップデート

次にData Flow Server側の設定を変更します。

次コマンドでファイルを作成・更新をしてください。

```yaml
INGRESS_SUBDOMAIN=$(kubectl get pod -n kube-system -l app=addon-http-application-routing-external-dns -o jsonpath='{.items[0].spec.containers[0].args}' | jq . | grep domain-filter | awk -F '"' '{print $2}' | awk -F '=' '{print $2}')

cat <<EOF > $HOME/scdf/spring-cloud-data-flow/apps/data-flow/kustomize/overlays/dev/application-security.yaml
spring:
  security:
    oauth2:                                                           
      client:
        registration:
          dex:                                                        
            client-id: dataflow-server
            client-secret: scdf
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            authorization-grant-type: authorization_code
            scope:
            - openid
            - email
            - profile
            - groups
            - offline_access
        provider:
          dex:
            issuer-uri: https://dex.${INGRESS_SUBDOMAIN}
            user-name-attribute: name
      resourceserver:
        jwt:
          jwk-set-uri: https://dex.${INGRESS_SUBDOMAIN}/keys
  cloud:
    dataflow:
      security:
        authorization:
          provider-role-mappings:
            dex:
              map-oauth-scopes: true
              role-mappings:
                ROLE_CREATE: openid
                ROLE_DEPLOY: openid
                ROLE_DESTROY: openid
                ROLE_MANAGE: openid
                ROLE_MODIFY: openid
                ROLE_SCHEDULE: openid
                ROLE_VIEW: openid
EOF


cat <<EOF > $HOME/scdf/spring-cloud-data-flow/apps/data-flow/kustomize/overlays/dev/deployment-patch-monitoring.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: scdf-server
spec:
  template:
    spec:
      containers:
      - name: scdf-server
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: database,monitoring,security # Updated
        volumeMounts:
          - name: dockerconfig
            mountPath: /workspace/runtime/secrets/dockerconfig
            readOnly: true
      imagePullSecrets:
      - name: scdf-image-regcred
      volumes:
        - name: dockerconfig
          secret:
            secretName: scdf-image-regcred
EOF

cat <<EOF > $HOME/scdf/spring-cloud-data-flow/apps/data-flow/kustomize/overlays/dev/kustomization.yaml
images:
- name: springcloud/spring-cloud-dataflow-server # used for Kustomize matching
  newName: registry.pivotal.io/p-scdf-for-kubernetes/spring-cloud-dataflow-pro-server
  newTag: 1.4.1_scdf-k8s-1.4.0
  digest: sha256:bd7725a3fd2476546fe20fb40b67f5b00b8d27e5358586ed4a57e3a865271373
configMapGenerator:
- name: scdf-server
  files:
  - bootstrap.yaml
  - application.yaml
  - application-database.yaml
  - application-monitoring.yaml
  - application-security.yaml # Added
bases:
- ../../base
patches:
- deployment-patch.yaml
- deployment-database-patch.yaml
- service-patch.yaml
EOF
```

再度、次のコマンドでインストールします。

```
$HOME/scdf/spring-cloud-data-flow/bin/install-dev.sh \
  --database postgresql \
  --broker rabbitmq \
  --monitoring prometheus
```
再度、`https://data-flow.${INGRESS_SUBDOMAIN}/dashboard`にアクセスすると、Data FlowのLoginページへリダイレクトされます。
DexのURLをクリックしてください。
![image](https://user-images.githubusercontent.com/106908/147951257-f7a8bad4-08f6-4e8d-859e-819463dad895.png)

Dexのログインフォームへリダイレクトされます。ユーザー名 `admin@example.com` パスワード `CHANGEME` を入力して、"Login"ボタンを押してください。

![image](https://user-images.githubusercontent.com/106908/147951365-48a59614-1ffc-4f30-9f62-078f88725885.png)

無事ログインできていたら右上にユーザ名`admin`が表示されます。

![image](https://user-images.githubusercontent.com/106908/147952639-9dde0927-0217-47cf-b66f-e1ea66da5e2a.png)
