k8s集群异地云服务器(公网)搭建

集群间的同一网段部署很简单,网上教程很多。但是有的时候想学习或者测试之类的种种原因手里没有足够的局域网机器的话,想利用多台异地云服务器搭建k8s集群的话才建议参考本文。


机器准备

  • 主节点centos7.8(阿里云ECS)
    机器名: k8s-master
    公网ip:47.9x.17x.9x
  • 从节点centos7.9(腾讯云EVM)
    机器名: k8s-node1
    公网ip:121.4x.9x.7x

设置hostname

  • master节点设置:

    hostnamectl set-hostname k8s-master
    
  • node1节点设置:

      hostnamectl set-hostname k8s-node1
    

修改/etc/hosts文件

将127.0.0.1 和hostname绑定(所有节点都需要)


配置节点间ssh互信

配置ssh互信,那么节点之间就能无密访问,方便日后执行自动化部署

ssh-keygen     # 每台机器执行这个命令, 一路回车即可

ssh-keygen -t rsa -C "[email protected]"
代码参数含义:
-t 指定密钥类型,默认是 rsa ,可以省略。
-C 设置注释文字,比如邮箱。
-f 指定密钥文件存储文件名。

ssh-copy-id node # 到master上拷贝公钥到其他节点,这里需要输入 yes和密码
默认的文件名为.ssh/id_rsa.pub

ssh-copy-id -i .ssh/id_rsa.pub  用户名字@192.168.x.xxx

系统参数配置


bash:
# 关闭Selinux
sed -i 's/SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config

# 永久关闭swap区
swapoff -a
sed -ri 's/.*swap.*/#&/' /etc/fstab
echo "vm.swappiness = 0">> /etc/sysctl.conf  

# 修改内核参数
cat < /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
EOF
sysctl -p /etc/sysctl.d/k8s.conf

# 完整/etc/sysctl.conf  配置

vm.swappiness = 0
kernel.sysrq = 1

net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1

net.ipv4.neigh.default.gc_stale_time = 120

# see details in https://help.aliyun.com/knowledge_detail/39428.html
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_announce = 2

# see details in https://help.aliyun.com/knowledge_detail/41334.html
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2

# 生效/etc/sysctl.conf文件设置
sysctl -p


建立虚拟网卡(所有节点)

将公网ip当做虚拟网卡来对待,以便后续的k8s集群的pod ip采用公网ip来加入集群

# step1 ,注意替换你的公网IP进去
cat > /etc/sysconfig/network-scripts/ifcfg-eth0:1 <

主机端口设置

默认阿里云ECS所有入网方向端口都需要自己手动开启,腾讯云EVM入网方向所有端口都放开了。

  • master端口开放

    协议 方向 端口 说明
    TCP 入站 6443 Kubernetes API server
    TCP 入站 2379-2380 etcd server client API
    TCP 入站 10250 Kubelet API
    TCP 入站 10251 kube-scheduler
    TCP 入站 10252 kube-controller-manager
    UDP 入站 8472 k8s fannel vxlan

    默认master是不允许进行非系统的pod部署的,后续k8smaster节点部署起来之后如果执行将master允许pod 部署的话(也就是执行了下面的命令):

    kubectl taint node k8s-master node-role.kubernetes.io/master-
    

    那么master节点也需要开放30000-32767的入站方向的tcp端口

  • node节点端口开放

    协议 方向 端口 说明
    TCP 入站 10250 Kubelet API
    TCP 入站 30000-32767 k8s NodePort ServicesI

Docker安装或者升级(master节点和所有node节点)

首先查询关于docker的软件包

  rpm -qa | grep docker

结果如下:


查到后开始卸载软件

yum remove docker-ce*

成功后查看docker版本看看还在不在

docker version

然后执行脚本开始升级到最新版本(截止发文的时候docker的版本是 20.10.0)

curl -fsSL https://get.docker.com/ | sh

查看版本会发现已经升级到最新版
启动docker

systemctl start docker.service

将docker加入开启自启服务中

systemctl enable docker

最后查看下docker信息

docker info

CentOS7.x系统自带的3.10.x内核存在一些Bug,Docker运行不稳定,建议升级内核

#下载内核源
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
# 安装最新版本内核
yum --enablerepo=elrepo-kernel install -y kernel-lt
# 查看可用内核
cat /boot/grub2/grub.cfg |grep menuentry
# 设置开机从新内核启动
grub2-set-default "CentOS Linux (4.4.230-1.el7.elrepo.x86_64) 7 (Core)"
# 查看内核启动项
grub2-editenv list
# 重启系统使内核生效
reboot
# 查看内核版本是否生效
uname -r

查看是什么系统
lsb_release -a

docker安装成功后

# 设置守护程序
cat > /etc/docker/daemon.json <

registry-mirrors中的镜像地址选择阿里云的,也可以选择别家的
登录进入阿里云镜像服务中心,获取镜像地址


k8s集群异地云服务器(公网)搭建_第1张图片
镜像设置
mkdir -p /etc/systemd/system/docker.service.d

重启Docker服务

systemctl daemon-reload
systemctl enable docker
systemctl restart docker

Maste节点部署

当上述步骤完成后,我们依照以下步骤来完成主节点的安装:

  1. Kubeadm以及相关工具包的安装(截止发文的时候k8s的版本是v1.20.0)
    安装脚本如下所示:
    # 配置源
    echo '#k8s
    [kubernetes]
    name=Kubernetes
    baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
    enabled=1
    gpgcheck=0
    '>/etc/yum.repos.d/kubernetes.repo
    # kubeadm和相关工具包

       yum -y install kubelet kubeadm kubectl kubernetes-cni
    

    安装成功后的k8s版本为:


    k8s集群异地云服务器(公网)搭建_第2张图片

    重启kubelet

         systemctl daemon-reload
         systemctl enable kubelet
    
  2. 批量拉取k8s相关镜像
    制作下载镜像的脚本 可按照如下的设置
    vim docker.sh

     #!/bin/bash
     url=registry.aliyuncs.com/google_containers
     version=v1.20.0
     images=(`kubeadm config images list --kubernetes-version=$version|awk -F '/' '{print $2}'`)
     for imagename in ${images[@]} ; do
       docker pull $url/$imagename
       docker tag $url/$imagename k8s.gcr.io/$imagename
       docker rmi -f $url/$imagename
     done
    

    执行docker脚本

     ./docker.sh  
    

    所有组件pull成功后。查看

     docker images
    

    结果如下所示:


    k8s集群异地云服务器(公网)搭建_第3张图片

初始化主节点

默认的kubeadm-config.yml文件可以使用以下命令获取:

kubeadm config print init-defaults > kubeadm-config.yml

# step1 添加配置文件,注意替换下面的IP
cat > kubeadm-config.yml <

# 初始化过程中可能会报错:

[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1


clipboard.png

解决: echo "1" >/proc/sys/net/bridge/bridge-nf-call-iptables

如果没有上述文件则使用 modprobe br_netfilter 命令

[ERROR Swap]: running with swap on is not supported. Please disable swap [preflight] If you know what you are doing, you can make a check non-fatal with --ignore-preflight-errors=...

解决:
swapoff -a
vim /etc/fstab
# 将下面的这行注释掉
#/dev/mapper/rhel-swap swap swap defaults 0 0

# 初始化如果失败的话,想再重新初始化的话执行下面操作重置:

kubeadm reset
ifconfig cni0 down
ip link delete cni0
ifconfig flannel.1 down
ip link delete flannel.1
rm -rf /var/lib/cni/
rm -rf /var/lib/etcd

# 如果集群开启ipvs的话,也需要将ipvs重置,以确保不受之前的错误配置影响

ipvsadm -C

ps:集群token管理

默认token的有效期为24小时,当过期之后,该token就不可用了,如果后续有nodes节点加入,解决方法如下:重新生成新的token ==> kubeadm token create
# 1.查看当前的token列表
[root@K8S00 ~]# kubeadm token list
TOKEN                     TTL         EXPIRES                     USAGES                   DESCRIPTION                                                EXTRA GROUPS
7mjtn4.9kds6sabcouxaugd   23h         xxxxx  authentication,signing   The default bootstrap token generated by 'kubeadm init'.   system:bootstrappers:kubeadm:default-node-token

# 2.重新生成新的token
[root@K8S00 ~]# kubeadm token create
369tcl.oe4punpoj9gaijh7

# 3.再次查看当前的token列表
[root@K8S00 ~]# kubeadm token list
TOKEN                     TTL         EXPIRES                     USAGES                   DESCRIPTION                                                EXTRA GROUPS
369tcl.oe4punpoj9gaijh7   23h         xxxxx   authentication,signing                                                        system:bootstrappers:kubeadm:default-node-token
7mjtn4.9kds6sabcouxaugd   23h         xxxxx   authentication,signing   The default bootstrap token generated by 'kubeadm init'.   system:bootstrappers:kubeadm:default-node-token

# 4.获取ca证书sha256编码hash值
[root@K8S00 ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
7ae10591aa593c2c36fb965d58964a84561e9ccd416ffe7432550a0d0b7e4f90

# 5.节点加入集群
[root@k8s-node03 ~]# kubeadm join --token 369tcl.oe4punpoj9gaijh7(新的token) --discovery-token-ca-cert-hash sha256:7ae10591aa593c2c36fb965d58964a84561e9ccd416ffe7432550a0d0b7e4f90(ca证书sha256编码hash值) 47.9x.17x.9x:6443 --ski

至此,master节点部署完毕,现在来查看下pod运行情况

kubectl  get pod -n kube-system
k8s集群异地云服务器(公网)搭建_第4张图片
clipboard.png

# 查看一下集群pod,确认个组件都处于Running 状态
# 注意由于此时k8s的cni组件还没部署,此时的dns日志里面应该会报错,等待cni组件中,所以coredns 暂时还无法正常启动。
使用 kubectl get pod -n kube-system -o wide 命令查看的话可以看到系统pod节点加入的ip地址均为公网ip

# 出于安全考虑,默认配置下Kubernetes不会将Pod调度到Master节点。如果希望将k8s-master也当作Node使用,可以执行如下命令:

kubectl taint node k8s-master node-role.kubernetes.io/master-

其中k8s-master是主机节点hostname如果要恢复Master Only状态,执行如下命令:

kubectl taint node k8s-master node-role.kubernetes.io/master="":NoSchedule

kube-proxy代理方案由iptables改为ipvs

这里的kube-proxy组件默认是使用iptables方案进行启动的,我们来将它改成由ipvs方式启动

1.加载内核模快

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

sysctl -p


lsmod|grep ip_vs

modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4

yum install ipvsadm ipset -y


2.修改kube-proxy配置
kubectl edit configmap kube-proxy -n kube-system
   minSyncPeriod: 0s
      scheduler: ""
      syncPeriod: 30s
    kind: KubeProxyConfiguration
    metricsBindAddress: 127.0.0.1:10249
    mode: "ipvs"                          # 修改此处
    nodePortAddresses: null


3.删除所有kube-proxy的pod
kubectl delete pod xxx -n kube-system
4.校验
kubectl logs kube-proxy-xxx -n kube-system 日志出现Using ipvs Proxier即可
5.检查ipvs代理规则
kubectl get svc --all-namespaces
# ipvsadm -ln
#可以看到service对应的很多规则

给集群部署flannel 网络组件

fannel简单易用,无特殊需求采用这种方案,它目前默认采用vxlan模式工作,xvlan采用udp 8472端口和各个节点通信

Flnnel的VXLAN模式有两种:

VXLAN: 原生的VXLAN,即扩展的虚拟LAN
Directrouting:直接路由型

下载fannel文件

wget https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml

# 共修改两个地方,一个是args下,添加
args:
- --public-ip=$(PUBLIC_IP) # 添加此参数,申明公网IP
- --iface=eth0 # 添加此参数,绑定网卡

# 然后是env下
env:
- name: PUBLIC_IP #添加环境变量
valueFrom:
fieldRef:
fieldPath: status.podIP

# 此处将vxlan工作模式改为Directrouting+vxlan结合的方式,它具有如果两个节点在同一网段时使用host-gw通信,如果不在同一网段中,即 当前pod所在节点与目标pod所在节点中间有路由器,就使用VXLAN这种方式,使用叠加网络的优点。
# 需要将configmap中的net-conf.json做调整:

  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan",
        "Directrouting": true #添加该属性
      }
    }

# 最终完整的文件为:


---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: psp.flannel.unprivileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
    seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
    apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
    apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
  privileged: false
  volumes:
    - configMap
    - secret
    - emptyDir
    - hostPath
  allowedHostPaths:
    - pathPrefix: '/etc/cni/net.d'
    - pathPrefix: '/etc/kube-flannel'
    - pathPrefix: '/run/flannel'
  readOnlyRootFilesystem: false
  # Users and groups
  runAsUser:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  # Privilege Escalation
  allowPrivilegeEscalation: false
  defaultAllowPrivilegeEscalation: false
  # Capabilities
  allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
  defaultAddCapabilities: []
  requiredDropCapabilities: []
  # Host namespaces
  hostPID: false
  hostIPC: false
  hostNetwork: true
  hostPorts:
    - min: 0
      max: 65535
  # SELinux
  seLinux:
    # SELinux is unused in CaaSP
    rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
rules:
  - apiGroups: ['extensions']
    resources: ['podsecuritypolicies']
    verbs: ['use']
    resourceNames: ['psp.flannel.unprivileged']
  - apiGroups:
      - ''
    resources:
      - pods
    verbs:
      - get
  - apiGroups:
      - ''
    resources:
      - nodes
    verbs:
      - list
      - watch
  - apiGroups:
      - ''
    resources:
      - nodes/status
    verbs:
      - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
  - kind: ServiceAccount
    name: flannel
    namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan",
        "Directrouting": true
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-system
  labels:
    tier: node
    app: flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: kubernetes.io/os
                    operator: In
                    values:
                      - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
        - operator: Exists
          effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
        - name: install-cni
          image: quay.io/coreos/flannel:v0.13.1-rc1
          command:
            - cp
          args:
            - -f
            - /etc/kube-flannel/cni-conf.json
            - /etc/cni/net.d/10-flannel.conflist
          volumeMounts:
            - name: cni
              mountPath: /etc/cni/net.d
            - name: flannel-cfg
              mountPath: /etc/kube-flannel/
      containers:
        - name: kube-flannel
          image: quay.io/coreos/flannel:v0.13.1-rc1
          command:
            - /opt/bin/flanneld
          args:
            - --ip-masq
            - --kube-subnet-mgr
            - --public-ip=$(PUBLIC_IP) # 添加此参数,申明公网IP
            - --iface=eth0 # 添加此参数,绑定网卡
          resources:
            requests:
              cpu: '100m'
              memory: '50Mi'
            limits:
              cpu: '100m'
              memory: '50Mi'
          securityContext:
            privileged: false
            capabilities:
              add: ['NET_ADMIN', 'NET_RAW']
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: PUBLIC_IP #添加环境变量
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          volumeMounts:
            - name: run
              mountPath: /run/flannel
            - name: flannel-cfg
              mountPath: /etc/kube-flannel/
      volumes:
        - name: run
          hostPath:
            path: /run/flannel
        - name: cni
          hostPath:
            path: /etc/cni/net.d
        - name: flannel-cfg
          configMap:
            name: kube-flannel-cfg

执行fannel文件:

  kubectl apply -f kube-fannel.yml

执行完后可以在查看k8s集群的pod节点:

  kubectl get pod -n kube-system -o wide

可以发现此时fannel和dns都启动成功。

# 刷新coredns报错 Failed to list *v1.Namespace: Get "https://10.1.0.1:443/api/v1/namespaces?limit=500&resourceVersion=0": dial tcp 10.1.0.1:443: connect: no route to host

很有可能是防火墙(iptables)规则错乱或者缓存导致的,可以依次执行以下命令进行解决:

  systemctl stop kubelet
  systemctl stop docker
  iptables --flush
  iptables -tnat --flush
  systemctl start kubelet
  systemctl start docker

至此master节点pod全部部署完毕。


检查集群状态(选做)

  kubectl get cs

会发现此时其实 scheduler 和controller manager 健康检查是失败的


k8s集群异地云服务器(公网)搭建_第5张图片
clipboard.png
  1. 先检查这两个端口是否启动了
    telnet或者ps都可以查看
  2. 确认kube-scheduler和kube-controller-manager组件配置是否禁用了非安全端口
    配置文件路径:
    /etc/kubernetes/manifests/kube-scheduler.conf
    /etc/kubernetes/manifests/kube-controller-manager.yaml
    如controller-manager组件的配置如下:可以去掉--port=0这个设置,
    k8s集群异地云服务器(公网)搭建_第6张图片
    clipboard.png

然后重启 sudo systemctl restart kubelet
重新查看 kubectl get cs 应该就解决问题了。


配置k8s集群 命令补全

(仅master)

yum install -y bash-completion

source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
source ~/.bashrc


工作节点部署

安装k8s步骤和主节点一样,直到初始化master节点这里时,在ndoe节点不需要初始化,而是执行join命令加入master节点

执行join命令前需要做调整。

修改kubelet启动参数(重点,所有node节点都要操作)

# 此文件安装kubeadm后就存在了
vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf

# 注意,这步很重要,如果不做,节点仍然会使用内网IP注册进集群
# 在末尾添加参数 --node-ip=node节点的公网IP

# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/sysconfig/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS --node-ip=121.4x.9x.7x

执行刚才master提示的join命令

kubeadm join 47.9x.17x.9x:6443 --token abcdef.0123456789abcdef
--discovery-token-ca-cert-hash sha256:dd008a0ab88d25d7addb121ab11024d6e7c8e44639d2e48e662eb6319cd40b04

待执行完毕再查看集群。


k8s集群异地云服务器(公网)搭建_第7张图片
clipboard.png

重置k8s集群

在搭建集群时,可能会遇到节点配置失败的情况,此时可以将该节点移出集群,在将节点移出集群时,应该将该节点上正在运行的Pod进行驱离。
例如:
驱离名为"k8s-node-1"的节点上的pod(master上操作)
[root@k8s-master ~]# kubectl drain k8s-node-1 --delete-local-data --force --ignore-daemonsets


删除节点(master上)
[root@k8s-master ~]# kubectl delete node k8s-node-1


重置节点(node上-也就是在被删除的节点上)
[root@k8s-node-1 ~]# kubeadm reset


需要注意:
注1:需要把master也驱离、删除、重置,这里给我坑死了,第一次没有驱离和删除master,最后的结果是查看结果一切正常,但coredns死活不能用,切勿尝试

到此,整个k8s集群部署完毕,接下来需要对集群进行验证,看各个节点是否可用,部署svc服务或者pod时网段是否能互相访问,考虑到本文篇幅已经太长了,后续再进行验证。

你可能感兴趣的:(k8s集群异地云服务器(公网)搭建)