首页 » 云自动化 » k8s » 正文

Kubernet 高可用集群搭建(上)

前排前排:

近期公司忙于重构,线上和线下均采用K8S,且均为高可用架构。为了日后便于分析问题,也为了开源的精神,故将此篇文章分享于此。(还是采用个人虚拟机,资源有限,但是服务的部署还是一一涵盖的!)

还是采用CentOS 7 版本系统,由于启用了 TLS 双向认证、RBAC 授权等严格的安全机制,建议从头开始部署,否则可能会认证、授权等失败!

一、组件版本 && 集群环境

组件版本
  • Kubernetes 1.8.6
  • Docker 17.10.0-ce
  • Etcd 3.2.9
  • Flanneld
  • TLS 认证通信(所有组件,如etcd、kubernetes master 和node)
  • RBAC 授权
  • kubelet TLS Bootstrapping
  • kubedns、dashboard、heapster等插件
  • harbor,使用nfs后端存储
etcd 集群 && k8s master 机器 && k8s node 机器
IP Address Role 组件
192.168.161.161 master1 etcd,kube-scheduler,kube-controller-manager,kube-apiserver
192.168.161.162 master2 etcd,kube-scheduler,kube-controller-manager,kube-apiserver
192.168.161.163 master3 etcd,kube-scheduler,kube-controller-manager,kube-apiserver
192.168.161.164 node1 kube-proxy,kubelet,docker,flanneld
192.168.161.165 node2 kube-proxy,kubelet,docker,flanneld
192.168.161.200 vip api-server 高可用_ip
首先安装前要确认以下几项都已将准备完成:
  • 所有节点彼此网络互通,并且master1 SSH 登入其他节点为 passwdless。
  • 所有防火墙与 SELinux 已关闭。如 CentOS:
# systemctl stop firewalld && systemctl disable firewalld && setenforce 0 && sed -i '/SELINUX/s/enforcing/disabled/' /etc/selinux/config

关闭Swap

swapoff -a 
sed 's/.*swap.*/#&/' /etc/fstab

修改 /etc/fstab 文件,注释掉 SWAP 的自动挂载,使用free -m确认swap已经关闭。

所有节点需要设定/etc/host解析到所有主机。

192.168.161.161 master1
192.168.161.162 master2
192.168.161.163 master3
192.168.161.164 node1
192.168.161.165 node2

所有节点需要设定/etc/sysctl.d/k8s.conf的系统参数。

# cat <<EOF > /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

加载br_netfilter

# modprobe br_netfilter
# echo "modprobe br_netfilter" >> /etc/rc.local

刷新使之生效

# sysctl -p /etc/sysctl.d/k8s.conf

设置iptables策略为 ACCEPT

# /sbin/iptables -P FORWARD ACCEPT

# echo  "sleep 60 && /sbin/iptables -P FORWARD ACCEPT" >> /etc/rc.local

安装依赖包

# yum install -y epel-release

# yum install -y yum-utils device-mapper-persistent-data lvm2 net-tools conntrack-tools wget

二、Kubernet 高可用集群搭建–创建证书

创建 CA 证书和秘钥

kubernetes 系统各组件需要使用 TLS 证书对通信进行加密,本文档使用 CloudFlare 的 PKI 工具集 cfssl 来生成 Certificate Authority (CA) 证书和秘钥文件,CA 是自签名的证书,用来签名后续创建的其它 TLS 证书。

生成的 CA 证书和秘钥文件如下:

ca-key.pem
ca.pem
kubernetes-key.pem
kubernetes.pem
kube-proxy.pem
kube-proxy-key.pem
admin.pem
admin-key.pem

使用证书的组件如下:

  • etcd:使用 ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kube-apiserver:使用 ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kubelet:使用 ca.pem;
  • kube-proxy:使用 ca.pem、kube-proxy-key.pem、kube-proxy.pem;
  • kubectl:使用 ca.pem、admin-key.pem、admin.pem;
  • kube-controller-manager:使用 ca-key.pem、ca.pem

以下操作都在 master 节点即 192.168.161.161 上执行,证书只需要创建一次即可,以后在向集群中添加新节点时只要将 /etc/kubernetes/ 目录下的证书拷贝到新节点上即可

安装 CFSSL

wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
chmod +x cfssl_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl

wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x cfssljson_linux-amd64
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson

wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo

export PATH=/usr/local/bin:$PATH

创建 CA 配置文件

mkdir -p /etc/kubernetes/ssl && cd /etc/kubernetes/ssl
cat > ca-config.json << EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "8760h"
      }
    }
  }
}
EOF

ca-config.json:可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;

signing:表示该证书可用于签名其它证书;生成的 ca.pem 证书中 CA=TRUE;

server auth:表示 client 可以用该 CA 对 server 提供的证书进行验证;

client auth:表示 server 可以用该 CA 对 client 提供的证书进行验证;

创建 CA 证书签名请求:

cat > ca-csr.json << EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF

“CN”:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;

“O”:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);

生成 CA 证书和私钥:

# cfssl gencert -initca ca-csr.json | cfssljson -bare ca

创建 kubernetes 证书签名请求文件:

已经被坑过一次了,一定要把所有的机器都添加进去包括虚拟IP

添加IP地址的时候,需要想到有个域名转发的nginx server也需要添加如下

也就是说 当你后期扩容节点的时候,如果你这边没有写扩容节点的IP,是不可以扩容的。

cat > kubernetes-csr.json << EOF
{
   "CN": "kubernetes",
    "hosts": [
      "127.0.0.1",
      "192.168.161.161",
      "192.168.161.162",
      "192.168.161.163",
      "192.168.161.200",
      "10.254.0.1",
      "kubernetes",
      "kubernetes.default",
      "kubernetes.default.svc",
      "kubernetes.default.svc.cluster",
      "kubernetes.default.svc.cluster.local"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "BeiJing",
            "L": "BeiJing",
            "O": "k8s",
            "OU": "System"
        }
    ]
}
EOF

如上4个ip替换成自己服务器的ip。

hosts 中的内容可以为空,即使按照上面的配置,向集群中增加新节点后也不需要重新生成证书。

如果 hosts 字段不为空则需要指定授权使用该证书的 IP 或域名列表,由于该证书后续被 etcd 集群和 kubernetes master 集群使用,所以上面分别指定了 etcd 集群、kubernetes master 集群的主机 IP 和 kubernetes 服务的服务 IP(一般是 kue-apiserver 指定的 service-cluster-ip-range 网段的第一个IP,如 10.254.0.1。

生成 kubernetes 证书和私钥

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

# ls kubernetes*
kubernetes.csr  kubernetes-csr.json  kubernetes-key.pem  kubernetes.pem

创建 admin 证书

cat > admin-csr.json << EOF
{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
EOF
  • 后续 kube-apiserver 使用 RBAC 对客户端(如 kubelet、kube-proxy、Pod)请求进行授权;
  • kube-apiserver 预定义了一些 RBAC 使用的 RoleBindings,如 cluster-admin 将 Groupsystem:masters 与 Role cluster-admin 绑定,该 Role 授予了调用kube-apiserver 的所有API的权限
  • OU 指定该证书的 Group 为 system:masters,kubelet 使用该证书访问 kube-apiserver时 ,由于证书被 CA 签名,所以认证通过,同时由于证书用户组为经过预授权的system:masters,所以被授予访问所有 API 的权限;

生成 admin 证书和私钥

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

# ls admin*
admin.csr  admin-csr.json  admin-key.pem  admin.pem

创建 kube-proxy 证书

cat > kube-proxy-csr.json << EOF
{
  "CN": "system:kube-proxy",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF
  • CN 指定该证书的 User 为 system:kube-proxy;
  • kube-apiserver 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;

生成 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

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

校验证书

以 kubernetes 证书为例

使用 Opsnssl 命令

$ openssl x509  -noout -text -in  kubernetes.pem
...
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=Kubernetes
        Validity
            Not Before: Apr  5 05:36:00 2017 GMT
            Not After : Apr  5 05:36:00 2018 GMT
        Subject: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=kubernetes
...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                DD:52:04:43:10:13:A9:29:24:17:3A:0E:D7:14:DB:36:F8:6C:E0:E0
            X509v3 Authority Key Identifier:
                keyid:44:04:3B:60:BD:69:78:14:68:AF:A0:41:13:F6:17:07:13:63:58:CD

            X509v3 Subject Alternative Name:
                DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster, DNS:kubernetes.default.svc.cluster.local, IP Address:127.0.0.1, IP Address:172.20.0.112, IP Address:172.20.0.113, IP Address:172.20.0.114, IP Address:172.20.0.115, IP Address:10.254.0.1
...
  • 确认 Issuer 字段的内容和 ca-csr.json 一致;
  • 确认 Subject 字段的内容和 kubernetes-csr.json 一致;
  • 确认 X509v3 Subject Alternative Name 字段的内容和 kubernetes-csr.json 一致;
  • 确认 X509v3 Key Usage、Extended Key Usage 字段的内容和 ca-config.json 中 kubernetesprofile 一致;

使用 Cfssl-Certinfo 命令

$ cfssl-certinfo -cert kubernetes.pem
...
{
  "subject": {
    "common_name": "kubernetes",
    "country": "CN",
    "organization": "k8s",
    "organizational_unit": "System",
    "locality": "BeiJing",
    "province": "BeiJing",
    "names": [
      "CN",
      "BeiJing",
      "BeiJing",
      "k8s",
      "System",
      "kubernetes"
    ]
  },
  "issuer": {
    "common_name": "Kubernetes",
    "country": "CN",
    "organization": "k8s",
    "organizational_unit": "System",
    "locality": "BeiJing",
    "province": "BeiJing",
    "names": [
      "CN",
      "BeiJing",
      "BeiJing",
      "k8s",
      "System",
      "Kubernetes"
    ]
  },
  "serial_number": "174360492872423263473151971632292895707129022309",
  "sans": [
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster",
    "kubernetes.default.svc.cluster.local",
    "127.0.0.1",
    "10.64.3.7",
    "10.254.0.1"
  ],
  "not_before": "2018-06-05T05:36:00Z",
  "not_after": "2019-06-05T05:36:00Z",
  "sigalg": "SHA256WithRSA",
...

分发证书

将生成的证书和秘钥文件(后缀名为.pem)拷贝到所有master和node节点的 /etc/kubernetes/ssl 目录下:

需要在所有的机器上面创建/etc/kubernetes/ssl目录

$ mkdir -p /etc/kubernetes/ssl

$ cp *.pem /etc/kubernetes/ssl

master1

$ scp *.pem 192.168.161.162:/etc/kubernetes/ssl

$ scp *.pem 192.168.161.163:/etc/kubernetes/ssl

$ scp *.pem 192.168.161.164:/etc/kubernetes/ssl

$ scp *.pem 192.168.161.165:/etc/kubernetes/ssl

三、Kubernet 高可用集群搭建–Etcd集群

在三个节点都安装etcd,下面的操作需要在三个节点都执行一遍

下载etcd安装包

# wget https://github.com/coreos/etcd/releases/download/v3.2.12/etcd-v3.2.12-linux-amd64.tar.gz

# tar -xvf etcd-v3.2.12-linux-amd64.tar.gz

# sudo mv etcd-v3.2.12-linux-amd64/etcd* /usr/local/bin
创建工作目录
mkdir -p /var/lib/etcd      (根据实际业务场景,修改数据盘位置)
创建systemd unit 文件

当然 在其它两台机器上面的地址需要更改为另外两台机器的IP。

cat > etcd.service << EOF
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos

[Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
ExecStart=/usr/local/bin/etcd \\
  --name master1 \\
  --cert-file=/etc/kubernetes/ssl/kubernetes.pem \\
  --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \\
  --peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \\
  --peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \\
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \\
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \\
  --initial-advertise-peer-urls https://192.168.161.161:2380 \\
  --listen-peer-urls https://192.168.161.161:2380 \\
  --listen-client-urls https://192.168.161.161:2379,http://127.0.0.1:2379 \\
  --advertise-client-urls https://192.168.161.161:2379 \\
  --initial-cluster-token etcd-cluster-0 \\
  --initial-cluster master1=https://192.168.161.161:2380,master2=https://192.168.161.162:2380,master3=https://192.168.161.163:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

如上的IP地址192.168.161.XXX 请逐一替换成你自己服务器的IP。 不要忽略name

下面给出常用配置的参数和它们的解释,方便理解:

--name:方便理解的节点名称,默认为 default,在集群中应该保持唯一,可以使用 hostname
--data-dir:服务运行数据保存的路径,默认为 ${name}.etcd
--snapshot-count:指定有多少事务(transaction)被提交时,触发截取快照保存到磁盘
--heartbeat-intervalleader 多久发送一次心跳到 followers。默认值是 100ms
--eletion-timeout:重新投票的超时时间,如果 follow 在该时间间隔没有收到心跳包,会触发重新投票,默认为 1000 ms
--listen-peer-urls:和同伴通信的地址,比如 http://ip:2380,如果有多个,使用逗号分隔。需要所有节点都能够访问,所以不要使用 localhost
--listen-client-urls:对外提供服务的地址:比如 http://ip:2379,http://127.0.0.1:2379,客户端会连接到这里和 etcd 交互
--advertise-client-urls:对外公告的该节点客户端监听地址,这个值会告诉集群中其他节点
--initial-advertise-peer-urls:该节点同伴监听地址,这个值会告诉集群中其他节点
--initial-cluster:集群中所有节点的信息,格式为 node1=http://ip1:2380,node2=http://ip2:2380,…。注意:这里的 node1 是节点的 --name 指定的名字;后面的 ip1:2380  --initial-advertise-peer-urls 指定的值
--initial-cluster-state:新建集群的时候,这个值为 new;假如已经存在的集群,这个值为 existing
--initial-cluster-token:创建集群的 token,这个值每个集群保持唯一。这样的话,如果你要重新创建集群,即使配置和之前一样,也会再次生成新的集群和节点 uuid;否则会导致多个集群之间的冲突,造成未知的错误

所有以 –init 开头的配置都是在 bootstrap 集群的时候才会用到,后续节点的重启会被忽略。

NOTE:所有的参数也可以通过环境变量进行设置,–my-flag 对应环境变量的 ETCD_MY_FLAG;但是命令行指定的参数会覆盖环境变量对应的值。

指定 etcd 的工作目录为 /var/lib/etcd,数据目录为 /var/lib/etcd,需在启动服务前创建这个目录,否则启动服务的时候会报错“Failed at step CHDIR spawning /usr/bin/etcd: No such file or directory”;

为了保证通信安全,需要指定 etcd 的公私钥(cert-file和key-file)、Peers 通信的公私钥和 CA 证书(peer-cert-file、peer-key-file、peer-trusted-ca-file)、客户端的CA证书(trusted-ca-file);

创建 kubernetes.pem 证书时使用的 kubernetes-csr.json 文件的 hosts 字段包含所有 etcd 节点的IP,否则证书校验会出错;

–initial-cluster-state 值为 new 时,–name 的参数值必须位于 –initial-cluster 列表中;

启动 etcd 服务

cp etcd.service /etc/systemd/system/

systemctl daemon-reload

systemctl enable etcd

systemctl start etcd

systemctl status etcd

最先启动的 etcd 进程会卡住一段时间,等待其它节点上的 etcd 进程加入集群,为正常现象。

如上操作请确认一定要在三台机器上面都要执行;(单节点ETCD除外)

验证etcd服务,在任何一个etcd节点执行:

如果不添加密钥参数是会报错的:

[root@master1 ssl]# etcdctl cluster-health
failed to check the health of member 1d6f3edf4016ed5d on https://192.168.161.163:2379: Get https://192.168.161.163:2379/health: x509: certificate signed by unknown authority
member 1d6f3edf4016ed5d is unreachable: [https://192.168.161.163:2379] are all unreachable
failed to check the health of member 3d9715816751f109 on https://192.168.161.162:2379: Get https://192.168.161.162:2379/health: x509: certificate signed by unknown authority
member 3d9715816751f109 is unreachable: [https://192.168.161.162:2379] are all unreachable
failed to check the health of member d5fabdfaee58386c on https://192.168.161.161:2379: Get https://192.168.161.161:2379/health: x509: certificate signed by unknown authority
member d5fabdfaee58386c is unreachable: [https://192.168.161.161:2379] are all unreachable
cluster is unhealthy

使用密钥方式检查集群状态

[root@master1 ssl]# etcdctl \
    --ca-file=/etc/kubernetes/ssl/ca.pem \
    --cert-file=/etc/kubernetes/ssl/kubernetes.pem \
    --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
    cluster-health
    
member 1d6f3edf4016ed5d is healthy: got healthy result from https://192.168.161.163:2379
member 3d9715816751f109 is healthy: got healthy result from https://192.168.161.162:2379
member d5fabdfaee58386c is healthy: got healthy result from https://192.168.161.161:2379
cluster is healthy

部署 Flannel

在三个节点都安装Flannel,下面的操作需要在三个节点都执行一遍

下载安装Flannel

# wget https://github.com/coreos/flannel/releases/download/v0.9.1/flannel-v0.9.1-linux-amd64.tar.gz

# mkdir flannel

# tar -xzvf flannel-v0.9.1-linux-amd64.tar.gz -C flannel

# cp flannel/{flanneld,mk-docker-opts.sh} /usr/local/bin

向 etcd 写入网段信息,这两个命令只需要任意一个节点上执行一次就可以

etcdctl --endpoints=https://192.168.161.161:2379,https://192.168.161.162:2379,https://192.168.161.163:2379 \
--ca-file=/etc/kubernetes/ssl/ca.pem \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
mkdir /kubernetes/network

etcdctl --endpoints=https://192.168.161.161:2379,https://192.168.161.162:2379,https://192.168.161.163:2379 \
--ca-file=/etc/kubernetes/ssl/ca.pem \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
mk /kubernetes/network/config '{"Network":"172.30.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}'

注意如上更换成自己的IP

创建systemd unit 文件 在node机器上面都需要执行

cat > flanneld.service << EOF
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service

[Service]
Type=notify
ExecStart=/usr/local/bin/flanneld \\
  -etcd-cafile=/etc/kubernetes/ssl/ca.pem \\
  -etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem \\
  -etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem \\
  -etcd-endpoints=https://192.168.161.161:2379,https://192.168.161.162:2379,https://192.168.161.163:2379 \\
  -etcd-prefix=/kubernetes/network
ExecStartPost=/usr/local/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
Restart=on-failure

[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
EOF

mk-docker-opts.sh 脚本将分配给 flanneld 的 Pod 子网网段信息写入到 /run/flannel/docker 文件中,后续 docker 启动时使用这个文件中参数值设置 docker0 网桥;

flanneld 使用系统缺省路由所在的接口和其它节点通信,对于有多个网络接口的机器(如,内网和公网),可以用 -iface=enpxx 选项值指定通信接口;

启动Flannel

mv flanneld.service /etc/systemd/system/

systemctl daemon-reload

systemctl enable flanneld

systemctl start flanneld

systemctl status flanneld

检查flannel服务状态

[root@master1 src]# /usr/local/bin/etcdctl  --endpoints=https://192.168.161.161:2379,https://192.168.161.162:2379,https://192.168.161.163:2379  --ca-file=/etc/kubernetes/ssl/ca.pem  --cert-file=/etc/kubernetes/ssl/kubernetes.pem  --key-file=/etc/kubernetes/ssl/kubernetes-key.pem  ls /kubernetes/network/subnets

/kubernetes/network/subnets/172.30.68.0-24
/kubernetes/network/subnets/172.30.46.0-24

四、Kubernet 高可用集群搭建–kubectl

部署 kubectl 工具,创建kubeconfig文件

kubectl是kubernetes的集群管理工具,任何节点通过kubetcl都可以管理整个k8s集群。

本文是在 master节点 部署,部署成功后会生成 /root/.kube/config 文件,kubectl就是通过这个获取 kube-apiserver 地址、证书、用户名等信息,所以这个文件需要保管好。

下载安装包

# cd /usr/local/src

# wget https://dl.k8s.io/v1.8.6/kubernetes-client-linux-amd64.tar.gz

# tar -xzvf kubernetes-client-linux-amd64.tar.gz

# sudo cp kubernetes/client/bin/kube* /usr/local/bin/

# chmod a+x /usr/local/bin/kube*

# export PATH=/usr/local/bin:$PATH

创建/root/.kube/config

# 设置集群参数,--server指定VIPip
kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=https://192.168.161.200:6443

# 设置客户端认证参数
kubectl config set-credentials admin \
  --client-certificate=/etc/kubernetes/ssl/admin.pem \
  --embed-certs=true \
  --client-key=/etc/kubernetes/ssl/admin-key.pem

# 设置上下文参数
kubectl config set-context kubernetes \
  --cluster=kubernetes \
  --user=admin

# 设置默认上下文
kubectl config use-context kubernetes

注意:如上是注解,标注是后期更清晰些

admin.pem 证书 O 字段值为 system:masters,kube-apiserver 预定义的 RoleBinding cluster-admin 将 Group system:masters 与 Role cluster-admin 绑定,该 Role 授予了调用kube-apiserver 相关 API 的权限

创建bootstrap.kubeconfig

kubelet访问kube-apiserver的时候是通过bootstrap.kubeconfig进行用户验证。

//生成token 变量
# export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')

# cat > token.csv <<EOF
${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF

分发 token.csv 文件

 token.csv 文件拷贝到所有Master  Node /etc/kubernetes/ 目录:

$ cp token.csv /etc/kubernetes/

master01

$ scp /etc/kubernetes/token.csv 192.168.161.162:/etc/kubernetes/

$ scp /etc/kubernetes/token.csv 192.168.161.163:/etc/kubernetes/

$ scp /etc/kubernetes/token.csv 192.168.161.164:/etc/kubernetes/

$ scp /etc/kubernetes/token.csv 192.168.161.165:/etc/kubernetes/

//设置集群参数--serverVIP节点ip
# kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=https://192.168.161.200:6443 \
  --kubeconfig=bootstrap.kubeconfig

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

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

//设置默认上下文
# kubectl config use-context default --kubeconfig=bootstrap.kubeconfig

# cp bootstrap.kubeconfig /etc/kubernetes/

创建kube-proxy.kubeconfig

# 设置集群参数 --server参数为 VIP ip
kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=https://192.168.161.200:6443 \
  --kubeconfig=kube-proxy.kubeconfig

# 设置客户端认证参数
kubectl config set-credentials kube-proxy \
  --client-certificate=/etc/kubernetes/ssl/kube-proxy.pem \
  --client-key=/etc/kubernetes/ssl/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

# cp kube-proxy.kubeconfig /etc/kubernetes/

设置集群参数和客户端认证参数时 –embed-certs 都为 true,这会将 certificate-authority、client-certificate 和 client-key 指向的证书文件内容写入到生成的 kube-proxy.kubeconfig 文件中;

kube-proxy.pem 证书中 CN 为 system:kube-proxy,kube-apiserver 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;

分发 kubeconfig

将两个 kubeconfig 文件拷贝到所有 的 节点的 /etc/kubernetes/ 目录:

master01

$ scp /etc/kubernetes/*.kubeconfig 192.168.161.164:/etc/kubernetes/

$ scp /etc/kubernetes/*.kubeconfig 192.161.161.165:/etc/kubernetes/

分发 kubeconfig 文件

 ~/.kube/config 文件拷贝到运行 kubectl 命令的机器的 ~/.kube/ 目录下。

其它所有masternode节点创建目录:

$ mkdir -p /root/.kube

master01

$ scp ~/.kube/config 192.168.161.162:/root/.kube

$ scp ~/.kube/config 192.168.161.163:/root/.kube

$ scp ~/.kube/config 192.168.161.164:/root/.kube

$ scp ~/.kube/config 192.168.161.165:/root/.kube

五、Kubernet 高可用集群搭建–master安装组件

下载安装文件

# wget https://dl.k8s.io/v1.8.6/kubernetes-server-linux-amd64.tar.gz

# tar -xzvf kubernetes-server-linux-amd64.tar.gz

# cp -r kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler,kubectl,kube-proxy,kubelet} /usr/local/bin/

配置和启动 kube-apiserver(两台master,master2上面直接修改master2的IP即可)

cat > kube-apiserver.service << EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
After=etcd.service

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
  --logtostderr=true \\
  --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,NodeRestriction \\
  --advertise-address=192.168.161.161 \\
  --bind-address=0.0.0.0 \\
  --insecure-bind-address=0.0.0.0 \\
  --authorization-mode=Node,RBAC \\
  --runtime-config=rbac.authorization.k8s.io/v1alpha1 \\
  --kubelet-https=true \\
  --enable-bootstrap-token-auth \\
  --token-auth-file=/etc/kubernetes/token.csv \\
  --service-cluster-ip-range=10.254.0.0/16 \\
  --service-node-port-range=8400-10000 \\
  --tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem \\
  --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \\
  --client-ca-file=/etc/kubernetes/ssl/ca.pem \\
  --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \\
  --etcd-cafile=/etc/kubernetes/ssl/ca.pem \\
  --etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem \\
  --etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem \\
  --etcd-servers=https://192.168.161.161:2379,https://192.168.161.162:2379,https://192.168.161.163:2379 \\
  --enable-swagger-ui=true \\
  --allow-privileged=true \\
  --apiserver-count=3 \\
  --audit-log-maxage=30 \\
  --audit-log-maxbackup=3 \\
  --audit-log-maxsize=100 \\
  --audit-log-path=/var/lib/audit.log \\
  --event-ttl=1h \\
  --v=2
Restart=on-failure
RestartSec=5
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

注意 更换成自己的IP

authorization-mode=RBAC 指定在安全端口使用 RBAC 授权模式,拒绝未通过授权的请求;

kube-schedulerkube-controller-manager 一般和 kube-apiserver 部署在同一台机器上,它们使用非安全端口和 kube-apiserver通信; 

kubeletkube-proxykubectl 部署在其它 Node 节点上,如果通过安全端口访问 kube-apiserver,则必须先通过 TLS 证书认证,再通过 RBAC 授权; 

kube-proxykubectl 通过在使用的证书里指定相关的 UserGroup 来达到通过 RBAC 授权的目的; 

如果使用了 kubelet TLS Boostrap 机制,则不能再指定 kubelet-certificate-authority、–kubelet-client-certificate  kubelet-client-key 选项,否则后续 kube-apiserver 校验 kubelet 证书时出现 x509: certificate signed by unknown authority 错误; 

admission-control 值必须包含 ServiceAccount,否则部署集群插件时会失败;

bind-address 不能为 127.0.0.1 

runtime-config配置为rbac.authorization.k8s.io/v1beta1,表示运行时的apiVersion

service-cluster-ip-range 指定 Service Cluster IP 地址段,该地址段不能路由可达; 

service-node-port-range 指定 NodePort 的端口范围; 

缺省情况下 kubernetes 对象保存在 etcd /registry 路径下,可以通过 etcd-prefix 参数进行调整;

启动 kube-apiserver

# cp kube-apiserver.service /etc/systemd/system/

systemctl daemon-reload

systemctl enable kube-apiserver

systemctl start kube-apiserver

systemctl status kube-apiserver

部署kube-apiserver 高可用

安装haproxy

$ yum install -y haproxy

配置haproxy

由于集群内部有的组建是通过非安全端口访问apiserver 的,有的是通过安全端口访问apiserver 的,所以我们要配置http 和https 两种代理方式,配置文件 /etc/haproxy/haproxy.cfg:(Master1和Master2上直接Copy此配置即可:

listen stats
  bind    *:9000
  mode    http
  stats   enable
  stats   hide-version
  stats   uri       /stats
  stats   refresh   30s
  stats   realm     Haproxy\ Statistics
  stats   auth      Admin:Password

frontend k8s-api
    bind 192.168.161.200:6443
    mode tcp
    option tcplog
    tcp-request inspect-delay 5s
    tcp-request content accept if { req.ssl_hello_type 1 }
    default_backend k8s-api

backend k8s-api
    mode tcp
    option tcplog
    option tcp-check
    balance roundrobin
    default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
    server k8s-api-1 192.168.161.161:6443 check
    server k8s-api-2 192.168.161.162:6443 check

frontend k8s-http-api
    bind 192.168.161.200:8080
    mode tcp
    option tcplog
    default_backend k8s-http-api

backend k8s-http-api
    mode tcp
    option tcplog
    option tcp-check
    balance roundrobin
    default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
    server k8s-http-api-1 192.168.161.161:8080 check
    server k8s-http-api-2 192.168.161.162:8080 check

通过上面的配置文件我们可以看出通过https的访问将请求转发给apiserver 的6443端口了,http的请求转发到了apiserver 的8080端口。

启动haproxy

systemctl start haproxy
systemctl enable haproxy
systemctl status haproxy

然后我们可以通过上面9000端口监控我们的haproxy的运行状态(192.168.161.161:9000/stats):

mark

上面我们的haproxy的确可以代理我们的两个master 上的apiserver 了,但是还不是高可用的,如果master01 这个节点down 掉了,那么我们haproxy 就不能正常提供服务了。这里我们可以使用两种方法来实现高可用

方式1:使用阿里云SLB(我们线上用的方式)

这种方式实际上是最省心的,在阿里云上建一个内网的SLB,将master01 与master02 添加到SLB 机器组中,转发80(http)和443(https)端口即可(注意下面的提示)

注意:阿里云的负载均衡是四层TCP负责,不支持后端ECS实例既作为Real Server又作为客户端向所在的负载均衡实例发送请求。因为返回的数据包只在云服务器内部转发,不经过负载均衡,所以在后端ECS实例上去访问负载均衡的服务地址是不通的。什么意思?就是如果你要使用阿里云的SLB的话,那么你不能在apiserver节点上使用SLB(比如在apiserver 上安装kubectl,然后将apiserver的地址设置为SLB的负载地址使用),因为这样的话就可能造成回环了,所以简单的做法是另外用两个新的节点做HA实例,然后将这两个实例添加到SLB 机器组中。

方式2:使用keepalived

KeepAlived 是一个高可用方案,通过 VIP(即虚拟 IP)和心跳检测来实现高可用。其原理是存在一组(两台)服务器,分别赋予 Master、Backup 两个角色,默认情况下Master 会绑定VIP 到自己的网卡上,对外提供服务。Master、Backup 会在一定的时间间隔向对方发送心跳数据包来检测对方的状态,这个时间间隔一般为 2 秒钟,如果Backup 发现Master 宕机,那么Backup 会发送ARP 包到网关,把VIP 绑定到自己的网卡,此时Backup 对外提供服务,实现自动化的故障转移,当Master 恢复的时候会重新接管服务。非常类似于路由器中的虚拟路由器冗余协议(VRRP)

mark

开启路由转发,这里我们定义虚拟IP为:192.168.161.200

$ vim /etc/sysctl.conf
# 添加以下内容
net.ipv4.ip_forward = 1
net.ipv4.ip_nonlocal_bind = 1

# 验证并生效
$ sysctl -p
# 验证是否生效
$ cat /proc/sys/net/ipv4/ip_forward
1

安装keepalived:

$ yum install -y keepalived

我们这里将master1 设置为Master,master2 设置为Backup,修改配置:

vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   notification_email {
   }
   router_id kube_api
}

vrrp_script check_k8s {
    # 自身状态检测
    script "/etc/keepalived/chk_k8s_master.sh"
    interval 3
    weight 5
}

vrrp_instance haproxy-vip {
    # 使用单播通信,默认是组播通信
    unicast_src_ip 192.168.161.161
    unicast_peer {
        192.168.161.162
    }
    # 初始化状态
    state BACKUP
    # 虚拟ip 绑定的网卡
    interface ens33
    # ID 要与Backup 配置一致
    virtual_router_id 51
    # 默认启动优先级,要比Backup 大点,但要控制量,保证自身状态检测生效
    priority 99
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        # 虚拟ip 地址
        192.168.161.200/24
    }
    track_script {
        check_k8s
    }
}
自身状态监测脚本,这里通过 api-server 是否在监听端口(6433 端口)来判断状态。
#!/bin/bash

d=`date --date today +%Y%m%d_%H:%M:%S`

n=`ps -C kube-apiserver --no-heading|wc -l`

if [ $n -eq "0" ]; then
        systemctl start kube-apiserver
        n2=`ps -C kube-apiserver --no-heading|wc -l`
        if [ $n2 -eq "0"  ]; then
                echo "$d Kube-apiserver down,keepalived will stop" >> /var/log/check_ng.log
                systemctl stop keepalived
        fi
fi

统一的方式在master02 节点上安装keepalived,修改配置,只需要将state 更改成BACKUP,priority更改成99,unicast_src_ip 与unicast_peer 地址修改即可。

启动keepalived:

$ systemctl start keepalived
$ systemctl enable keepalived
# 查看日志
$ journalctl -f -u keepalived

查看IP

[root@master1 keepalived]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:26:e9:ae brd ff:ff:ff:ff:ff:ff
    inet 192.168.161.161/24 brd 192.168.161.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet 192.168.161.200/24 scope global secondary ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::2e63:8644:7484:a297/64 scope link 
       valid_lft forever preferred_lft forever

六、Kubernet 高可用集群搭建–Master组件

配置和启动 kube-controller-manager

scheduler,controller-manager在集群中只能在一台主机开启,这里我们可以在启动controller-manager和scheduler 只要加上 –leader-elect=true 参数就可以同时启动,系统会自动选举leader。

3台master 部署controller-manager,以master1为例,替换各master的ip地址,serviceip范围和pod可获取的ip范围

cat > kube-controller-manager.service << EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \
  --logtostderr=true  \
  --address=127.0.0.1 \
  --master=https://192.168.161.200:6443 \
  --allocate-node-cidrs=true \
  --service-cluster-ip-range=10.254.0.0/16 \
  --cluster-cidr=172.30.0.0/16 \
  --cluster-name=kubernetes \
  --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem \
  --cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \
  --service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem \
  --root-ca-file=/etc/kubernetes/ssl/ca.pem \
  --leader-elect=true \
  --v=2
Restart=on-failure
LimitNOFILE=65536
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF 
启动 kube-controller-manager
# cp kube-controller-manager.service /etc/systemd/system/

# systemctl daemon-reload

# systemctl enable kube-controller-manager

# systemctl start kube-controller-manager

三台部署scheduler,以master01为例,替换各master的ip地址

cat > kube-scheduler.service << EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
  --logtostderr=true \\
  --address=127.0.0.1 \\
  --master=https://192.168.161.200:6443 \\
  --leader-elect=true \\
  --v=2
Restart=on-failure
LimitNOFILE=65536
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
启动 kube-scheduler
# cp kube-scheduler.service /etc/systemd/system/

# systemctl daemon-reload

# systemctl enable kube-scheduler

# systemctl start kube-scheduler

kube-controller-manager 和kube-scheduler 的高可用

Kubernetes 的管理层服务包括kube-scheduler和kube-controller-manager。kube-scheduler和kube-controller-manager使用一主多从的高可用方案,在同一时刻只允许一个服务处以具体的任务。Kubernetes中实现了一套简单的选主逻辑,依赖Etcd实现scheduler和controller-manager的选主功能。如果scheduler和controller-manager在启动的时候设置了leader-elect参数,它们在启动后会先尝试获取leader节点身份,只有在获取leader节点身份后才可以执行具体的业务逻辑。它们分别会在Etcd中创建kube-scheduler和kube-controller-manager的endpoint,endpoint的信息中记录了当前的leader节点信息,以及记录的上次更新时间。leader节点会定期更新endpoint的信息,维护自己的leader身份。每个从节点的服务都会定期检查endpoint的信息,如果endpoint的信息在时间范围内没有更新,它们会尝试更新自己为leader节点。scheduler服务以及controller-manager服务之间不会进行通信,利用Etcd的强一致性,能够保证在分布式高并发情况下leader节点的全局唯一性。整体方案如下图所示:

mark

当集群中的leader节点服务异常后,其它节点的服务会尝试更新自身为leader节点,当有多个节点同时更新endpoint时,由Etcd保证只有一个服务的更新请求能够成功。通过这种机制sheduler和controller-manager可以保证在leader节点宕机后其它的节点可以顺利选主,保证服务故障后快速恢复。当集群中的网络出现故障时对服务的选主影响不是很大,因为scheduler和controller-manager是依赖Etcd进行选主的,在网络故障后,可以和Etcd通信的主机依然可以按照之前的逻辑进行选主,就算集群被切分,Etcd也可以保证同一时刻只有一个节点的服务处于leader状态。

验证 master 节点功能

Master1 上面:

[root@master1 ~]# kubectl get cs
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"}   

Master2 上面:

[root@master2 ~]# kubectl get cs
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"}   
赞 (9)

本文共 2 个回复

  • eryajf 2018/11/11 21:02

    久违了,这中间也一直都过来逛逛,一直都没见到更新文章,原来是搞大事情了啊,,最近也一直在追k8s,十分感慨,真真的好东西。你们竟然已经投入到了生产当中,可敬可佩,希望后续再分享一些持续部署方面的,大赞。

    • zhdya 博主 2018/11/12 08:14

      @ eryajf 很多这方面的资料,也包含一些故障的处理,现在有时间了,我会大量更新~

发表评论