# 使用NCP Operator集成K8s
###### tags: `NCP`
  本文介绍一下使用NCP Operator集成K8s与NSX-T的方法和注意事项. 除Operator方法外原集成方式依然可用, 原方法文档可以参考张敏(Matt)制作的手册. 张敏的文档中阐述了集成的准备事项, 部分原理, 和详细步骤以及问题排查问题方法. 我无法写得比他更好了, 所以本文只做少许补充材料, 主要说明如果使用NCP Operator集成K8s的方法和优点.
  Matt文档链接: [NSX-T-NCP-k8s-部署手册-v3.1.0](https://onevmw-my.sharepoint.com/:w:/g/personal/taoh_vmware_com/EfdanmNpuLdPqqj9Ai1LThsBZJjpLGhjXF0LaLWTWTNA4A?e=28bRx4)
## NCP集成结构
图一, 说明了NCP可以理解为一个NSX-T的K8s Operator, 并不负责具体的转发层面执行工作, 只是作为指令的转发和API的翻译工作.

<br>
图二, 步骤7, NSX CNI为容器提供端口配置, 因为NSX CNI是用Python2写的(在/opt/cni/bin下), 所以每个VM Node必须安装Python2, 否则启动Pod时会报错无法启动网络沙箱容器.

<br>
图三阐述了OVS, NSX Kube Proxy,的工作职责

<br>
NSX CNI控制OVS为每个容器仓(Pod)打上本地VLAN标签, 到达NSX-T的Segment时, NSX-T通过Hyperbus上的节点标签知道的Pod网络的归属. 从而才能将Pod分配到正确的Segment, 和执行对Pod的分布式防火墙.

## NCP Operator
Operator是NCP的Oprator,并没有实现NCP+NCP Operator一体化. 简而言之NCP Operator目的是为了简化安装和集成NSX-T与K8s. 提炼几点要点NCP Operator要点:
1. NCP Operator简化集成参数, 集中在一个Configmap中完成
2. 简化TLS Ingress和OVS Kernel module的安装
3. 批量为VM Node打上Hyperbus所需要的标签
4. 简化与Openshift的集成
个人觉得生产部署中第三点很重要, VM Node节点多的话, 能够批量自动打标签不但快, 最终要的是减少了犯错的概率. 如果使用原安装方式集成的话, 可以用我个人写的批量为VM Node打标签的脚本[Rock写的批量为VIF打标签的脚本](https://github.com/Rock981119/tagnsxnode_vifs)

## 配置
### 准备事项
1. 每个VM Node最少两张VIF, VIF1作为Kubelet和管理网卡, VIF2作为数据通讯网卡托管给OVS
2. VM Node的两张网卡最好在相同T1 Router下, 否则需要手工做No SNAT.
3. 解压NCP安装包, 上传NCP Operator镜像, 推荐使用私有镜像仓库.
4. 规划好你想要的拓扑, 可以参考张敏的文档
其余地址池如,Pod Subnet, LB VIP Pool, Egress NAT Pool等可以参考官方文档或张敏的文档.
### 演示环境
| Product | Version |
| ------- | ------------------ |
| NCP | 3.1.2 |
| NSX-T | 3.1.0 |
| K8s | v1.18.9 |
| OS | Ubuntu 18.04.5 LTS |
| Kernel | 4.15.0-112-generic |
| Runtime | docker://19.3.13 |
NCP压缩包解压后, 在operator文件夹下kubernetes内有安装所需的所有YAML
```
/operator/kubernetes
kubernetes ls
configmap.yaml operator.nsx.vmware.com_ncpinstalls_crd.yaml role_binding.yaml
lb-secret.yaml operator.nsx.vmware.com_v1_ncpinstall_cr.yaml service_account.yaml
namespace.yaml operator.yaml
nsx-secret.yaml role.yaml
```
### 准备YAML
* 先编辑operator.yaml
Operator的自己的镜像在压缩包内, 上传至私有镜像库或直接上传到节点上.
该环境变量指定了待会部署NCP时从何处拉取镜像, 我填的是私有镜像库
- name: NCP_IMAGE
value: "192.168.31.250:5000/nsx-ncp:3.1.2"
```
apiVersion: apps/v1
kind: Deployment
metadata:
name: nsx-ncp-operator
namespace: nsx-system-operator
spec:
replicas: 1
selector:
matchLabels:
name: nsx-ncp-operator
template:
metadata:
labels:
name: nsx-ncp-operator
spec:
hostNetwork: true
serviceAccountName: nsx-ncp-operator
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
- effect: NoSchedule
key: node.kubernetes.io/not-ready
- effect: NoSchedule
key: node.kubernetes.io/network-unavailable
volumes:
- hostPath: {path: /etc/os-release}
name: host-os-release
containers:
- name: nsx-ncp-operator
image: vmware/nsx-container-plugin-operator
command: ["/bin/bash", "-c", "nsx-ncp-operator --zap-time-encoding=iso8601"]
volumeMounts:
- {mountPath: /host/etc/os-release, name: host-os-release}
imagePullPolicy: IfNotPresent
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: "nsx-ncp-operator"
- name: NCP_IMAGE
value: "192.168.31.250:5000/nsx-ncp:3.1.2"
- name: WATCH_NAMESPACE
value: "nsx-system-operator"
```
编辑configmap.yaml, 篇幅问题, 至粘贴跟测试环境相关的选项
```
[coe]
adaptor = kubernetes
cluster = k8s-03-cluster
node_type = HOSTVM
[nsx_v3]
policy_nsxapi = True
nsx_api_managers = 192.168.31.245
nsx_api_user = admin
nsx_api_password = VMware1!VMware1!
insecure = True
use_native_loadbalancer = True
default_ingress_class_nsx = True
client_ssl_profile = k8s-lb-profile
## 可选项, 如果要开始NSX-T LB对TLS Ingress的支持. k8s-lb-profile是我自定义的, 系统内有内置的Profile可以选择
lb_default_cert_path = /etc/nsx-ujo/lb-cert/tls.crt
## 可选项, 如果要开始NSX-T LB对TLS Ingress的支持. LB默认证书的挂载位置
lb_priv_key_path = /etc/nsx-ujo/lb-cert/tls.key
## LB默认私钥的挂载位置
service_size = SMALL
no_snat_ip_blocks = K8SC3_Pod_NoNAT_IPB
external_ip_pools = K8SC3_Egress_IPP
container_ip_blocks = K8SC3_Pod_IPB
top_tier_router = K8C3-T1
## 选择了T1作为SNAT执行点
single_tier_topology = True
## 拓扑模型选择Per T1 Per Cluster
single_tier_sr_topology = False
external_ip_pools_lb = K8SC3_Ingress_IPP
overlay_tz = c86d3989-ae46-461d-a0f9-caa63c195515
edge_cluster = ff8ba3ff-e48d-49b6-b8ff-d498149290ad
## TZ & edge_cluster必须填写UUID, 不能写名字
x_forwarded_for = INSERT
[k8s]
apiserver_host_ip = 172.16.0.200
apiserver_host_port = 6443
enable_multus = True
[nsx_node_agent]
ovs_bridge = br-int
ovs_uplink_port = ens192
```
由于我需要开启NSX-T LB 对TLS Ingress的支持, 需要上传一张默认LB证书, 官方文档有提供自签名证书的脚本如下:
```
#!/bin/bash
host="www.example.com"
filename=server
openssl genrsa -out ca.key 4096 openssl req -key ca.key -new -x509 -days 365 -sha256 -extensions v3_ca -out ca.crt -subj "/C=US/ST=CA/L=Palo Alto/O=OS3/OU=Eng/CN=${host}"
openssl req -out ${filename}.csr -new -newkey rsa:2048 -nodes -keyout ${filename}.key -subj "/C=US/ST=CA/L=Palo Alto/O=OS3/OU=Eng/CN=${host}" openssl x509 -req -days 360 -in ${filename}.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${filename}.crt -sha256
```
**Tips**: NSX-T不可以使用根证书与私钥作为LB证书, 手工上传时就会报错. 必须采用跟为证书为CSR签发的证书, 如官方脚本的例子. 如果你使用自己制作的证书, 最后手工上传测试, 无报错再使用.
将证书文档输出为Base64格式后, 填入lb-secret.yaml
```
cat tls.crt |base64 -w0
cat tls.key |base64 -w0
```
lb-secret.yaml
```
apiVersion: v1
data: {tls.crt: "<cat tls.crt |base64 -w0>", tls.key: "<cat tls.key |base64 -w0>"}
kind: Secret
metadata: {name: lb-secret, namespace: nsx-system-operator}
type: kubernetes.io/tls
```
最后编辑operator.nsx.vmware.com_v1_ncpinstall_cr.yaml, 我将addNodeTag设为True, Oper将自动为每个VM Node的VIF打标签.
```
apiVersion: operator.nsx.vmware.com/v1
kind: NcpInstall
metadata:
name: ncp-install
namespace: nsx-system-operator
spec:
ncpReplicas: 1
addNodeTag: True
```
建议按顺序Apply YAMLs, 当然你整个文件夹一次性Apply也行.
namespace.yaml
role.yaml
role_binding.yaml
service_account.yaml
lb-secret.yaml
nsx-secret.yaml
configmap.yaml
operator.nsx.vmware.com_ncpinstalls_crd.yaml
operator.nsx.vmware.com_v1_ncpinstall_cr.yaml
operator.yaml
## 校验
正常部署后大概会是这样一番景象, NCP Pod与Node Agent正常工作, CoreDNS正常工作.

NSX-T的界面中出现了跟namespace相关的segment

在Transmit Segment上检查每个Node VIF是否已经打上了正确的标签. 如果没有, 确保operator.nsx.vmware.com_v1_ncpinstall_cr.yaml里的addNodeTag设为True, 重启Operator, 再回来检查.

最后检查默认LB证书是否上传成功


如果都校验功过说明你已经集成成功了, 部署几个Pod, 尝试发布Tls Ingress试试吧.