kubernetes-v1.14 node安装

1、安装docker

1.1、增加docker 源

1
2
3
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

        官方的源比较慢,可以增加阿里的源

1
# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
1.1.1 node节点安装插件
1
2
3
# yum install -y epel-release
# yum install -y yum-utils ipvsadm telnet wget net-tools conntrack ipset jq iptables curl sysstat libseccomp socat nfs-utils fuse fuse-devel
# yum install -y python-pip python-devel yum-utils device-mapper-persistent-data lvm2

1.2、安装docker

1
#  yum -y install docker-ce

1.3、修改docker systemd unit 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# cat /usr/lib/systemd/system/docker.service |egrep -Ev "^$|^#"
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
BindsTo=containerd.service
After=network-online.target firewalld.service containerd.service
Wants=network-online.target
Requires=docker.socket
[Service]
Type=notify
EnvironmentFile=-/run/flannel/docker
ExecStart=/usr/bin/dockerd $DOCKER_NETWORK_OPTIONS
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
  • dockerd 运行时会调用其它 docker 命令,如 docker-proxy,所以需要将 docker 命令所在的目录加到 PATH 环境变量中;
  • flanneld 启动时将网络配置写入 /run/flannel/docker 文件中,dockerd 启动前读取该文件中的环境变量 DOCKER_NETWORK_OPTIONS ,然后设置 docker0 网桥网段;
  • 如果指定了多个 EnvironmentFile 选项,则必须将 /run/flannel/docker 放在最后(确保 docker0 使用 flanneld 生成的 bip 参数);
  • docker 需要以 root 用于运行;

1.4、启动 docker 服务

1
# systemctl daemon-reload && systemctl enable docker && systemctl restart docker && systemctl status docker

1.5、检查 docker0 网桥

1
2
3
4
5
6
7
8
9
# /usr/sbin/ip addr show flannel.1 && /usr/sbin/ip addr show docker0
3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN
link/ether 8a:be:12:b9:ab:b8 brd ff:ff:ff:ff:ff:ff
inet 172.30.128.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP
link/ether 02:42:eb:ec:ae:94 brd ff:ff:ff:ff:ff:ff
inet 172.30.128.1/21 brd 172.30.135.255 scope global docker0
valid_lft forever preferred_lft forever
1.5.1、查看 docker 的状态信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# ps -elfH|grep docker
0 S root 1436 975 0 80 0 - 28167 - 10:54 pts/0 00:00:00 grep --color=auto docker
4 S root 1265 1 1 80 0 - 122095 futex_ 10:54 ? 00:00:00 /usr/bin/dockerd --bip=172.30.112.1/21 --ip-masq=false --mtu=1450

# docker info
vClient:
Debug Mode: false

Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 18.09.6
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 4.4.193-1.el7.elrepo.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 7.796GiB
Name: k8s-node-2.kxl
ID: GJEA:U6PT:NMHM:KWD2:DOIJ:U6XW:6N3U:4QZN:F5PT:CQXH:MZKU:VATL
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine

2、部署 kubelet 组件

        kubelet 运行在每个 worker 节点上,接收 kube-apiserver 发送的请求,管理 Pod 容器,执行交互式命令,如 exec、run、logs 等。
        kubelet 启动时自动向 kube-apiserver 注册节点信息,内置的 cadvisor 统计和监控节点的资源使用情况。
        为确保安全,部署时关闭了 kubelet 的非安全 http 端口,对请求进行认证和授权,拒绝未授权的访问(如 apiserver、heapster 的请求)。

2.1、创建 kubelet bootstrap kubeconfig 文件

NODE_NAMES 里面的值是node的主机名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# NODE_NAMES=(node-01 node-02 node-03)
for node_name in ${NODE_NAMES[@]}
do
echo ">>> ${node_name}"

# 创建 token
export BOOTSTRAP_TOKEN=$(kubeadm token create \
--description kubelet-bootstrap-token \
--groups system:bootstrappers:${node_name} \
--kubeconfig ~/.kube/config)

# 设置集群参数
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/ssl/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig

# 设置客户端认证参数
kubectl config set-credentials kubelet-bootstrap \
--token=${BOOTSTRAP_TOKEN} \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig

# 设置上下文参数
kubectl config set-context default \
--cluster=kubernetes \
--user=kubelet-bootstrap \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig

# 设置默认上下文
kubectl config use-context default --kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
done

# for node_name in ${NODE_NAMES[@]}
do
echo ">>> ${node_name}"
scp kubelet-bootstrap-${node_name}.kubeconfig root@${node_name}:/etc/kubernetes/kubelet-bootstrap.kubeconfig
done
  • 向 kubeconfig 写入的是 token,bootstrap 结束后 kube-controller-manager 为 kubelet 创建 client 和 server 证书;

  • 查看 kubeadm 为各节点创建的 token:
    master 节点查看

    1
    2
    3
    4
    5
    # kubeadm token list --kubeconfig ~/.kube/config
    TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
    016e9x.306t91l832suzg8i 19h 2019-09-17T11:29:43+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node-03
    4l4tcx.juy6qs9rmrnfpbig 19h 2019-09-17T11:29:43+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node-01
    64pk36.vbhvbmtojpskyclt 19h 2019-09-17T11:29:43+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node-02
    • token 有效期为 1 天,超期后将不能再被用来 boostrap kubelet,且会被 kube-controller-manager 的 tokencleaner 清理;
    • kube-apiserver 接收 kubelet 的 bootstrap token 后,将请求的 user 设置为 system:bootstrap:,group 设置为 system:bootstrappers,后续将为这个 group 设置 ClusterRoleBinding;
  • 查看各 token 关联的 Secret:

    1
    2
    3
    4
    # kubectl get secrets  -n kube-system|grep bootstrap-token
    bootstrap-token-016e9x bootstrap.kubernetes.io/token 7 4h25m
    bootstrap-token-4l4tcx bootstrap.kubernetes.io/token 7 4h25m
    bootstrap-token-64pk36 bootstrap.kubernetes.io/token 7 4h25m

2.2、创建和分发 kubelet 参数配置文件

从 v1.10 开始,部分 kubelet 参数需在配置文件中配置,kubelet –help 会提示:

1
DEPRECATED: This parameter should be set via the config file specified by the Kubelet's --config flag
  • 创建 kubelet 参数配置文件模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    # cat /etc/kubernetes/kubelet-config.yaml
    kind: KubeletConfiguration
    apiVersion: kubelet.config.k8s.io/v1beta1
    address: "##node_ip##"
    staticPodPath: ""
    syncFrequency: 1m
    fileCheckFrequency: 20s
    httpCheckFrequency: 20s
    staticPodURL: ""
    port: 10250
    readOnlyPort: 0
    rotateCertificates: true
    serverTLSBootstrap: true
    authentication:
    anonymous:
    enabled: false
    webhook:
    enabled: true
    x509:
    clientCAFile: "/etc/kubernetes/ssl/ca.pem"
    authorization:
    mode: Webhook
    registryPullQPS: 0
    registryBurst: 20
    eventRecordQPS: 0
    eventBurst: 20
    enableDebuggingHandlers: true
    enableContentionProfiling: true
    healthzPort: 10248
    healthzBindAddress: "##node_ip##"
    clusterDomain: "cluster.local"
    clusterDNS:
    - "10.254.0.2"
    nodeStatusUpdateFrequency: 10s
    nodeStatusReportFrequency: 1m
    imageMinimumGCAge: 2m
    imageGCHighThresholdPercent: 85
    imageGCLowThresholdPercent: 80
    volumeStatsAggPeriod: 1m
    kubeletCgroups: ""
    systemCgroups: ""
    cgroupRoot: ""
    cgroupsPerQOS: true
    cgroupDriver: cgroupfs
    runtimeRequestTimeout: 10m
    hairpinMode: promiscuous-bridge
    maxPods: 100
    # podCIDR: "172.30.0.0/16"
    podPidsLimit: -1
    resolvConf: /etc/resolv.conf
    maxOpenFiles: 1000000
    kubeAPIQPS: 1000
    kubeAPIBurst: 2000
    serializeImagePulls: false
    evictionHard:
    memory.available: "100Mi"
    nodefs.available: "10%"
    nodefs.inodesFree: "5%"
    imagefs.available: "15%"
    evictionSoft: {}
    enableControllerAttachDetach: true
    failSwapOn: true
    containerLogMaxSize: 20Mi
    containerLogMaxFiles: 10
    systemReserved: {}
    kubeReserved: {}
    systemReservedCgroup: ""
    kubeReservedCgroup: ""
    enforceNodeAllocatable: ["pods"]
    • address:kubelet 安全端口(https,10250)监听的地址,不能为 127.0.0.1,否则 kube-apiserver、heapster 等不能调用 kubelet 的 API;
    • readOnlyPort=0:关闭只读端口(默认 10255),等效为未指定;
    • authentication.anonymous.enabled:设置为 false,不允许匿名�访问 10250 端口;
    • authentication.x509.clientCAFile:指定签名客户端证书的 CA 证书,开启 HTTP 证书认证;
    • authentication.webhook.enabled=true:开启 HTTPs bearer token 认证;
    • 对于未通过 x509 证书和 webhook 认证的请求(kube-apiserver 或其他客户端),将被拒绝,提示 Unauthorized;
    • authroization.mode=Webhook:kubelet 使用 SubjectAccessReview API 查询 kube-apiserver 某 user、group 是否具有操作资源的权限(RBAC);
    • featureGates.RotateKubeletClientCertificate、featureGates.RotateKubeletServerCertificate:自动 rotate 证书,证书的有效期取决于 kube-controller-manager 的 –experimental-cluster-signing-duration 参数;
    • 需要 root 账户运行;

2.3、创建kubelet systemd unit 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# cat /usr/lib/systemd/system/kubelet.service 
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/log/k8s/kubelet
ExecStart=/usr/bin/kubelet \
--allow-privileged=true \
--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig \
--cert-dir=/etc/kubernetes/ssl \
--network-plugin=cni \
--cni-conf-dir=/etc/cni/net.d \
--cni-bin-dir=/opt/cni/bin \
--container-runtime=docker \
--container-runtime-endpoint=unix:///var/run/dockershim.sock \
--root-dir=/var/lib/kubelet \
--kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
--config=/etc/kubernetes/kubelet-config.yaml \
--hostname-override=##node_ip## \
--pod-infra-container-image=registry.cn-beijing.aliyuncs.com/images_k8s/pause-amd64:3.1 \
--image-pull-progress-deadline=15m \
--volume-plugin-dir=/var/lib/kubelet/kubelet-plugins/volume/exec/ \
--logtostderr=true \
--v=2
Restart=always
RestartSec=5
StartLimitInterval=0

[Install]
WantedBy=multi-user.target
  • 如果设置了 –hostname-override 选项,则 kube-proxy 也需要设置该选项,否则会出现找不到 Node 的情况;
  • –bootstrap-kubeconfig:指向 bootstrap kubeconfig 文件,kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求;
  • K8S approve kubelet 的 csr 请求后,在 –cert-dir 目录创建证书和私钥文件,然后写入 –kubeconfig 文件;
  • –pod-infra-container-image 不使用 redhat 的 pod-infrastructure:latest 镜像,它不能回收容器的僵尸;

2.4、Bootstrap Token Auth 和授予权限

        kubelet 启动时查找 –kubeletconfig 参数对应的文件是否存在,如果不存在则使用 –bootstrap-kubeconfig 指定的 kubeconfig 文件向 kube-apiserver 发送证书签名请求 (CSR)。
        kube-apiserver 收到 CSR 请求后,对其中的 Token 进行认证,认证通过后将请求的 user 设置为 system:bootstrap:,group 设置为 system:bootstrappers,这一过程称为 Bootstrap Token Auth。

默认情况下,这个 user 和 group 没有创建 CSR 的权限,kubelet 启动失败,错误日志如下:

1
2
3
4
5
6
Sep 16 11:39:43 node-02 kubelet[20385]: E0916 11:39:43.858672   20385 reflector.go:126] k8s.io/client-go/informers/factory.go:133: Failed to list *v1beta1.RuntimeClass: Unauthorized
Sep 16 11:39:43 node-02 kubelet[20385]: E0916 11:39:43.860429 20385 reflector.go:126] k8s.io/client-go/informers/factory.go:133: Failed to list *v1beta1.CSIDriver: Unauthorized
Sep 16 11:39:43 node-02 kubelet[20385]: E0916 11:39:43.903098 20385 kubelet.go:2244] node "172.21.16.240" not found
Sep 16 11:39:43 node-02 kubelet[20385]: E0916 11:39:43.985568 20385 reflector.go:126] k8s.io/kubernetes/pkg/kubelet/kubelet.go:442: Failed to list *v1.Service: Unauthorized
Sep 16 11:39:43 node-02 kubelet[20385]: E0916 11:39:43.986781 20385 reflector.go:126] k8s.io/kubernetes/pkg/kubelet/config/apiserver.go:47: Failed to list *v1.Pod: Unauthorized
Sep 16 11:39:43 node-02 kubelet[20385]: E0916 11:39:43.987454 20385 reflector.go:126] k8s.io/kubernetes/pkg/kubelet/kubelet.go:451: Failed to list *v1.Node: Unauthorized

解决办法是:创建一个 clusterrolebinding,将 group system:bootstrappers 和 clusterrole system:node-bootstrapper 绑定:

1
# kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers

2.5、启动 kubelet 服务

1
2
# mkdir -p /var/log/k8s/kubelet
# systemctl daemon-reload && systemctl enable kubelet && systemctl restart kubelet && systemctl status kubelet
  • 启动服务前必须先创建工作目录;
  • 关闭 swap 分区,否则 kubelet 会启动失败;

        kubelet 启动后使用 –bootstrap-kubeconfig 向 kube-apiserver 发送 CSR 请求,当这个 CSR 被 approve 后,kube-controller-manager 为 kubelet 创建 TLS 客户端证书、私钥和 –kubeletconfig 文件。
        注意:kube-controller-manager 需要配置 –cluster-signing-cert-file 和 –cluster-signing-key-file 参数,才会为 TLS Bootstrap 创建证书和私钥。

1
2
3
4
5
6
7
8
9
# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-bwcbm 82s system:bootstrap:016e9x Pending
csr-gqdhf 105s system:bootstrap:64pk36 Pending
csr-q995g 6m57s system:bootstrap:4l4tcx Pending
csr-xx45v 7m33s system:bootstrap:4l4tcx Pending

# kubectl get nodes
No resources found.

2.6、自动 approve CSR 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# cat /etc/kubernetes/csr-crb.yaml
# Approve all CSRs for the group "system:bootstrappers"
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: auto-approve-csrs-for-group
subjects:
- kind: Group
name: system:bootstrappers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
apiGroup: rbac.authorization.k8s.io
---
# To let a node of the group "system:nodes" renew its own credentials
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: node-client-cert-renewal
subjects:
- kind: Group
name: system:nodes
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
apiGroup: rbac.authorization.k8s.io
---
# A ClusterRole which instructs the CSR approver to approve a node requesting a
# serving cert matching its client cert.
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: approve-node-server-renewal-csr
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/selfnodeserver"]
verbs: ["create"]
---
# To let a node of the group "system:nodes" renew its own server credentials
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: node-server-cert-renewal
subjects:
- kind: Group
name: system:nodes
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: approve-node-server-renewal-csr
apiGroup: rbac.authorization.k8s.io
  • auto-approve-csrs-for-group:自动 approve node 的第一次 CSR; 注意第一次 CSR 时,请求的 Group 为 system:bootstrappers;
  • node-client-cert-renewal:自动 approve node 后续过期的 client 证书,自动生成的证书 Group 为 system:nodes;
  • node-server-cert-renewal:自动 approve node 后续过期的 server 证书,自动生成的证书 Group 为 system:nodes;

2.6、等查看 kubelet 的情况

待一段时间(1-10 分钟),三个节点的 CSR 都被自动 approved:

1
2
3
4
5
6
7
8
# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-2t4bj 2m58s system:node:172.21.16.240 Pending
csr-2z2mq 4m14s system:node:172.21.16.204 Pending
csr-bwcbm 6m6s system:bootstrap:016e9x Approved,Issued
csr-gqdhf 6m29s system:bootstrap:64pk36 Approved,Issued
csr-q995g 11m system:bootstrap:4l4tcx Approved,Issued
csr-xx45v 12m system:bootstrap:4l4tcx Pending
  • 所有节点均 ready:
    1
    2
    3
    4
    5
    # kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    172.21.16.204 Ready <none> 4m17s v1.14.6
    172.21.16.240 Ready <none> 3m2s v1.14.6
    172.21.16.231 Ready <none> 3s v1.14.6

kube-controller-manager 为各 node 生成了 kubeconfig 文件和公私钥:

1
2
3
4
5
6
# ls -l /etc/kubernetes/kubelet.kubeconfig
-rw------- 1 root root 2311 Sep 16 11:31 /etc/kubernetes/kubelet.kubeconfig

# ls -l /etc/kubernetes/ssl/|grep kubelet
-rw------- 1 root root 1281 Sep 16 11:43 kubelet-client-2019-09-16-11-43-20.pem
lrwxrwxrwx 1 root root 58 Sep 16 11:43 kubelet-client-current.pem -> /etc/kubernetes/ssl/kubelet-client-2019-09-16-11-43-20.pem

没有自动生成 kubelet server 证书;

2.8、手动 approve server cert csr

基于安全性考虑,CSR approving controllers 不会自动 approve kubelet server 证书签名请求,需要手动 approve:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-2t4bj 3m5s system:node:172.21.16.240 Pending
csr-2z2mq 4m21s system:node:172.21.16.204 Pending
csr-bwcbm 6m13s system:bootstrap:016e9x Approved,Issued
csr-gqdhf 6m36s system:bootstrap:64pk36 Approved,Issued
csr-gtkrt 7s system:node:172.21.16.231 Pending
csr-q995g 11m system:bootstrap:4l4tcx Approved,Issued
csr-xx45v 12m system:bootstrap:4l4tcx Pending

# kubectl certificate approve csr-2t4bj
certificatesigningrequest.certificates.k8s.io/csr-2t4bj approved

# kubectl certificate approve csr-2z2mq
certificatesigningrequest.certificates.k8s.io/csr-2z2mq approved

# kubectl certificate approve csr-gtkrt
certificatesigningrequest.certificates.k8s.io/csr-gtkrt approved

# ls -l /etc/kubernetes/ssl/|grep kubelet
-rw------- 1 root root 1281 Sep 16 11:43 kubelet-client-2019-09-16-11-43-20.pem
lrwxrwxrwx 1 root root 58 Sep 16 11:43 kubelet-client-current.pem -> /etc/kubernetes/ssl/kubelet-client-2019-09-16-11-43-20.pem
-rw------- 1 root root 1305 Sep 16 11:44 kubelet-server-2019-09-16-11-44-12.pem
lrwxrwxrwx 1 root root 58 Sep 16 11:44 kubelet-server-current.pem -> /etc/kubernetes/ssl/kubelet-server-2019-09-16-11-44-12.pem

2.9、kubelet 提供的 API 接口

kubelet 启动后监听多个端口,用于接收 kube-apiserver 或其它客户端发送的请求:

1
2
3
4
# netstat -lnpt|grep kubelet
tcp 0 0 127.0.0.1:43042 0.0.0.0:* LISTEN 22726/kubelet
tcp 0 0 172.21.16.231:10248 0.0.0.0:* LISTEN 22726/kubelet
tcp6 0 0 :::10250 :::* LISTEN 22726/kubelet
  • 10248: healthz http 服务;
  • 10250: https 服务,访问该端口时需要认证和授权(即使访问 /healthz 也需要);
  • 未开启只读端口 10255;
  • 从 K8S v1.10 开始,去除了 –cadvisor-port 参数(默认 4194 端口),不支持访问 cAdvisor UI & API。

由于关闭了匿名认证,同时开启了 webhook 授权,所有访问 10250 端口 https API 的请求都需要被认证和授权。
        预定义的 ClusterRole system:kubelet-api-admin 授予访问 kubelet 所有 API 的权限(kube-apiserver 使用的 kubernetes 证书 User 授予了该权限):

1
2
3
4
5
6
7
8
9
10
11
12
13
# kubectl describe clusterrole system:kubelet-api-admin
Name: system:kubelet-api-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
nodes/log [] [] [*]
nodes/metrics [] [] [*]
nodes/proxy [] [] [*]
nodes/spec [] [] [*]
nodes/stats [] [] [*]
nodes [] [] [get list watch proxy]

2.10、kubelet api 认证和授权

kubelet 配置了如下认证参数:

  • authentication.anonymous.enabled:设置为 false,不允许匿名访问 10250 端口;
  • authentication.x509.clientCAFile:指定签名客户端证书的 CA 证书,开启 HTTPs 证书认证;
  • authentication.webhook.enabled=true:开启 HTTPs bearer token 认证;

同时配置了如下授权参数:

  • authroization.mode=Webhook:开启 RBAC 授权

        kubelet 收到请求后,使用 clientCAFile 对证书签名进行认证,或者查询 bearer token 是否有效。如果两者都没通过,则拒绝请求,提示 Unauthorized:

1
2
3
4
5
# curl -s --cacert /etc/kubernetes/ssl/ca.pem https://172.21.16.231:10250/metrics
Unauthorized

# curl -s --cacert /etc/kubernetes/ssl/ca.pem -H "Authorization: Bearer 123456" https://172.21.16.231:10250/metrics
Unauthorized

        通过认证后,kubelet 使用 SubjectAccessReview API 向 kube-apiserver 发送请求,查询证书或 token 对应的 user、group 是否有操作资源的权限(RBAC);

2.11、证书认证和授权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 权限不足的证书;
# curl -s --cacert /etc/kubernetes/ssl/ca.pem --cert /etc/kubernetes/ssl/kube-controller-manager.pem --key /etc/kubernetes/ssl/kube-controller-manager-key.pem https://172.21.16.231:10250/metrics
Forbidden (user=system:kube-controller-manager, verb=get, resource=nodes, subresource=metrics)

# 使用部署 kubectl 命令行工具时创建的、具有最高权限的 admin 证书;
# curl -s --cacert /etc/kubernetes/ssl/ca.pem --cert /etc/kubernetes/ssl/admin.pem --key /etc/kubernetes/ssl/admin-key.pem https://172.21.16.231:10250/metrics
# HELP apiserver_audit_event_total Counter of audit events generated and sent to the audit backend.
# TYPE apiserver_audit_event_total counter
apiserver_audit_event_total 0
# HELP apiserver_audit_requests_rejected_total Counter of apiserver requests rejected due to an error in audit logging backend.
# TYPE apiserver_audit_requests_rejected_total counter
apiserver_audit_requests_rejected_total 0
# HELP apiserver_client_certificate_expiration_seconds Distribution of the remaining lifetime on the certificate used to authenticate a request.
# TYPE apiserver_client_certificate_expiration_seconds histogram
apiserver_client_certificate_expiration_seconds_bucket{le="0"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="1800"} 0
  • –cacert、–cert、–key 的参数值必须是文件路径,如上面的 ./admin.pem 不能省略 ./,否则返回 401 Unauthorized;

2.12、bear token 认证和授权

        创建一个 ServiceAccount,将它和 ClusterRole system:kubelet-api-admin 绑定,从而具有调用 kubelet API 的权限:

1
2
3
4
5
6
7
# kubectl create sa kubelet-api-test
# kubectl create clusterrolebinding kubelet-api-test --clusterrole=system:kubelet-api-admin --serviceaccount=default:kubelet-api-test
# SECRET=$(kubectl get secrets | grep kubelet-api-test | awk '{print $1}')
# TOKEN=$(kubectl describe secret ${SECRET} | grep -E '^token' | awk '{print $2}')
# echo ${TOKEN}

# curl -s --cacert /etc/kubernetes/ssl/ca.pem -H "Authorization: Bearer ${TOKEN}" https://172.21.16.231:10250/metrics|head

3、cadvisor 和 metrics

        cadvisor 是内嵌在 kubelet 二进制中的,统计所在节点各容器的资源(CPU、内存、磁盘、网卡)使用情况的服务。
浏览器访问 https://172.21.16.231:10250/metricshttps://172.21.16.231:10250/metrics/cadvisor 分别返回 kubelet 和 cadvisor 的 metrics。
img

注意:

  • kubelet.config.json 设置 authentication.anonymous.enabled 为 false,不允许匿名证书访问 10250 的 https 服务;
  • 参考kubelet提供api请求接口,创建和导入相关证书,然后访问上面的 10250 端口;

3.1、获取 kubelet 的配置

从 kube-apiserver 获取各节点 kubelet 的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# 使用部署 kubectl 命令行工具时创建的、具有最高权限的 admin 证书;

# curl -sSL --cacert /etc/kubernetes/ssl/ca.pem --cert /etc/kubernetes/ssl/admin.pem --key /etc/kubernetes/ssl/admin-key.pem ${KUBE_APISERVER}/api/v1/nodes/172.21.16.231/proxy/configz | jq '.kubeletconfig|.kind="KubeletConfiguration"|.apiVersion="kubelet.config.k8s.io/v1beta1"'
{
"syncFrequency": "1m0s",
"fileCheckFrequency": "20s",
"httpCheckFrequency": "20s",
"address": "0.0.0.0",
"port": 10250,
"rotateCertificates": true,
"serverTLSBootstrap": true,
"authentication": {
"x509": {
"clientCAFile": "/etc/kubernetes/ssl/ca.pem"
},
"webhook": {
"enabled": true,
"cacheTTL": "2m0s"
},
"anonymous": {
"enabled": false
}
},
"authorization": {
"mode": "Webhook",
"webhook": {
"cacheAuthorizedTTL": "5m0s",
"cacheUnauthorizedTTL": "30s"
}
},
"registryPullQPS": 0,
"registryBurst": 20,
"eventRecordQPS": 0,
"eventBurst": 20,
"enableDebuggingHandlers": true,
"enableContentionProfiling": true,
"healthzPort": 10248,
"healthzBindAddress": "172.21.16.231",
"oomScoreAdj": -999,
"clusterDomain": "cluster.local",
"clusterDNS": [
"10.254.0.2"
],
"streamingConnectionIdleTimeout": "4h0m0s",
"nodeStatusUpdateFrequency": "10s",
"nodeStatusReportFrequency": "1m0s",
"nodeLeaseDurationSeconds": 40,
"imageMinimumGCAge": "2m0s",
"imageGCHighThresholdPercent": 85,
"imageGCLowThresholdPercent": 80,
"volumeStatsAggPeriod": "1m0s",
"cgroupsPerQOS": true,
"cgroupDriver": "cgroupfs",
"cpuManagerPolicy": "none",
"cpuManagerReconcilePeriod": "10s",
"runtimeRequestTimeout": "10m0s",
"hairpinMode": "promiscuous-bridge",
"maxPods": 100,
"podCIDR": "172.30.0.0/16",
"podPidsLimit": -1,
"resolvConf": "/etc/resolv.conf",
"cpuCFSQuota": true,
"cpuCFSQuotaPeriod": "100ms",
"maxOpenFiles": 1000000,
"contentType": "application/vnd.kubernetes.protobuf",
"kubeAPIQPS": 1000,
"kubeAPIBurst": 2000,
"serializeImagePulls": false,
"evictionHard": {
"memory.available": "100Mi"
},
"evictionPressureTransitionPeriod": "5m0s",
"enableControllerAttachDetach": true,
"makeIPTablesUtilChains": true,
"iptablesMasqueradeBit": 14,
"iptablesDropBit": 15,
"failSwapOn": true,
"containerLogMaxSize": "20Mi",
"containerLogMaxFiles": 10,
"configMapAndSecretChangeDetectionStrategy": "Watch",
"enforceNodeAllocatable": [
"pods"
],
"kind": "KubeletConfiguration",
"apiVersion": "kubelet.config.k8s.io/v1beta1"
}

4、部署 kube-proxy 组件

        kube-proxy 运行在所有 worker 节点上,它监听 apiserver 中 service 和 endpoint 的变化情况,创建路由规则以提供服务 IP 和负载均衡功能。

4.1、创建 kube-proxy 证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat > kube-proxy-csr.json <<EOF
{
"CN": "system:kube-proxy",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
  • CN:指定该证书的 User 为 system:kube-proxy;

  • 预定义的 RoleBinding system:node-proxier 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;

  • 该证书只会被 kube-proxy 当做 client 证书使用,所以 hosts 字段为空;

  • 生成证书和私钥

    1
    2
    3
    4
    # cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes  kube-proxy-csr.json | cfssljson -bare kube-proxy

    # ls kube-proxy*.pem
    kube-proxy-key.pem kube-proxy.pem

4.2、创建和分发 kubeconfig 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# kubectl config set-cluster kubernetes \
--certificate-authority=ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kube-proxy.kubeconfig

# kubectl config set-credentials kube-proxy \
--client-certificate=kube-proxy.pem \
--client-key=kube-proxy-key.pem \
--embed-certs=true \
--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
  • –embed-certs=true:将 ca.pem 和 admin.pem 证书内容嵌入到生成的 kubectl-proxy.kubeconfig 文件中(不加时,写入的是证书文件路径)

4.3、创建 kube-proxy 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# cat /etc/kubernetes/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
burst: 200
kubeconfig: "/etc/kubernetes/kube-proxy.kubeconfig"
qps: 100
bindAddress: 0.0.0.0
healthzBindAddress: ##node_ip##:10256
metricsBindAddress: ##node_ip##:10249
enableProfiling: true
clusterCIDR: 10.244.0.0/16
hostnameOverride: ##node_ip##
mode: "ipvs"
portRange: ""
kubeProxyIPTablesConfiguration:
masqueradeAll: false
kubeProxyIPVSConfiguration:
scheduler: rr
excludeCIDRs: []
  • bindAddress: 监听地址;
  • clientConnection.kubeconfig: 连接 apiserver 的 kubeconfig 文件;
  • clusterCIDR: kube-proxy 根据 –cluster-cidr 判断集群内部和外部流量,指定 –cluster-cidr 或 –masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT;
  • hostnameOverride: 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 ipvs 规则;
  • mode: 使用 ipvs 模式;

4.4、创建kube-proxy systemd unit 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
# cat /usr/lib/systemd/system/kube-proxy.service 
[Service]
WorkingDirectory=/var/log/k8s/kube-proxy
ExecStart=/usr/bin/kube-proxy \
--config=/etc/kubernetes/kube-proxy-config.yaml \
--logtostderr=true \
--v=2
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

4.5、启动 kube-proxy 服务

1
2
3
# mkdir -p /var/log/k8s/kube-proxy

# systemctl daemon-reload && systemctl enable kube-proxy && systemctl restart kube-proxy && systemctl status kube-proxy

4.5、检查

  • 查看监听端口

    1
    2
    3
    # netstat -lnpt|grep kube-prox
    tcp 0 0 172.21.16.231:10256 0.0.0.0:* LISTEN 27423/kube-proxy
    tcp 0 0 172.21.16.231:10249 0.0.0.0:* LISTEN 27423/kube-proxy
  • 查看 ipvs 路由规则

    1
    2
    3
    4
    5
    6
    7
    8
    # ipvsadm -ln
    IP Virtual Server version 1.2.1 (size=4096)
    Prot LocalAddress:Port Scheduler Flags
    -> RemoteAddress:Port Forward Weight ActiveConn InActConn
    TCP 10.254.0.1:443 rr
    -> 172.21.17.30:6443 Masq 1 0 0
    -> 172.21.17.31:6443 Masq 1 0 0
    -> 172.21.16.110:6443 Masq 1 0 0
坚持原创技术分享,您的支持将鼓励我继续创作!
0%