新人が Kubernetes を VIM として CNF を Tacker でデプロイする話

# 目次

[1. はじめに](#はじめに)
[2. 環境](#環境)
[3. インストール手順](#インストール手順)
[4. 発生したエラー](#発生したエラー)
[5. まとめ](#まとめ)

# 参考 URL

- [ETSI NFV-SOL CNF (Containerized VNF) Deployment](https://docs.openstack.org/tacker/latest/user/etsi_containerized_vnf_usage_guide.html)
- [VNF と CNF の違い](https://www.redhat.com/ja/topics/cloud-native-apps/vnf-and-cnf-whats-the-difference)
- [VNF Package](https://docs.openstack.org/tacker/latest/user/vnf-package.html) 
- [Kubernetes VIM Installation](https://docs.openstack.org/tacker/latest/install/kubernetes_vim_installation.html)
- [Master NodeにPodをデプロイするための設定](https://qiita.com/nykym/items/dcc572c21885543d94c8)
- [Kuryr installation as a Kubernetes network addon](https://docs.openstack.org/kuryr-kubernetes/latest/installation/containerized.html)

# はじめに

こんにちは。新入社員として約 3 か月目の新人です。

この記事では NFV という言葉すら知らなかった新人が [Install via Devstack]( https://docs.openstack.org/tacker/latest/install/devstack.html) を参考にして DevStack 環境をインストールし、VIM として OpenStack と Kubernetes を登録するまでの手順と発生したエラーを複数の章に分けて記載していきます。

- 第 1 章: [新人が DevStack 上で Tacker を動かそうとする話](https://qiita.com/yoshi_1010/items/c1db52099b5897e049d0)

[Install via Devstack]( https://docs.openstack.org/tacker/latest/install/devstack.html) を実際に試します。

- 第 2 章: [新人が DevStack 上で TOSCA を使用して VNF を VM としてデプロイする話](https://qiita.com/yoshi_1010/items/1dbe4fe3c11f2b743853)

[ETSI NFV-SOL VNF Deployment as VM with TOSCA](https://docs.openstack.org/tacker/latest/user/etsi_vnf_deployment_as_vm_with_tosca.html) を実際に試します。

- 第 3 章: [新人が DevStack 上で LCM Operation User Data を使用して VNF を VM としてデプロイする話](https://qiita.com/yoshi_1010/items/d4ef35f8ede6f4d498f1)

[ETSI NFV-SOL VNF Deployment as VM with LCM Operation User Data](https://docs.openstack.org/tacker/latest/user/etsi_vnf_deployment_as_vm_with_user_data.html) を実際に試します。

- 第 4 章: この記事です。

[ETSI NFV-SOL CNF (Containerized VNF) Deployment](https://docs.openstack.org/tacker/latest/user/etsi_containerized_vnf_usage_guide.html) を実際に試します。

今回のインストールで自分が調べた用語は以下にまとめました。

## CNF (Containerized Network Function) 

コンテナ化されたネットワーク機能を提供します。

## CSAR (Cloud Service Archive) 

ZIP ファイル形式を使用したアーカイブファイルです。
その構造は TOSCA Simple Profile YAML v1.2 仕様に準拠しています。

アーカイブするファイル構造は [VNF Package](https://docs.openstack.org/tacker/latest/user/vnf-package.html) に記載されています。
 
# 環境

ESXi 上に Devstack をインストールし、環境に対して一部設定を追加しています。
詳しくは [新人が DevStack 上で Tacker を動かそうとする話](https://qiita.com/yoshi_1010/items/c1db52099b5897e049d0) と [新人が DevStack 上で TOSCA を使用して VNF を VM としてデプロイする話](https://qiita.com/yoshi_1010/items/1dbe4fe3c11f2b743853) をお読みください。

# インストール手順

この手順は本環境でインストール完了までうまくいったものです。
手順完成までに発生したエラーは [4. 発生したエラー](#発生したエラー) にまとめます。

## 実行ユーザとディレクトリ

stack ユーザで実行します。

```
sudo -u stack -i
```

自分で作成したファイルを配置する work ディレクトリを作成します。

```
mkdir ~/work/
```

work ディレクトリに移動します。

```
cd ~/work
```

## Kubernetes 環境の準備 (kubeadm)

Tacker が ETSINFV-SOL コンテナ化 VNF を Kubernetes にデプロイする方法について記載します。

本環境では kuryr-kubernetes が動作しないため、Devstack 内の Kubernetes を VIM として利用できませんでした。
この kuryr-kubernetes は VM を再起動すると停止し、動作しなくなります。
原因は現在も不明です。
そのため、新しい VM 上に kubeadm で Kubernetes を起動させ、VIM として利用します。

この手順は Devstack を動作させている環境とは別の環境で動作させなければなりません。
Devstack を動作させている環境では etcd が動作しています。
そのため、同環境へ新たに Kubernetes を動作させると etcd が競合してしまいます。

本手順完了後の環境は下記の図のようになります。
この図は [1. はじめに](#はじめに) で述べた章をすべて実施済みの状態です。

![4記事目.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735085/dc5501a7-4976-5270-5933-c0589811ace6.png)

### swap の無効化

Kubernetes の init 時にエラーが発生してしまうため、swap を無効化します。

fstab のバックアップファイルを作成します。

```
sudo cp -ai /etc/fstab{,.default}
```

swap を無効化します。
下記の項目をコメントアウトします。

```
$ vim  /etc/fstab
/swap.img     none    swap    sw      0       0
```

確認します。

```
$ diff /etc/fstab{.default,}
12c12
< /swap.img     none    swap    sw      0       0
---
> #/swap.img    none    swap    sw      0       0
```

この設定により再起動後も swap が無効化された状態で起動しますが、再起動なしで swap を無効化する場合は下記を実行します。

```
sudo swapoff -a
```

### カーネルパラメータの設定

ネットワークブリッジを通過するパケットに対し iptables ルールを適用するかどうかを指定するカーネルパラメータ net.bridge.bridge-nf-call-iptables を確認します。

1 と設定されている場合は問題ありません。

```
$ cat /proc/sys/net/bridge/bridge-nf-call-iptables
1
```

0 と設定されている場合は以下を実行することで設定を 1 に変更します。

```
echo net.bridge.bridge-nf-call-iptables = 1 > /etc/sysctl.d/99-bridge-nf-call-iptables
sysctl -p /etc/sysctl.d/99-bridge-nf-call-iptables 
```

### Docker driver の変更

Kubernetes v1.21 から、kubeadm の標準の cgroup の driver が cgroupfs から systemd に変更されました。
そのため、docker の driver を確認し、必要であれば systemd に変更します。

確認します。　

```
$ docker info | grep Cgroup
  Cgroup Driver: cgroupfs
  Cgroup Version: 1
```

daemon.json のバックアップを作成します。

```
sudo cp -ai /etc/docker/daemon.json{,.default}
```

daemon.json ファイルに下記を追記します。

```
$ sudo vim /etc/docker/daemon.json
・・・
  "exec-opts": ["native.cgroupdriver=systemd"],
・・・
```

daemon のリロードと docker.service の再起動を行います。

```
sudo systemctl daemon-reload
sudo systemctl restart docker.service
```

設定を確認します。

```
$ docker info | grep Cgroup
 Cgroup Driver: systemd
 Cgroup Version: 1
```

### Master ノードのセットアップ

Kubernetes クラスタの構築をします。

```
$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
I0715 06:29:47.841987  107552 version.go:255] remote version is much newer: v1.24.3; falling back to: stable-1.22
[init] Using Kubernetes version: v1.22.12
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local yoshitest] and IPs [10.96.0.1 10.100.2.210]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [localhost yoshitest] and IPs [10.100.2.210 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [localhost yoshitest] and IPs [10.100.2.210 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 17.502563 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.22" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node yoshitest as control-plane by adding the labels: [node-role.kubernetes.io/master(deprecated) node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node yoshitest as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: slx8x6.kvl1eks1u9auzbm6
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.100.2.210:6443 --token slx8x6.kvl1eks1u9auzbm6 \
        --discovery-token-ca-cert-hash sha256:06c48716c874f3565250f79837e07db67e7e98e493d45072818a335c07f0a633
```

.kube ディレクトリをホームディレクトリに配置します。
これにより kubectl コマンドなどが使用可能になります。

```
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
```

Kubernetes が動作状況を確認します。

```
$ kubectl get node
NAME        STATUS   ROLES                  AGE   VERSION
yoshitest   Ready    control-plane,master   11m   v1.22.3

$ kubectl get pod --all-namespaces
NAMESPACE     NAME                                READY   STATUS              RESTARTS   AGE
kube-system   coredns-78fcd69978-bflnm            0/1     ContainerCreating   0          11m
kube-system   coredns-78fcd69978-dh6kg            0/1     ContainerCreating   0          11m
kube-system   etcd-yoshitest                      1/1     Running             1          11m
kube-system   kube-apiserver-yoshitest            1/1     Running             1          11m
kube-system   kube-controller-manager-yoshitest   1/1     Running             1          11m
kube-system   kube-proxy-p84m8                    1/1     Running             0          11m
kube-system   kube-scheduler-yoshitest            1/1     Running             1          11m
```

### CNI インストール

CNI プラグインとして Calico をインストールします。
作業用のディレクトリは ~/work/calico とします。

```
mkdir -p ~/work/calico
cd ~/work/calico
```

インストールに必要なファイルをダウンロードします。

```
curl https://projectcalico.docs.tigera.io/manifests/tigera-operator.yaml -O
curl https://projectcalico.docs.tigera.io/manifests/custom-resources.yaml -O
```

customer-resources.yaml の spec.calicoNetwork.ipPools.cidr の値を `kubeadm init --pod-network-cidr=` で指定した値に変更します。

```
$ vim custom-resources.yaml
・・・
spec:
  # Configures Calico networking.
  calicoNetwork:
    # Note: The ipPools section cannot be modified post-install.
    ipPools:
    - blockSize: 26
      cidr: 10.244.0.0/16
・・・
```

Calico をインストールします。

```
kubectl apply -f tigera-operator.yaml
kubectl apply -f custom-resources.yaml
```

起動確認します。

```
$ kubectl get pod -n tigera-operator
NAME                              READY   STATUS    RESTARTS   AGE
tigera-operator-d8cbfddcc-dmfww   1/1     Running   0          29s

$ kubectl get pod -n calico-system
NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-7f87b64bd9-fzshn   1/1     Running   0          5m13s
calico-node-dgwhm                          1/1     Running   0          5m13s
calico-typha-5fdf9ffc5c-bvmp9              1/1     Running   0          5m14s
```

### Master ノードの Taint 情報削除

今回は Worker ノードを作成せず、Master ノードのみで動作させます。
Master ノードには Pod が展開されないようにするためにデフォルトで node-role.kubernetes.io/master:NoSchedule という Taint が付与されています。
このままでは Worker ノードを準備しない限り Pod が展開できないため、本環境ではこの Taint 情報を削除します。

```
$ kubectl get node
NAME        STATUS   ROLES                  AGE   VERSION
yoshitest   Ready    control-plane,master   33m   v1.22.3

$ kubectl taint nodes yoshitest node-role.kubernetes.io/master:NoSchedule-
node/yoshitest untainted
```

### サンプルマニフェストの起動確認

サンプルのマニフェストとして下記のマニフェストファイルを準備し、Pod の起動を試します。

```
$ cat << _EOF_ > sample.yaml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
_EOF_
```

起動します。

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

確認します。

```
$ kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-66b6c48dd5-glcrt   1/1     Running   0          10m
nginx-deployment-66b6c48dd5-t7s4n   1/1     Running   0          10m
```

## Kubernetes VIM

ここから先の作業は基本的に Devstack が動作しているマシン上で実施してください。
[VIM 用の設定ファイル作成に必要な情報取得] のみ Kubernetes Master ノードで実施します。

### VIM 用の設定ファイル作成に必要な情報取得

ファイル作成に必要な下記の値を収集します。
この手順のみ Kubernetes Master ノードで実施します。

- auth_url

<自身の IP>:6443 です。

コマンドで調べる場合は下記の通りです。

```
$ kubectl cluster-info
Kubernetes control plane is running at https://10.100.2.210:6443
CoreDNS is running at https://10.100.2.210:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

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

- bearer_token

ベアトークンを含む Kubernetes シークレット名を確認します。

Kubernetes データモデルでは、.data に含まれる値は Base64 形式でエンコードされるため、ベアトークンとして使用するには、`base64 --decode` または `base64 -d` コマンドでデコードする必要があります。

```
$  kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-qm98x   kubernetes.io/service-account-token   3      59m

$ TOKEN=$(kubectl get secret default-token-qm98x -o jsonpath="{.data.token}" | base64 --decode) && echo $TOKEN
eyJhbGciOiJSUzI1NiIsImtpZCI6IjNGVXpEeGIxbU9xMGY0NEpEeGt4bkhrMDJidU9GSUVMUFN0RlRGMi16R1kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tcW05OHgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImY2MGQxYWNlLTU3YmQtNDY1OC05YTBjLWIxMTdmMzNmY2EyYiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.kPwKalaP5123zJ7Lw29PCHpT_mU5uwjUkbtKFbn9rtCfUAXCEwOveTFf3F9s08RudaQKy5CJQrj5TqAS_7lQ2EQePKBL6_pW9YKxqUUNRw8krF7xa-ghTON2B0HKdTl51ZMPKwgjLkydrvH7JQT5C11WBbot0auct-m2wBlL1M8ahUdaHcv--nCHm4JF7NKMd8wW0ASmGBGV3Fei8GEUVMCp-GicrDzp8A0dgbVY9NZkWoed4cP6eSsVN_FaHwt2V4SqrvgjkNvDttFID9sL7UkcKGz8KIJSLzQnX6DVDuUyXjSpz7TsvAN2-DV2oO8fn35WlhLw9YZQ3t264qs0bQ
```

このトークンを使用する前に、ユーザーはこのトークンに管理者の役割を適用する必要があります。

```
$ kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole cluster-admin --serviceaccount=default:default
clusterrolebinding.rbac.authorization.k8s.io/cluster-admin-binding created
```

- ssl_ca_cert

Kubernetes クラスターへの認証に関する詳細情報を入手します。

```
$ kubectl get secrets default-token-qm98x  -o jsonpath="{.data.ca\.crt}" | base64 --decode
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIyMDcxNTA2Mjk0OVoXDTMyMDcxMjA2Mjk0OVowFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK6q
Q/LNRChV6tw2ucfNhV8sBBvWyt+0KtFyDA16h7Tm49wBBzx/Y/cF4UGBFUETcYgA
9zUbrcPqnBeyVXD9Td5R0y/oqPXuX3wBzetalcSe3OlZiAiG28asWf1jaXGCDgmk
hfQBz2OQi0jO98XoG014ok7Ortgz0v6ZV/9tcGPCwxlsERRXCYpeytHE4aCI/aky
8fnT7SGQ506WD7x4DnSYluH5ceH3NeQ56qIsLZUk4G75LVSTr26r7bds6n81urgV
pJi9OMaxA1SWveEoH5serSy5nNfLzjFZhS7r3KDkUl3yokltlQq61K3rHLNQoz+Z
+BpklMSQj8a9thkylj8CAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFFNNpaDF4hs8BcdxmNoUyUcvVomAMBUGA1UdEQQO
MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAF72CNi5EUFeUYLxP9Qf
buSD2hQaDcgBr+W9de/BFGXF1jm+lUag92Jd7+EIHwt27Pf4mRpZLce6pB3bhzQ6
oc7b5vs9k3B+3gByPKW52RZrz7cXL1nfz2TxgBEqiruZiviZiZB7f/KkMZDeU+fW
c5R96oBr4/eGqtTh2P6q5UKYixOoYjhnZGCQOYG+SJInIqKQUBmbqaazCXCZNxdv
1fPzBe73AnMdiFl9AzEc38htf912dh5SRzKmkH47cU/m+7GbzXLsjhOtGWay/rV4
thpePCADCWBYrL62VZoRJs+VW7jNPTJwTMkfUuKN6KwSXugJVvAGRVsrV9rnTuOO
sbQ=
-----END CERTIFICATE-----
```

API エンドポイントとベアラートークンが使用可能であることを確認できます。

```
$ curl -k https://10.100.2.210:6443/api/ -H "Authorization: Bearer $TOKEN"
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.100.2.210:6443"
    },
    {
      "clientCIDR": "10.96.0.0/12",
      "serverAddress": "10.96.0.1:443"
    }
  ]
}
```

### VIM 用の設定ファイル作成

取得した情報を利用して構成ファイルを作成します。
ここから先の手順は Devstack が動作しているマシン上で実施します。

```
cat << _EOF_ > ~/work/vim-k8s.yaml
auth_url: "https://10.100.2.210:6443"
project_name: "default"
bearer_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6IjNGVXpEeGIxbU9xMGY0NEpEeGt4bkhrMDJidU9GSUVMUFN0RlRGMi16R1kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tcW05OHgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImY2MGQxYWNlLTU3YmQtNDY1OC05YTBjLWIxMTdmMzNmY2EyYiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.kPwKalaP5123zJ7Lw29PCHpT_mU5uwjUkbtKFbn9rtCfUAXCEwOveTFf3F9s08RudaQKy5CJQrj5TqAS_7lQ2EQePKBL6_pW9YKxqUUNRw8krF7xa-ghTON2B0HKdTl51ZMPKwgjLkydrvH7JQT5C11WBbot0auct-m2wBlL1M8ahUdaHcv--nCHm4JF7NKMd8wW0ASmGBGV3Fei8GEUVMCp-GicrDzp8A0dgbVY9NZkWoed4cP6eSsVN_FaHwt2V4SqrvgjkNvDttFID9sL7UkcKGz8KIJSLzQnX6DVDuUyXjSpz7TsvAN2-DV2oO8fn35WlhLw9YZQ3t264qs0bQ"
ssl_ca_cert: "-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIyMDcxNTA2Mjk0OVoXDTMyMDcxMjA2Mjk0OVowFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK6q
Q/LNRChV6tw2ucfNhV8sBBvWyt+0KtFyDA16h7Tm49wBBzx/Y/cF4UGBFUETcYgA
9zUbrcPqnBeyVXD9Td5R0y/oqPXuX3wBzetalcSe3OlZiAiG28asWf1jaXGCDgmk
hfQBz2OQi0jO98XoG014ok7Ortgz0v6ZV/9tcGPCwxlsERRXCYpeytHE4aCI/aky
8fnT7SGQ506WD7x4DnSYluH5ceH3NeQ56qIsLZUk4G75LVSTr26r7bds6n81urgV
pJi9OMaxA1SWveEoH5serSy5nNfLzjFZhS7r3KDkUl3yokltlQq61K3rHLNQoz+Z
+BpklMSQj8a9thkylj8CAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFFNNpaDF4hs8BcdxmNoUyUcvVomAMBUGA1UdEQQO
MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAF72CNi5EUFeUYLxP9Qf
buSD2hQaDcgBr+W9de/BFGXF1jm+lUag92Jd7+EIHwt27Pf4mRpZLce6pB3bhzQ6
oc7b5vs9k3B+3gByPKW52RZrz7cXL1nfz2TxgBEqiruZiviZiZB7f/KkMZDeU+fW
c5R96oBr4/eGqtTh2P6q5UKYixOoYjhnZGCQOYG+SJInIqKQUBmbqaazCXCZNxdv
1fPzBe73AnMdiFl9AzEc38htf912dh5SRzKmkH47cU/m+7GbzXLsjhOtGWay/rV4
thpePCADCWBYrL62VZoRJs+VW7jNPTJwTMkfUuKN6KwSXugJVvAGRVsrV9rnTuOO
sbQ=
-----END CERTIFICATE-----"
type: "kubernetes"
_EOF_
```

### Kubernetes VIM の登録

Kubernetes VIM を Tacker に登録します。
test-vim-k8s-kubeadm は登録する Kubernetes VIM の名前です。

```
$ openstack vim register --config-file vim-k8s.yaml test-vim-k8s-kubeadm --fit-width
+----------------+---------------------------------------------------------------------------------------------------------+
| Field          | Value                                                                                                   |
+----------------+---------------------------------------------------------------------------------------------------------+
| auth_cred      | {                                                                                                       |
|                |     "bearer_token": "***",                                                                              |
|                |     "ssl_ca_cert": "b'gAAAAABi0SJYjdfgr98GZaz2sFM5kRw8ZDgZUWkWFwdqQaE1uRLcOiQz3qW4Si66ECS7vApgPHieINreB |
|                | k4ECVvZH9CUGyQWCW1FvVsM7U_6M2jpVjsPDTE9s2h78WI4OIA_qw24zQtafubWuWmMeAv2h3ZtIeeo8vxrxMt6UsVNlpgyFrDE6fyG |
|                | 0zZ7j9-HFFbHxTUXrr75maiBqQISmzpxvcG-URCyb-                                                              |
|                | qdJIqPKSZ38isVlzpU5NpgEuuTxqMBdNPADGzUk0eGDiBHNLRjNQ1j-braFxFR9iXfxpHcsLbKtZztaacO-                     |
|                | uJEjtpL1yXZ1lPbwGXurwWf4j2UzsxrnsWoHR1rdNV5PCnefIj-                                                     |
|                | PIEub_kVOWSF33YuFqHgR7XbkAmMGJalRzE521rCGA64BlG7k1Es-                                                   |
|                | SyN89YCeEEtnIVf6Kk3ylXfnw0SIefuN48VSj0UV1WJlkcEzXPTAEWueUIjj9pAxaaCW-                                   |
|                | znFpqv4_3NZDyG5RYEuDqNo8pi7Vj4kBsT9NK-RcCeAg3cCQcaKsi_SD0NQ0NoWQd5eKWelSeDxTzhTlYpIN_SD7f85ab-          |
|                | pKPrp4x_F-rPgTcOchrtYBQ3yZvg-zNihmuQLe1yKa_Hf9RjKzc5_I_t4pjoPy140n3QyefuM8yGN973MovEXe8WrjcS_z02VHs_nYB |
|                | 1snD4vxhnbUVxNy0TbnAvcujVR_Y2jzw71iUsNVVwXLT3-81xcs5T7B6m-aFH0icyLQy3Ixvj-mm4GVbya8xCGidIaF1iIBV-NXHUng |
|                | 0jmsGxRnoOfmLnUef1ek_TFYLmv43cbhbLLdiaPMMWdYdhN5z7AoJiWhL6cVlKmsd1WWKjBPW_GqQgFnlqN1l6CeL5G5_GFfsulcLxk |
|                | b06Astvyvv0yMozjHgBMI79WbYmOyqo0NnMCkJFnJLAumP-jy_RtHeKpV8tq41j7BSm-ehc2vdgiWSuCXI5m0J49sDv9yQ631UBQmTf |
|                | Nve1ANonQBIB6_sMon52chThE0JL6QtX_N4y0pe0HCyibQAh5a6d0qo2yUAQXS9pPctPkXw7IQpCi2pfr6WmD4SxY5GVegjZg6ySlY6 |
|                | X-yShYHw88_ixnulGnLZDcXZp9Y14XwYCGOdY25wekuuvronkKJVnb7cedDV6jj8jTYwkLBuQLfAeJI7nG78DsrOs-              |
|                | PlsVv4M2hgCgEbDRKckw9ZHcnPAKSGYGP-ongmwhobbkRYLCi8qSuSuCsUT47AvGjsC7yOUDVk0iX-DrQ2V5R-Nns-              |
|                | pWOj25kz5EB0FxNkAWjVjyMaXYw3Ic3_p_hciOj6eE1CtIsVncHF50TlXVkOMrsbzgpH7zWRrbHh4VFKuk66Zrtgh-              |
|                | iGv8LkhpOqLoG7x6ayfVmxjzZU2i--F4AKc8EBPXj-_05n6EeOgIEe4szLpUUy_UfeHsYhgkEeGkWWA6RR0HpzF-                |
|                | uwKnsX1aMfCdFTUmnuvrJWnRJhAsMG-G3sChGo90_G5nNhbXMf6YWcMk8pocNjvVNLJ6vqvgH5aEsnSklbmI2XYHbFRgbCeKRJp'",  |
|                |     "auth_url": "https://10.100.2.210:6443",                                                            |
|                |     "username": "None",                                                                                 |
|                |     "key_type": "barbican_key",                                                                         |
|                |     "secret_uuid": "***",                                                                               |
|                |     "password": "***"                                                                                   |
|                | }                                                                                                       |
| auth_url       | https://10.100.2.210:6443                                                                               |
| created_at     | 2022-07-15 08:16:25.496115                                                                              |
| description    |                                                                                                         |
| id             | e31459a6-1380-4fd8-930b-a70dc0500614                                                                    |
| is_default     | False                                                                                                   |
| name           | test-vim-k8s-kubeadm                                                                                    |
| placement_attr | {                                                                                                       |
|                |     "regions": [                                                                                        |
|                |         "calico-apiserver",                                                                             |
|                |         "calico-system",                                                                                |
|                |         "default",                                                                                      |
|                |         "kube-node-lease",                                                                              |
|                |         "kube-public",                                                                                  |
|                |         "kube-system",                                                                                  |
|                |         "tigera-operator"                                                                               |
|                |     ]                                                                                                   |
|                | }                                                                                                       |
| project_id     | cd492cafc33c4d6b878ee570f6aeae21                                                                        |
| status         | PENDING                                                                                                 |
| type           | kubernetes                                                                                              |
| updated_at     | None                                                                                                    |
| vim_project    | {                                                                                                       |
|                |     "name": "default"                                                                                   |
|                | }                                                                                                       |
+----------------+---------------------------------------------------------------------------------------------------------+
```

登録した VIM の Status が REACHABLE であるかどうかを確認します。

```
$ openstack vim list
+--------------------------------------+----------------------+----------------------------------+------------+------------+-----------+
| ID                                   | Name                 | Tenant_id                        | Type       | Is Default | Status    |
+--------------------------------------+----------------------+----------------------------------+------------+------------+-----------+
| e31459a6-1380-4fd8-930b-a70dc0500614 | test-vim-k8s-kubeadm | cd492cafc33c4d6b878ee570f6aeae21 | kubernetes | False      | REACHABLE |
+--------------------------------------+----------------------+----------------------------------+------------+------------+-----------+
```

環境変数として VIM ID を定義しておきます。

```
export VIM_ID=e31459a6-1380-4fd8-930b-a70dc0500614
```

## VNF パッケージの準備 

VNF パッケージには、VNFD に加えて CNF 定義が含まれている必要があります。

### VNF パッケージのディレクトリの作成

VNF パッケージを配置するディレクトリを作成します。

```
mkdir -p ~/work/deployment/{TOSCA-Metadata,Definitions,Files/kubernetes}
```

### Kubernetes オブジェクトファイルの作成

CSAR VNF パッケージには、デプロイする Kubernetes リソースを定義するオブジェクトファイルが含まれている必要があります。
様々な yaml ファイルのコンテンツに応じて、KubernetesAPI リソースを作成できます。

簡単な Deployment リソースを作成します。

 ```
cat << _EOF_ > ~/work/deployment/Files/kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: curry-probe-test001
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      selector: curry-probe-test001
  template:
    metadata:
      labels:
        selector: curry-probe-test001
        app: webserver
    spec:
      containers:
      - name: nginx-liveness-probe
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          protocol: TCP
      - image: celebdor/kuryr-demo
        imagePullPolicy: IfNotPresent
        name: kuryr-demo-readiness-probe
        ports:
        - containerPort: 8080
          protocol: TCP
_EOF_
```

インスタンス化パラメーターに vdu_mapping が含まれていない場合、このファイルの metadata.name は、デプロイメントフレーバー定義ファイルの対応する VDU の properties.name と同じである必要があります。
今回の手順では metadata.name は後程作成する helloworld3_df_simple.yaml ファイルの topology_template.node_templates.VDU1.properties.name と同じです。

### TOSCA.meta ファイルの作成

TOSCA.Meta ファイルには、TOSCA.Meta ファイル、CSAR のバージョンや定義ファイルファイルの情報が含まれています。
また、Artifact ファイルの名前、コンテンツタイプ、暗号化方式、および Artifact ファイルのハッシュ値が必要です。
ここでの Artifact ファイルとは作成したオブジェクトファイルが該当します。

Artifact ファイルのハッシュ値を取得します。

```
$ sha256sum ~/work/deployment/Files/kubernetes/deployment.yaml
4154adca79b0dc3829f69c82758736df674ffdd89603b4e827bd81afab6b4028  deployment/Files/kubernetes/deployment.yaml
```

TOSCA.meta ファイルを作成します。

```
cat << _EOF_ > ./deployment/TOSCA-Metadata/TOSCA.meta
TOSCA-Meta-File-Version: 1.0
Created-by: dummy_user
CSAR-Version: 1.1
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml

Name: Files/kubernetes/deployment.yaml
Content-Type: application/yaml
Algorithm: SHA-256
Hash: 4154adca79b0dc3829f69c82758736df674ffdd89603b4e827bd81afab6b4028
_EOF_
```

### ETSI 定義ファイルのダウンロード

公式文書をダウンロードします。 
ETSI GS NFV-SOL 001 は、TOSCA 仕様に基づいて VNFD の構造と形式を指定します。

```
cd ~/work/deployment/Definitions
wget https://forge.etsi.org/rep/nfv/SOL001/raw/v2.6.1/etsi_nfv_sol001_common_types.yaml
wget https://forge.etsi.org/rep/nfv/SOL001/raw/v2.6.1/etsi_nfv_sol001_vnfd_types.yaml
```

### VNFD の作成

CNF に必要なすべてのコンポーネントが Kubernetes リソースファイルで指定されるため、VNFD には VDU、接続ポイント、仮想リンクなどの Kubernetes リソース情報は含まれません。

CNF の定義を含む VNFD ファイルを作成します。

```
cat << _EOF_ > ~/work/deployment/Definitions/helloworld3_top.vnfd.yaml
tosca_definitions_version: tosca_simple_yaml_1_2

description: Sample VNF

imports:
  - etsi_nfv_sol001_common_types.yaml
  - etsi_nfv_sol001_vnfd_types.yaml
  - helloworld3_types.yaml
  - helloworld3_df_simple.yaml

topology_template:
  inputs:
    selected_flavour:
      type: string
      description: VNF deployment flavour selected by the consumer. It is provided in the API

  node_templates:
    VNF:
      type: company.provider.VNF
      properties:
        flavour_id: { get_input: selected_flavour }
        descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000003
        provider: Company
        product_name: Sample VNF
        software_version: '1.0'
        descriptor_version: '1.0'
        vnfm_info:
          - Tacker
      requirements:
        #- virtual_link_external # mapped in lower-level templates
        #- virtual_link_internal # mapped in lower-level templates
_EOF_
```

VNF のパラメータータイプとデフォルト値を定義します。

```
cat << _EOF_ > ~/work/deployment/Definitions/helloworld3_types.yaml
tosca_definitions_version: tosca_simple_yaml_1_2

description: VNF type definition

imports:
  - etsi_nfv_sol001_common_types.yaml
  - etsi_nfv_sol001_vnfd_types.yaml

node_types:
  company.provider.VNF:
    derived_from: tosca.nodes.nfv.VNF
    properties:
      descriptor_id:
        type: string
        constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000003 ] ]
        default: b1bb0ce7-ebca-4fa7-95ed-4840d7000003
      descriptor_version:
        type: string
        constraints: [ valid_values: [ '1.0' ] ]
        default: '1.0'
      provider:
        type: string
        constraints: [ valid_values: [ 'Company' ] ]
        default: 'Company'
      product_name:
        type: string
        constraints: [ valid_values: [ 'Sample VNF' ] ]
        default: 'Sample VNF'
      software_version:
        type: string
        constraints: [ valid_values: [ '1.0' ] ]
        default: '1.0'
      vnfm_info:
        type: list
        entry_schema:
          type: string
          constraints: [ valid_values: [ Tacker ] ]
        default: [ Tacker ]
      flavour_id:
        type: string
        constraints: [ valid_values: [ simple ] ]
        default: simple
      flavour_description:
        type: string
        default: "falvour"
    requirements:
      - virtual_link_external:
          capability: tosca.capabilities.nfv.VirtualLinkable
      - virtual_link_internal:
          capability: tosca.capabilities.nfv.VirtualLinkable
_EOF_
```
 
VNF 入力のパラメータータイプを定義します。

```
cat << _EOF_ > ~/work/deployment/Definitions/helloworld3_df_simple.yaml
tosca_definitions_version: tosca_simple_yaml_1_2

description: Simple deployment flavour for Sample VNF

imports:
  - etsi_nfv_sol001_common_types.yaml
  - etsi_nfv_sol001_vnfd_types.yaml
  - helloworld3_types.yaml

topology_template:
  inputs:
    descriptor_id:
      type: string
    descriptor_version:
      type: string
    provider:
      type: string
    product_name:
      type: string
    software_version:
      type: string
    vnfm_info:
      type: list
      entry_schema:
        type: string
    flavour_id:
      type: string
    flavour_description:
      type: string

  substitution_mappings:
    node_type: company.provider.VNF
    properties:
      flavour_id: simple
    requirements:
      virtual_link_external: []

  node_templates:
    VNF:
      type: company.provider.VNF
      properties:
        flavour_description: A simple flavour

    VDU1:
      type: tosca.nodes.nfv.Vdu.Compute
      properties:
        name: curry-probe-test001
        description: kubernetes controller resource as VDU
        vdu_profile:
          min_number_of_instances: 1
          max_number_of_instances: 3

  policies:
    - scaling_aspects:
        type: tosca.policies.nfv.ScalingAspects
        properties:
          aspects:
            vdu1_aspect:
              name: vdu1_aspect
              description: vdu1 scaling aspect
              max_scale_level: 2
              step_deltas:
                - delta_1

    - vdu1_initial_delta:
        type: tosca.policies.nfv.VduInitialDelta
        properties:
          initial_delta:
            number_of_instances: 1
        targets: [ VDU1 ]

    - vdu1_scaling_aspect_deltas:
        type: tosca.policies.nfv.VduScalingAspectDeltas
        properties:
          aspect: vdu1_aspect
          deltas:
            delta_1:
              number_of_instances: 1
        targets: [ VDU1 ]

    - instantiation_levels:
        type: tosca.policies.nfv.InstantiationLevels
        properties:
          levels:
            instantiation_level_1:
              description: Smallest size
              scale_info:
                vdu1_aspect:
                  scale_level: 0
            instantiation_level_2:
              description: Largest size
              scale_info:
                vdu1_aspect:
                  scale_level: 2
          default_level: instantiation_level_1

    - vdu1_instantiation_levels:
        type: tosca.policies.nfv.VduInstantiationLevels
        properties:
          levels:
            instantiation_level_1:
              number_of_instances: 1
            instantiation_level_2:
              number_of_instances: 3
        targets: [ VDU1 ]
_EOF_
```

### VNF パッケージの圧縮

CSAR パッケージはアップロード用に zip ファイルに圧縮する必要があります。

```
cd ~/work/deployment
zip deployment.zip -r Definitions/ Files/ TOSCA-Metadata/
```

## VNF パッケージの作成とアップロード

Tacker で空の VNF パッケージオブジェクトを作成し、作成した圧縮 VNF パッケージをアップロードします。

### VNF パッケージの作成

空の VNF パッケージを作成します。
VNF パッケージが正常に作成されると、ID、リンク、オンボーディング状態、運用状態、使用状態などの情報が返されます。

```
$ openstack vnf package create
+-------------------+-------------------------------------------------------------------------------------------------+
| Field             | Value                                                                                           |
+-------------------+-------------------------------------------------------------------------------------------------+
| ID                | d765618d-a455-4fc5-99e2-ab457d34f92c                                                            |
| Links             | {                                                                                               |
|                   |     "self": {                                                                                   |
|                   |         "href": "/vnfpkgm/v1/vnf_packages/d765618d-a455-4fc5-99e2-ab457d34f92c"                 |
|                   |     },                                                                                          |
|                   |     "packageContent": {                                                                         |
|                   |         "href": "/vnfpkgm/v1/vnf_packages/d765618d-a455-4fc5-99e2-ab457d34f92c/package_content" |
|                   |     }                                                                                           |
|                   | }                                                                                               |
| Onboarding State  | CREATED                                                                                         |
| Operational State | DISABLED                                                                                        |
| Usage State       | NOT_IN_USE                                                                                      |
| User Defined Data | {}                                                                                              |
+-------------------+-------------------------------------------------------------------------------------------------+
```

VNF パッケージ ID を環境変数として登録します。

```
export VNF_PACKAGE_ID=d765618d-a455-4fc5-99e2-ab457d34f92c
```

確認します。
下記の状態であれば、作成が成功したことを示します。

```
$ openstack vnf package show $VNF_PACKAGE_ID \
-c 'Onboarding State' -c 'Operational State' -c 'Usage State' 
+-------------------+------------+
| Field             | Value      |
+-------------------+------------+
| Onboarding State  | CREATED    |
| Operational State | DISABLED   |
| Usage State       | NOT_IN_USE |
+-------------------+------------+
```

### VNF パッケージをアップロード

[VNF パッケージの圧縮] で作成した zip ファイルを作成した空の VNF パッケージにアップロードします。

```
cd ~/work/deployment

$ openstack vnf package upload --path deployment.zip $VNF_PACKAGE_ID
Upload request for VNF package 082f732d-4cb3-474b-8be0-cdace3224c6f has been accepted.
```

確認します。
下記の状態であれば問題ありません。

```
$ openstack vnf package show $VNF_PACKAGE_ID \
-c 'Onboarding State' -c 'Operational State' -c 'Usage State' -c 'VNFD ID'
+-------------------+--------------------------------------+
| Field             | Value                                |
+-------------------+--------------------------------------+
| Onboarding State  | ONBOARDED                            |
| Operational State | ENABLED                              |
| Usage State       | NOT_IN_USE                           |
| VNFD ID           | b1bb0ce7-ebca-4fa7-95ed-4840d7000003 |
+-------------------+--------------------------------------+
```

VNFD ID を環境変数として登録します。

```
export VNFD_ID=b1bb0ce7-ebca-4fa7-95ed-4840d7000003
```

## CNF の作成

CNF を作成します。

default VIM が設定されていない場合、下記のように未定義のエラーが出てしまうため、設定します。

```
$ openstack vnflcm create $VNFD_ID
Default VIM is not defined. 

$ openstack vim set --is-default True $VIM_ID
```

default VIM が設定されている場合は下記のように実行結果が表示されます。

```
$ openstack vnflcm create $VNFD_ID
+-----------------------------+------------------------------------------------------------------------------------------------------------------+
| Field                       | Value                                                                                                            |
+-----------------------------+------------------------------------------------------------------------------------------------------------------+
| ID                          | 952a2324-0ea5-4831-af87-4d9549f7b35a                                                                             |
| Instantiation State         | NOT_INSTANTIATED                                                                                                 |
| Links                       | {                                                                                                                |
|                             |     "self": {                                                                                                    |
|                             |         "href": "http://localhost:9890/vnflcm/v1/vnf_instances/952a2324-0ea5-4831-af87-4d9549f7b35a"             |
|                             |     },                                                                                                           |
|                             |     "instantiate": {                                                                                             |
|                             |         "href": "http://localhost:9890/vnflcm/v1/vnf_instances/952a2324-0ea5-4831-af87-4d9549f7b35a/instantiate" |
|                             |     }                                                                                                            |
|                             | }                                                                                                                |
| VNF Configurable Properties |                                                                                                                  |
| VNF Instance Description    | None                                                                                                             |
| VNF Instance Name           | vnf-952a2324-0ea5-4831-af87-4d9549f7b35a                                                                         |
| VNF Product Name            | Sample VNF                                                                                                       |
| VNF Provider                | Company                                                                                                          |
| VNF Software Version        | 1.0                                                                                                              |
| VNFD ID                     | b1bb0ce7-ebca-4fa7-95ed-4840d7000003                                                                             |
| VNFD Version                | 1.0                                                                                                              |
| vnfPkgId                    |                                                                                                                  |
+-----------------------------+------------------------------------------------------------------------------------------------------------------+
```

環境変数として VNF INSTANCE ID を定義します。

```
export VNF_INSTANCE_ID=952a2324-0ea5-4831-af87-4d9549f7b35a  
```

確認します。

```
$ openstack vnf package show $VNF_PACKAGE_ID \
-c 'Usage State'
+-------------+--------+
| Field       | Value  |
+-------------+--------+
| Usage State | IN_USE |
+-------------+--------+

$ openstack vnflcm show $VNF_INSTANCE_ID \
-c 'Instantiation State'
+---------------------+------------------+
| Field               | Value            |
+---------------------+------------------+
| Instantiation State | NOT_INSTANTIATED |
+---------------------+------------------+
```

## CNF のインスタンス化

CNF をインスタンス化します。

### リクエストパラメータファイルの作成

必要な情報を取得し、インスタンスリクエストに必要なパラメータファイルを作成します。

- vimConnectionInfo.vimId: e31459a6-1380-4fd8-930b-a70dc0500614

CNF をインスタンス化する VIM の ID を `openstack vim list` コマンドで取得します。
このコマンドで複数の VIM が出力された場合は自身が使用したい VIM の ID を 1 つ選択してください。
今回は新たに登録した Kubernetes を指定するため、test-vim-k8s-kubeadm の ID を選択します。

```
$ openstack vim list
+--------------------------------------+----------------------+----------------------------------+------------+------------+-----------+
| ID                                   | Name                 | Tenant_id                        | Type       | Is Default | Status    |
+--------------------------------------+----------------------+----------------------------------+------------+------------+-----------+
| e31459a6-1380-4fd8-930b-a70dc0500614 | test-vim-k8s-kubeadm | cd492cafc33c4d6b878ee570f6aeae21 | kubernetes | False      | REACHABLE |
+--------------------------------------+----------------------+----------------------------------+------------+------------+-----------+
```

リクエストパラメータファイルを作成します。

additionalParamsには、Kubernetes リソース定義ファイルのパスが含まれています。
lcm-kubernetes-def-files がリストである必要があることに注意してください。
ユーザーは、リソースをデプロイする必要がある名前空間を指定することもできます。

```
cat << _EOF_ > ~/work/instance_kubernetes.json
{
  "flavourId": "simple",
  "additionalParams": {
    "lcm-kubernetes-def-files": [
      "Files/kubernetes/deployment.yaml"
    ],
    "namespace": "default"
  },
  "vimConnectionInfo": [
    {
      "id": "8a3adb69-0784-43c7-833e-aab0b6ab4470",
      "vimId": "e31459a6-1380-4fd8-930b-a70dc0500614",
      "vimType": "kubernetes"
    }
  ]
}
_EOF_
```

### インスタンス化

CNF インスタンスをインスタンス化します。

```
$ openstack vnflcm instantiate $VNF_INSTANCE_ID instance_kubernetes.json
Instantiate request for VNF Instance 952a2324-0ea5-4831-af87-4d9549f7b35a has been accepted.
```

確認します。
インスタンス化状態が INSTANTIATED の場合、インスタンス化が成功したことを示します。

```
$ openstack vnflcm show  $VNF_INSTANCE_ID -c 'Instantiation State'
+---------------------+--------------+
| Field               | Value        |
+---------------------+--------------+
| Instantiation State | INSTANTIATED |
+---------------------+--------------+
```

NOT_INSTANTIATED から変わりがない場合は `openstack vnflcm op list` などのコマンドで経過を確認することもできます。
Operation State が PROCESSING の場合、処理中を意味します。

### Kubernetes の Deployment 確認

コンテナ化された CNF がターゲットの Kubernetes VIM 環境で実行されていることを確認します。
このコマンドは Kubernetes Master ノードで実施します。

READY が1/1の場合、デプロイメントが正常に作成されたことを示します。
`-A` オプションを付与することでどの名前空間上で動作しているかを確認することもできます。

```
$ kubectl get deployment
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
curry-probe-test001   1/1     1            1           2m28s

$ kubectl get deployment -A
NAMESPACE          NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
calico-apiserver   calico-apiserver          2/2     2            2           97m
calico-system      calico-kube-controllers   1/1     1            1           101m
calico-system      calico-typha              1/1     1            1           101m
default            curry-probe-test001       1/1     1            1           2m37s
default            nginx-deployment          2/2     2            2           94m
kube-system        coredns                   2/2     2            2           120m
tigera-operator    tigera-operator           1/1     1            1           102m
```

# 発生したエラー

## CNF がインスタンス化されない

下記のコマンド実行時のエラーです。
このエラーは Devstack インストール時、一緒にインストールされた Kubernetes を VIM として利用していた際に発生したエラーです。

```
$ openstack vnflcm instantiate $VNF_INSTANCE_ID instance_kubernetes.json
```

### Failed to create resource

```
tacker-conductor[1171]: : kubernetes.client.exceptions.ApiException: (409)
tacker-conductor[1171]: 2022-07-13 01:12:51.559 ERROR tacker.vnflcm.vnflcm_driver [req-3a222d79-e537-4cb1-8722-33c1127deae8 admin admin] Unable to instantiate vnf instance f30d5550-99f4-45f5-93b9-8c84e0c56a2d due to error : Failed to create resource.: tacker.common.exceptions.CreateApiFalse: Failed to create resource.
tacker-conductor[1171]: 2022-07-13 01:12:51.560 ERROR root [req-3a222d79-e537-4cb1-8722-33c1127deae8 admin admin] Original exception being dropped: ['Traceback (most recent call last):\n', '  File "/opt/stack/tacker/tacker/vnfm/infra_drivers/kubernetes/k8s/translate_outputs.py", line 473, in deploy_k8s\n    self._select_k8s_client_and_api(\n', '  File "/opt/stack/tacker/tacker/vnfm/infra_drivers/kubernetes/k8s/translate_outputs.py", line 400, in _select_k8s_client_and_api\n    response = getattr(k8s_client_obj, self.method_value.get(kind))(\n', '  File "/usr/local/lib/python3.8/dist-packages/kubernetes/client/api/apps_v1_api.py", line 342, in create_namespaced_deployment\n    return self.create_namespaced_deployment_with_http_info(namespace, body, **kwargs)  # noqa: E501\n', '  File "/usr/local/lib/python3.8/dist-packages/kubernetes/client/api/apps_v1_api.py", line 437, in create_namespaced_deployment_with_http_info\n    return self.api_client.call_api(\n', '  File "/usr/local/lib/python3.8/dist-packages/kubernetes/client/api_client.py", line 348, in call_api\n    return self.__call_api(resource_path, method,\n', '  File "/usr/local/lib/python3.8/dist-packages/kubernetes/client/api_client.py", line 180, in __call_api\n    response_data = self.request(\n', '  File "/usr/local/lib/python3.8/dist-packages/kubernetes/client/api_client.py", line 391, in request\n    return self.rest_client.POST(url,\n', '  File "/usr/local/lib/python3.8/dist-packages/kubernetes/client/rest.py", line 275, in POST\n    return self.request("POST", url,\n', '  File "/usr/local/lib/python3.8/dist-packages/kubernetes/client/rest.py", line 234, in request\n    raise ApiException(http_resp=r)\n', 'kubernetes.client.exceptions.ApiException: (409)\nReason: Conflict\nHTTP response headers: HTTPHeaderDict({\'Audit-Id\': \'f779dfd4-2b9f-4745-9b0b-77e3c4a027e3\', \'Cache-Control\': \'no-cache, private\', \'Content-Type\': \'application/json\', \'X-Kubernetes-Pf-Flowschema-Uid\': \'278f6e23-4a3c-407d-a8d8-5865a79d827d\', \'X-Kubernetes-Pf-Prioritylevel-Uid\': \'109eaedd-4f4b-4c19-bd08-cc1a9c775c05\', \'Date\': \'Wed, 13
Content-Length\': \'250\'})\nHTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"deployments.apps \\"curry-probe-test001\\" already exists","reason":"AlreadyExists","details":{"name":"curry-probe-test001","group":"apps","kind":"deployments"},"code":409}\n\n\n', '\nDuring handling of the above exception, another exception occurred:\n\n', 'Traceback (most recent call last):\n', '  File "/opt/stack/tacker/tacker/vnflcm/vnflcm_driver.py", line 371, in _instantiate_vnf\n    instance_id = self._vnf_manager.invoke(\n', '  File "/opt/stack/tacker/tacker/common/driver_manager.py", line 71, in invoke\n    return getattr(driver, method_name)(**kwargs)\n', '  File "/opt/stack/tacker/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py", line 1837, in instantiate_vnf\n    k8s_objs = transformer.deploy_k8s(k8s_objs)\n', '  File "/opt/stack/tacker/tacker/vnfm/infra_drivers/kubernetes/k8s/translate_outputs.py", line 494, in deploy_k8s\n    raise exceptions.CreateApiFalse(error=msg)\n', 'tacker.common.exceptions.CreateApiFalse: Failed to create resource.\n']: tacker.common.exceptions.VnfInstantiationFailed: Vnf instantiation failed for vnf f30d5550-99f4-45f5-93b9-8c84e0c56a2d, error: Failed to create resource.
tacker-conductor[1171]: 2022-07-13 01:12:51.560 DEBUG tacker.common.log [req-3a222d79-e537-4cb1-8722-33c1127deae8 admin admin] tacker.conductor.conductor_server.Conductor method _change_vnf_status called with arguments (<tacker.context.Context object at 0x7fb5d4844a90>, 'f30d5550-99f4-45f5-93b9-8c84e0c56a2d', ('ACTIVE', 'INACTIVE', 'ERROR', 'PENDING_INSTANTIATE', 'PENDING_CREATE', 'PENDING_UPDATE', 'PENDING_DELETE', 'PENDING_SCALE_IN', 'PENDING_SCALE_OUT', 'PENDING_HEAL', 'PENDING_TERMINATE', 'PENDING_CHANGE_EXT_CONN'), 'ERROR') {} from (pid=1171) wrapper /opt/stack/tacker/tacker/common/log.py:33
tacker-conductor[1171]: 2022-07-13 01:12:51.563 DEBUG tacker.conductor.conductor_server [req-3a222d79-e537-4cb1-8722-33c1127deae8 admin admin] Change status of vnf f30d5550-99f4-45f5-93b9-8c84e0c56a2d from PENDING_CREATE to ERROR from (pid=1171) _change_vnf_status /opt/stack/tacker/tacker/conductor/conductor_server.py:643
```

`Failed to create resource.` など、原因はよくわからないですが、Kubernetes で正常に Pod が立ち上がっていないようでした。
VNF パッケージ内のマニフェストや Kubernetes 側に問題ありそうなので確認します。

### Kubernetes disk-pressure で NoSchedule

```
$ kubectl get pod -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
curry-probe-test001-766bdd79bf-jwmlf   0/2     Pending   0          19h   <none>   <none>   <none>           <none>

$ kubectl get node
NAME         STATUS   ROLES    AGE     VERSION
yoshitest2   Ready    <none>   5d23h   v1.22.3

$ kubectl describe node yoshitest2

Name:               yoshitest2
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=yoshitest2
                    kubernetes.io/os=linux
Annotations:        node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Thu, 07 Jul 2022 02:26:46 +0000
Taints:             node.kubernetes.io/disk-pressure:NoSchedule
Unschedulable:      false

...

Events:
  Type     Reason                Age                      From     Message
  ----     ------                ----                     ----     -------
  Warning  EvictionThresholdMet  11m (x20720 over 2d11h)  kubelet  Attempting to reclaim ephemeral-storage
  Warning  FreeDiskSpaceFailed   83s (x718 over 2d11h)    kubelet  (combined from similar events): failed to garbage collect required amount of images. Wanted to free 8139422924 bytes, but freed 0 bytes
  
$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                   READY   STATUS    RESTARTS        AGE
default       curry-probe-test001-766bdd79bf-jwmlf   0/2     Pending   0               19h
kube-system   kube-apiserver-yoshitest2              1/1     Running   1 (5d23h ago)   5d23h
kube-system   kube-controller-manager-yoshitest2     1/1     Running   1 (5d23h ago)   5d23h
kube-system   kube-scheduler-yoshitest2              1/1     Running   1 (5d23h ago)   5d23h
```

Node や Pod の describe を確認すると、上記のような結果になりました。

- 参考: [Master NodeにPodをデプロイするための設定](https://qiita.com/nykym/items/dcc572c21885543d94c8)

`node.kubernetes.io/disk-pressure` は Node のディスクが不足している場合に発生します。
そのため、ディスクの使用状況を確認します。

```
$ df -h
Filesystem                         Size  Used Avail Use% Mounted on
udev                               7.8G     0  7.8G   0% /dev
tmpfs                              1.6G  2.1M  1.6G   1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv   39G   37G   40M 100% /
tmpfs                              7.9G   20K  7.9G   1% /dev/shm
...
```

確認すると使用率が 100% になっていたことがわかりました。
対応として 1 度マシンをシャットダウンし、 ESXi 上でディスクの割り当てを倍にしました。

### networkPlugin cni failed to teardown pod

仮想マシンのディスクを拡張し、改めてインスタンス化を実行した際に発生したエラーです。

```
$ kubectl get pod -o wide
NAME                                   READY   STATUS              RESTARTS   AGE   IP       NODE         NOMINATED NODE   READINESS GATES
curry-probe-test001-766bdd79bf-jwmlf   0/2     ContainerCreating   0          22h   <none>   yoshitest2   <none>           <none>
curry-probe-test001-766bdd79bf-xfgnf   0/2     ContainerCreating   0          58m   <none>   yoshitest2   <none>           <none>

$ kubectl describe pod curry-probe-test001-766bdd79bf-jwmlf 

...

Events:
  Type     Reason               Age                    From               Message
  ----     ------               ----                   ----               -------
  Warning  FailedScheduling     158m (x1186 over 22h)  default-scheduler  0/1 nodes are available: 1 node(s) had taint {node.kubernetes.io/disk-pressure: }, that the pod didn't tolerate.                         
  Normal   Scheduled            59m                    default-scheduler  Successfully assigned default/curry-probe-test001-766bdd79bf-jwmlf to yoshitest2
  Warning  Evicted              56m (x5 over 59m)      kubelet            The node was low on resource: ephemeral-storage.
  Warning  ExceededGracePeriod  29m (x46 over 59m)     kubelet            Container runtime did not kill the pod within specified grace period. 
  Warning  FailedKillPod        4m11s (x286 over 59m)  kubelet            error killing pod: failed to "KillPodSandbox" for "c17d2b89-7786-46d6-a005-7e866347517f" with KillPodSandboxError: "rpc error: code = Unknown desc = networkPlugin cni failed to teardown pod \"curry-probe-test001-766bdd79bf-jwmlf_default\" network: Looks like http://localhost:5036/delNetwork cannot be reached. Is kuryr-daemon running?; Post \"http://localhost:5036/delNetwork\": dial tcp 127.0.0.1:5036: connect: connection refused"

```

`Is kuryr-daemon running?` となっており、kuryr-daemon が動作している？ と書かれています。
また、`localhost:5036/delNetwork cannot be reached ` より、通信ができていないようです。
通常、Devstack インストールの stack.sh 実行時完了に kuryr-daemon は動作しているはずですが、動作していません。

現状態から kuryr-kubernetes を動作させるためのコマンドを実行し、動作するかを試します。
実行コマンドは正常に kuryr-kubernetes が動作している環境にて ps コマンドを使用して調べました。

```
$ /usr/bin/python3 /usr/local/bin/kuryr-k8s-controller --config-dir /etc/kuryr
INFO kuryr_kubernetes.config [-] Logging enabled!
INFO kuryr_kubernetes.config [-] /usr/local/bin/kuryr-k8s-controller version 6.0.1
CRITICAL kuryr-kubernetes [-] Unhandled error: KeyError: 'KUBERNETES_SERVICE_HOST'
ERROR kuryr-kubernetes Traceback (most recent call last):
ERROR kuryr-kubernetes   File "/usr/local/bin/kuryr-k8s-controller", line 10, in <module>
ERROR kuryr-kubernetes     sys.exit(start())
ERROR kuryr-kubernetes   File "/opt/stack/kuryr-kubernetes/kuryr_kubernetes/controller/service.py", line 180, in start
ERROR kuryr-kubernetes     clients.setup_clients()
ERROR kuryr-kubernetes   File "/opt/stack/kuryr-kubernetes/kuryr_kubernetes/clients.py", line 62, in setup_clients
ERROR kuryr-kubernetes     setup_kubernetes_client()
ERROR kuryr-kubernetes   File "/opt/stack/kuryr-kubernetes/kuryr_kubernetes/clients.py", line 72, in setup_kubernetes_client
ERROR kuryr-kubernetes     host = os.environ['KUBERNETES_SERVICE_HOST']
ERROR kuryr-kubernetes   File "/usr/lib/python3.8/os.py", line 675, in __getitem__
ERROR kuryr-kubernetes     raise KeyError(key) from None
ERROR kuryr-kubernetes KeyError: 'KUBERNETES_SERVICE_HOST'
```

`Unhandled error: KeyError: 'KUBERNETES_SERVICE_HOST'` となっています。
KUBERNETES_SERVICE_HOST とは API を利用するための外向きのアドレスです。
.kube/config にこの情報が記録されています。
同様に `KUBERNETES_SERVICE_PORT_HTTPS` も設定しなければ同様のエラーが発生するため、定義します。

```
$ export KUBERNETES_SERVICE_HOST=10.100.2.220
$ export KUBERNETES_SERVICE_PORT_HTTPS=6443
```

定義後、再度コマンドを実行します。

```
$ /usr/bin/python3 /usr/local/bin/kuryr-k8s-controller --config-dir /etc/kuryr
INFO kuryr_kubernetes.config [-] Logging enabled!
INFO kuryr_kubernetes.config [-] /usr/local/bin/kuryr-k8s-controller version 6.0.1
CRITICAL kuryr-kubernetes [-] Unhandled error: RuntimeError: Unable to find token_file  : /var/run/secrets/kubernetes.io/serviceaccount/token
ERROR kuryr-kubernetes Traceback (most recent call last):
ERROR kuryr-kubernetes   File "/usr/local/bin/kuryr-k8s-controller", line 10, in <module>
ERROR kuryr-kubernetes     sys.exit(start())
ERROR kuryr-kubernetes   File "/opt/stack/kuryr-kubernetes/kuryr_kubernetes/controller/service.py", line 180, in start
ERROR kuryr-kubernetes     clients.setup_clients()
ERROR kuryr-kubernetes   File "/opt/stack/kuryr-kubernetes/kuryr_kubernetes/clients.py", line 62, in setup_clients
ERROR kuryr-kubernetes     setup_kubernetes_client()
ERROR kuryr-kubernetes   File "/opt/stack/kuryr-kubernetes/kuryr_kubernetes/clients.py", line 82, in setup_kubernetes_client
ERROR kuryr-kubernetes     _clients[_KUBERNETES_CLIENT] = k8s_client.K8sClient(api_root)
ERROR kuryr-kubernetes   File "/opt/stack/kuryr-kubernetes/kuryr_kubernetes/k8s_client.py", line 66, in __init__
ERROR kuryr-kubernetes     raise RuntimeError(
ERROR kuryr-kubernetes RuntimeError: Unable to find token_file  : /var/run/secrets/kubernetes.io/serviceaccount/token
```

正常な環境であれば `/var/run/secrets/kubernetes.io/serviceaccount/` があるそうですが、エラーが発生している本環境では `/var/run` までしかありませんでした。

次に kuryr-kubernetes 系のサービスが復活できるかを試しました。
下記のサイトで Kuryr サービスが Kubernetes network アドオンとしてインストールされていることがわかりました。

- 参考: [Kuryr installation as a Kubernetes network addon](https://docs.openstack.org/kuryr-kubernetes/latest/installation/containerized.html)
 
stack.sh では下記のコマンドで kuryr-kubernetes をインストールしているようです。

```
source /opt/stack/kuryr-kubernetes/devstack/plugin.sh stack pre-install
source /opt/stack/kuryr-kubernetes/devstack/plugin.sh stack install
source /opt/stack/kuryr-kubernetes/devstack/plugin.sh stack post-config
source /opt/stack/kuryr-kubernetes/devstack/plugin.sh stack extra
source /opt/stack/kuryr-kubernetes/devstack/plugin.sh stack test-config
```

上記のコマンド実行しましたが、$DEST などの様々な環境変数が解決できず、not found エラーが発生しました。

DevStack ではアンインストール用のスクリプトとして unstack.sh が提供されています。
また、ローカルリポジトリを含め完全削除するためには clean.sh スクリプトが提供されています。
この 2 つのスクリプト実行後に stack.sh やり直しました。

```
$ ./unstack.sh
$ ./clean.sh
$ ./stack.sh

... 

[Call Trace]
./stack.sh:1282:start_ovn_services
/opt/stack/devstack/lib/neutron-legacy:519:start_ovn
/opt/stack/devstack/lib/neutron_plugins/ovn_agent:700:wait_for_sock_file
/opt/stack/devstack/lib/neutron_plugins/ovn_agent:178:die
[ERROR] /opt/stack/devstack/lib/neutron_plugins/ovn_agent:178 Socket /var/run/openvswitch/ovnnb_db.sock not found
Error on exit
```

unstack.sh と clean.sh では初回インストール時に設定されたネットワークの設定が削除されません。
そのため、2 回目以降のインストール時に不整合が生じ、エラーとなっているようです。

対応として、環境を作成し直し、再度 stack.sh スクリプトを実行します。

結果として、kuryr-kubernetes が動作しませんでした。

```
stack@yoshitest:~/devstack$ kubectl get pod --all-namespaces
NAMESPACE     NAME                                READY   STATUS    RESTARTS        AGE
kube-system   kube-apiserver-yoshitest            1/1     Running   1 (3m26s ago)   28s
kube-system   kube-controller-manager-yoshitest   1/1     Running   1 (3m26s ago)   51s
kube-system   kube-scheduler-yoshitest            1/1     Running   1 (3m26s ago)   50s

stack@yoshitest:~/devstack$ ps aux |grep kuryr
stack      14446  0.0  0.0   6384  2472 pts/0    S+   07:33   0:00 grep --color=auto kuryr
```

`docker images` コマンドで Docker イメージがプルされていることは確認できました。
しかし、`docker ps` コマンドで確認すると、起動している様子はありませんでした。

```
$ sudo docker images
REPOSITORY                           TAG        IMAGE ID       CREATED         SIZE
kuryr/cni                            latest     d5eef84bb247   2 hours ago     1.28GB
<none>                               <none>     29ba6174d4c5   2 hours ago     883MB
kuryr/controller                     latest     d0868ab345af   2 hours ago     1.06GB
k8s.gcr.io/kube-apiserver            v1.22.12   d922296a8b85   17 hours ago    128MB
k8s.gcr.io/kube-scheduler            v1.22.12   5ee90014579c   17 hours ago    52.7MB
k8s.gcr.io/kube-controller-manager   v1.22.12   f5ef4d6e36ee   17 hours ago    122MB
k8s.gcr.io/kube-proxy                v1.22.12   761817698b62   17 hours ago    104MB
quay.io/centos/centos                stream8    f8a44dfb7278   4 weeks ago     339MB
k8s.gcr.io/etcd                      3.5.0-0    004811815584   13 months ago   295MB
k8s.gcr.io/coredns/coredns           v1.8.4     8d147537fb7d   13 months ago   47.6MB
quay.io/kuryr/golang                 1.16       d5dc529b0ee7   14 months ago   862MB
k8s.gcr.io/pause                     3.5        ed210e3e4a5b   16 months ago   683kB

$ sudo docker ps -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS                        PORTS     NAMES
728597d87b27   k8s.gcr.io/pause:3.5   "/pause"                 10 minutes ago   Exited (0) 10 minutes ago               k8s_POD_nginx-deployment-66b6c48dd5-pdlws_default_e2c38d0e-6dbf-40fd-8c9a-c6a5a5b17514_0
ad3c146a8c38   k8s.gcr.io/pause:3.5   "/pause"                 10 minutes ago   Exited (0) 10 minutes ago               k8s_POD_nginx-deployment-66b6c48dd5-msvcp_default_3ec4a739-b003-4420-928d-09ceeab5c3ba_0
8e465e2810ab   5ee90014579c           "kube-scheduler --au…"   37 minutes ago   Up 37 minutes                           k8s_kube-scheduler_kube-scheduler-yoshitest_kube-system_acb57d0b7dff68b61cfe45619134da6c_1
e6bddd398239   d922296a8b85           "kube-apiserver --ad…"   37 minutes ago   Up 37 minutes                           k8s_kube-apiserver_kube-apiserver-yoshitest_kube-system_83bd29bbe6554e93295c44ac4a1ffb04_1
a14ae872f42d   f5ef4d6e36ee           "kube-controller-man…"   37 minutes ago   Up 37 minutes                           k8s_kube-controller-manager_kube-controller-manager-yoshitest_kube-system_badbf50c21b4819f89f3693e164f4804_1
6d50fd7b9147   k8s.gcr.io/pause:3.5   "/pause"                 37 minutes ago   Up 37 minutes                           k8s_POD_kube-controller-manager-yoshitest_kube-system_badbf50c21b4819f89f3693e164f4804_1
0fb02e37c808   k8s.gcr.io/pause:3.5   "/pause"                 37 minutes ago   Up 37 minutes                           k8s_POD_kube-apiserver-yoshitest_kube-system_83bd29bbe6554e93295c44ac4a1ffb04_1
008e41fb4f9b   k8s.gcr.io/pause:3.5   "/pause"                 37 minutes ago   Up 37 minutes                           k8s_POD_kube-scheduler-yoshitest_kube-system_acb57d0b7dff68b61cfe45619134da6c_1
aa681ca00be7   f5ef4d6e36ee           "kube-controller-man…"   2 hours ago      Exited (255) 38 minutes ago             k8s_kube-controller-manager_kube-controller-manager-yoshitest_kube-system_badbf50c21b4819f89f3693e164f4804_0
2b904fbf4145   5ee90014579c           "kube-scheduler --au…"   2 hours ago      Exited (255) 38 minutes ago             k8s_kube-scheduler_kube-scheduler-yoshitest_kube-system_acb57d0b7dff68b61cfe45619134da6c_0
1f8929b099c6   d922296a8b85           "kube-apiserver --ad…"   2 hours ago      Exited (255) 38 minutes ago             k8s_kube-apiserver_kube-apiserver-yoshitest_kube-system_83bd29bbe6554e93295c44ac4a1ffb04_0
9447e7c252f8   k8s.gcr.io/pause:3.5   "/pause"                 2 hours ago      Exited (255) 38 minutes ago             k8s_POD_kube-scheduler-yoshitest_kube-system_acb57d0b7dff68b61cfe45619134da6c_0
ca6f69e6faf7   k8s.gcr.io/pause:3.5   "/pause"                 2 hours ago      Exited (255) 38 minutes ago             k8s_POD_kube-controller-manager-yoshitest_kube-system_badbf50c21b4819f89f3693e164f4804_0
3abbfee59682   k8s.gcr.io/pause:3.5   "/pause"                 2 hours ago      Exited (255) 38 minutes ago             k8s_POD_kube-apiserver-yoshitest_kube-system_83bd29bbe6554e93295c44ac4a1ffb04_0
```

本環境で  kuryr-kubernetes 系のサービスが動作しない原因は不明です。
kuryr-kubernetes は OpenStack と Kubernetes を同じ Neutron ネットワークで接続するためのサービスです。
これは Devstack の Kubernetes で CNI の役割を果たします。
そのため、kuryr-kubernetes が動作しなければ新規の Pod を立てることができません。

Kubernetes は CNI として kuryr-kubernetes を使用する必要はなく、Calico などを使用できます。
しかし、Devstack の Kubernetes では kuryr-kubernetes プラグインをインストールしなければ Kubernetes がインストールされません。
そのため、別途 Kubernetes 環境を準備し、VIM として使用します。
[3. インストール手順](#インストール手順) のように kubeadm で Kubernetes をインストールし、VIM として登録して利用したところ VNF として Pod を立てることができました。

## Kubernetes インストールできない

Kubernetes インストール時に発生したエラーです。

### tcp 127.0.0.1:10248: connect: connection refused

下記のコマンド実行時のエラーです。

```
$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
```

本環境のエラーログの記録をしておりませんでしたが、[kubeadmを用いたKubernetes再インストールに失敗する](https://qiita.com/rottneer/items/5dad5193b1577c4edc29)] のエラーと同様のエラーが発生しました。

本環境の Docker の driver を確認します。

```
$ docker info | grep Cgroup
  Cgroup Driver: cgroupfs
  Cgroup Version: 1
 ```

下記を追記し、daemon のリロードと docker.service の再起動を行います。

```
$  vim /etc/docker/daemon.json
・・・
  "exec-opts": ["native.cgroupdriver=systemd"],
・・・

$ systemctl daemon-reload
$ systemctl restart docker

$ docker info | grep Cgroup
 Cgroup Driver: systemd
 Cgroup Version: 1
```

この状態で再度 `sudo kubeadm init --pod-network-cidr=10.244.0.0/16` を実行したところ、無事に完了しました。

### sample.yaml が起動しない

下記のコマンド実行時のエラーです。

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

nginx-deployment-66b6c48dd5-glcrt は本環境で立ち上がった Pod を指定してください。
`kubectl get pod` コマンドで調べることができます。

```
$ kubectl describe pod nginx-deployment-66b6c48dd5-glcrt

...

Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  1s (x3 over 2m44s)  default-scheduler  0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
```

Master ノードには node-role.kubernetes.io/master:NoSchedule という Taints が設定されているため、Pod が展開されません。
そのため、この Taint 情報を外す必要があります。

```
$ kubectl describe node yoshitest

...

Taints:             node-role.kubernetes.io/master:NoSchedule
```

Taint 情報を外します。

```
kubectl taint nodes yoshitest node-role.kubernetes.io/master:NoSchedule-
node/master untainted
```

# まとめ

kuryr-kubernetes が再起動時に動作しないため、DevStack の Kubernetes を使用できなかったです。
Vagrant を使用して DevStack を起動した場合は再起動等で停止することはないそうですが、自分は試してないのでわからないです。
自分も試したいです。

ここまでお読みいただき、ありがとうございました。