### Setup/Network Plugin/Calico

## 06-安装calico网络组件.md

calico 是k8s社区最流行的网络插件之一，也是k8s-conformance test 默认使用的网络插件，功能丰富，支持network policy；是当前kubeasz项目的默认网络插件。

如果需要安装calico，请在`clusters/xxxx/hosts`文件中设置变量 `CLUSTER_NETWORK="calico"`，参考[这里](../config_guide.md)

``` bash
roles/calico/
├── tasks
│   └── main.yml
├── templates
│   ├── calico-csr.json.j2
│   ├── calicoctl.cfg.j2
│   ├── calico-v3.15.yaml.j2
│   ├── calico-v3.19.yaml.j2
│   └── calico-v3.8.yaml.j2
└── vars
    └── main.yml
```
请在另外窗口打开`roles/calico/tasks/main.yml`文件，对照看以下讲解内容。

### 创建calico 证书申请

``` bash
{
  "CN": "calico",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "HangZhou",
      "L": "XS",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
```
calico 使用客户端证书，所以hosts字段可以为空；后续可以看到calico证书用在四个地方：

- calico/node 这个docker 容器运行时访问 etcd 使用证书
- cni 配置文件中，cni 插件需要访问 etcd 使用证书
- calicoctl 操作集群网络时访问 etcd 使用证书
- calico/kube-controllers 同步集群网络策略时访问 etcd 使用证书

### 创建 calico DaemonSet yaml文件和rbac 文件

请对照 roles/calico/templates/calico.yaml.j2文件注释和以下注意内容

+ 详细配置参数请参考[calico官方文档](https://projectcalico.docs.tigera.io/reference/node/configuration)
+ 配置ETCD_ENDPOINTS 、CA、证书等，所有{{ }}变量与ansible hosts文件中设置对应
+ 配置集群POD网络 CALICO_IPV4POOL_CIDR={{ CLUSTER_CIDR }}
+ 配置FELIX_DEFAULTENDPOINTTOHOSTACTION=ACCEPT 默认允许Pod到Node的网络流量，更多[felix配置选项](https://projectcalico.docs.tigera.io/reference/felix/configuration)

### 安装calico 网络

+ 安装前检查主机名不能有大写字母，只能由`小写字母` `-` `.` 组成 (name must consist of lower case alphanumeric characters, '-' or '.' (regex: [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*))(calico-node v3.0.6以上已经解决主机大写字母问题)
+ **安装前必须确保各节点主机名不重复** ，calico node name 由节点主机名决定，如果重复，那么重复节点在etcd中只存储一份配置，BGP 邻居也不会建立。
+ 安装之前必须确保`kube_master`和`kube_node`节点已经成功部署
+ 轮询等待calico 网络插件安装完成，删除之前kube_node安装时默认cni网络配置

### [可选]配置calicoctl工具 [calicoctl.cfg.j2](roles/calico/templates/calicoctl.cfg.j2)

``` bash
apiVersion: projectcalico.org/v3
kind: CalicoAPIConfig
metadata:
spec:
  datastoreType: "etcdv3"
  etcdEndpoints: {{ ETCD_ENDPOINTS }}
  etcdKeyFile: /etc/calico/ssl/calico-key.pem
  etcdCertFile: /etc/calico/ssl/calico.pem
  etcdCACertFile: {{ ca_dir }}/ca.pem
```

### 验证calico网络

执行calico安装成功后可以验证如下：(需要等待镜像下载完成，有时候即便上一步已经配置了docker国内加速，还是可能比较慢，请确认以下容器运行起来以后，再执行后续验证步骤)

``` bash
kubectl get pod --all-namespaces
NAMESPACE     NAME                                       READY     STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-5c6b98d9df-xj2n4   1/1       Running   0          1m
kube-system   calico-node-4hr52                          2/2       Running   0          1m
kube-system   calico-node-8ctc2                          2/2       Running   0          1m
kube-system   calico-node-9t8md                          2/2       Running   0          1m
```

**查看网卡和路由信息**

先在集群创建几个测试pod:  `kubectl run test --image=busybox --replicas=3 sleep 30000`

``` bash
# 查看网卡信息
ip a
```

+ 可以看到包含类似cali1cxxx的网卡，是calico为测试pod生成的
+ tunl0网卡现在不用管，是默认生成的，当开启IPIP 特性时使用的隧道

``` bash
# 查看路由
route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 ens3
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 ens3
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.20.3.64     192.168.1.34    255.255.255.192 UG    0      0        0 ens3
172.20.33.128   0.0.0.0         255.255.255.192 U     0      0        0 *
172.20.33.129   0.0.0.0         255.255.255.255 UH    0      0        0 caliccc295a6d4f
172.20.104.0    192.168.1.35    255.255.255.192 UG    0      0        0 ens3
172.20.166.128  192.168.1.63    255.255.255.192 UG    0      0        0 ens3
```

**查看所有calico节点状态**

``` bash
calicoctl node status
Calico process is running.

IPv4 BGP status
+--------------+-------------------+-------+----------+-------------+
| PEER ADDRESS |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+--------------+-------------------+-------+----------+-------------+
| 192.168.1.34 | node-to-node mesh | up    | 12:34:00 | Established |
| 192.168.1.35 | node-to-node mesh | up    | 12:34:00 | Established |
| 192.168.1.63 | node-to-node mesh | up    | 12:34:01 | Established |
+--------------+-------------------+-------+----------+-------------+
```

**BGP 协议是通过TCP 连接来建立邻居的，因此可以用netstat 命令验证 BGP Peer**

``` bash
netstat -antlp|grep ESTABLISHED|grep 179
tcp        0      0 192.168.1.66:179        192.168.1.35:41316      ESTABLISHED 28479/bird      
tcp        0      0 192.168.1.66:179        192.168.1.34:40243      ESTABLISHED 28479/bird      
tcp        0      0 192.168.1.66:179        192.168.1.63:48979      ESTABLISHED 28479/bird
```

**查看etcd中calico相关信息**

因为这里calico网络使用etcd存储数据，所以可以在etcd集群中查看数据

+ calico 3.x 版本默认使用 etcd v3存储，**登录集群的一个etcd 节点**，查看命令：

``` bash
# 查看所有calico相关数据
ETCDCTL_API=3 etcdctl --endpoints="http://127.0.0.1:2379" get --prefix /calico
# 查看 calico网络为各节点分配的网段
ETCDCTL_API=3 etcdctl --endpoints="http://127.0.0.1:2379" get --prefix /calico/ipam/v2/host
```


## 下一步：[设置 BGP Route Reflector](calico-bgp-rr.md)

---

### Setup/Network Plugin/Calico Bgp Rr

# calico 配置 BGP Route Reflectors

`Calico`作为`k8s`的一个流行网络插件，它依赖`BGP`路由协议实现集群节点上的`POD`路由互通；而路由互通的前提是节点间建立 BGP Peer 连接。BGP 路由反射器（Route Reflectors，简称 RR）可以简化集群BGP Peer的连接方式，它是解决BGP扩展性问题的有效方式；具体来说：

- 没有 RR 时，所有节点之间需要两两建立连接（IBGP全互联），节点数量增加将导致连接数剧增、资源占用剧增
- 引入 RR 后，其他 BGP 路由器只需要与它建立连接并交换路由信息，节点数量增加连接数只是线性增加，节省系统资源

calico-node 版本 v3.3 开始支持内建路由反射器，非常方便，因此使用 calico 作为网络插件可以支持大规模节点数的`K8S`集群。

- 建议集群节点数大于50时，应用BGP Route Reflectors 特性

## 前提条件

k8s 集群使用calico网络插件部署成功。本实验环境为按照kubeasz安装的2主2从集群，calico 版本 v3.19.4。

```
$ kubectl get node
NAME           STATUS                     ROLES    AGE    VERSION
192.168.1.1   Ready,SchedulingDisabled   master   178m   v1.13.1
192.168.1.2   Ready,SchedulingDisabled   master   178m   v1.13.1
192.168.1.3   Ready                      node     178m   v1.13.1
192.168.1.4   Ready                      node     178m   v1.13.1
$ kubectl get pod -n kube-system -o wide | grep calico
calico-kube-controllers-77487546bd-jqrlc   1/1     Running   0          179m   192.168.1.3   192.168.1.3   <none>           <none>
calico-node-67t5m                          2/2     Running   0          179m   192.168.1.1   192.168.1.1   <none>           <none>
calico-node-drmhq                          2/2     Running   0          179m   192.168.1.2   192.168.1.2   <none>           <none>
calico-node-rjtkv                          2/2     Running   0          179m   192.168.1.4   192.168.1.4   <none>           <none>
calico-node-xtspl                          2/2     Running   0          179m   192.168.1.3   192.168.1.3   <none>           <none>
```
查看当前集群中BGP连接情况：可以看到集群中4个节点两两建立了 BGP 连接

```
/* Detailed source-code truncated for AI context efficiency. */
```

## kubeasz 自动安装启用 route reflector

- 修改`/etc/kubeasz/clusters/xxx/config.yml`文件，设置配置项`CALICO_RR_ENABLED: true` 
- 重新执行网络安装 `dk ezctl setup xxx 07`

执行完成，检查bgp连接验证即可。

### 附：手动安装route reflector 过程讲解

- 选择并配置 Route Reflector 节点

首先查看当前集群中的节点：

```
$ calicoctl get node -o wide
NAME     ASN       IPV4              IPV6
k8s401   (64512)   192.168.1.1/24
k8s402   (64512)   192.168.1.2/24
k8s403   (64512)   192.168.1.3/24
k8s404   (64512)   192.168.1.4/24
```

可以在集群中选择1个或多个节点作为 rr 节点，这里先选择节点：k8s401

``` bash
#配置routeReflectorClusterID
calicoctl patch node k8s401 -p '{"spec": {"bgp": {"routeReflectorClusterID": "244.0.0.1"}}}'

#配置node label
calicoctl patch node k8s401 -p '{"metadata": {"labels": {"route-reflector": "true"}}}'
```

- 配置 BGP node 与 Route Reflector 的连接建立规则

``` bash
$ cat << EOF | calicoctl create -f -
kind: BGPPeer
apiVersion: projectcalico.org/v3
metadata:
  name: peer-with-route-reflectors
spec:
  nodeSelector: all()
  peerSelector: route-reflector == 'true'
EOF
```

- 配置全局禁用全连接（BGP full mesh）

```
$ cat << EOF | calicoctl create -f -
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  logSeverityScreen: Info
  nodeToNodeMeshEnabled: false
  asNumber: 64512
EOF
```

- 验证增加 rr 之后的bgp 连接情况

```
/* Detailed source-code truncated for AI context efficiency. */
```
可以看到所有其他节点都与所选rr节点建立bgp连接。

- 再增加一个 rr 节点(略)

步骤同上，添加成功后可以看到所有其他节点都与两个rr节点建立bgp连接，两个rr节点之间也建立bgp连接。对于节点数较多的`K8S`集群建议配置2-3个 RR 节点。

## 参考文档

- 1.[Calico bgp 配置指南](https://projectcalico.docs.tigera.io/reference/resources/bgpconfig)
- 2.[BGP路由反射器基础](https://www.sohu.com/a/140033025_761420)

---

### Setup/Network Plugin/Cilium

# 06-安装cilium网络组件

`cilium` 是一个革新的网络与安全组件；基于 linux 内核新技术--`BPF`，它可以透明、零侵入地实现服务间安全策略与可视化，主要优势如下：

- 支持L3/L4, L7(如：HTTP/gRPC/Kafka)的安全策略
- 支持基于安全ID而不是地址+端口的传统防火墙策略
- 支持基于Overlay或Native Routing的扁平多节点pod网络
  - Overlay VXLAN 方式类似于 flannel 的VXLAN后端
- 高性能负载均衡，支持DSR
- 支持事件、策略跟踪和监控集成

cilium 项目文档比较完整，建议仔细阅读[官网文档]()

## kubeasz 集成安装 cilium

kubeasz 3.3.1 更新重写了cilium 安装流程，使用helm charts 方式，配置文件在 roles/cilium/templates/values.yaml.j2，请阅读原charts中values.yaml 文件后自定义修改。

- https://docs.cilium.io/en/stable/installation/k8s-install-helm/#k8s-install-helm
- 相关镜像已经离线打包并推送到本地镜像仓库，通过 `ezdown -X` 命令下载cilium等额外镜像

### 0.检查系统内核版本

- Linux kernel >= 4.9.17，如需升级请阅读文档[升级内核](guide/kernel_upgrade.md)
- etcd >= 3.1.0 or consul >= 0.6.4

### 1.选择cilium网络后安装

- 参考[快速指南](../quickStart.md)，设置`/etc/kubeasz/clusters/xxx/hosts`文件中变量 `CLUSTER_NETWORK="cilium"` 
- 下载额外镜像 `./ezdown -X cilium 和 ./ezdown -X network-check`
- 执行集群安装 `dk ezctl setup xxx all`

注意默认设置未集成cilium_hubble，可以在`/etc/kubeasz/clusters/xxx/config.yml`配置启用后再开始安装。

- cilium_connectivity_check：检查集群cilium网络是否工作正常，非常实用
- cilium_hubble：很酷很实用的监控、策略追踪排查工具

Cilium CLI 和 Hubble CLI 二进制已经默认包含在kubeasz-ext-bin 1.2.0及之后的版本中 https://github.com/kubeasz/dockerfiles/blob/master/kubeasz-ext-bin/Dockerfile

### 2.验证

一键安装完成后如下，注意cilium_connectivity_check 中带`multi-node`的检查任务需要多节点集群才能完成

```
/* Detailed source-code truncated for AI context efficiency. */
```

检查 cilium 节点状态

```
cilium status
    /¯¯\
 /¯¯\__/¯¯\    Cilium:         OK
 \__/¯¯\__/    Operator:       OK
 /¯¯\__/¯¯\    Hubble:         OK
 \__/¯¯\__/    ClusterMesh:    disabled
    \__/

DaemonSet         cilium             Desired: 4, Ready: 4/4, Available: 4/4
Deployment        cilium-operator    Desired: 1, Ready: 1/1, Available: 1/1
Deployment        hubble-relay       Desired: 1, Ready: 1/1, Available: 1/1
Deployment        hubble-ui          Desired: 1, Ready: 1/1, Available: 1/1
Containers:       cilium             Running: 4
                  cilium-operator    Running: 1
                  hubble-relay       Running: 1
                  hubble-ui          Running: 1
Cluster Pods:     17/17 managed by Cilium
Image versions    hubble-relay       easzlab.io.local:5000/cilium/hubble-relay:v1.11.6: 1
                  hubble-ui          easzlab.io.local:5000/cilium/hubble-ui:v0.9.0: 1
                  hubble-ui          easzlab.io.local:5000/cilium/hubble-ui-backend:v0.9.0: 1
                  cilium             easzlab.io.local:5000/cilium/cilium:v1.11.6: 4
                  cilium-operator    easzlab.io.local:5000/cilium/operator-generic:v1.11.6: 1
```

## cilium network policy

cilium network policy 提供了比k8s network policy更丰富的网络安全策略功能，有兴趣的请阅读官网文档，以下是一个有趣的小例子：

- [星战死星登陆系统](cilium-example.md)

---

### Setup/Network Plugin/Cilium Example

## 开始使用 cilium

以下为简要翻译 `cilium doc`上的一个应用示例[原文](https://docs.cilium.io/en/stable/gettingstarted/http/)，部署在单节点k8s 环境的实践。

### 部署示例应用

官方文档用几个`pod/svc` 抽象一个有趣的应用场景（星战迷）：星战中帝国方建造了被称为“终极武器”的“死星”，它是一个卫星大小的战斗空间站，它的核心是使用凯伯晶体（Kyber Crystal）的超级激光炮，剧中它的首秀就以完全火力摧毁了“杰达圣城”（Jedha）。下面将用运行于 k8s上的 pod/svc/cilium 等模拟“死星“的一个“飞船登陆”系统安全策略设计。

- deploy/deathstar：作为控制整个“死星”的飞船登陆管理系统，它暴露一个SVC，提供HTTP REST 接口给飞船请求登陆使用；
- pod/tiefighter：作为“帝国”方的常规战斗飞船，它会调用上述 HTTP 接口，请求登陆“死星”；
- pod/xwing：作为“盟军”方的飞行舰，它也尝试调用 HTTP 接口，请求登陆“死星”；

<img alt="cilium_http_gsg" width="400" height="300" src="https://docs.cilium.io/en/stable/_images/cilium_http_gsg.png">

根据文件[http-sw-app.yaml](../../../roles/cilium/files/star_war_example/http-sw-app.yaml) 创建 `$ kubectl create -f http-sw-app.yaml` 后，验证如下：

``` bash
$ kubectl get pods,svc
NAME                             READY     STATUS    RESTARTS   AGE
pod/deathstar-5fc7c7795d-djf2q   1/1       Running   0          4h
pod/deathstar-5fc7c7795d-hrgst   1/1       Running   0          4h
pod/tiefighter                   1/1       Running   0          4h
pod/xwing                        1/1       Running   0          4h

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/deathstar    ClusterIP   10.68.242.130   <none>        80/TCP    4h
service/kubernetes   ClusterIP   10.68.0.1       <none>        443/TCP   5h
```
每个 POD 在 `cilium` 中都表示为 `Endpoint`，初始每个 `Endpoint` 的”进出安全策略“状态均为 `Disabled`，如下：(已省略部分无关 POD 信息)

```
/* Detailed source-code truncated for AI context efficiency. */
```

### 检查初始状态

当然“死星”应该只允许“帝国”的飞船着陆，因为没有应用任何策略，所以初始状态下“帝国”和“联盟”的飞船都可以登陆，如下测试：

``` bash
$ kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed # 成功着陆
$ kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed # 成功着陆
```

### 应用 L3/L4 策略

现在我们应用策略，仅让带有标签 `org=empire`的飞船登陆“死星”；那么带有标签 `org=alliance`的“联盟”飞船将禁止登陆；这个就是我们熟悉的传统L3/L4 防火墙策略，并跟踪连接（会话）状态；

<img alt="cilium_http_l3_l4_gsg" width="400" height="300" src="https://docs.cilium.io/en/stable/_images/cilium_http_l3_l4_gsg.png">

根据文件[sw_l3_l4_policy.yaml](../../../roles/cilium/files/star_war_example/sw_l3_l4_policy.yaml) 创建 `$ kubectl apply -f sw_l3_l4_policy.yaml` 后，验证如下：

``` bash
$ kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed # 成功着陆

$ kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
# 失败超时
```

### 查看安全策略

再次执行 `cilium endpoint list`，可以看到标签带`deathstar`的 POD 已经应用了 `Ingress`方向的策略：

```
/* Detailed source-code truncated for AI context efficiency. */
```

查看具体策略内容 `kubectl describe cnp rule1`

### L7 安全策略

上述的策略可以进行简单的安全防护了，但是“死星”的这个系统还有很多复杂的功能；比如它还提供了一个内部维护接口，如果被不合理调用将带来严重灾难性后果，也许“联盟”勇士劫持了一架“帝国”飞船正在进行这个任务（虽然我们内心希望他能够成功摧毁“死星”）。不幸的是“死星”系统设计者考虑到这个风险，它有办法严格限制每架飞船能够请求的权限。

没有限制飞船请求权限时，如下运行：

``` bash
$ kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Panic: deathstar exploded

goroutine 1 [running]:
main.HandleGarbage(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
        /code/src/github.com/empire/deathstar/
        temp/main.go:9 +0x64
main.main()
        /code/src/github.com/empire/deathstar/
        temp/main.go:5 +0x85
```

<img alt="cilium_http_l3_l4_l7_gsg" width="400" height="300" src="https://docs.cilium.io/en/stable/_images/cilium_http_l3_l4_l7_gsg.png">

限制L7 的安全策略，根据文件[sw_l3_l4_l7_policy.yaml](../../../roles/cilium/files/star_war_example/sw_l3_l4_l7_policy.yaml) 创建 `$ kubectl apply -f sw_l3_l4_l7_policy.yaml` 后，验证如下：

``` bash
$ kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
$ kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Access denied
```

我们同样可以使用 `kubectl desribe cnp`检查更新的策略，或者使用 `cilium` 命令行：

``` bash
$ kubectl exec -n kube-system cilium-6t5vx -- cilium policy get
[
  {
    "endpointSelector": {
      "matchLabels": {
        "any:class": "deathstar",
        "any:org": "empire",
        "k8s:io.kubernetes.pod.namespace": "default"
      }
    },
    "ingress": [
      {
        "fromEndpoints": [
          {
            "matchLabels": {
              "any:org": "empire",
              "k8s:io.kubernetes.pod.namespace": "default"
            }
          }
        ],
        "toPorts": [
          {
            "ports": [
              {
                "port": "80",
                "protocol": "TCP"
              }
            ],
            "rules": {
              "http": [
                {
                  "path": "/v1/request-landing",
                  "method": "POST"
                }
              ]
            }
          }
        ]
      }
    ],
    "labels": [
      {
        "key": "io.cilium.k8s.policy.name",
        "value": "rule1",
        "source": "k8s"
      },
      {
        "key": "io.cilium.k8s.policy.namespace",
        "value": "default",
        "source": "k8s"
      }
    ]
  }
]
Revision: 267
```
我们看到 `cilium` 可以实现 `7层 HTTP `协议的请求方法（GET/PUT/POST等）、路径（/v1/request-landing）等等安全策略；另外，它还可以防护其他应用（如：Kafka, gRPC, Elasticsearch），可以去官网文档示例学习！

## 参考资料

- [cilium github](https://github.com/cilium/cilium)
- [cilium doc](http://docs.cilium.io)

---

### Setup/Network Plugin/Flannel

## 06-安装flannel网络组件.md

`Flannel`是最早应用到k8s集群的网络插件之一，简单高效，且提供多个后端`backend`模式供选择；本文介绍以`DaemonSet Pod`方式集成到k8s集群，需要在所有master节点和node节点安装。

### kubeasz 集成安装flannel

- 参考[快速指南](../quickStart.md)，设置`/etc/kubeasz/clusters/xxx/hosts`文件中变量 `CLUSTER_NETWORK="flannel"`
- 下载额外镜像 `./ezdown -X flannel`
- 执行集群安装 `dk ezctl setup xxx all`

### 配置介绍

Flannel CNI 插件的配置文件可以包含多个`plugin` 或由其调用其他`plugin`；`Flannel DaemonSet Pod`运行以后会生成`/run/flannel/subnet.env `文件，例如：

``` bash
FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.17.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true
```
然后它利用这个文件信息去配置和调用`bridge`插件来生成容器网络，调用`host-local`来管理`IP`地址，例如：

``` bash
{
	"name": "mynet",
	"type": "bridge",
	"mtu": 1472,
	"ipMasq": false,
	"isGateway": true,
	"ipam": {
		"type": "host-local",
		"subnet": "10.1.17.0/24"
	}
}
```
- 更多相关介绍请阅读：
  - [flannel kubernetes 集成](https://github.com/coreos/flannel/blob/master/Documentation/kubernetes.md)
  - [flannel cni 插件](https://github.com/containernetworking/plugins/tree/master/plugins/meta/flannel)
  - [更多 cni 插件](https://github.com/containernetworking/plugins)

- `Flannel DaemonSet` yaml配置文件

请阅读 `roles/flannel/templates/kube-flannel.yaml.j2` 内容，注意：

+ 注意：本安装方式，flannel 通过 apiserver 接口读取 podCidr 信息，详见 https://github.com/coreos/flannel/issues/847；因此想要修改节点pod网段掩码，请在`clusters/xxxx/config.yml` 中修改`NODE_CIDR_LEN`配置项
+ 配置相关RBAC 权限和 `service account`
+ 配置`ConfigMap`包含 CNI配置和 flannel配置(指定backend等)，在文件中相关设置对应

### 验证flannel网络

执行flannel安装成功后可以验证如下：(需要等待镜像下载完成，有时候即便上一步已经配置了docker国内加速，还是可能比较慢，请确认以下容器运行起来以后，再执行后续验证步骤)

``` bash
# kubectl get pod --all-namespaces
NAMESPACE     NAME                    READY     STATUS    RESTARTS   AGE
kube-system   kube-flannel-ds-m8mzm   1/1       Running   0          3m
kube-system   kube-flannel-ds-mnj6j   1/1       Running   0          3m
kube-system   kube-flannel-ds-mxn6k   1/1       Running   0          3m
```
在集群创建几个测试pod:  `kubectl run test --image=busybox --replicas=3 sleep 30000`

``` bash
# kubectl get pod --all-namespaces -o wide|head -n 4
NAMESPACE     NAME                    READY     STATUS    RESTARTS   AGE       IP             NODE
default       busy-5956b54c8b-ld4gb   1/1       Running   0          9m        172.20.2.7     192.168.1.1
default       busy-5956b54c8b-lj9l9   1/1       Running   0          9m        172.20.1.5     192.168.1.2
default       busy-5956b54c8b-wwpkz   1/1       Running   0          9m        172.20.0.6     192.168.1.3

# 查看路由
# ip route
default via 192.168.1.254 dev ens3 onlink 
192.168.1.0/24 dev ens3  proto kernel  scope link  src 192.168.1.1 
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown 
172.20.0.0/24 via 192.168.1.3 dev ens3 
172.20.1.0/24 via 192.168.1.2 dev ens3 
172.20.2.0/24 dev cni0  proto kernel  scope link  src 172.20.2.1 
```
在各节点上分别 ping 这三个POD IP地址，确保能通：

``` bash
ping 172.20.2.7
ping 172.20.1.5
ping 172.20.0.6
```

---

### Setup/Network Plugin/Kube Ovn

## 06-安装kube-ovn网络组件.md

(以下文档暂未更新，以插件官网文档为准)

由灵雀云开源的网络组件 kube-ovn，将已被 openstack 社区采用的成熟网络虚拟化技术 ovs/ovn 引入 kubernetes 平台；为 kubernetes 网络打开了新的大门，令人耳目一新；强烈推荐大家试用该网络组件，反馈建议以帮助项目早日走向成熟。

- 介绍 https://blog.csdn.net/alauda_andy/article/details/88886128
- 项目地址 https://github.com/alauda/kube-ovn

### 特性介绍

kube-ovn 提供了针对企业应用场景下容器网络实用功能，并为实现更高级的网络管理控制提供了可能性；现有主要功能:

- 1.Namespace 和子网的绑定，以及子网间的访问控制;
- 2.静态IP分配;
- 3.动态QoS;
- 4.分布式和集中式网关;
- 5.内嵌 LoadBalancer;
- 6.Pod IP对外直接暴露
- 7.流量镜像
- 8.IPv6

### kubeasz 集成安装 kube-ovn

kube-ovn 的安装十分简单，详见项目的安装文档；基于 kubeasz，以下两步将安装一个集成了 kube-ovn 网络的 k8s 集群；

- 在 ansible hosts 中设置变量 `CLUSTER_NETWORK="kube-ovn"`
- 执行安装 `ansible-playbook 90.setup.yml` 或者 `ezctl setup`

kubeasz 项目为`kube-ovn`网络生成的 ansible role 如下：

``` bash
roles/kube-ovn
├── defaults
│   └── main.yml		# kube-ovn 相关配置文件
├── tasks
│   └── main.yml		# 安装执行文件
└── templates
    ├── crd.yaml.j2	        # crd 模板
    ├── kube-ovn.yaml.j2	# kube-ovn yaml 模板
    └── ovn.yaml.j2		    # ovn yaml 模板
    
```

安装成功后，可以验证所有 k8s 集群功能正常，查看集群的 pod 网络如下：

```
/* Detailed source-code truncated for AI context efficiency. */
```

直观上 kube-ovn 与传统 k8s 网络（flannel/calico等）比较最大的不同是 pod 子网的分配：

- 传统网络插件下，集群中 pod 一般是不同 node 节点分配不同的子网；然后通过 overlay 等技术打通不同 node 节点的 pod 子网；
- kube-ovn 中 pod 网络根据其所在的 namespace 而定； namespace 在创建时可以根据 annotation 来配置它的子网/网关等参数；默认使用 10.16.0.0/16 的子网；

### 测试 namespace 子网分配

新建一个 subnet 并绑定 namespace 测试分配一个新的 pod 子网

```
# 创建一个 namespace: test-ns
$ cat > test-ns.yaml << EOF
apiVersion: v1
kind: Namespace
metadata:
  annotations:
  name: test-ns
EOF
$ kubectl apply -f test-ns.yaml

# 创建一个 subnet: test-subnet 并绑定 namespace test-ns
$ cat > test-subnet.yaml << EOF
apiVersion: kubeovn.io/v1
kind: Subnet
metadata:
  name: test-subnet
spec:
  protocol: IPv4
  default: false
  namespaces:
  - test-ns
  cidrBlock: 10.17.0.0/24
  gateway: 10.17.0.1
  excludeIps:
  - 10.17.0.1..10.17.0.10
EOF
$ kubectl apply -f test-subnet.yaml

# 在 test-ns 中创建 nginx 部署
$ kubectl run -n test-ns nginx --image=nginx --replicas=2 --port=80 --expose

# 在 default 中创建 busy 客户端
$ kubectl run busy --image=busybox sleep 360000
```

创建成功后，查看 pod 地址的分配，可以看到确实 test-ns 中 pod 使用新的子网，而 default 中 pod 使用了默认子网，并验证 pod 之间的联通性（默认可通）

```
/* Detailed source-code truncated for AI context efficiency. */
```

- 更多的测试（pod网络QOS限速，namespace网络隔离等）请参考 kube-ovn 项目说明文档

### 延伸阅读

- [kube-ovn 官方文档](https://github.com/alauda/kube-ovn/tree/master/docs)
- [从 Bridge 到 OVS，探索虚拟交换机](https://www.cnblogs.com/bakari/p/8097439.html)

---

### Setup/Network Plugin/Kube Router

# kube-router 网络组件

(以下文档暂未更新，以插件官网文档为准)

kube-router是一个简单、高效的网络插件，它提供一揽子解决方案：  
- 基于GoBGP 提供Pod 网络互联（Routing）
- 使用ipsets优化的iptables 提供网络策略支持（Firewall/NetworkPolicy）
- 基于IPVS/LVS 提供高性能服务代理（Service Proxy）(注：由于 k8s 新版本中 ipvs 已可用，因此这里不选择启用kube-router基于ipvs的service proxy)

更多介绍请前往`https://github.com/cloudnativelabs/kube-router`

## 配置

本项目提供多种网络插件可选，如果需要安装kube-router，请在/etc/kubeasz/hosts文件中设置变量 `CLUSTER_NETWORK="kube-router"`，更多设置请查看`roles/kube-router/defaults/main.yml`

- kube-router需要在所有master节点和node节点安装

## 安装

- 单步安装已经集成：`ansible-playbook 90.setup.yml`
- 分步安装请执行：`ansible-playbook 06.network.yml`

## 验证

- 1.pod间网络联通性：略

- 2.host路由表

``` bash
# master上路由
root@master1:~$ ip route
...
172.20.1.0/24 via 192.168.1.2 dev ens3  proto 17 
172.20.2.0/24 via 192.168.1.3 dev ens3  proto 17 
...

# node3上路由
root@node3:~$ ip route
... 
172.20.0.0/24 via 192.168.1.1 dev ens3  proto 17 
172.20.1.0/24 via 192.168.1.2 dev ens3  proto 17 
172.20.2.0/24 dev kube-bridge  proto kernel  scope link  src 172.20.2.1 
...
```

- 3.bgp连接状态

``` bash
# master上
root@master1:~$ netstat -antlp|grep router|grep LISH|grep 179
tcp        0      0 192.168.1.1:179        192.168.1.3:58366      ESTABLISHED 26062/kube-router
tcp        0      0 192.168.1.1:42537      192.168.1.2:179        ESTABLISHED 26062/kube-router

# node3上
root@node3:~$ netstat -antlp|grep router|grep LISH|grep 179
tcp        0      0 192.168.1.3:58366      192.168.1.1:179        ESTABLISHED 18897/kube-router
tcp        0      0 192.168.1.3:179        192.168.1.2:43928      ESTABLISHED 18897/kube-router

```

- 4.NetworkPolicy有效性，验证参照[这里](../../guide/networkpolicy.md)

- 5.ipset列表查看

``` bash
$ ipset list
...
Name: kube-router-pod-subnets
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536 timeout 0
Size in memory: 672
References: 2
Members:
172.20.1.0/24 timeout 0
172.20.2.0/24 timeout 0
172.20.0.0/24 timeout 0

Name: kube-router-node-ips
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536 timeout 0
Size in memory: 416
References: 1
Members:
192.168.1.1 timeout 0
192.168.1.2 timeout 0
192.168.1.3 timeout 0
...
```

---

### Setup/Network Plugin/Network Check

# network-check

网络测试组件，根据cilium connectivity-check 脚本修改而来；利用cronjob 定期检测集群各节点、容器、serviceip、nodeport等之间的网络联通性；可以方便的判断当前集群网络是否正常。

目前检测如下：

``` bash
kubectl get cronjobs.batch -n network-test
NAME                                  SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
test01-pod-to-container               */5 * * * *   False     0        3m19s           6d3h
test02-pod-to-node-nodeport           */5 * * * *   False     0        3m19s           6d3h
test03-pod-to-multi-node-clusterip    */5 * * * *   False     1        6d3h            6d3h
test04-pod-to-multi-node-headless     */5 * * * *   False     1        6d3h            6d3h
test05-pod-to-multi-node-nodeport     */5 * * * *   False     1        6d3h            6d3h
test06-pod-to-external-1111           */5 * * * *   False     0        3m19s           6d3h
test07-pod-to-external-fqdn-baidu     */5 * * * *   False     0        3m19s           6d3h
test08-host-to-multi-node-clusterip   */5 * * * *   False     1        6d3h            6d3h
test09-host-to-multi-node-headless    */5 * * * *   False     1        6d3h            6d3h
```

+ 带`multi-node`的测试需要多节点集群才能运行，如果单节点集群，测试pod会处于`Pending`状态
+ 带`external`的测试需要节点能够访问互联网，否则测试会失败

## 启用网络检测

- 下载额外容器镜像 `./ezdown -X network-check`

- 配置集群，在配置文件`/etc/kubeasz/clusters/xxx/config.yml` (xxx为集群名) 修改如下选项

```
# network-check 自动安装
network_check_enabled: true
network_check_schedule: "*/5 * * * *"  # 检测频率，默认5分钟执行一次
```

- 安装网络检测插件 `docker exec -it kubeasz ezctl setup xxx 07`

## 检查测试结果

大约等待5分钟左右，查看运行结果，如果pod 状态为`Completed` 表示检测正常通过。

```
kubectl get pod -n network-test
NAME                                                 READY   STATUS      RESTARTS   AGE
echo-server-58d7bb7f6-77ps6                          1/1     Running     0          6d4h
echo-server-host-cc87c966d-bk57t                     1/1     Running     0          6d4h
test01-pod-to-container-27606775-q6xlb               0/1     Completed   0          3m10s
test02-pod-to-node-nodeport-27606775-x2v5d           0/1     Completed   0          3m10s
test03-pod-to-multi-node-clusterip-27597895-cbq8d    0/1     Pending     0          6d4h
test04-pod-to-multi-node-headless-27597895-qzsgz     0/1     Pending     0          6d4h
test05-pod-to-multi-node-nodeport-27597895-kb5r7     0/1     Pending     0          6d4h
test06-pod-to-external-1111-27606775-p6v8s           0/1     Completed   0          3m10s
test07-pod-to-external-fqdn-baidu-27606775-qdfwd     0/1     Completed   0          3m10s
test08-host-to-multi-node-clusterip-27597895-qsgn9   0/1     Pending     0          6d4h
test09-host-to-multi-node-headless-27597895-hpkt5    0/1     Pending     0          6d4h
```

+ pod 状态为`Completed` 表示检测正常通过
+ pod 状态为`Pending` 表示该检测需要多节点的k8s集群才会运行

## 禁用网络检测

如果集群已经开启网络检测，检测结果符合预期，并且不想继续循环检测时，只要删除对应namespace即可

```
kubectl delete ns network-test
```

---

### Setup/00 Planning And Overall Intro

## 00-集群规划和基础参数设定

### HA architecture

<img alt="ha-3x" width="500" height="380" src="../pics/ha-3x.svg">

- 注意1：确保各节点时区设置一致、时间同步。 如果你的环境没有提供NTP 时间同步，推荐集成安装[chrony](../guide/chrony.md)
- 注意2：确保在干净的系统上开始安装，不要使用曾经装过kubeadm或其他k8s发行版的环境
- 注意3：建议操作系统升级到新的稳定内核，请结合阅读[内核升级文档](../guide/kernel_upgrade.md)
- 注意4：在公有云上创建多主集群，请结合阅读[在公有云上部署 kubeasz](kubeasz_on_public_cloud.md)

## 高可用集群所需节点配置如下

|角色|数量|描述|
|:-|:-|:-|
|部署节点|1|运行ansible/ezctl命令，一般复用第一个master节点|
|etcd节点|3|注意etcd集群需要1,3,5,...奇数个节点，一般复用master节点|
|master节点|2|高可用集群至少2个master节点|
|node节点|n|运行应用负载的节点，可根据需要提升机器配置/增加节点数|

机器配置：
- master节点：4c/8g内存/50g硬盘
- worker节点：建议8c/32g内存/200g硬盘以上

注意：默认配置下容器运行时和kubelet会占用/var的磁盘空间，如果磁盘分区特殊，可以设置config.yml中的容器运行时和kubelet数据目录：`CONTAINERD_STORAGE_DIR` `DOCKER_STORAGE_DIR` `KUBELET_ROOT_DIR`

在 kubeasz 2x 版本，多节点高可用集群安装可以使用2种方式

- 1.按照本文步骤先规划准备，预先配置节点信息后，直接安装多节点高可用集群
- 2.先部署单节点集群 [AllinOne部署](quickStart.md)，然后通过 [节点添加](../op/op-index.md) 扩容成高可用集群

## 部署步骤

以下示例创建一个4节点的多主高可用集群，文档中命令默认都需要root权限运行。

### 1.基础系统配置

+ 2c/4g内存/40g硬盘（该配置仅测试用）
+ 最小化安装`Ubuntu 16.04 server`或者`CentOS 7 Minimal`
+ 配置基础网络、更新源、SSH登录等

### 2.在每个节点安装依赖工具

推荐使用ansible in docker 容器化方式运行，无需安装额外依赖。

### 3.准备ssh免密登陆

配置从部署节点能够ssh免密登陆所有节点，并且设置python软连接

``` bash
#$IP为所有节点地址包括自身，按照提示输入yes 和root密码
ssh-copy-id $IP 
```

### 4.在部署节点编排k8s安装

- 4.1 下载项目源码、二进制及离线镜像

下载工具脚本ezdown，举例使用kubeasz版本3.5.0

``` bash
export release=3.5.0
wget https://github.com/easzlab/kubeasz/releases/download/${release}/ezdown
chmod +x ./ezdown
```

下载kubeasz代码、二进制、默认容器镜像（更多关于ezdown的参数，运行./ezdown 查看）

``` bash
# 国内环境
./ezdown -D
# 海外环境
#./ezdown -D -m standard
```

【可选】下载额外容器镜像（cilium,flannel,prometheus等）

``` bash
# 按需下载
./ezdown -X flannel
./ezdown -X prometheus
...
```

【可选】下载离线系统包 (适用于无法使用yum/apt仓库情形)

``` bash
./ezdown -P
```

上述脚本运行成功后，所有文件（kubeasz代码、二进制、离线镜像）均已整理好放入目录`/etc/kubeasz`

- 4.2 创建集群配置实例

``` bash
# 容器化运行kubeasz
./ezdown -S

# 创建新集群 k8s-01
docker exec -it kubeasz ezctl new k8s-01
2021-01-19 10:48:23 DEBUG generate custom cluster files in /etc/kubeasz/clusters/k8s-01
2021-01-19 10:48:23 DEBUG set version of common plugins
2021-01-19 10:48:23 DEBUG cluster k8s-01: files successfully created.
2021-01-19 10:48:23 INFO next steps 1: to config '/etc/kubeasz/clusters/k8s-01/hosts'
2021-01-19 10:48:23 INFO next steps 2: to config '/etc/kubeasz/clusters/k8s-01/config.yml'
```
然后根据提示配置'/etc/kubeasz/clusters/k8s-01/hosts' 和 '/etc/kubeasz/clusters/k8s-01/config.yml'：根据前面节点规划修改hosts 文件和其他集群层面的主要配置选项；其他集群组件等配置项可以在config.yml 文件中修改。

- 4.3 开始安装
如果你对集群安装流程不熟悉，请阅读项目首页 **安装步骤** 讲解后分步安装，并对 **每步都进行验证**  

``` bash
#建议使用alias命令，查看~/.bashrc 文件应该包含：alias dk='docker exec -it kubeasz'
source ~/.bashrc

# 一键安装，等价于执行docker exec -it kubeasz ezctl setup k8s-01 all
dk ezctl setup k8s-01 all

# 或者分步安装，具体使用 dk ezctl help setup 查看分步安装帮助信息
# dk ezctl setup k8s-01 01
# dk ezctl setup k8s-01 02
# dk ezctl setup k8s-01 03
# dk ezctl setup k8s-01 04
...
```

更多ezctl使用帮助，请参考[这里](ezctl.md)

[后一篇](01-CA_and_prerequisite.md)

---

### Setup/01 CA And Prerequisite

# 01-创建证书和环境准备

本步骤主要完成: 

- (deprecated) role:os-harden，（未更新上游项目，未验证最新k8s集群安装，不建议启用）可选系统加固，符合linux安全基线，详见[upstream](https://github.com/dev-sec/ansible-collection-hardening/tree/master/roles/os_hardening)
- (optional) role:chrony，[可选集群节点时间同步](../guide/chrony.md)
- role:deploy，创建CA证书、集群组件访问apiserver所需的各种kubeconfig
- role:prepare，系统基础环境配置、分发CA证书、kubectl客户端安装

## deploy 角色

主要任务讲解：roles/deploy/tasks/main.yml

### 创建 CA 证书

kubernetes 系统各组件需要使用 TLS 证书对通信进行加密，使用 CloudFlare 的 PKI 工具集生成自签名的 CA 证书，用来签名后续创建的其它 TLS 证书。[参考阅读](https://coreos.com/os/docs/latest/generate-self-signed-certificates.html)

根据认证对象可以将证书分成三类：服务器证书`server cert`，客户端证书`client cert`，对等证书`peer cert`(既是`server cert`又是`client cert`)，在kubernetes 集群中需要的证书种类如下：

+ `etcd` 节点需要标识自己服务的`server cert`，也需要`client cert`与`etcd`集群其他节点交互，当然可以分别指定2个证书，为方便这里使用一个对等证书
+ `master` 节点需要标识 apiserver服务的`server cert`，也需要`client cert`连接`etcd`集群，这里也使用一个对等证书
+ `kubectl` `calico` `kube-proxy` 只需要`client cert`，因此证书请求中 `hosts` 字段可以为空
+ `kubelet` 需要标识自己服务的`server cert`，也需要`client cert`请求`apiserver`，也使用一个对等证书

整个集群要使用统一的CA 证书，只需要在ansible控制端创建，然后分发给其他节点；为了保证安装的幂等性，如果已经存在CA 证书，就跳过创建CA 步骤

#### 创建 CA 配置文件 [ca-config.json.j2](../../roles/deploy/templates/ca-config.json.j2)
``` bash
{
  "signing": {
    "default": {
      "expiry": "{{ CERT_EXPIRY }}"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "{{ CERT_EXPIRY }}"
      },
      "kcfg": {
        "usages": [
            "signing",
            "key encipherment",
            "client auth"
        ],
        "expiry": "{{ CUSTOM_EXPIRY }}"
      }
    }
  }
}
```
+ `signing`：表示该证书可用于签名其它证书；生成的 ca.pem 证书中 `CA=TRUE`；
+ `server auth`：表示可以用该 CA 对 server 提供的证书进行验证；
+ `client auth`：表示可以用该 CA 对 client 提供的证书进行验证；
+ `profile kubernetes` 包含了`server auth`和`client auth`，所以可以签发三种不同类型证书；expiry 证书有效期，默认50年
+ `profile kcfg` 在后面客户端kubeconfig证书管理中用到

#### 创建 CA 证书签名请求 [ca-csr.json.j2](../../roles/deploy/templates/ca-csr.json.j2)
``` bash
{
  "CN": "kubernetes-ca",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "HangZhou",
      "L": "XS",
      "O": "k8s",
      "OU": "System"
    }
  ],
  "ca": {
    "expiry": "876000h"
  }
}
```
- `ca expiry` 指定ca证书的有效期，默认100年

#### 生成CA 证书和私钥
``` bash
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
```

### 生成 kubeconfig 配置文件

kubectl使用~/.kube/config 配置文件与kube-apiserver进行交互，且拥有管理 K8S集群的完全权限，

准备kubectl使用的admin 证书签名请求 [admin-csr.json.j2](../../roles/deploy/templates/admin-csr.json.j2)

``` bash
{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "HangZhou",
      "L": "XS",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}

```
+ kubectl 使用客户端证书可以不指定hosts 字段
+ 证书请求中 `O` 指定该证书的 Group 为 `system:masters`，而 `RBAC` 预定义的 `ClusterRoleBinding` 将 Group `system:masters` 与 ClusterRole `cluster-admin` 绑定，这就赋予了kubectl**所有集群权限**

``` bash
$ kubectl describe clusterrolebinding cluster-admin
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:  ClusterRole
  Name:  cluster-admin
Subjects:
  Kind   Name            Namespace
  ----   ----            ---------
  Group  system:masters  
```

#### 生成 admin 用户证书

```
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin
```

#### 生成 ~/.kube/config 配置文件

使用`kubectl config` 生成kubeconfig 自动保存到 ~/.kube/config，生成后 `cat ~/.kube/config`可以验证配置文件包含 kube-apiserver 地址、证书、用户名等信息。

```
kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=127.0.0.1:8443
kubectl config set-credentials admin --client-certificate=admin.pem --embed-certs=true --client-key=admin-key.pem
kubectl config set-context kubernetes --cluster=kubernetes --user=admin
kubectl config use-context kubernetes
```

### 生成 kube-proxy.kubeconfig 配置文件

创建 kube-proxy 证书请求

``` bash
{
  "CN": "system:kube-proxy",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "HangZhou",
      "L": "XS",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
```
+ kube-proxy 使用客户端证书可以不指定hosts 字段
+ CN 指定该证书的 User 为 system:kube-proxy，预定义的 ClusterRoleBinding system:node-proxier 将User system:kube-proxy 与 Role system:node-proxier 绑定，授予了调用 kube-apiserver Proxy 相关 API 的权限；

``` bash
$ kubectl describe clusterrolebinding system:node-proxier
Name:         system:node-proxier
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:  ClusterRole
  Name:  system:node-proxier
Subjects:
  Kind  Name               Namespace
  ----  ----               ---------
  User  system:kube-proxy  
```

#### 生成 system:kube-proxy 用户证书

```
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
```

#### 生成 kube-proxy.kubeconfig

使用`kubectl config` 生成kubeconfig 自动保存到 kube-proxy.kubeconfig

```
kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=127.0.0.1:8443 --kubeconfig=kube-proxy.kubeconfig
kubectl config set-credentials kube-proxy --client-certificate=kube-proxy.pem --embed-certs=true --client-key=kube-proxy-key.pem --kubeconfig=kube-proxy.kubeconfig
kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
```

### 创建kube-controller-manager 和 kube-scheduler 组件的kubeconfig 文件

过程与创建kube-proxy.kubeconfig 类似，略。

## prepare 角色

请在另外窗口打开[roles/prepare/tasks/main.yml](../../roles/prepare/tasks/main.yml) 文件，比较简单直观

1. 设置基础操作系统软件和系统参数，请阅读脚本中的注释内容
1. 创建一些基础文件目录、环境变量以及添加本地镜像仓库`easzlab.io.local`的域名解析
1. 分发kubeconfig等配置文件


[后一篇](02-install_etcd.md)

---

### Setup/02 Install Etcd

## 02-安装etcd集群

kuberntes 集群使用 etcd 存储所有数据，是最重要的组件之一，注意 etcd集群需要奇数个节点(1,3,5...)，本文档使用3个节点做集群。

请在另外窗口打开[roles/etcd/tasks/main.yml](../../roles/etcd/tasks/main.yml) 文件，对照看以下讲解内容。

### 创建etcd证书

注意：证书是在部署节点创建好之后推送到目标etcd节点上去的，以增加ca证书的安全性

创建ectd证书请求 [etcd-csr.json.j2](../../roles/etcd/templates/etcd-csr.json.j2)

``` bash
{
  "CN": "etcd",
  "hosts": [
{% for host in groups['etcd'] %}
    "{{ host }}",
{% endfor %}
    "127.0.0.1"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "HangZhou",
      "L": "XS",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
```
+ etcd使用对等证书，hosts 字段必须指定授权使用该证书的 etcd 节点 IP，这里枚举了所有ectd节点的地址

###  创建etcd 服务文件 [etcd.service.j2](../../roles/etcd/templates/etcd.service.j2)

``` bash
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos

[Service]
Type=notify
WorkingDirectory={{ ETCD_DATA_DIR }}
ExecStart={{ bin_dir }}/etcd \
  --name=etcd-{{ inventory_hostname }} \
  --cert-file={{ ca_dir }}/etcd.pem \
  --key-file={{ ca_dir }}/etcd-key.pem \
  --peer-cert-file={{ ca_dir }}/etcd.pem \
  --peer-key-file={{ ca_dir }}/etcd-key.pem \
  --trusted-ca-file={{ ca_dir }}/ca.pem \
  --peer-trusted-ca-file={{ ca_dir }}/ca.pem \
  --initial-advertise-peer-urls=https://{{ inventory_hostname }}:2380 \
  --listen-peer-urls=https://{{ inventory_hostname }}:2380 \
  --listen-client-urls=https://{{ inventory_hostname }}:2379,http://127.0.0.1:2379 \
  --advertise-client-urls=https://{{ inventory_hostname }}:2379 \
  --initial-cluster-token=etcd-cluster-0 \
  --initial-cluster={{ ETCD_NODES }} \
  --initial-cluster-state={{ CLUSTER_STATE }} \
  --data-dir={{ ETCD_DATA_DIR }} \
  --wal-dir={{ ETCD_WAL_DIR }} \
  --snapshot-count=50000 \
  --auto-compaction-retention=1 \
  --auto-compaction-mode=periodic \
  --max-request-bytes=10485760 \
  --quota-backend-bytes=8589934592
Restart=always
RestartSec=15
LimitNOFILE=65536
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target
```

+ 完整参数列表请使用 `etcd --help` 查询
+ 注意etcd 即需要服务器证书也需要客户端证书，为方便使用一个peer 证书代替两个证书
+ `--initial-cluster-state` 值为 `new` 时，`--name` 的参数值必须位于 `--initial-cluster` 列表中
+ `--snapshot-count` `--auto-compaction-retention` 一些性能优化参数，请查阅etcd项目文档
+ 设置`--data-dir` 和`--wal-dir` 使用不同磁盘目录，可以避免磁盘io竞争，提高性能，具体请参考etcd项目文档

### 验证etcd集群状态

+ systemctl status etcd 查看服务状态
+ journalctl -u etcd 查看运行日志
+ 在任一 etcd 集群节点上执行如下命令

```
/* Detailed source-code truncated for AI context efficiency. */
```

- 所有节点可达：etcdctl endpoint health 对所有三个节点都返回 healthy。
- 有且仅有一个领导者：etcdctl endpoint status 显示一个节点 is leader: true，另外两个节点 is leader: false。
- Raft 任期一致：所有三个节点的 raft term 值完全相同。
- Raft 索引同步：所有节点的 raft index 值相差不大（跟随者与领导者的差距在可接受范围内）。
- 无活跃告警：etcdctl alarm list 返回空。
- 节点间网络稳定：没有频繁的领导者切换（通过监控 etcd_server_leader_changes_seen_total 指标）。
- 磁盘空间充足：没有 NOSPACE 告警，且磁盘使用率在安全阈值内（例如低于80%）。

### 磁盘性能

快速的磁盘是 etcd 部署性能和稳定性的最关键因素。

磁盘速度慢会增加 etcd 请求延迟，并可能损害集群稳定性。由于 etcd 的共识协议依赖于将元数据持久地存储到日志中，因此大多数 etcd 集群成员必须将每个请求写入磁盘。此外，etcd 还会逐步将其状态检查点写入磁盘，以便截断此日志。如果这些写入耗时过长，心跳可能会超时并触发选举，从而损害集群的稳定性。通常，要判断磁盘速度是否足以满足 etcd 的要求，可以使用fio等基准测试工具。

etcd 对磁盘写入延迟非常敏感。通常需要 50 的顺序 IOPS（例如，7200 RPM 磁盘）。对于负载较重的集群，建议使用 500 的顺序 IOPS（例如，典型的本地 SSD 或高性能虚拟化块设备）。请注意，大多数云提供商发布的是并发 IOPS，而不是顺序 IOPS；发布的并发 IOPS 可能比顺序 IOPS 高出 10 倍。要测量实际的顺序 IOPS，我们建议使用磁盘基准测试工具，例如diskbench或fio。

``` bash
# 测试示例
mkdir test-data
fio --rw=write --ioengine=sync --fdatasync=1 --directory=test-data --size=2200m --bs=2300 --name=mytest
```



[后一篇](03-container_runtime.md)

---

### Setup/03 Container Runtime

# 03-安装容器运行时

项目根据k8s版本提供不同的默认容器运行时：

- k8s 版本 < 1.24 时，支持docker containerd 可选
- k8s 版本 >= 1.24 时，仅支持 containerd

## 安装containerd

作为 CNCF 毕业项目，containerd 致力于提供简洁、可靠、可扩展的容器运行时；它被设计用来集成到 kubernetes 等系统使用，而不是像 docker 那样独立使用。

- 安装指南 https://github.com/containerd/cri/blob/master/docs/installation.md
- 客户端 circtl 使用指南 https://github.com/containerd/cri/blob/master/docs/crictl.md
- man 文档 https://github.com/containerd/containerd/tree/master/docs/man

## kubeasz 集成安装 containerd

- 注意：k8s 1.24以后，项目已经设置默认容器运行时为 containerd，无需手动修改
- 执行安装：分步安装`ezctl setup xxxx 03`，一键安装`ezctl setup xxxx all`

## 命令对比

|命令           |docker         |crictl（推荐） |ctr                    |
|:-             |:-             |:-             |:-                     |
|查看容器列表   |docker ps      |crictl ps      |ctr -n k8s.io c ls     |
|查看容器详情   |docker inspect |crictl inspect |ctr -n k8s.io c info   |
|查看容器日志   |docker logs    |crictl logs    |无                     |
|容器内执行命令 |docker exec    |crictl exec    |无                     |
|挂载容器       |docker attach  |crictl attach  |无                     |
|容器资源使用   |docker stats   |crictl stats   |无                     |
|创建容器       |docker create  |crictl create  |ctr -n k8s.io c create |
|启动容器       |docker start   |crictl start   |ctr -n k8s.io run      |
|停止容器       |docker stop    |crictl stop    |无                     |
|删除容器       |docker rm      |crictl rm      |ctr -n k8s.io c del    |
|查看镜像列表   |docker images  |crictl images  |ctr -n k8s.io i ls     |
|查看镜像详情   |docker inspect |crictl inspecti|无                     |
|拉取镜像       |docker pull    |crictl pull    |ctr -n k8s.io i pull   |
|推送镜像       |docker push    |无             |ctr -n k8s.io i push   |
|删除镜像       |docker rmi     |crictl rmi     |ctr -n k8s.io i rm     |
|查看Pod列表    |无             |crictl pods    |无                     |
|查看Pod详情    |无             |crictl inspectp|无                     |
|启动Pod        |无             |crictl runp    |无                     |
|停止Pod        |无             |crictl stopp   |无                     |


[后一篇](04-install_kube_master.md)

---

### Setup/04 Install Kube Master

# 04-安装kube_master节点

部署master节点主要包含三个组件`apiserver` `scheduler` `controller-manager`，其中：

- apiserver提供集群管理的REST API接口，包括认证授权、数据校验以及集群状态变更等
  - 只有API Server才直接操作etcd
  - 其他模块通过API Server查询或修改数据
  - 提供其他模块之间的数据交互和通信的枢纽
- scheduler负责分配调度Pod到集群内的node节点
  - 监听kube-apiserver，查询还未分配Node的Pod
  - 根据调度策略为这些Pod分配节点
- controller-manager由一系列的控制器组成，它通过apiserver监控整个集群的状态，并确保集群处于预期的工作状态

## 高可用机制

- apiserver 无状态服务，可以通过外部负载均衡实现高可用，如项目采用的两种高可用架构：HA-1x (#584)和 HA-2x (#585)
- controller-manager 组件启动时会进行类似选举（leader）；当多副本存在时，如果原先leader挂掉，那么会选举出新的leader，从而保证高可用；
- scheduler 类似选举机制

## 安装流程

``` bash
cat playbooks/04.kube-master.yml
- hosts: kube_master
  roles:
  - kube-lb        # 四层负载均衡，监听在127.0.0.1:6443，转发到真实master节点apiserver服务
  - kube-master    #
  - kube-node      # 因为网络、监控等daemonset组件，master节点也推荐安装kubelet和kube-proxy服务
  ... 
```

### 创建 kubernetes 证书签名请求

``` bash
{
  "CN": "kubernetes",
  "hosts": [
    "127.0.0.1",
{% if groups['ex_lb']|length > 0 %}
    "{{ hostvars[groups['ex_lb'][0]]['EX_APISERVER_VIP'] }}",
{% endif %}
{% for host in groups['kube_master'] %}
    "{{ host }}",
{% endfor %}
    "{{ CLUSTER_KUBERNETES_SVC_IP }}",
{% for host in MASTER_CERT_HOSTS %}
    "{{ host }}",
{% endfor %}
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster",
    "kubernetes.default.svc.cluster.local"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "HangZhou",
      "L": "XS",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
```
kubernetes apiserver 使用对等证书，创建时hosts字段需要配置：
- 如果配置 ex_lb，需要把 EX_APISERVER_VIP 也配置进去
- 如果需要外部访问 apiserver，可选在config.yml配置 MASTER_CERT_HOSTS
- `kubectl get svc` 将看到集群中由api-server 创建的默认服务 `kubernetes`，因此也要把 `kubernetes` 服务名和各个服务域名也添加进去

### 创建apiserver的服务配置文件

```
/* Detailed source-code truncated for AI context efficiency. */
```
+ Kubernetes 对 API 访问需要依次经过认证、授权和准入控制(admission controll)，认证解决用户是谁的问题，授权解决用户能做什么的问题，Admission Control则是资源管理方面的作用。
+ 关于authorization-mode=Node,RBAC v1.7+支持Node授权，配合NodeRestriction准入控制来限制kubelet仅可访问node、endpoint、pod、service以及secret、configmap、PV和PVC等相关的资源；需要注意的是v1.7中Node 授权是默认开启的，v1.8中需要显式配置开启，否则 Node无法正常工作
+ 详细参数配置请参考`kube-apiserver --help`，关于认证、授权和准入控制请[阅读](https://github.com/feiskyer/kubernetes-handbook/blob/master/components/apiserver.md)
+ 增加了访问kubelet使用的证书配置，防止匿名访问kubelet的安全漏洞，详见[漏洞说明](../mixes/01.fix_kubelet_annoymous_access.md)

### 创建controller-manager 的服务文件

``` bash
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
ExecStart={{ bin_dir }}/kube-controller-manager \
  --allocate-node-cidrs=true \
  --authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
  --authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
  --bind-address=0.0.0.0 \
  --cluster-cidr={{ CLUSTER_CIDR }} \
  --cluster-name=kubernetes \
  --cluster-signing-cert-file={{ ca_dir }}/ca.pem \
  --cluster-signing-key-file={{ ca_dir }}/ca-key.pem \
  --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
  --leader-elect=true \
  --node-cidr-mask-size={{ NODE_CIDR_LEN }} \
  --root-ca-file={{ ca_dir }}/ca.pem \
  --service-account-private-key-file={{ ca_dir }}/ca-key.pem \
  --service-cluster-ip-range={{ SERVICE_CIDR }} \
  --use-service-account-credentials=true \
  --v=2
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```
+ --cluster-cidr 指定 Cluster 中 Pod 的 CIDR 范围，该网段在各 Node 间必须路由可达(flannel/calico 等网络插件实现)
+ --service-cluster-ip-range 参数指定 Cluster 中 Service 的CIDR范围，必须和 kube-apiserver 中的参数一致
+ --cluster-signing-* 指定的证书和私钥文件用来签名为 TLS BootStrap 创建的证书和私钥
+ --root-ca-file 用来对 kube-apiserver 证书进行校验，指定该参数后，才会在Pod 容器的 ServiceAccount 中放置该 CA 证书文件
+ --leader-elect=true 使用多节点选主的方式选择主节点。只有主节点才会启动所有控制器，而其他从节点则仅执行选主算法

### 创建scheduler 的服务文件

``` bash
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
ExecStart={{ bin_dir }}/kube-scheduler \
  --authentication-kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
  --authorization-kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
  --bind-address=0.0.0.0 \
  --kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
  --leader-elect=true \
  --v=2
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

+ --leader-elect=true 部署多台机器组成的 master 集群时选举产生一个处于工作状态的 kube-controller-manager 进程

### 在master 节点安装 node 服务: kubelet kube-proxy 

项目master 分支使用 DaemonSet 方式安装网络插件，如果master 节点不安装 kubelet 服务是无法安装网络插件的，如果 master 节点不安装网络插件，那么通过`apiserver` 方式无法访问 `dashboard` `kibana`等管理界面，[ISSUES #130](https://github.com/easzlab/kubeasz/issues/130)

在master 节点也同时成为 node 节点后，默认业务 POD也会调度到 master节点；可以使用 `kubectl cordon`命令禁止业务 POD调度到 master节点。

### master 集群的验证

运行 `ansible-playbook 04.kube-master.yml` 成功后，验证 master节点的主要组件：

``` bash
# 查看进程状态
systemctl status kube-apiserver
systemctl status kube-controller-manager
systemctl status kube-scheduler
# 查看进程运行日志
journalctl -u kube-apiserver
journalctl -u kube-controller-manager
journalctl -u kube-scheduler
```
执行 `kubectl get componentstatus` 可以看到

``` bash
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok                   
controller-manager   Healthy   ok                   
etcd-0               Healthy   {"health": "true"}   
etcd-2               Healthy   {"health": "true"}   
etcd-1               Healthy   {"health": "true"} 
```

[后一篇](05-install_kube_node.md)

---

### Setup/05 Install Kube Node

## 05-安装kube_node节点

`kube_node` 是集群中运行工作负载的节点，前置条件需要先部署好`kube_master`节点，它需要部署如下组件：

``` bash
cat playbooks/05.kube-node.yml
- hosts: kube_node
  roles:
  - { role: kube-lb, when: "inventory_hostname not in groups['kube_master']" }
  - { role: kube-node, when: "inventory_hostname not in groups['kube_master']" }
```

+ kube-lb：由nginx裁剪编译的四层负载均衡，用于将请求转发到主节点的 apiserver服务
+ kubelet：kube_node上最主要的组件
+ kube-proxy： 发布应用服务与负载均衡

### 创建cni 基础网络插件配置文件

因为后续需要用 `DaemonSet Pod`方式运行k8s网络插件，所以kubelet.server服务必须开启cni相关参数，并且提供cni网络配置文件

### 创建 kubelet 的服务文件

+ 根据官方建议独立使用 kubelet 配置文件，详见roles/kube-node/templates/kubelet-config.yaml.j2
+ 必须先创建工作目录 `/var/lib/kubelet`

```
/* Detailed source-code truncated for AI context efficiency. */
```
+ --ExecStartPre=/bin/mkdir -p xxx 对于某些系统（centos7）cpuset和hugetlb 是默认没有初始化system.slice 的，需要手动创建，否则在启用--kube-reserved-cgroup 时会报错Failed to start ContainerManager Failed to enforce System Reserved Cgroup Limits
+ 关于kubelet资源预留相关配置请参考 https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/

### 创建 kube-proxy kubeconfig 文件

该步骤已经在 deploy节点完成，[roles/deploy/tasks/main.yml](../../roles/deploy/tasks/main.yml)

+ 生成的kube-proxy.kubeconfig 配置文件需要移动到/etc/kubernetes/目录，后续kube-proxy服务启动参数里面需要指定

### 创建 kube-proxy服务文件

``` bash
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart={{ bin_dir }}/kube-proxy \
  --config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=always
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
```

请注意 [kube-proxy-config](../../roles/kube-node/templates/kube-proxy-config.yaml.j2) 文件的注释说明

### 验证 node 状态

``` bash
systemctl status kubelet	# 查看状态
systemctl status kube-proxy
journalctl -u kubelet		# 查看日志
journalctl -u kube-proxy 
```
运行 `kubectl get node` 可以看到类似

``` bash
NAME           STATUS    ROLES     AGE       VERSION
192.168.1.42   Ready     <none>    2d        v1.9.0
192.168.1.43   Ready     <none>    2d        v1.9.0
192.168.1.44   Ready     <none>    2d        v1.9.0
```


[后一篇](06-install_network_plugin.md)

---

### Setup/06 Install Network Plugin

## 06-安装网络组件

首先回顾下K8S网络设计原则，在配置集群网络插件或者实践K8S 应用/服务部署请牢记这些原则：

- 1.每个Pod都拥有一个独立IP地址，Pod内所有容器共享一个网络命名空间
- 2.集群内所有Pod都在一个直接连通的扁平网络中，可通过IP直接访问
  - 所有容器之间无需NAT就可以直接互相访问
  - 所有Node和所有容器之间无需NAT就可以直接互相访问
  - 容器自己看到的IP跟其他容器看到的一样
- 3.Service cluster IP只可在集群内部访问，外部请求需要通过NodePort、LoadBalance或者Ingress来访问

`Container Network Interface (CNI)`是目前CNCF主推的网络模型，它由两部分组成：

- CNI Plugin负责给容器配置网络，它包括两个基本的接口
  - 配置网络: AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
  - 清理网络: DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
- IPAM Plugin负责给容器分配IP地址

Kubernetes Pod的网络是这样创建的：
- 0. 每个Pod除了创建时指定的容器外，都有一个kubelet启动时指定的`基础容器`，即`pause`容器 
- 1. kubelet创建`基础容器`生成network namespace
- 2. kubelet调用网络CNI driver，由它根据配置调用具体的CNI 插件
- 3. CNI 插件给`基础容器`配置网络
- 4. Pod 中其他的容器共享使用`基础容器`的网络

本项目基于CNI driver 调用各种网络插件来配置kubernetes的网络，常用CNI插件有 `flannel` `calico` `cilium`等等，这些插件各有优势，也在互相借鉴学习优点，比如：在所有node节点都在一个二层网络时候，flannel提供hostgw实现，避免vxlan实现的udp封装开销，估计是目前最高效的；calico也针对L3 Fabric，推出了IPinIP的选项，利用了GRE隧道封装；因此这些插件都能适合很多实际应用场景。

项目当前内置支持的网络插件有：`calico` `cilium` `flannel` `kube-ovn` `kube-router`

### 安装讲解

- [安装calico](network-plugin/calico.md)
- [安装cilium](network-plugin/cilium.md)  
- [安装flannel](network-plugin/flannel.md)
- [安装kube-ovn](network-plugin/kube-ovn.md) 暂未更新
- [安装kube-router](network-plugin/kube-router.md) 暂未更新

### 参考
- [kubernetes.io networking docs](https://kubernetes.io/docs/concepts/cluster-administration/networking/) 
- [feiskyer-kubernetes指南网络章节](https://github.com/feiskyer/kubernetes-handbook/blob/master/zh/network/network.md)


[后一篇](07-install_cluster_addon.md)

---

### Setup/07 Install Cluster Addon

# 07-安装集群主要插件

目前挑选一些常用、必要的插件自动集成到安装脚本之中:  

## 集群默认安装

- [coredns](../guide/kubedns.md)
- [nodelocaldns](../guide/kubedns.md)
- [metrics-server](../guide/metrics-server.md)

kubeasz 默认安装上述基础插件，并支持离线方式安装(./ezdown -D 命令会自动下载组件镜像，并推送到本地镜像仓库easzlab.io.local:5000)

## 集群可选安装

- [headlamp](../guide/headlamp.md)
- [prometheus](../guide/prometheus.md)
- [network_check](network-plugin/network-check.md)
- [nfs_provisioner]()

Headlamp 请按使用说明通过 Helm 手动安装。其余可选插件默认不安装，可以在配置文件(clusters/xxx/config.yml)中开启；`./ezdown -X` 会下载对应的额外组件镜像，并推送到本地镜像仓库 `easzlab.io.local:5000`。

## 安装脚本

详见`roles/cluster-addon/` 目录

- 1.根据hosts文件中配置的`CLUSTER_DNS_SVC_IP` `CLUSTER_DNS_DOMAIN`等参数生成kubedns.yaml和coredns.yaml文件
- 2.注册变量pod_info，pod_info用来判断现有集群是否已经运行各种插件
- 3.根据pod_info和`配置开关`逐个进行/跳过插件安装

## 下一步

- [创建ex_lb节点组](ex-lb.md), 向集群外提供高可用apiserver
- [创建集群持久化存储](08-cluster-storage.md)

---

### Setup/08 Cluster Storage

# K8S 集群存储 

## 前言
在kubernetes(k8s)中对于存储的资源抽象了两个概念，分别是PersistentVolume(PV)、PersistentVolumeClaim(PVC)。
- PV是集群中的资源
- PVC是对这些资源的请求。

如上面所说PV和PVC都只是抽象的概念，在k8s中是通过插件的方式提供具体的存储实现。目前包含有NFS、iSCSI和云提供商指定的存储系统，更多的存储实现[参考官方文档](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes)。

以下介绍两种`provisioner`, 可以提供静态或者动态的PV

- [nfs-provisioner](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner): NFS存储目录供应者
- [local-path-provisioner](https://github.com/rancher/local-path-provisioner): 本地存储目录供应者

## NFS存储目录供应者

首先我们需要一个NFS服务器，用于提供底层存储。通过文档[nfs-server](../guide/nfs-server.md)，我们可以创建一个NFS服务器。

### 静态 PV
- 创建静态 pv，指定容量，访问模式，回收策略，存储类等

``` bash
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-es-0
spec:
  capacity:
    storage: 4Gi
  accessModes:
    - ReadWriteMany
  volumeMode: Filesystem
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: "es-storage-class"
  nfs:
    # 根据实际共享目录修改
    path: /share/es0
    # 根据实际 nfs服务器地址修改
    server: 192.168.1.208
```
- 创建 pvc即可绑定使用上述 pv了，具体请看后文 test pod例子

### 创建动态PV

在一个工作k8s 集群中，`PVC`请求会很多，如果每次都需要管理员手动去创建对应的 `PV`资源，那就很不方便；因此 K8S还提供了多种 `provisioner`来动态创建 `PV`，不仅节省了管理员的时间，还可以根据`StorageClasses`封装不同类型的存储供 PVC 选用。

项目中以nfs-client-provisioner为例 https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

- 1.编辑集群配置文件：clusters/${集群名}/config.yml

``` bash
... 省略
# 在role:cluster-addon 中启用nfs-provisioner 安装
nfs_provisioner_install: "yes"			# 修改为yes
nfs_provisioner_namespace: "kube-system"
nfs_provisioner_ver: "v4.0.1"
nfs_storage_class: "managed-nfs-storage"	
nfs_server: "192.168.31.244"			# 修改为实际nfs server地址
nfs_path: "/data/nfs"				# 修改为实际的nfs共享目录

```

- 2.创建 nfs provisioner

``` bash
$ dk ezctl setup ${集群名} 07 

# 执行成功后验证
$ kubectl get pod --all-namespaces |grep nfs-client
kube-system   nfs-client-provisioner-84ff87c669-ksw95      1/1     Running     0          21m
```

- 3.验证使用动态 PV

在目录clusters/${集群名}/yml/nfs-provisioner/ 有个测试例子

``` bash
$ kubectl apply -f /etc/kubeasz/clusters/hello/yml/nfs-provisioner/test-pod.yaml

# 验证测试pod
kubectl get pod
NAME       READY   STATUS      RESTARTS   AGE
test-pod   0/1     Completed   0          6h36m

# 验证自动创建的pv 资源，
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS          REASON   AGE
pvc-44d34a50-e00b-4f6c-8005-40f5cc54af18   2Mi        RWX            Delete           Bound    default/test-claim   managed-nfs-storage            6h36m

# 验证PVC已经绑定成功：STATUS字段为 Bound
kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
test-claim   Bound    pvc-44d34a50-e00b-4f6c-8005-40f5cc54af18   2Mi        RWX            managed-nfs-storage   6h37m
```

另外，Pod启动完成后，在挂载的目录中创建一个`SUCCESS`文件。我们可以到NFS服务器去看下：

```
.
└── default-test-claim-pvc-44d34a50-e00b-4f6c-8005-40f5cc54af18
    └── SUCCESS
```
如上，可以发现挂载的时候，nfs-client根据PVC自动创建了一个目录，我们Pod中挂载的`/mnt`，实际引用的就是该目录，而我们在`/mnt`下创建的`SUCCESS`文件，也自动写入到了这里。

后面当我们需要为上层应用提供持久化存储时，只需要提供`StorageClass`即可。很多应用都会根据`StorageClass`来创建他们的所需的PVC, 最后再把PVC挂载到他们的Deployment或StatefulSet中使用，比如：efk、jenkins等

## 本地存储目录供应者

当应用对于磁盘I/O性能要求高，比较适合本地文件目录存储，特别地可以本地挂载SSD磁盘（注意本地磁盘需要配置raid冗余策略）。Local Path Provisioner 可以方便地在k8s集群中使用本地文件目录存储。

在kubeasz项目中集成安装

- 1.编辑集群配置文件：clusters/${集群名}/config.yml

``` bash
... 省略
local_path_provisioner_install: "yes" # 修改为yes
# 设置默认本地存储路径
local_path_provisioner_dir: "/opt/local-path-provisioner"
```

- 2.创建 local path provisioner

``` bash
$ dk ezctl setup ${集群名} 07

# 执行成功后验证
$ kubectl get pod --all-namespaces |grep provisioner
```

- 3.验证使用（略）

---

### Setup/Config Guide

# 个性化集群参数配置

`kubeasz`创建集群主要在以下两个地方进行配置：(假设集群名xxxx)

- clusters/xxxx/hosts 文件（模板在example/hosts.multi-node）：集群主要节点定义和主要参数配置、全局变量
- clusters/xxxx/config.yml（模板在examples/config.yml）：其他参数配置或者部分组件附加参数

## clusters/xxxx/hosts (ansible hosts)

如[集群规划与安装概览](00-planning_and_overall_intro.md)中介绍，主要包括集群节点定义和集群范围的主要参数配置

- 尽量保持配置简单灵活
- 尽量保持配置项稳定

常用设置项：

- 修改容器运行时: CONTAINER_RUNTIME="containerd"
- 修改集群网络插件：CLUSTER_NETWORK="calico"
- 修改容器网络地址：CLUSTER_CIDR="192.168.0.0/16"
- 修改NodePort范围：NODE_PORT_RANGE="30000-32767"

## clusters/xxxx/config.yml

主要包括集群某个具体组件的个性化配置，具体组件的配置项可能会不断增加；可以在不做任何配置更改情况下使用默认值创建集群

根据实际需要配置 k8s 集群，常用举例

- 配置使用离线安装系统包：INSTALL_SOURCE: "offline" （需要ezdown -P 下载离线系统软件）
- 配置CA证书以及其签发证书的有效期
- 配置 apiserver 支持公网域名：MASTER_CERT_HOSTS
- 配置 cluster-addon 组件安装
- ...

---

### Setup/Ex Lb

## EX-LB 负载均衡部署

根据[HA 2x架构](00-planning_and_overall_intro.md)，k8s集群自身高可用已经不依赖于外部 lb 服务；但是有时我们要从外部访问 apiserver（比如 CI 流程），就需要 ex_lb 来请求多个 apiserver；

还有一种情况是需要[负载转发到ingress服务](../op/loadballance_ingress_nodeport.md)，也需要部署ex_lb；

**注意：当遇到公有云环境无法自建 ex_lb 服务时，可以配置对应的云负载均衡服务**

### ex_lb 服务组件

更新：kubeasz 3.0.2 重写了ex-lb服务安装，利用最小化依赖编译安装的二进制文件，不依赖于linux发行版；优点是可以统一版本和简化离线安装部署，并且理论上能够支持更多linux发行版


ex_lb 服务由 keepalived 和 l4lb 组成：
- l4lb：是一个精简版（仅支持四层转发）的nginx编译二进制版本
- keepalived：利用主备节点vrrp协议通信和虚拟地址，消除l4lb的单点故障；keepalived保持存活，它是基于VRRP协议保证所谓的高可用或热备的，这里用来预防l4lb的单点故障。

keepalived与l4lb配合，实现master的高可用过程如下：

+ 1.keepalived利用vrrp协议生成一个虚拟地址(VIP)，正常情况下VIP存活在keepalive的主节点，当主节点故障时，VIP能够漂移到keepalived的备节点，保障VIP地址高可用性。
+ 2.在keepalived的主备节点都配置相同l4lb负载配置，并且监听客户端请求在VIP的地址上，保障随时都有一个l4lb负载均衡在正常工作。并且keepalived启用对l4lb进程的存活检测，一旦主节点l4lb进程故障，VIP也能切换到备节点，从而让备节点的l4lb进行负载工作。
+ 3.在l4lb的配置中配置多个后端真实kube-apiserver的endpoints，并启用存活监测后端kube-apiserver，如果一个kube-apiserver故障，l4lb会将其剔除负载池。

#### 安装l4lb

#### 配置l4lb (roles/ex-lb/templates/l4lb.conf.j2)

配置由全局配置和三个upstream servers配置组成：
- apiservers 用于转发至多个apiserver
- ingress-nodes 用于转发至node节点的ingress http服务，[参阅](../op/loadballance_ingress_nodeport.md)
- ingress-tls-nodes 用于转发至node节点的ingress https服务

#### 安装keepalived

#### 配置keepalived主节点 [keepalived-master.conf.j2](../../roles/ex-lb/templates/keepalived-master.conf.j2)

``` bash
global_defs {
}

vrrp_track_process check-l4lb {
    process l4lb
    weight -60
    delay 3
}

vrrp_instance VI-01 {
    state MASTER
    priority 120
    unicast_src_ip {{ inventory_hostname }}
    unicast_peer {
{% for h in groups['ex_lb'] %}{% if h != inventory_hostname %}
        {{ h }}
{% endif %}{% endfor %}
    }
    dont_track_primary
    interface {{ LB_IF }}
    virtual_router_id {{ ROUTER_ID }}
    advert_int 3
    track_process {
        check-l4lb
    }
    virtual_ipaddress {
        {{ EX_APISERVER_VIP }}
    }
}
```
+ vrrp_track_process 定义了监测l4lb进程是否存活，如果进程不存在，根据`weight -60`设置将主节点优先级降低60，这样原先备节点将变成主节点。
+ vrrp_instance 定义了vrrp组，包括优先级、使用端口、router_id、心跳频率、检测脚本、虚拟地址VIP等
+ 特别注意 `virtual_router_id` 标识了一个 VRRP组，在同网段下必须唯一，否则出现 `Keepalived_vrrp: bogus VRRP packet received on eth0 !!!`类似报错
+ 配置 vrrp 协议通过单播发送

#### 配置keepalived备节点 [keepalived-backup.conf.j2](../../roles/ex-lb/templates/keepalived-backup.conf.j2)

+ 备节点的配置类似主节点，除了优先级和检测脚本，其他如 `virtual_router_id` `advert_int` `virtual_ipaddress`必须与主节点一致

### 启动 keepalived 和 l4lb 后验证

+ lb 节点验证

``` bash
systemctl status l4lb 	# 检查进程状态
journalctl -u l4lb		# 检查进程日志是否有报错信息
systemctl status keepalived 	# 检查进程状态
journalctl -u keepalived	# 检查进程日志是否有报错信息
```
+ 在 keepalived 主节点

``` bash
ip a				# 检查 master的 VIP地址是否存在
```
### keepalived 主备切换演练

1. 尝试关闭 keepalived主节点上的 l4lb进程，然后在keepalived 备节点上查看 master的 VIP地址是否能够漂移过来，并依次检查上一步中的验证项。
1. 尝试直接关闭 keepalived 主节点系统，检查各验证项。

---

### Setup/Ezctl

# ezctl 命令行介绍

## 为什么使用 ezctl

kubeasz 项目使用ezctl 方便地创建和管理多个k8s 集群，ezctl 使用shell 脚本封装ansible-playbook 执行命令，它十分轻量、简单和易于扩展。

### 使用帮助

随时运行 ezctl 获取命令行提示信息，如下

```
/* Detailed source-code truncated for AI context efficiency. */
```

- 命令集 1：集群安装相关操作
  - 显示当前所有管理的集群
  - 切换默认集群
  - 创建新集群配置
  - 安装新集群
  - 启动临时停止的集群
  - 临时停止某个集群（包括集群内运行的pod）
  - 升级集群k8s组件版本
  - 删除集群
  - 备份集群（仅etcd数据，不包括pv数据和业务应用数据）
  - 从备份中恢复集群
  - 创建单机集群（类似 minikube）
- 命令集 2：集群节点操作
  - 增加 etcd 节点
  - 增加主节点
  - 增加工作节点
  - 删除 etcd 节点
  - 删除主节点
  - 删除工作节点
- 命令集3：额外操作
  - 管理客户端kubeconfig

#### 举例创建、安装新集群流程

- 1.首先创建集群配置实例 

``` bash
~# ezctl new k8s-01
2021-01-19 10:48:23 DEBUG generate custom cluster files in /etc/kubeasz/clusters/k8s-01
2021-01-19 10:48:23 DEBUG set version of common plugins
2021-01-19 10:48:23 DEBUG cluster k8s-01: files successfully created.
2021-01-19 10:48:23 INFO next steps 1: to config '/etc/kubeasz/clusters/k8s-01/hosts'
2021-01-19 10:48:23 INFO next steps 2: to config '/etc/kubeasz/clusters/k8s-01/config.yml'
```
然后根据提示配置'/etc/kubeasz/clusters/k8s-01/hosts' 和 '/etc/kubeasz/clusters/k8s-01/config.yml'；为方便测试我们在hosts里面设置单节点集群（etcd/kube_master/kube_node配置同一个节点，注意节点需先设置ssh免密码登陆）, config.yml 使用默认配置即可。

- 2.然后开始安装集群

``` bash
# 一键安装
ezctl setup k8s-01 all

# 或者分步安装，具体使用 ezctl help setup 查看分步安装帮助信息
# ezctl setup k8s-01 01
# ezctl setup k8s-01 02
# ezctl setup k8s-01 03
# ezctl setup k8s-01 04
... 
```

- 3.重复步骤1，2可以创建、管理多个k8s集群（建议ezctl使用独立的部署节点）

ezctl 创建管理的多集群拓扑如下

```
+----------------+               +-----------------+
|ezctl 1.1.1.1   |               |cluster-aio:     |
+--+---+---+-----+               |                 |
   |   |   |                     |master 4.4.4.4   |
   |   |   +-------------------->+etcd   4.4.4.4   |
   |   |                         |node   4.4.4.4   |
   |   +--------------+          +-----------------+
   |                  |
   v                  v
+--+------------+ +---+----------------------------+
| cluster-1:    | | cluster-2:                     |
|               | |                                |
| master 2.2.2.1| | master 3.3.3.1/3.3.3.2         |
| etcd   2.2.2.2| | etcd   3.3.3.1/3.3.3.2/3.3.3.3 |
| node   2.2.2.3| | node   3.3.3.4/3.3.3.5/3.3.3.6 |
+---------------+ +--------------------------------+
```

That's it! 赶紧动手测试吧，欢迎通过 Issues 和 PRs 反馈您的意见和建议！

---

### Setup/Kubeasz On Public Cloud

# 公有云上部署 kubeasz

在公有云上使用`kubeasz`部署`k8s`集群需要注意以下几个常见问题。

### 安全组

注意虚机的安全组规则配置，一般集群内部节点之间端口全部放开即可；

### 网络组件

一般公有云对网络限制较多，跨节点 pod 通讯需要使用 OVERLAY 添加报头；默认配置详见example/config.yml

- flannel 使用 vxlan 模式：`FLANNEL_BACKEND: "vxlan"`
- calico 开启 ipinip：`CALICO_IPV4POOL_IPIP: "Always"`
- kube-router 开启 ipinip：`OVERLAY_TYPE: "full"`

### 节点公网访问

可以在安装时每个节点绑定`弹性公网地址`(EIP)，装完集群解绑；也可以开通NAT网关，或者利用iptables自建上网网关等方式

### 负载均衡

一般云厂商会限制使用`keepalived+haproxy`自建负载均衡，你可以根据云厂商文档使用云负载均衡（内网）四层TCP负载模式；

- kubeasz 2x 版本已无需依赖外部负载均衡实现apiserver的高可用，详见 [2x架构](https://github.com/easzlab/kubeasz/blob/dev2/docs/setup/00-planning_and_overall_intro.md#ha-architecture)
- kubeasz 1x 及以前版本需要负载均衡实现apiserver高可用，详见 [1x架构](https://github.com/easzlab/kubeasz/blob/dev1/docs/setup/00-planning_and_overall_intro.md#ha-architecture)

### 时间同步

一般云厂商提供的虚机都已默认安装时间同步服务，无需自行安装。 

### 访问 APISERVER

在公有云上安装完集群后，需要在公网访问集群 apiserver，而我们在安装前可能没有规划公网IP或者公网域名；而 apiserver 肯定需要 https 方式访问，在证书创建时需要加入公网ip/域名；可以参考这里[修改 APISERVER（MASTER）证书](../op/ch_apiserver_cert.md)

## 在公有云上部署多主高可用集群

处理好以上讨论的常见问题后，在公有云上使用 kubeasz 安装集群与自有环境没有差异。

- 使用 kubeasz 2x 版本安装单节点、单主多节点、多主多节点 k8s 集群，云上云下的预期安装体验完全一致

---

### Setup/Mix Arch

# 混合架构集群部署

混合架构集群本文是指集群中既有linux amd64架构机器，也有linux arm64架构机器；这里只记录一个简单的操作说明，实际操作注意风险。

## 部署思路

1. 先选定一台amd64架构的机器做“amd64部署机”，使用它先部署amd64架构的集群
2. 选一台arm64架构的机器做“arm64部署机”，复制amd64部署机的/etc/kubeasz目录文件（除去目录中的bin、down子目录），然后重新下载arm64架构的二进制和镜像，然后添加arm64节点到原有集群即可

## 操作步骤

1. 假设已经正常部署了amd64架构的三节点集群
2. 在“amd64部署机” 目录 /etc/kubeasz 中移除子目录 bin 和 down，然后把整体/etc/kubeasz 目录复制到“arm64部署机”

```
# 登录amd64部署机
cd /etc/kubeasz; mv bin down /tmp/; scp -r /etc/kubeasz root@{_ip_arm64}:/etc/
# 复制完成后找回 bin 和 down 子目录
mv /tmp/bin /etc/kubeasz/; mv /tmp/down /etc/kubeasz/
```
3. 登录“arm64部署机”，执行下载，其他准备工作

```
cd /etc/kubeasz
# 下载基础部分
./ezdown -D
# 下载额外部分（如有）
./ezdown -X ...
# 运行部署容器
./ezdown -S
# 配置机器ssh免密码登录，集群所有节点都免密，包括待新增arm64节点
ssh-copy-id xx.xx.xx.xx
ssh-copy-id ...
# 复制kubeconfig
mkdir /root/.kube/; cp clusters/default/kubectl.kubeconfig /root/.kube/config
```
4. 添加arm64新节点到集群

```
source ~/.bashrc
# 添加新节点 x.x.x.x
dk ezctl add-node default x.x.x.x
```
5. 验证

```
$ kubectl get node -owide
NAME           STATUS   ROLES    AGE    VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
k8s-x.x.x-19   Ready    master   5d8h   v1.33.1   x.x.x.19   <none>        Ubuntu 20.04.4 LTS   5.4.0-122-generic    containerd://2.1.1
k8s-x.x.x-90   Ready    node     5d8h   v1.33.1   x.x.x.90   <none>        Ubuntu 22.04.5 LTS   5.15.0-134-generic   containerd://2.1.1
k8s-x.x.x-91   Ready    node     5d8h   v1.33.1   x.x.x.91   <none>        Ubuntu 22.04.5 LTS   5.15.0-134-generic   containerd://2.1.1
k8s-x.x.x-93   Ready    node     79s    v1.33.1   x.x.x.93   <none>        Ubuntu 22.04.5 LTS   5.15.0-140-generic   containerd://2.1.1

$ kubectl describe node|grep beta.kubernetes.io/arch
Labels:             beta.kubernetes.io/arch=amd64
Labels:             beta.kubernetes.io/arch=amd64
Labels:             beta.kubernetes.io/arch=amd64
Labels:             beta.kubernetes.io/arch=arm64
```

## 小结
通过以上步骤，成功实现了在amd64集群中添加arm64节点；充分展示kubeasz 项目部署集群的灵活性和可配置性；部署过程中ansible执行的过程性输出内容，以近乎白盒的方式展示每一个细节；假如出错有详细的说明，帮助定位，并且随时可以修改执行脚本，安装的幂等性保证随时可以重新安装以修复错误。`Hack it, and have fun!`

---

### Setup/Multi Os

# 操作系统说明

目前发现部分使用新内核的linux发行版，k8s 安装使用 cgroup v2版本时，有时候安装会失败，需要删除/清理集群后重新安装。已报告可能发生于 Alma Linux 9, Rocky Linux 9, Fedora 37；建议如下步骤处理：

- 1.确认系统使用的cgroup v2版本
```
stat -fc %T /sys/fs/cgroup/ 
cgroup2fs
```
- 2.初次安装时kubelet可能启动失败，日志报错类似：err="openat2 /sys/fs/cgroup/kubepods.slice/cpu.weight: no such file or directory"

- 3.建议删除集群然后重新安装，一般能够成功
```
# 删除集群
dk ezctl destroy xxxx

# 重启
reboot

# 启动后重新安装
dk ezctl setup xxxx all
```

## Debian

- Debian 11：默认可能没有安装iptables，使用kubeasz 安装前需要执行：(仅部署节点需要)

``` bash 
apt update

apt install iptables -y
```

## openEuler

- openEuler 24.03 需要安装iptables

``` bash
yum install iptables -y
```

## openSUSE

- openSUSE Leap 15.4：需要安装iptables

``` bash
zypper install iptables
ln -s /usr/sbin/iptables /sbin/iptables
```

---

### Setup/Multi Platform

# 多架构支持

kubeasz 3.4.1 以后支持多CPU架构，当前已支持linux amd64和linux arm64，更多架构支持根据后续需求来计划。

## 使用方式

kubeasz 多架构安装逻辑：根据部署机器（执行ezdown/ezctl命令的机器）的架构，会自动判断下载对应amd64/arm64的二进制文件和容器镜像，然后推送安装到整个集群。

- 暂不支持自动部署混合架构集群，如有需要可以按[说明文档](mix_arch.md)手动操作。
- harbor目前仅支持amd64安装

## 架构支持备忘

#### k8s核心组件本身提供多架构的二进制文件/容器镜像下载，项目调整了下载二进制文件的容器dockerfile

- https://github.com/easzlab/dockerfile-kubeasz-k8s-bin

#### kubeasz其他用到的二进制或镜像，重新调整了容器创建dockerfile

- https://github.com/easzlab/dockerfile-kubeasz-ext-bin
- https://github.com/easzlab/dockerfile-kubeasz-ext-build
- https://github.com/easzlab/dockerfile-kubeasz-sys-pkg
- https://github.com/easzlab/dockerfile-kubeasz-mirrored-images
- https://github.com/easzlab/dockerfile-kubeasz
- https://github.com/easzlab/dockerfile-ansible

#### 其他组件(coredns/network plugin/dashboard/metrics-server等)一般都提供多架构的容器镜像，可以直接下载拉取

---

### Setup/Offline Install

# 离线安装集群

使用kubeasz 离线安装 k8s集群需要下载四个部分：

- kubeasz 项目代码
- 二进制文件（k8s、etcd、containerd等组件）
- 容器镜像文件（calico、coredns、metrics-server等容器镜像）
- 系统软件安装包（ipset、libseccomp2等，仅无法使用本地yum/apt源时需要）

## 离线文件准备

在一台能够访问互联网的服务器上执行：

- 下载工具脚本ezdown，举例使用kubeasz版本3.6.0

``` bash
export release=3.6.0
wget https://github.com/easzlab/kubeasz/releases/download/${release}/ezdown
chmod +x ./ezdown
```

- 使用工具脚本下载（更多关于ezdown的参数，运行./ezdown 查看）

下载kubeasz代码、二进制、默认容器镜像

``` bash
# 国内环境
./ezdown -D
```

[可选]如果需要更多组件，请下载额外容器镜像（cilium,flannel,prometheus等）

``` bash
./ezdown -X flannel
./ezdown -X prometheus
...
```

下载离线系统包 (适用于无法使用yum/apt仓库情形)

``` bash
# 如果操作系统是ubuntu 22.04
./ezdown -P ubuntu_22
```

上述脚本运行成功后，所有文件（kubeasz代码、二进制、离线镜像）均已整理好放入目录`/etc/kubeasz`

- `/etc/kubeasz` 包含 kubeasz 版本为 ${release} 的发布代码
- `/etc/kubeasz/bin` 包含 k8s/etcd/docker/cni 等二进制文件
- `/etc/kubeasz/down` 包含集群安装时需要的离线容器镜像
- `/etc/kubeasz/down/packages` 包含集群安装时需要的系统基础软件

## 离线安装

上述下载完成后，把`/etc/kubeasz`整个目录复制到目标离线服务器相同目录，然后在离线服务器/etc/kubeasz目录下执行：

- 离线安装 docker，检查本地文件，正常会提示所有文件已经下载完成，并上传到本地私有镜像仓库

```
./ezdown -D
./ezdown -X flannel
./ezdown -X prometheus
...
```

- 启动 kubeasz 容器

```
./ezdown -S
```

- 设置参数允许离线安装系统软件包

```
sed -i 's/^INSTALL_SOURCE.*$/INSTALL_SOURCE: "offline"/g' /etc/kubeasz/example/config.yml 
```

- 举例安装单节点集群，参考 https://github.com/easzlab/kubeasz/blob/master/docs/setup/quickStart.md

``` bash
source ~/.bashrc
dk ezctl start-aio
# 或者执行 docker exec -it kubeasz ezctl start-aio
```

- 多节点集群，进入kubeasz 容器内 `docker exec -it kubeasz bash`，参考https://github.com/easzlab/kubeasz/blob/master/docs/setup/00-planning_and_overall_intro.md 进行集群规划和设置后使用./ezctl 命令安装

---

### Setup/QuickStart

## 快速指南

本文档适用于kubeasz 3.3.1以上版本，部署单节点集群(aio)，作为快速体验k8s集群的测试环境。

### 1.基础系统配置

- 准备一台虚机配置内存4G/硬盘30G以上
- 最小化安装`Ubuntu 22.04 server`
- 配置基础网络、更新源、SSH登录等

**注意:** 确保在干净的系统上开始安装，不能使用曾经装过kubeadm或其他k8s发行版的环境

### 2.下载文件

- 下载工具脚本ezdown，举例使用kubeasz版本3.6.7

``` bash
export release=3.6.7
wget https://github.com/easzlab/kubeasz/releases/download/${release}/ezdown
chmod +x ./ezdown
```

- 使用工具脚本下载（更多关于ezdown的参数，运行./ezdown 查看）

下载kubeasz代码、二进制、默认容器镜像

``` bash
# 国内环境
./ezdown -D
# 海外环境
#./ezdown -D -m standard
```

【可选】下载额外容器镜像（cilium,flannel,prometheus等）

``` bash
# 按需下载
./ezdown -X prometheus
...
```

【可选】下载离线系统包 (适用于无法使用yum/apt仓库情形)

``` bash
./ezdown -P
```

上述脚本运行成功后，所有文件（kubeasz代码、二进制、离线镜像）均已整理好放入目录`/etc/kubeasz`

- `/etc/kubeasz` 包含 kubeasz 版本为 ${release} 的发布代码
- `/etc/kubeasz/bin` 包含 k8s/etcd/docker/cni 等二进制文件
- `/etc/kubeasz/down` 包含集群安装时需要的离线容器镜像
- `/etc/kubeasz/down/packages` 包含集群安装时需要的系统基础软件

### 3.安装集群

- 容器化运行 kubeasz

```
./ezdown -S
```

- 使用默认配置安装 aio 集群

```
docker exec -it kubeasz ezctl start-aio
# 如果安装失败，查看日志排除后，使用如下命令重新安装aio集群
# docker exec -it kubeasz ezctl setup default all
```

### 4.验证安装

``` bash
$ source ~/.bashrc
$ kubectl version         # 验证集群版本     
$ kubectl get node        # 验证节点就绪 (Ready) 状态
$ kubectl get pod -A      # 验证集群pod状态，默认已安装网络插件、coredns、metrics-server等
$ kubectl get svc -A      # 验证集群服务状态
```

- 可选安装 [Headlamp](../guide/headlamp.md) 查看和管理集群

### 5.清理

以上步骤创建的K8S开发测试环境请尽情折腾，碰到错误尽量通过查看日志、上网搜索、提交`issues`等方式解决；当然你也可以清理集群后重新创建。

在宿主机上，按照如下步骤清理

- 清理集群 `docker exec -it kubeasz ezctl destroy default`
- 重启节点，以确保清理残留的虚拟网卡、路由等信息

---

### Release Notes/Kubeasz 3.7.0

## kubeasz 3.7.0

首个 kubeasz 3.7.x 版本发布，默认使用nftables 模式，包含大量组件更新。

### 版本/组件更新

- k8s: v1.36.3
- etcd: v3.7.1
- containerd: 2.3.3
- runc: v1.5.1
- cni: v1.9.1
- calico: v3.32.1
- flannel: v0.28.9
- coredns: 1.14.6
- nodelocaldns: 1.26.8
- metrics: v0.9.0
- pause: 3.10.2
- headlamp: 0.43.0
- openebs: 4.5.1
- higress: 2.2.3
- node-problem-detector: v1.35.3
- kube-prometheus-stack: 88.1.5

### 重要更新

- 支持 k8s v1.36集群默认使用 kube-proxy 的nftables 模式，需要较新的系统内核支持(kernal >= 5.13 and nft >= 1.0.0)；更新默认支持的操作系统列表，尽量选择较新系统内核的稳定linux发行版。
- 新增组件node-problem-detector 和若干自定义检查插件。
- 新增组件 headlamp 替换 dashboard，使用更加方便。
- 新增组件 higress ingress controller 替换 ingress-nginx。

### 其他更新

- 新增 easzlab/kubeasz 镜像签名信息和SBOM 信息，感谢kobihikri #1538
- 新增openeuler22/24系统软件离线包下载，感谢[whowhopipi](https://github.com/easzlab/dockerfile-kubeasz-sys-pkg/pull/12)
- 部分文档更新

---

### Op/Ch Apiserver Cert

# 修改 APISERVER（MASTER）证书

`kubeasz` 创建集群后，APISERVER（MASTER）证书默认 CN 包含如下`域名`和`IP`：参见`roles/kube-master/templates/kubernetes-csr.json.j2`

```
  "hosts": [
    "127.0.0.1",
{% if groups['ex_lb']|length > 0 %}
    "{{ hostvars[groups['ex_lb'][0]]['EX_APISERVER_VIP'] }}",
{% endif %}
{% for host in groups['kube_master'] %}
    "{{ host }}",
{% endfor %}
    "{{ CLUSTER_KUBERNETES_SVC_IP }}",
{% for host in MASTER_CERT_HOSTS %}
    "{{ host }}",
{% endfor %}
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster",
    "kubernetes.default.svc.cluster.local"
  ],
```

有的时候（比如apiserver地址通过边界防火墙的NAT转换成公网IP访问，或者需要添加公网域名访问）我们需要在 APISERVER（MASTER）证书中添加一些`域名`或者`IP`，可以方便操作如下：

## 1.修改配置文件`/etc/kubeasz/clusters/${集群名}/config.yaml`

``` bash
# k8s 集群 master 节点证书配置，可以添加多个ip和域名（比如增加公网ip和域名）
MASTER_CERT_HOSTS:
  - "10.1.1.1"
  - "k8s.test.io"
  #- "www.test.com"
```

## 2.执行新证书生成并重启apiserver

``` bash
$ ezctl setup ${集群名} 04 -t change_cert,restart_master 
```

---

### Op/Cluster Restore

# K8S 集群备份与恢复

虽然 K8S 集群可以配置成多主多节点的高可用的部署，还是有必要了解下集群的备份和容灾恢复能力；在高可用k8s集群中 etcd集群保存了整个集群的状态，因此这里的备份与恢复重点就是：

- 从运行的etcd集群备份数据到磁盘文件
- 从etcd备份文件恢复数据，从而使集群恢复到备份时状态

## 备份与恢复操作说明

- 1.首先搭建一个测试集群，部署几个测试deployment，验证集群各项正常后，进行一次备份(假设集群名为k8s-01)：

``` bash
$ ezctl backup k8s-01
# 或者如下手动执行ansible命令
# ansible-playbook -i clusters/k8s-01/hosts -e @clusters/k8s-01/config.yml  playbooks/94.backup.yml
```

执行完毕可以在部署主机的备份目录下检查备份情况，示例如下：

```
/etc/kubeasz/clusters/k8s-01/backup/
├── snapshot_202106201205.db
├── snapshot_202106211406.db
└── snapshot.db
```
其中，snapshot.db始终为最近一次备份文件

- 2.模拟误删除操作（略）

- 3.恢复集群及验证

可以在 `roles/cluster-restore/defaults/main.yml` 文件中配置需要恢复的 etcd备份版本（从上述备份目录中选取），默认使用最近一次备份；执行恢复后，需要一定时间等待 pod/svc 等资源恢复重建。

``` bash
$ ezctl restore k8s-01
# 或者如下手动执行ansible命令
# ansible-playbook -i clusters/k8s-01/hosts -e @clusters/k8s-01/config.yml  playbooks/95.restore.yml
```
如果集群主要组件（master/etcd/node）等出现不可恢复问题，可以尝试使用如下步骤 [清理]() --> [创建]() --> [恢复]()

``` bash
$ ezctl clean k8s-01
# 或者如下手动执行ansible命令
# ansible-playbook -i clusters/k8s-01/hosts -e @clusters/k8s-01/config.yml  playbooks/99.clean.yml
$ ezctl setup k8s-01 01
$ ezctl setup k8s-01 02
$ ezctl setup k8s-01 03
$ ezctl setup k8s-01 04
$ ezctl setup k8s-01 05
...
$ ezctl restore k8s-01
# ansible-playbook -i clusters/k8s-01/hosts -e @clusters/k8s-01/config.yml  playbooks/95.restore.yml
```

## 参考

- https://etcd.io/docs/v3.4/op-guide/recovery/

---

### Op/Force Ch Certs

# 强制更新CA和所有证书

- WARNNING: 此命令使用需要小心谨慎，确保了解功能背景和可能的结果；执行后，它会重新创建集群CA证书以及由它颁发的所有其他证书；一般适合于集群admin.conf不小心泄露，为了避免集群被非法访问，重新创建CA，从而使已泄漏的admin.conf失效。

- 如果需要分发受限的kubeconfig，强烈建议使用[自定义权限和期限的kubeconfig](kcfg-adm.md)

## 使用帮助

确认需要强制更新后，在ansible 控制节点使用如下命令：(xxx 表示需要操作的集群名)

``` bash
docker exec -it kubeasz ezctl kca-renew xxx
# 或者使用 dk ezctl kca-renew xxx
```

上述命令执行后，按序进行以下的操作：详见`playbooks/96.update-certs.yml`

- 重新生成CA证书，以及各种kubeconfig
- 签发新etcd证书，并使用新证书重启etcd服务
- 签发新kube-apiserver 证书，并重启kube-apiserver/kube-controller-manager/kube-scheduler 服务
- 签发新kubelet 证书，并重启kubelet/kube-proxy 服务
- 重启网络组件pod
- 重启其他集群组件pod

- **特别注意：** 如果集群中运行的业务负载pod需要访问apiserver，需要重启这些pod

## 检查验证

更新完毕，注意检查集群组件日志和容器pod日志，确认集群处于正常状态

- 集群组件日志：使用journalctl -u xxxx.service -f 依次检查 etcd.service/kube-apiserver.service/kube-controller-manager.service/kube-scheduler.service/kubelet.service/kube-proxy.service
- 容器pod日志：使用 kubectl logs 方式检查容器日志

---

### Op/Kcfg Adm

# 管理客户端kubeconfig 

默认 k8s集群安装成功后生成客户端kubeconfig，它拥有集群管理的所有权限（不要将这个admin权限、50年期限的kubeconfig流露出去）；而我们经常需要将限定权限、限定期限的kubeconfig 分发给普通用户；利用cfssl签发自定义用户证书和k8s灵活的rbac权限绑定机制，ezctl 工具封装了这个功能。

## 使用帮助

```
ezctl help kcfg-adm
Usage: ezctl kcfg-adm <cluster> <args>
available <args>:
    -A     to add a client kubeconfig with a newly created user
    -D     to delete a client kubeconfig with the existed user
    -L     to list all of the users
    -e     to set expiry of the user certs in hours (ex. 24h, 8h, 240h)
    -t     to set a user-type (admin or view)
    -u     to set a user-name prefix

examples: ./ezctl kcfg-adm test-k8s -L
          ./ezctl kcfg-adm default -A -e 240h -t admin -u jack
          ./ezctl kcfg-adm default -D -u jim-202101162141
```

- 可以设置过期时间
- 可以设置权限：管理员权限（admin）和只读权限（view）

## 使用举例

- 1.查看集群k8s-01当前自定义kubeconfig

```
ezctl kcfg-adm k8s-01 -L
2021-01-24 16:32:43 INFO list-kcfg k8s-01
2021-01-24 16:32:43 INFO list-kcfg in cluster:k8s-01

USER                           TYPE            EXPIRY(+8h if in Asia/Shanghai)
---------------------------------------------------------------------------------

2021-01-24 16:32:43 INFO list-kcfg k8s-01 success
```
初始情况下列表为空

- 2.增加集群k8s-01一个自定义用户kubeconfig，用户名user01，期限24h，只读权限

```
ezctl kcfg-adm k8s-01 -A -u user01 -e 24h -t view
2021-01-24 17:32:33 INFO add-kcfg k8s-01
2021-01-24 17:32:33 INFO add-kcfg in cluster:k8s-01 with user:user01-202101241732

PLAY [localhost] *****************************************************************************************************

...（此处省略输出） 

TASK [deploy : debug] ************************************************************************************************
ok: [localhost] => {
    "msg": "查看user01-202101241732自定义kubeconfig：/etc/kubeasz/clusters/k8s-01/ssl/users/user01-202101241732.kubeconfig"
}

PLAY RECAP ***********************************************************************************************************
localhost                  : ok=12   changed=10   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

2021-01-24 17:32:41 INFO add-kcfg k8s-01 success
```
生成的kubeconfig位于 /etc/kubeasz/clusters/k8s-01/ssl/users/user01-202101241732.kubeconfig

- 3.再增加一个用户user02，期限240h，admin权限

```
ezctl kcfg-adm k8s-01 -A -u user02 -e 240h -t admin
2021-01-24 18:38:47 INFO add-kcfg k8s-01
2021-01-24 18:38:47 INFO add-kcfg in cluster:k8s-01 with user:user02-202101241838

PLAY [localhost] *****************************************************************************************************

...（此处省略输出）

TASK [deploy : debug] ************************************************************************************************
ok: [localhost] => {
    "msg": "查看user02-202101241838自定义kubeconfig：/etc/kubeasz/clusters/k8s-01/ssl/users/user02-202101241838.kubeconfig"
}

PLAY RECAP ***********************************************************************************************************
localhost                  : ok=12   changed=9    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

2021-01-24 18:38:55 INFO add-kcfg k8s-01 success
```

- 4.再次查看集群k8s-01当前自定义kubeconfig

```
ezctl kcfg-adm k8s-01 -L
2021-01-24 18:40:30 INFO list-kcfg k8s-01
2021-01-24 18:40:30 INFO list-kcfg in cluster:k8s-01

USER                           TYPE            EXPIRY(+8h if in Asia/Shanghai)
---------------------------------------------------------------------------------
user02-202101241838            cluster-admin   2021-02-03T10:34:00Z
user01-202101241732            view            2021-01-25T09:28:00Z

2021-01-24 18:40:31 INFO list-kcfg k8s-01 success
```

- 5.删除user01-202101241732 权限

``` bash
ezctl kcfg-adm k8s-01 -D -u user01-202101241732
2021-01-24 21:41:50 INFO del-kcfg k8s-01
2021-01-24 21:41:50 INFO del-kcfg in cluster:k8s-01 with user:user01-202101241732
clusterrolebinding.rbac.authorization.k8s.io "crb-user01-202101241732" deleted
2021-01-24 21:41:50 INFO del-kcfg k8s-01 success

ezctl kcfg-adm k8s-01 -L
2021-01-24 21:42:02 INFO list-kcfg k8s-01
2021-01-24 21:42:02 INFO list-kcfg in cluster:k8s-01

USER                           TYPE            EXPIRY(+8h if in Asia/Shanghai)
---------------------------------------------------------------------------------
user02-202101241838            cluster-admin   2021-02-03T10:34:00Z

2021-01-24 21:42:02 INFO list-kcfg k8s-01 success
```

---

### Op/Loadballance Ingress Nodeport

# 配置负载转发 ingress nodeport

向集群外暴露 ingress-controller 本身的服务端口（80/443/8080）一般有以下三种方法：

- 1.部署ingress-controller时使用`hostNetwork: true`，这样就可以直接使用上述端口，可能与host已listen端口冲突
- 2.部署ingress-controller时使用`LoadBalancer`类型服务，需要集群支持`LoadBalancer`
- 3.部署ingress-controller时使用`nodePort`类型服务，然后在集群外使用 haproxy/f5 等配置 virtual server 集群

本文档讲解使用 haproxy 配置 ingress的 VS 集群，前提是配置了自建`ex_lb`节点

## 1.配置 ex_lb 参数开启转发 ingress nodeport

``` bash
# 编辑 roles/ex-lb/defaults/main.yml，配置如下变量
INGRESS_NODEPORT_LB: "yes"
INGRESS_TLS_NODEPORT_LB: "yes"
```

## 2.重新配置启动LB节点服务

``` bash
$ ezctl setup ${集群名} ex-lb 
```

## 3.验证 ex_lb 节点的 haproxy 服务配置 `/etc/haproxy/haproxy.cfg` 包含如下配置

``` bash
... 前文省略
listen kube_master
        bind 0.0.0.0:8443
        mode tcp
        option tcplog
        balance roundrobin
        server 192.168.1.1 192.168.1.1:6443 check inter 2000 fall 2 rise 2 weight 1
        server 192.168.1.2 192.168.1.2:6443 check inter 2000 fall 2 rise 2 weight 1

listen ingress-node
        bind 0.0.0.0:80
        mode tcp
        option tcplog
        balance roundrobin
        server 192.168.1.3 192.168.1.3:23456 check inter 2000 fall 2 rise 2 weight 1
        server 192.168.1.4 192.168.1.4:23456 check inter 2000 fall 2 rise 2 weight 1

listen ingress-node-tls
        bind 0.0.0.0:443
        mode tcp
        option tcplog
        balance roundrobin
        server 192.168.1.3 192.168.1.3:23457 check inter 2000 fall 2 rise 2 weight 1
        server 192.168.1.4 192.168.1.4:23457 check inter 2000 fall 2 rise 2 weight 1
```

验证成功后，我们可以方便的去做[配置ingress](../guide/ingress.md)和[配置https ingress](../guide/ingress-tls.md)实验了。

---

### Op/Op Etcd

# 管理 etcd 集群

Etcd 集群支持在线改变集群成员节点，可以增加、修改、删除成员节点；不过改变成员数量仍旧需要满足集群成员多数同意原则（quorum），另外请记住集群成员数量变化的影响：

- 注意：如果etcd 集群有故障节点，务必先删除故障节点，然后添加新节点，[参考FAQ](https://etcd.io/docs/v3.4.0/faq/)
- 增加 etcd 集群节点, 提高集群稳定性
- 增加 etcd 集群节点, 提高集群读性能（所有节点数据一致，客户端可以从任意节点读取数据）
- 增加 etcd 集群节点, 降低集群写性能（所有节点数据一致，每一次写入会需要所有节点数据同步）

## 备份 etcd 数据

1. 手动在任意正常 etcd 节点上执行备份：

``` bash
# snapshot备份
$ ETCDCTL_API=3 etcdctl snapshot save backup.db
# 查看备份
$ ETCDCTL_API=3 etcdctl --write-out=table snapshot status backup.db
```

2. 使用 kubeasz 备份
_cluster_name_ 为 k8s-01

``` bash 
ezctl backup k8s-01
```

使用 crontab 定时备份示例(使用 容器化的 kubeasz，每日01:01 备份)
```
1 1 * * * /usr/bin/docker exec -i kubeasz ezctl backup k8s-01
```

备份文件在 

```
{{ base_dir }}/clusters/k8s-01/backup
```

## etcd 集群节点操作

执行如下 (假设待操作节点为 192.168.1.11，集群名称test-k8s)：

- 增加 etcd 节点：

``` bash
# ssh 免密码登录
$ ssh-copy-id 192.168.1.11

# 新增节点
$ ezctl add-etcd test-k8s 192.168.1.11
```

- 删除 etcd 节点：`$ ezctl del-etcd test-k8s 192.168.1.11`

具体操作流程参考 ezctl中 add-etcd/del-etcd 相关函数和playbooks/ 目录的操作剧本

### 验证 etcd 集群

``` bash
# 登录任意etcd节点验证etcd集群状态
$ export ETCDCTL_API=3 
$ etcdctl member list

# 验证所有etcd节点服务状态和日志
$ systemctl status etcd
$ journalctl -u etcd -f
```

## 参考

- 官方文档 https://etcd.io/docs/v3.5/op-guide/runtime-configuration/

---

### Op/Op Index

# 集群运维管理指南 operation guide

- [管理 NODE 节点](op-node.md)
- [管理 MASTER 节点](op-master.md)
- [管理 ETCD 节点](op-etcd.md)
- [升级 K8S 版本](upgrade.md)
- [集群备份与恢复](cluster_restore.md)
- [管理分发用户 kubeconfig](kcfg-adm.md)
- [修改 APISERVER 证书](ch_apiserver_cert.md)
- [强制更新CA和所有证书](force_ch_certs.md)
- [配置负载转发 ingress nodeport](loadballance_ingress_nodeport.md)

---

### Op/Op Master

# 管理 kube_master 节点

## 1.增加 kube_master 节点

新增`kube_master`节点大致流程为：(参考ezctl 中add-master函数和playbooks/23.addmaster.yml)
- [可选]新节点安装 chrony 时间同步
- 新节点预处理 prepare
- 新节点安装 container runtime 
- 新节点安装 kube_master 服务
- 新节点安装 kube_node 服务
- 新节点安装网络插件相关
- 禁止业务 pod调度到新master节点
- 更新 node 节点 haproxy 负载均衡并重启

### 操作步骤

执行如下 (假设待增加节点为 192.168.1.11, 集群名称test-k8s)：

``` bash
# ssh 免密码登录
$ ssh-copy-id 192.168.1.11

# 新增节点
$ ezctl add-master test-k8s 192.168.1.11

# 同理，重复上面步骤再新增节点并自定义nodename
$ ezctl add-master test-k8s 192.168.1.12 k8s_nodename=master-03
```

### 验证

``` bash
# 在新节点master 服务状态
$ systemctl status kube-apiserver 
$ systemctl status kube-controller-manager
$ systemctl status kube-scheduler

# 查看新master的服务日志
$ journalctl -u kube-apiserver -f

# 查看集群节点，可以看到新 master节点 Ready, 并且禁止了POD 调度功能
$ kubectl get node
NAME           STATUS                     ROLES     AGE       VERSION
192.168.1.1    Ready,SchedulingDisabled   <none>    3h        v1.9.3
192.168.1.2    Ready,SchedulingDisabled   <none>    3h        v1.9.3
192.168.1.3    Ready                      <none>    3h        v1.9.3
192.168.1.4    Ready                      <none>    3h        v1.9.3
192.168.1.11   Ready,SchedulingDisabled   <none>    2h        v1.9.3	# 新增 master节点
```

## 2.删除 kube_master 节点


删除`kube_master`节点大致流程为：(参考ezctl 中del-master函数和playbooks/33.delmaster.yml)
- 检测是否可以删除
- 迁移节点 pod
- 删除 master 相关服务及文件
- 删除 node 相关服务及文件
- 从集群删除 node 节点
- 从 ansible hosts 移除节点
- 在 ansible 控制端更新 kubeconfig
- 更新 node 节点 haproxy 配置

### 操作步骤

``` bash
$ ezctl del-master test-k8s 192.168.1.11  # 假设待删除节点 192.168.1.11
```

### 验证

略

---

### Op/Op Node

# 管理 node 节点

目录
- 1.增加 kube_node 节点
- 2.批量增加 kube_node 节点
- 3.增加非标准ssh端口节点
- 4.删除 kube_node 节点

## 1.增加 kube_node 节点

新增`kube_node`节点大致流程为：(参考ezctl 里面add-node函数 和 playbooks/22.addnode.yml)
- [可选]新节点安装 chrony 时间同步
- 新节点预处理 prepare
- 新节点安装 container runtime
- 新节点安装 kube_node 服务
- 新节点安装网络插件相关

### 操作步骤

执行如下 (假设待增加节点为 192.168.1.11，k8s集群名为 test-k8s)：

``` bash
# ssh 免密码登录
$ ssh-copy-id 192.168.1.11

# 新增节点
$ ezctl add-node test-k8s 192.168.1.11

# 同理，重复上面步骤再新增节点并自定义nodename
$ ezctl add-node test-k8s 192.168.1.12 k8s_nodename=worker-03
```

### 验证

``` bash
# 验证新节点状态
$ kubectl get node

# 验证新节点的网络插件calico 或flannel 的Pod 状态
$ kubectl get pod -n kube-system

# 验证新建pod能否调度到新节点，略
```

## 2.批量增加 kube_node 节点

当一次需要新增多台 work 节点时，可使用 `add-nodes` 一次性提交多个 IP，复用 Ansible 自身的并行能力，避免对每台节点重复 fork `ansible-playbook`。底层调用的仍然是 `playbooks/22.addnode.yml`，流程与 `add-node` 完全一致。

### 操作步骤

执行如下 (假设待增加节点为 192.168.1.21、192.168.1.22、192.168.1.23，k8s集群名为 test-k8s)：

``` bash
# ssh 免密码登录（每个新节点都需要先做）
$ ssh-copy-id 192.168.1.21
$ ssh-copy-id 192.168.1.22
$ ssh-copy-id 192.168.1.23

# 一次性批量新增节点
$ ezctl add-nodes test-k8s 192.168.1.21 192.168.1.22 192.168.1.23
```

### `add-node` 与 `add-nodes` 如何选择

| 场景 | 选用 |
| --- | --- |
| 只新增一台节点 | `add-node` |
| 需要给新节点附带 host_vars（如 `ansible_ssh_port=10022`、`k8s_nodename=worker-03` 等） | `add-node`（参见 §3） |
| 一次新增多台节点，且无需为每台节点单独配置 host_vars | `add-nodes` |

> 说明：`add-nodes` 的所有位置参数都按 IP 校验，不接受 inventory 行内变量。如果批量新增的节点中有部分需要 host_vars，请对这部分节点单独使用 `add-node` 添加。

## 3.增加非标准ssh端口节点

假设待添加节点192.168.2.1，ssh 端口 10022；然后执行 

``` bash
$ ssh-copy-id -p 10022 192.168.2.1
$ ezctl add-node test-k8s 192.168.2.1 ansible_ssh_port=10022
```

- 注意：如果在添加节点时需要设置其他个性化变量，可以同理在后面不断添加


## 4.删除 kube_node 节点

删除 node 节点流程：(参考ezctl 里面del-node函数 和 playbooks/32.delnode.yml)
- 检测是否可以删除
- 迁移节点上的 pod
- 删除 node 相关服务及文件
- 从集群删除 node

### 操作步骤

``` bash
$ ezctl del-node test-k8s 192.168.1.11 # 假设待删除节点为 192.168.1.11
```

### 验证

略

---

### Op/Upgrade

## k8s 集群升级

集群升级存在一定风险，请谨慎操作。 

- 支持k8s相同大版本基础上升级任意小版本，比如当前安装集群为1.25.0，你可以方便的升级到任何1.25.x版本
- 不建议跨大版本升级，一般大版本更新时k8s api有一些变动

### 备份etcd数据

- 自动备份

`kubeasz`项目也可以如下方便执行备份（假设集群名为k8s-01），详情阅读文档[备份恢复](cluster_restore.md)

```
dk ezctl backup k8s-01
```

- 手动备份 etcd数据，在任意 etcd节点上执行：

``` bash
# snapshot备份
$ ETCDCTL_API=3 etcdctl snapshot save backup.db
# 查看备份
$ ETCDCTL_API=3 etcdctl --write-out=table snapshot status backup.db
```

### k8s 升级小版本

快速升级`k8s`小版本，比较常见如`Bug修复` `特性发布`时使用。

- 首先去官网release下载待升级的k8s版本，例如`https://dl.k8s.io/v1.25.4/kubernetes-server-linux-amd64.tar.gz`
- 解压下载的tar.gz文件，找到如下`kube*`开头的二进制，复制替换kubeasz控制端目录`/etc/kubeasz/bin`对应文件
  - kube-apiserver
  - kube-controller-manager
  - kubectl
  - kubelet
  - kube-proxy
  - kube-scheduler

- 切换当前所在集群为升级集群, 在kubeasz控制端执行`dk ezctl checkout k8s-01`
- 在kubeasz控制端执行`dk ezctl upgrade k8s-01` 即可完成k8s 升级，不会中断业务应用


### 其他升级说明

其他升级是指升级k8s组件包括：`etcd版本` `docker版本`，一般不需要用到，不建议升级，以下仅作说明。

- 1.下载所有组件相关新的二进制解压并替换 `/etc/kubeasz/bin/` 目录下文件

- 2.升级 etcd: `ansible-playbook -i clusters/k8s-01/hosts -e @clusters/k8s-01/config.yml -t upgrade_etcd playbooks/02.etcd.yml`

- 3.升级 docker （建议使用k8s官方支持的docker稳定版本）
  - 如果可以接受短暂业务中断，执行 `ansible-playbook -t upgrade_docker 03.docker.yml`
  - 如果要求零中断升级，执行 `ansible-playbook -i clusters/k8s-01/hosts -e @clusters/k8s-01/config.yml -t download_docker playbooks/03.runtime.yml`，然后手动执行如下
    - 待升级节点，先应用`kubectl cordon`和`kubectl drain`命令迁移业务pod
    - 待升级节点执行 `systemctl restart docker`
    - 恢复节点可调度 `kubectl uncordon`

---

### Mixes/Conformance

# 关于K8S集群一致性认证

CNCF 一致性认证项目(https://github.com/cncf/k8s-conformance) 可以很方便帮助k8s搭建者和用户确认集群各项功能符合预期，既符合k8s设计标准。

# kubeasz 通过一致性测试

自kubeasz 3.0.0 版本，k8s v1.20.2开始，正式通过cncf一致性认证，成为cncf 官方认证安装工具；后续k8s主要版本发布或者kubeasz有大版本更新，会优先确保通过集群一致性认证。

- v1.35 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.35/kubeasz)
- v1.34 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.34/kubeasz)
- v1.33 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.33/kubeasz)
- v1.32 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.32/kubeasz)
- v1.31 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.31/kubeasz)
- v1.30 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.30/kubeasz)
- v1.29 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.29/kubeasz)
- v1.28 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.28/kubeasz)
- v1.27 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.27/kubeasz)
- v1.26 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.26/kubeasz)
- v1.25 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.25/kubeasz)
- v1.24 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.24/kubeasz)
- v1.23 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.23/kubeasz)
- v1.22 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.22/kubeasz)
- v1.21 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.21/kubeasz)
- v1.20 [已认证](https://github.com/cncf/k8s-conformance/tree/master/v1.20/kubeasz)


## Conformance Test

按照测试文档，注意以下几点：

1.解决qiang的问题，可以临时去国外公有云创建集群，然后运行测试项目。

2.集群要保障资源，建议3个节点

3.网络组件选择calico，其他组件可能有bug导致特定测试项失败


# 附：测试流程

## Node Provisioning

Provision 3 nodes for your cluster (OS: Ubuntu 20.04)

1 master node (4c16g)

2 worker node (4c16g)

for a High-Availability Kubernetes Cluster, read [more](https://github.com/easzlab/kubeasz/blob/master/docs/setup/00-planning_and_overall_intro.md)

## Install the cluster

(1) Download 'kubeasz' code, the binaries and offline images

```
export release=3.6.9
curl -C- -fLO --retry 3 https://github.com/easzlab/kubeasz/releases/download/${release}/ezdown
chmod +x ./ezdown
./ezdown -D -m ''
```

(2) install an all-in-one cluster

```
./ezdown -S
source ~/.bashrc
dk ezctl start-aio
```

(3) Add two worker nodes

```
ssh-copy-id ${worker1_ip}
dk ezctl add-node default ${worker1_ip}

ssh-copy-id ${worker2_ip}
dk ezctl add-node default ${worker2_ip}
```

## Run Conformance Test

The standard tool for running these tests is
[Sonobuoy](https://github.com/heptio/sonobuoy).  Sonobuoy is
regularly built and kept up to date to execute against all
currently supported versions of kubernetes.

Download a [binary release](https://github.com/heptio/sonobuoy/releases) of the CLI

Deploy a Sonobuoy pod to your cluster with:

```
$ sonobuoy run --plugin-env=e2e.E2E_EXTRA_ARGS="--ginkgo.v" --mode=certified-conformance 
```

**NOTE:** You can run the command synchronously by adding the flag `--wait` but be aware that running the Conformance tests can take an hour or more.

View actively running pods:

```
$ sonobuoy status
```

To inspect the logs:

```
$ sonobuoy logs
```

Once `sonobuoy status` shows the run as `completed`, copy the output directory from the main Sonobuoy pod to a local directory:

```
$ outfile=$(sonobuoy retrieve)
```

This copies a single `.tar.gz` snapshot from the Sonobuoy pod into your local
`.` directory. Extract the contents into `./results` with:

```
mkdir ./results; tar xzf $outfile -C ./results
```

**NOTE:** The two files required for submission are located in the tarball under **plugins/e2e/results/{e2e.log,junit.xml}**.

To clean up Kubernetes objects created by Sonobuoy, run:

```
sonobuoy delete
```

---

### Mixes/Donate

# 捐赠

如果觉得本项目对您有帮助，请小小鼓励下项目作者，谢谢！

支付宝码(左)和微信钱包码(右)

---

### Mixes/DoneList

## 前言

`kubeasz`项目开始于`2017.11`，半年多时间以来，从最开始单一的ansible部署脚本朝着提供部署高可用 K8S集群的完整解决方案的目标不断前进，接下去项目的发展需要各位的共同参与和贡献，希望越做越好，为国内k8s学习、实践者提供更多帮助。  

### 项目已完成部分 

<table border="0">
    <tr>
        <th>类型</th>
        <th>描述</th>
        <th>备注</th>
    </tr>
    <tr>
        <td>集群部署</td>
        <td>服务器基础安全加固与参数优化</td>
        <td>已完成</td>
    </tr>
    <tr>
        <td>基础服务</td>
        <td>集群监控告警-prometheus</td>
        <td>已完成基础，待优化</td>
    </tr>
    <tr>
        <td>应用服务</td>
        <td>jenkins集成</td>
        <td>已完成</td>
    </tr>
    <tr>
        <td>集群部署</td>
        <td>kube-router网络插件</td>
        <td>已完成</td>
    </tr>
    <tr>
        <td>基础服务</td>
        <td>metrics server</td>
        <td>已完成</td>
    </tr>
    <tr>
        <td>集群部署</td>
        <td>ipvs代理模式跟进</td>
        <td>已完成</td>
    </tr>
    <tr>
        <td>集群部署</td>
        <td>cilium网络插件</td>
        <td>已完成</td>
    </tr>
    <tr>
        <td>集群部署</td>
        <td>集群内时间同步-Chrony</td>
        <td>已完成</td>
    </tr>
</table>

---

### Mixes/HowToContribute

# 为项目`kubeasz`提交`pull request`

首先请核对下本地git config配置的用户名和邮箱与你github上的注册用户和邮箱一致，否则即使`pull request`被接受，贡献者列表中也看不到自己的名字，设置命令：

``` bash
$ git config --global user.email "you@example.com"
$ git config --global user.name "Your Name"
```

- 1.登录github，在本项目页面点击`fork`到自己仓库
- 2.clone 自己的仓库到本地：`git clone https://github.com/xxx/kubeasz.git`
- 3.在 master 分支添加原始仓库为上游分支：`git remote add upstream https://github.com/easzlab/kubeasz.git`
- 4.在本地新建开发分支：`git checkout -b dev`
- 5.在开发分支修改代码并提交：`git add .`, `git commit -am 'xx变更说明'`
- 6.切换至 master 分支，同步原始仓库：`git checkout master`， `git pull upstream master`
- 7.切换至 dev 分支，合并本地 master 分支（已经和原始仓库同步），可能需要解冲突：`git checkout dev`, `git merge master`
- 8.提交本地 dev 分支到自己的远程 dev 仓库：`git push origin dev`
- 9.在github自己仓库页面，点击`Compare & pull request`给原始仓库发 pull request 请求
- a.等待原作者回复（接受/拒绝）

---

### Guide/Argocd

# argocd 安装

用 GitOps 方式把 Kubernetes 声明式配置“自动、可观测、可回滚”地同步到集群的控制器；它是 Kubernetes 世界里 GitOps 的事实标准。

## 初始安装
- 建议使用helm chart 方式基础安装；后续用声明式方式配置cluster、project、repository等

## 服务暴露
- 建议使用ingress方式
- 备用：kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'

## 密码登录
- 获取初始化密码 `argocd admin initial-password -n argocd`
- 登录 `argocd login {nodeIP}:{nodePort}`
- 更新密码 `argocd account update-password`
- 重置遗忘密码 

```
kubectl -n argocd patch secret argocd-secret -p '{"data": {"admin.password": null, "admin.passwordMtime": null}}'
kubectl -n argocd delete pods -l app.kubernetes.io/name=argocd-server
```

## SSO 登录
- 参考文档：https://help.aliyun.com/zh/ram/obtain-user-information-through-oidc
- 阿里云控制台-RAM访问控制-集成管理-OAuth应用：创建应用 https://ram.console.aliyun.com/applications/create
  - OAuth 协议版本：2.0
  - 应用类型：Web应用
  - 回调地址：填写 https://${argocd-server-domain}/api/dex/callback
  - OAuth 范围：openid(必选), aliuid(可选), profile(可选)

- OAuth应用创建后，准备以下参数
  - "应用 ID" --> dex.config: connectors oidc.config.clientID
  - 创建应用密码 --> dex.config: connectors oidc.config.clientSecret

- 配置argocd-cm

```
  dex.config: |
    connectors:
    - type: oidc
      id: aliyun
      name: aliyun
      config:
        issuer: https://oauth.aliyun.com
        clientID: "406************"
        clientSecret: E8G***************************************************b6
        scopes:
        - profile
        - openid
        - aliuid
        getUserInfo: true
        userIDKey: uid
        userNameKey: uid
        claimMapping:
          preferred_username: name
          email: uid
```

- 配置argocd-rbac-cm

```
data:
  policy.csv: |
    # 设置普通用户app-dev 只读权限
    p, role:app-dev, projects, get, *, allow
    p, role:app-dev, applications, get, *, allow
    p, role:app-dev, logs, get, *, allow
    p, role:app-dev, exec, create, */*, allow

    # 设置测试项目，所有权限
    p, role:app-dev, applications, *, test-project/*, allow
    
    # 阿里云子账号 ID：2***********84
    g, "2***********84", role:admin
    g, "2***********27", role:app-dev

  policy.default: role:''
  scopes: '[name]'
```

## 支持 application in any namespace

- 配置 argocd-cm

```
data:
  # 设置argocd 资源标记方式，使用annotation，禁用labelKey
  # application.instanceLabelKey: argocd.argoproj.io/instance
  application.resourceTrackingMethod: annotation
```

- 配置 argocd-cmd-params-cm

```
data:
  #application.namespaces: app-team-one, app-team-two
  application.namespaces: '*'
  applicationsetcontroller.allowed.scm.providers: '*'
  applicationsetcontroller.namespaces: '*'
```

然后重启 argocd-server 和 argocd-application-controller

## 其他设置

- argocd 部署应用 ingress 资源一直Progressing，参考：https://github.com/argoproj/argo-cd/issues/14607

```
# 修改argocd-cm configmap，重启argocd-application-controller
data:
  resource.customizations: |
    networking.k8s.io/Ingress:
      health.lua: |
        hs = {}
        hs.status = "Healthy"
        hs.message = "Skip health check for Ingress"
        return hs
```

---

### Guide/Chrony

# chrony 时间同步

在安装k8s集群前需确保各节点时间同步；`chrony` 是一个优秀的 `NTP` 实现，性能比ntp好，且配置管理方便；它既可作时间服务器服务端，也可作客户端。

- `OpenStack` 社区也推荐使用 `chrony`实现各节点之间的时间同步

## 安装配置介绍

项目中选定一个节点(`groups.chrony[0]`)作为集群内部其他节点的时间同步源，而这个节点本身从公网源同步；当然如果整个集群都无法访问公网，那么请手动校准这个节点的时间后，仍旧可以作为内部集群的时间源服务器。

- 配置 chrony server，详见roles/chrony/templates/server.conf.j2 

- 配置 chrony client，详见roles/chrony/templates/client.conf.j2

## `kubeasz` 集成安装

- 修改 clusters/${cluster_name}/hosts 文件，在 `chrony`组中加入选中的节点ip
- [可选] 修改 clusters/${cluster_name}/config.yml 中的相关配置
-执行命令安装 `ezctl setup ${cluster_name} 01`

## 验证安装

- 检查chronyd服务状态 `systemctl status chronyd`
- 检查chronyd时间同步日志 `/var/log/chrony`

## 验证时间同步状态完成

chrony 服务启动后，chrony server 会与配置的公网参考时间服务器进行同步；server 同步完成后，chrony client 会与 server 进行时间同步；一般来说整个集群达到时间同步需要几十分钟。可以用如下命令检查，初始时 **NTP synchronized: no**，同步完成后 **NTP synchronized: yes**

``` bash
$ ansible -i clusters/${cluster_name}/hosts all -m shell -a 'timedatectl'
192.168.1.1 | SUCCESS | rc=0 >>
      Local time: Sat 2019-01-26 11:51:51 HKT
  Universal time: Sat 2019-01-26 03:51:51 UTC
        RTC time: Sat 2019-01-26 03:51:52
       Time zone: Asia/Hong_Kong (HKT, +0800)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no

192.168.1.4 | SUCCESS | rc=0 >>
      Local time: Sat 2019-01-26 11:51:51 HKT
  Universal time: Sat 2019-01-26 03:51:51 UTC
        RTC time: Sat 2019-01-26 03:51:52
       Time zone: Asia/Hong_Kong (HKT, +0800)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no

192.168.1.2 | SUCCESS | rc=0 >>
      Local time: Sat 2019-01-26 11:51:51 HKT
  Universal time: Sat 2019-01-26 03:51:51 UTC
        RTC time: Sat 2019-01-26 03:51:52
       Time zone: Asia/Hong_Kong (HKT, +0800)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no

192.168.1.3 | SUCCESS | rc=0 >>
      Local time: Sat 2019-01-26 11:51:51 HKT
  Universal time: Sat 2019-01-26 03:51:51 UTC
        RTC time: Sat 2019-01-26 03:51:52
       Time zone: Asia/Hong_Kong (HKT, +0800)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no
```

---

### Guide/Dashboard.1.6.3

## dashboard

本文档基于 dashboard 1.6.3版本，从 1.7.x 版本以后，dashboard 默认开启自带的登录验证界面，登录流程差异详见[2.x 版本](dashboard.2.x.md)。

+ 注意：实际测试k8s版本<=1.9.1支持dashboard 1.6.3, 建议k8s 1.9 以后使用 dashboard 新版本。

### 部署

``` bash
# 部署dashboard 主yaml配置文件
$ kubectl create -f /etc/kubeasz/manifests/dashboard/1.6.3/kubernetes-dashboard.yaml
# 部署基本密码认证配置[可选]，密码文件位于 /etc/kubernetes/ssl/basic-auth.csv
$ kubectl create -f /etc/kubeasz/manifests/dashboard/1.6.3/ui-admin-rbac.yaml
$ kubectl create -f /etc/kubeasz/manifests/dashboard/1.6.3/ui-read-rbac.yaml
```

请在另外窗口打开 [kubernetes-dashboard.yaml](../../manifests/dashboard/1.6.3/kubernetes-dashboard.yaml)

+ 由于 kube-apiserver 启用了 RBAC授权，dashboard使用的 ServiceAccount `kubernetes-dashboard` 必须有相应的权限去访问apiserver(在新版本1.8.0中，该访问权限已按最小化方式授权)，在1.6.3 版本，先粗放一点，把`kubernetes-dashboard` 与 集群角色 `cluster-admin` 绑定，这样dashboard就拥有了所有访问apiserver的权限。
+ 开发测试环境为了方便配置dashboard-service时候，指定 `NodePort`方式暴露服务，这样集群外部可以使用 `http://NodeIP:NodePort` 方式直接访问 dashboard，生产环境建议关闭该访问途径。

### 验证

``` bash
# 查看pod 运行状态
kubectl get pod -n kube-system | grep dashboard
kubernetes-dashboard-86bd8778bf-w4974      1/1       Running   0          12h
# 查看dashboard service
kubectl get svc -n kube-system|grep dashboard
kubernetes-dashboard   NodePort    10.68.7.67      <none>        80:5452/TCP	12h
# 查看集群服务
kubectl cluster-info|grep dashboard
kubernetes-dashboard is running at https://192.168.1.10:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy
# 查看pod 运行日志，关注有没有错误
kubectl logs kubernetes-dashboard-86bd8778bf-w4974 -n kube-system
```

### 访问

因为dashboard 作为k8s 原生UI，能够展示各种资源信息，甚至可以有修改、增加、删除权限，所以有必要对访问进行认证和控制，本项目预置部署的集群有以下安全设置：详见 [apiserver配置模板](../../roles/kube-master/templates/kube-apiserver.service.j2)

+ 启用 `TLS认证` `RBAC授权`等安全特性
+ 关闭 apiserver非安全端口8080的外部访问`--insecure-bind-address=127.0.0.1`
+ 关闭匿名认证`--anonymous-auth=false`
+ 补充启用基本密码认证 `--token-auth-file=/etc/kubernetes/ssl/basic-auth.csv`，[密码文件模板](../../roles/kube-master/templates/basic-auth.csv.j2)中按照每行(密码,用户名,序号)的格式，可以定义多个用户

#### 1. 临时访问：使用 `http://NodeIP:NodePort` 方式直接访问 dashboard，生产环境建议关闭该途径

#### 2. 用户+密码访问：安全性比证书方式差点，务必保管好密码文件`basic-auth.csv`

- 这里演示两种权限，使用admin 登录dashboard拥有所有权限，使用readonly 登录后仅查看权限，首先在 master节点文件 `/etc/kubernetes/ssl/basic-auth.csv` 确认用户名和密码，如果要增加或者修改用户，修改保存该文件后记得逐个重启你的master 节点
- 为了演示用户密码访问，如果你已经完成证书访问方式，你可以在浏览器删除证书，或者访问时候浏览器询问你证书时不选证书
- 2.1 设置用户admin 的RBAC 权限，如下运行配置文件 `kubectl create -f ui-admin-rbac.yaml`

``` bash
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ui-admin
rules:
- apiGroups:
  - ""
  resources:
  - services
  - services/proxy
  verbs:
  - '*'

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ui-admin-binding
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ui-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: admin
```  
- 2.2 设置用户readonly 的RBAC 权限，如下运行配置文件 `kubectl create -f ui-read-rbac.yaml`

``` bash
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ui-read
rules:
- apiGroups:
  - ""
  resources:
  - services
  - services/proxy
  verbs:
  - get
  - list
  - watch

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ui-read-binding
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ui-read
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: readonly
```
- 2.3 访问 `https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy` 使用 admin登录拥有所有权限，比如删除某个部署；使用 readonly登录只有查看权限，尝试删除某个部署会提示错误 `forbidden: User \"readonly\" cannot delete services/proxy in the namespace \"kube-system\"`

#### 3. 证书访问：最安全的方式，配置较复杂
- 使用集群CA 生成客户端证书，可以根据需要生成权限不同的证书，这里为了演示直接使用 kubectl使用的证书和key(在03.kubectl.yml阶段生成)，该证书拥有所有权限
- 指定格式导出该证书，进入`/etc/kubernetes/ssl`目录，使用命令`openssl pkcs12 -export -in admin.pem -inkey admin-key.pem -out kube-admin.p12` 提示输入证书密码和确认密码，可以用密码再增加一层保护，也可以直接回车跳过，完成后目录下多了 `kube-admin.p12`文件，将它分发给授权的用户
- 用户将 `kube-admin.p12` 双击导入证书即可，`IE` 和`Chrome` 中输入`https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy` 或者 `https://x.x.x.x:6443/ui` 即可访问。补充：最新firefox需要在浏览器中单独导入 [选项] - [隐私与安全] - [证书/查看证书] - [您的证书] 页面点击 [导入] 该证书

### 小结

+ dashboard 版本 1.6.3 访问控制实现较复杂，文档中给出的例子也有助于你理解 RBAC的灵活控制能力，当然最好去[官方文档](https://kubernetes.io/docs/admin/authorization/rbac/)学习一下，这块篇幅不长
+ 由于还未部署 Heapster 插件，当前 dashboard 不能展示 Pod、Nodes 的 CPU、内存等 metric 图形，后续部署 heapster后自然能够看到
+ 本文中的权限设置仅供演示用，生产环境请在此基础上修改成适合你安全需求的方式

---

### Guide/Dashboard.2.X

## dashboard

本文档基于 dashboard 2.2 版本，k8s 1.22 版本，因 dashboard 1.7 以后默认开启了自带的登录验证机制，因此不同版本登录有差异：

- 旧版（<= 1.6）建议通过apiserver访问，直接通过apiserver 认证授权机制去控制 dashboard权限，详见[旧版文档](dashboard.1.6.3.md)
- 新版（>= 1.7）可以使用自带的登录界面，使用不同Service Account Tokens 去控制访问 dashboard的权限

### 部署

参考 https://github.com/kubernetes/dashboard

+ 增加了通过`api-server`方式访问dashboard
+ 增加了`NodePort`方式暴露服务，这样集群外部可以使用 `https://NodeIP:NodePort` (注意是https不是http，区别于1.6.3版本) 直接访问 dashboard。

安装部署

``` bash
# ezctl 集成部署组件，xxxx 代表集群部署名
# dashboard 部署文件位于 /etc/kubeasz/clusters/xxxx/yml/dashboard/ 目录
./ezctl setup xxxx 07
```

### 验证部署

``` bash
# 查看pod 运行状态
kubectl get pod -n kube-system | grep dashboard
dashboard-metrics-scraper-856586f554-l6bf4   1/1     Running   0          35m
kubernetes-dashboard-698d4c759b-67gzg        1/1     Running   0          35m

# 查看dashboard service
kubectl get svc -n kube-system|grep dashboard
kubernetes-dashboard   NodePort    10.68.219.38   <none>        443:24108/TCP                   53s

# 查看pod 运行日志
kubectl logs -n kube-system kubernetes-dashboard-698d4c759b-67gzg
```

### 登陆

因为dashboard 作为k8s 原生UI，能够展示各种资源信息，甚至可以有修改、增加、删除权限，所以有必要对访问进行认证和控制，为演示方便这里使用 `https://NodeIP:NodePort` 方式访问 dashboard，支持两种登录方式：Kubeconfig、令牌(Token)

**注意：** 使用chrome浏览器访问 `https://NodeIP:NodePort` 可能提示安全风险无法访问，可以换firefox浏览器设置安全例外，继续访问。 

- Token令牌方式登录（admin）

选择 Token 方式登录，复制下面输出的admin token 字段到输入框

``` bash
# 获取 Bearer Token，找到输出中 ‘token:’ 开头的后面部分
$ kubectl describe -n kube-system secrets admin-user 
```

- Token令牌方式登录（只读）

选择 Token 方式登录，复制下面输出的read token 字段到输入框

``` bash
# 获取 Bearer Token，找到输出中 ‘token:’ 开头的后面部分
$ kubectl describe -n kube-system secrets dashboard-read-user 
```

- Kubeconfig登录（admin）
Admin kubeconfig文件默认位置：`/root/.kube/config`，该文件中默认没有token字段，使用Kubeconfig方式登录，还需要将token追加到该文件中，完整的文件格式如下：
```
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdxxxxxxxxxxxxxx
    server: https://192.168.1.2:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: admin
  name: kubernetes
current-context: kubernetes
kind: Config
preferences: {}
users:
- name: admin
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRxxxxxxxxxxx
    client-key-data: LS0tLS1CRUdJTxxxxxxxxxxxxxx
    token: eyJhbGcixxxxxxxxxxxxxxxx
```

- Kubeconfig登录（只读）
首先[创建只读权限 kubeconfig文件](../op/kcfg-adm.md)，然后类似追加只读token到该文件，略。

### 参考

- 1.[Dashboard docs](https://github.com/kubernetes/dashboard/blob/master/docs/README.md)
- 2.[a-read-only-kubernetes-dashboard](https://blog.cowger.us/2018/07/03/a-read-only-kubernetes-dashboard.html)

---

### Guide/Dashboard

## dashboard（已废弃）

Kubernetes Dashboard 已归档，不再维护。本项目推荐使用 [Headlamp](headlamp.md) 作为 Kubernetes Web UI。

### 部署（已废弃）

本文档基于 dashboard 7.12.0 版本，k8s 1.32 版本，dashboard 7.0.0 以后引入大量不兼容变化。
假设已经使用kubeasz 部署k8s集群完成；新版dashboard 部署如下：（以单机集群为例，其他情况请修改集群名称'default'为实际的名称）

``` bash
# 1. 修改 clusters/default/config.yml 文件，设置 dashboard_install: "yes"

# 2. 下载dashboard 需要的镜像
./ezdown -X dashboard

# 3. 执行安装，配置文件位于 clusters/default/yml/dashboard/ 目录
dk ezctl setup default 07
```

+ 增加`NodePort`方式暴露服务，这样集群外部可以使用 `https://NodeIP:NodePort` (注意是https不是http) 直接访问 dashboard。

### 验证

``` bash
# 查看pod 运行状态
kubectl get pod -n kube-system |grep kubernetes-dashboard
kubernetes-dashboard-api-6d77cb7964-4tklq               1/1     Running   0          17h
kubernetes-dashboard-auth-5fbd64f659-f9dst              1/1     Running   0          17h
kubernetes-dashboard-kong-6dcdbf5dfd-829h4              1/1     Running   0          17h
kubernetes-dashboard-metrics-scraper-7757c48476-4lcrq   1/1     Running   0          17h
kubernetes-dashboard-web-5f9f47979-7khrk                1/1     Running   0          17h

# 查看service
kubectl get svc -n kube-system |grep kong
kubernetes-dashboard-kong-proxy        NodePort    10.68.148.170   <none>   443:31544/TCP  17h
```

### 登陆

因为dashboard 作为k8s 原生UI，能够展示各种资源信息，甚至可以有修改、增加、删除权限，所以有必要对访问进行认证和控制，为演示方便这里使用 `https://NodeIP:NodePort` 方式访问 dashboard，目前支持登录方式：令牌(Token)

**注意：** 使用chrome浏览器访问 `https://NodeIP:NodePort` 可能提示安全风险无法访问，可以换firefox浏览器设置安全例外，继续访问。

- Token令牌方式登录（admin）

选择 Token 方式登录，复制下面输出的admin token 字段到输入框

``` bash
# 获取 Bearer Token，找到输出中 ‘token:’ 开头的后面部分
$ kubectl describe -n kube-system secrets admin-user 
```

- Token令牌方式登录（只读）

选择 Token 方式登录，复制下面输出的read token 字段到输入框

``` bash
# 获取 Bearer Token，找到输出中 ‘token:’ 开头的后面部分
$ kubectl describe -n kube-system secrets dashboard-read-user 
```

### 参考

- [旧版文档 dashboard 1.6.3](dashboard.1.6.3.md)
- [旧版文档 dashboard 2.x](dashboard.2.x.md)
- https://github.com/kubernetes/dashboard

---

### Guide/Harbor

# harbor 镜像仓库

Habor是由VMWare中国团队开源的企业级容器镜像仓库。特性包括：友好的用户界面，基于角色的访问控制，水平扩展，同步复制，AD/LDAP集成以及审计日志等。本文档仅说明单机安装harbor 服务。

- 目录
  - 安装步骤
  - 安装讲解
  - 配置docker/containerd信任harbor证书
  - 在k8s集群使用harbor
  - 管理维护

### 安装步骤

1. 下载离线安装包，成功后在/etc/kubeasz/down/目录下有离线包harbor-offline-installer-$HARBOR_VER.tgz

```
ezdown -D
ezdown -R
```

2. 利用ezctl [文档](../setup/ezctl.md) 创建一个新的集群，已有集群修改同样的文件

```
#clusters/xxx/hosts 中修改如下，配置harbor组下机器，设置NEW_INSTALL=true
...
# 'NEW_INSTALL': 'true' to install a harbor server; 'false' to integrate with existed one
[harbor]
192.168.1.8 NEW_INSTALL=true
...

#clusters/xxx/config.yml 中修改如下，按需修改HARBOR_DOMAIN/HARBOR_TLS_PORT 等配置项，举例如下
############################
# role:harbor
############################
# harbor version，完整版本号
HARBOR_VER: "v2.10.2"
HARBOR_DOMAIN: "harbor.yourdomain.com"
HARBOR_PATH: /var/data
HARBOR_TLS_PORT: 8443
HARBOR_REGISTRY: "{{ HARBOR_DOMAIN }}:{{ HARBOR_TLS_PORT }}"

# if set 'false', you need to put certs named harbor.pem and harbor-key.pem in directory 'down'
HARBOR_SELF_SIGNED_CERT: true

# install component
HARBOR_WITH_TRIVY: false
```

3. 配置完成后，执行 `./ezctl setup xxx harbor`，完成harbor安装和docker 客户端配置

- 安装验证

1. 在harbor节点使用`docker ps -a` 查看harbor容器组件运行情况
2. 浏览器访问地址（忽略证书报错） `https://${HARBOR_DOMAIN}:${HARBOR_TLS_PORT}`，管理员账号是 admin ，密码见harbor.yml文件 harbor_admin_password 对应值（默认密码 Harbor12345 已被随机生成的16位随机密码替换，不然存在安全隐患)

### 安装讲解

根据`playbooks/11.harbor.yml`文件，harbor节点需要以下步骤：

- role `os-harden` 系统安全加固（可选）
- role `chrony` 时间同步服务（可选）
- role `prepare` 基础系统环境准备
- role `docker` 安装docker
- role `harbor` 安装harbor
- 注意：`kube_node`节点在harbor部署完之后，需要配置harbor的证书（详见下节配置docker/containerd信任harbor证书），并可以在hosts里面添加harbor的域名解析，如果你的环境中有dns服务器，可以跳过hosts文件设置

1. 下载docker-compose可执行文件到$PATH目录
1. 自注册变量result判断是否已经安装harbor，避免重复安装问题
1. 解压harbor离线安装包到指定目录
1. 导入harbor所需 docker images
1. 创建harbor证书和私钥(复用集群的CA证书)
1. 修改harbor.yml配置文件
1. 启动harbor安装脚本

### 在k8s集群使用harbor

admin用户web登录后可以方便的创建项目，并指定项目属性(公开或者私有)；然后创建用户，并在项目`成员`选项中选择用户和权限；

#### 镜像上传

使用docker客户端登录`{{ HARBOR_REGISTRY }}`，然后把镜像tag成 `{{ HARBOR_REGISTRY }}/$项目名/$镜像名:$TAG` 之后，即可使用docker push 上传

``` bash
docker login harbor.test.com
Username: 
Password:
Login Succeeded
docker tag busybox:latest harbor.test.com/library/busybox:latest
docker push harbor.test.com/library/busybox:latest
The push refers to a repository [harbor.test.com/library/busybox]
0271b8eebde3: Pushed 
latest: digest: sha256:91ef6c1c52b166be02645b8efee30d1ee65362024f7da41c404681561734c465 size: 527
```
#### k8s中使用harbor

1. 如果镜像保存在harbor中的公开项目中，那么只需要在yaml文件中简单指定harbor私有镜像即可，例如

``` bash
apiVersion: v1
kind: Pod
metadata:
  name: test-busybox
spec:
  containers:
  - name: test-busybox
    image: harbor.test.com/xxx/busybox:latest
    imagePullPolicy: Always
```

2. 如果镜像保存在harbor中的私有项目中，那么yaml文件中使用该私有项目的镜像需要指定`imagePullSecrets`，例如

``` bash
apiVersion: v1
kind: Pod
metadata:
  name: test-busybox
spec:
  containers:
  - name: test-busybox
    image: harbor.test.com/xxx/busybox:latest
    imagePullPolicy: Always
  imagePullSecrets:
  - name: harborkey1
```
其中 `harborKey1`可以用以下两种方式生成：

+ 1.使用 `kubectl create secret docker-registry harborkey1 --docker-server=harbor.test.com --docker-username=admin --docker-password=Harbor12345 --docker-email=team@test.com`
+ 2.使用yaml配置文件生成 

``` bash
//harborkey1.yaml
apiVersion: v1
kind: Secret
metadata:
  name: harborkey1
  namespace: default
data:
    .dockerconfigjson: {base64 -w 0 ~/.docker/config.json}
type: kubernetes.io/dockerconfigjson
```
前面docker login会在~/.docker下面创建一个config.json文件保存鉴权串，这里secret yaml的.dockerconfigjson后面的数据就是那个json文件的base64编码输出（-w 0让base64输出在单行上，避免折行）

### 管理维护

+ 日志目录 `/var/log/harbor`
+ 数据目录 `/var/data` ，其中最主要是 `/var/data/database` 和 `/var/data/registry` 目录，如果你要彻底重新安装harbor，删除这两个目录即可

先进入harbor安装目录 `cd /var/data/harbor`，常规操作如下：

1. 暂停harbor `docker-compose stop` : docker容器stop，并不删除容器
2. 恢复harbor `docker-compose start` : 恢复docker容器运行
3. 停止harbor `docker-compose down -v` : 停止并删除docker容器
4. 启动harbor `docker-compose up -d` : 启动所有docker容器

修改harbor的运行配置，需要如下步骤：

``` bash
# 停止 harbor
 docker-compose down -v
# 修改配置
 vim harbor.yml
# 执行./prepare已更新配置到docker-compose.yml文件
 ./prepare
# 启动 harbor
 docker-compose up -d
```

---

### Guide/Headlamp

## headlamp

Kubernetes Dashboard 已归档，不再维护；本项目推荐使用 Headlamp 作为 Kubernetes Web UI。Headlamp 支持桌面客户端和集群内两种运行方式，本文介绍在集群内通过 Helm 安装的方法。

### 集成部署

假设已经使用kubeasz 部署k8s集群完成；headlamp 部署如下：（以单机集群为例，其他情况请修改集群名称'default'为实际的名称）

``` bash
# 1. 修改 clusters/default/config.yml 文件，设置 headlamp_install: "yes"

# 2. 下载需要的镜像
./ezdown -X headlamp

# 3. 执行安装，配置文件位于 clusters/default/yml/headlamp/ 目录
dk ezctl setup default 07
```

### 验证

``` bash
# 查看 Pod 运行状态
kubectl get pod -n kube-system -l app.kubernetes.io/name=headlamp

# 查看 Service
kubectl get svc -n kube-system headlamp
```

+ 增加`NodePort`方式暴露服务，这样集群外部可以使用 `http://NodeIP:NodePort` 直接访问 headlamp。

### 登陆

Headlamp 使用 Kubernetes RBAC 控制资源访问权限。桌面客户端会直接使用本机 kubeconfig；集群内访问可使用 ServiceAccount Token 或 OIDC 登录。

- Token令牌方式登录（admin）

选择 Token 方式登录，复制下面输出的admin token 字段到输入框

``` bash
# 获取 Bearer Token，找到输出中 ‘token:’ 开头的后面部分
$ kubectl describe -n kube-system secrets admin-user 
```

- Token令牌方式登录（只读）

选择 Token 方式登录，复制下面输出的read token 字段到输入框

``` bash
# 获取 Bearer Token，找到输出中 ‘token:’ 开头的后面部分
$ kubectl describe -n kube-system secrets dashboard-read-user 
```

将输出的 Token 粘贴到 Headlamp 登录页。以上两个内置的token 仅用于演示；生产环境应根据实际角色创建最小权限的 RBAC 规则，避免长期使用高权限 Token。

### 参考

- [Headlamp 集群内安装文档](https://headlamp.dev/docs/latest/installation/in-cluster/)
- [Headlamp 认证文档](https://headlamp.dev/docs/latest/installation/)
- [Kubernetes Dashboard 归档仓库](https://github.com/kubernetes-retired/dashboard)
- [旧版 Dashboard 文档 1.6.3](dashboard.1.6.3.md)
- [旧版 Dashboard 文档 2.x](dashboard.2.x.md)

---

### Guide/Helm

# Helm

`Helm`致力于成为k8s集群的应用包管理工具，希望像linux 系统的`RPM` `DPKG`那样成功；确实在k8s上部署复杂一点的应用很麻烦，需要管理很多yaml文件（configmap,controller,service,rbac,pv,pvc等等），而helm能够整齐管理这些文档：版本控制，参数化安装，方便的打包与分享等。  
- 建议积累一定k8s经验以后再去使用helm；对于初学者来说手工去配置那些yaml文件对于快速学习k8s的设计理念和运行原理非常有帮助，而不是直接去使用helm，面对又一层封装与复杂度。
- 本文基于helm 3（建议版本），helm 2 文档[请看这里](helm2.md)

## 安装 helm

在官方repo下载[release版本](https://github.com/helm/helm/releases)中自带的二进制文件即可（以Linux amd64为例）

```
wget https://get.helm.sh/helm-v3.2.1-linux-amd64.tar.gz
mv ./linux-amd64/helm /usr/bin
```

- 启用官方 charts 仓库

```
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
```
国内镜像
```
 helm repo add stable http://mirror.azure.cn/kubernetes/charts
```


## 使用 helm 安装应用

helm3 安装命令与 helm2 稍有变化，个人习惯先下载对应charts到本地然后按照固定目录格式安装，以创建一个redis集群举例：

- 创建 redis-cluster 目录
``` bash
mkdir -p /opt/charts/redis-cluster
cd /opt/charts/redis-cluster
```

- 下载最新stalbe/redis-ha
```
helm repo update
helm pull stable/redis-ha
```

- 解压 charts，复制 values.yaml设置
```
tar zxvf redis-ha-*.tgz
cp redis-ha/values.yaml .
```

- 创建 start.sh 脚本记录启动命令
```
cat > start.sh << EOF
#!/bin/sh
set -x

ROOT=$(cd `dirname $0`; pwd)
cd $ROOT

helm install redis \
	--create-namespace \
	--namespace dependency \
	-f ./values.yaml \
	./redis-ha
EOF
```

- 查看当前目录结构如下
```
tree .
.
├── redis-ha		# redis-ha 原始charts目录
├── start.sh		# 启动命名脚本
└── values.yaml		# 个性化参数配置
```

- 修改当前目录的 values.yaml 为你的个性化配置
``` bash
#举例values.yaml 配置如下，没有启用PV
#cat values.yaml
image:
  repository: redis
  tag: 5.0.6-alpine

replicas: 2

## Redis specific configuration options
redis:
  port: 6379
  masterGroupName: "mymaster"       # must match ^[\\w-\\.]+$) and can be templated
  config:
    ## For all available options see http://download.redis.io/redis-stable/redis.conf
    min-replicas-to-write: 1
    min-replicas-max-lag: 5   # Value in seconds
    maxmemory: "4g"       # Max memory to use for each redis instance. Default is unlimited.
    maxmemory-policy: "allkeys-lru"  # Max memory policy to use for each redis instance. Default is volatile-lru.
    repl-diskless-sync: "yes"
    rdbcompression: "yes"
    rdbchecksum: "yes"

  resources:
    requests:
      memory: 200Mi
      cpu: 100m
    limits:
      memory: 4000Mi

## Sentinel specific configuration options
sentinel:
  port: 26379
  quorum: 1

  resources:
    requests:
      memory: 200Mi
      cpu: 100m
    limits:
      memory: 200Mi

hardAntiAffinity: true

## Configures redis with AUTH (requirepass & masterauth conf params)
auth: false

persistentVolume:
  enabled: false

hostPath:
  path: "/data/mcs-redis/{{ .Release.Name }}"
```

- 执行安装
```
bash ./start.sh
```

- 查看安装
```
helm ls -A
NAME 	NAMESPACE 	REVISION	UPDATED                                	STATUS  	CHART         	APP VERSION
redis	dependency	1       	2020-05-28 20:57:31.166002853 +0800 CST	deployed	redis-ha-4.4.4	5.0.6

# 查看k8s上资源
kubectl get pod,svc -n dependency
NAME                          READY   STATUS    RESTARTS   AGE
pod/redis-redis-ha-server-0   2/2     Running   0          119s
pod/redis-redis-ha-server-1   2/2     Running   0          104s

NAME                                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)              AGE
service/redis-redis-ha              ClusterIP   None          <none>        6379/TCP,26379/TCP   119s
service/redis-redis-ha-announce-0   ClusterIP   10.68.41.65   <none>        6379/TCP,26379/TCP   119s
service/redis-redis-ha-announce-1   ClusterIP   10.68.64.49   <none>        6379/TCP,26379/TCP   119s
```

---

### Guide/Hpa

## Horizontal Pod Autoscaling

自动水平伸缩，是指运行在k8s上的应用负载(POD)，可以根据资源使用率进行自动扩容、缩容；我们知道应用的资源使用率通常都有高峰和低谷，所以k8s的`HPA`特性应运而生；它也是最能体现区别于传统运维的优势之一，不仅能够弹性伸缩，而且完全自动化！

根据 CPU 使用率或自定义 metrics 自动扩展 Pod 数量（支持 replication controller、deployment）；k8s1.6版本之前是通过kubelet来获取监控指标，1.6版本之后是通过api server、heapster或者kube-aggregator来获取监控指标。

### Metrics支持

根据不同版本的API中，HPA autoscale时靠以下指标来判断资源使用率：
- autoscaling/v1: CPU
- autoscaling/v2alpha1
  - 内存
  - 自定义metrics
  - 多metrics组合: 根据每个metric的值计算出scale的值，并将最大的那个值作为扩容的最终结果

### 基础示例

本实验环境基于k8s 1.8 和 1.9，仅使用`autoscaling/v1` 版本API，**注意确保**`k8s` 集群插件`kubedns` 和 `heapster` 工作正常。

``` bash
# 创建deploy和service
$ kubectl run php-apache --image=pilchard/hpa-example --requests=cpu=200m --expose --port=80

# 创建autoscaler
$ kubectl autoscale deploy php-apache --cpu-percent=50 --min=1 --max=10

# 等待3~5分钟查看hpa状态
$ kubectl get hpa php-apache
NAME         REFERENCE               TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   0% / 50%   1         10        1          3m

# 增加负载
$ kubectl run --rm -it load-generator --image=busybox /bin/sh
Hit enter for command prompt
$ while true; do wget -q -O- http://php-apache; done;

# 等待约5分钟查看hpa显示负载增加，且副本数目增加为4
$ kubectl get hpa php-apache
NAME         REFERENCE               TARGETS      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   430% / 50%   1         10        4          4m

# 注意k8s为了避免频繁增删pod，对副本的增加速度有限制
# 实验过程可以看到副本数目从1到4到8到10，大概都需要4~5分钟的缓冲期
$ kubectl get hpa php-apache
NAME         REFERENCE               TARGETS     MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   86% / 50%   1         10        8          9m
$ kubectl get hpa php-apache
NAME         REFERENCE               TARGETS     MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   52% / 50%   1         10        10         12m

# 清除负载，CTRL+C 结束上述循环程序，稍后副本数目变回1
$ kubectl get hpa php-apache
NAME         REFERENCE               TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   0% / 50%   1         10        1          17m
```

---

### Guide/Index

## 使用指南

### 附加组件安装

- 安装 [kubedns](kubedns.md)
- 安装 [headlamp](headlamp.md)
- 安装 [metrics-server](metrics-server.md)
- 安装 [prometheus](prometheus.md)
- 安装 [kubeapps](kubeapps.md)
- 安装 [ingress](ingress.md)
- 安装 [helm](helm.md)
- 安装 [efk](efk.md)
- 安装 [harbor](harbor.md)
- 安装 [metallb](metallb.md)

### 基础特性演示

- 自动水平伸缩 [Horizontal Pod Autoscaling](hpa.md)
- 网络安全策略 [Network Policy](networkpolicy.md)
- 滚动更新 [rollingupdate](rollingupdateWithZeroDowntime.md)

---

### Guide/Ingress

## Ingress简介

本文档已过期，安装最新版本，请参考相关官方文档。

ingress就是从外部访问k8s集群的入口，将用户的URL请求转发到不同的service上。ingress相当于nginx反向代理服务器，它包括的规则定义就是URL的路由信息；它的实现需要部署`Ingress controller`(比如 [traefik](https://github.com/containous/traefik) [ingress-nginx](https://github.com/kubernetes/ingress-nginx) 等)，`Ingress controller`通过apiserver监听ingress和service的变化，并根据规则配置负载均衡并提供访问入口，达到服务发现的作用。

- 未配置ingress：

集群外部 -> NodePort -> K8S Service

- 配置ingress:

集群外部 -> Ingress -> K8S Service

- **注意：ingress 本身也需要部署`Ingress controller`时使用以下几种方式让外部访问**
  - 使用`NodePort`方式
  - 使用`hostPort`方式
  - 使用LoadBalancer地址方式

- 以下讲解基于`Traefik`，如果想要了解`ingress-nginx`的原理与实践，推荐阅读博客[烂泥行天下](https://www.ilanni.com/?p=14501)的相关文章

### 部署 Traefik

Traefik 提供了一个简单好用 `Ingress controller`，下文侧重讲解 ingress部署和测试例子。请查看yaml配置 [traefik-ingress.yaml](../../manifests/ingress/traefik/traefik-ingress.yaml)，参考[traefik 官方k8s例子](https://github.com/containous/traefik/tree/master/examples/k8s)

#### 安装 traefik ingress-controller

``` bash
kubectl create -f /etc/kubeasz/manifests/ingress/traefik/traefik-ingress.yaml
```
+ 注意需要配置 `RBAC`授权
+ 注意`trafik pod`中 `80`端口为 traefik ingress-controller的服务端口，`8080`端口为 traefik 的管理WEB界面；为后续配置方便指定`80` 端口暴露`NodePort`端口为 `23456`(对应于在hosts配置中`NODE_PORT_RANGE`范围内可用端口)

#### 验证 traefik ingress-controller

``` bash
# kubectl get deploy -n kube-system traefik-ingress-controller
NAME                         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
traefik-ingress-controller   1         1         1            1           4m

# kubectl get svc -n kube-system traefik-ingress-service
NAME                      TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                       AGE
traefik-ingress-service   NodePort   10.68.69.170   <none>        80:23456/TCP,8080:34815/TCP   4m
```
+ 可以看到`traefik-ingress-service` 服务端口`80`暴露的nodePort确实为`23456`

#### 测试 ingress

+ 首先创建测试用K8S应用，并且该应用服务不用nodePort暴露，而是用ingress方式让外部访问

``` bash
kubectl run test-hello --image=nginx:alpine --expose --port=80
##
# kubectl get deploy test-hello
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
test-hello   1         1         1            1           56s
# kubectl get svc test-hello
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
test-hello   ClusterIP   10.68.124.115   <none>        80/TCP    1m
```
+ 然后为这个应用创建 ingress，`kubectl create -f /etc/kubeasz/manifests/ingress/test-hello.ing.yaml`

``` bash
# test-hello.ing.yaml内容
apiVersion: networking.k8s.io/v1beta1 
kind: Ingress
metadata:
  name: test-hello
spec:
  rules:
  - host: hello.test.com
    http:
      paths:
      - path: /
        backend:
          serviceName: test-hello
          servicePort: 80
```
+ 集群内部尝试访问: `curl -H Host:hello.test.com 10.68.69.170(traefik-ingress-service的服务地址)` 能够看到欢迎页面 `Welcome to nginx!`；
+ 在集群外部尝试访问(假定集群一个NodeIP为 192.168.1.1): `curl -H Host:hello.test.com 192.168.1.1:23456`，也能够看到欢迎页面 `Welcome to nginx!`，说明ingress测试成功

#### 为 traefik WEB 管理页面创建 ingress 规则 

`kubectl create -f /etc/kubeasz/manifests/ingress/traefik/traefik-ui.ing.yaml`

``` bash
# traefik-ui.ing.yaml内容
---
apiVersion: networking.k8s.io/v1beta1 
kind: Ingress
metadata:
  name: traefik-web-ui
  namespace: kube-system
spec:
  rules:
  - host: traefik-ui.test.com
    http:
      paths:
      - path: /
        backend:
          serviceName: traefik-ingress-service
          servicePort: 8080
```

+ 在集群外部可以使用 `curl -H Host:traefik-ui.test.com 192.168.1.1:23456` 尝试访问WEB管理页面，返回 `<a href="/dashboard/">Found</a>.`说明 traefik-ui的ingress配置生效了。

+ 在客户端主机也可以通过修改本机 `hosts` 文件，如上例子，增加两条记录：

``` text
192.168.1.1	hello.test.com
192.168.1.1	traefik-ui.test.com
```
打开浏览器输入域名 `http://hello.test.com:23456` 和 `http://traefik-ui.test.com:23456` 就可以访问k8s的应用服务了。

### 可选1: 使用`LoadBalancer`服务类型来暴露ingress，自有环境（非公有云）可以参考[metallb文档](metallb.md)

``` bash
# 修改traefik-ingress 使用 LoadBalancer服务
$ sed -i 's/NodePort$/LoadBalancer/g' /etc/kubeasz/manifests/ingress/traefik/traefik-ingress.yaml
# 创建traefik-ingress
$ kubectl apply -f /etc/kubeasz/manifests/ingress/traefik/traefik-ingress.yaml
# 验证
$ kubectl get svc --all-namespaces |grep traefik
kube-system   traefik-ingress-service   LoadBalancer   10.68.163.243   192.168.1.241   80:23456/TCP,8080:37088/TCP   1m
```
这时可以修改客户端本机 `hosts`文件：(如上例192.168.1.241)

``` text
192.168.1.241     hello.test.com
192.168.1.241     traefik-ui.test.com
```
打开浏览器输入域名 `http://hello.test.com` 和 `http://traefik-ui.test.com`可以正常访问。

### 可选2: 部署`ingress-service`的负载均衡

- 利用 nginx/haproxy 等集群，可以做代理转发以去掉 `23456`这个端口。如果你的集群根据本项目部署了高可用方案，那么可以利用`LB` 节点haproxy 来做，当然如果生产环境K8S应用已经部署非常多，建议还是使用独立的 `nginx/haproxy`集群。

具体参考[配置转发 ingress nodePort](../op/loadballance_ingress_nodeport.md)，如上配置访问集群`MASTER_IP`的`80`端口时，由haproxy代理转发到实际的node节点暴露的nodePort端口上了。这时可以修改客户端本机 `hosts`文件如下：(假定 MASTER_IP=192.168.1.10)

``` text
192.168.1.10     hello.test.com
192.168.1.10    traefik-ui.test.com
```
打开浏览器输入域名 `http://hello.test.com` 和 `http://traefik-ui.test.com`可以正常访问。

## 下一步[配置https ingress](ingress-tls.md)

---