K8s安装部署-----二进制安装部署

news2024/11/20 3:23:04

目录

前言

一、操作系统初始化配置

二、部署 etcd 集群

1、在master01节点操作

2、在 node01 节点上操作

3、在 node02 节点上操作

4、查看集群状态

二、部署docker引擎

三、部署 Master 组件

1、准备证书

2、准备二进制文件、token

3、启动kube-apiserver服务

4、启动 scheduler 服务

5、启动controller-manager 服务

6、生成kubectl连接集群的kubeconfig文件

7、通过kubectl工具查看当前集群组件状态

六、部署Worker node组件

1、在所有 node 节点上操作

2、在master01上生成文件scp到node01

3、node01安装kubelet服务

4、master01验证CSR请求

​编辑

5、node01安装proxy服务

6、node2启动安装kubelet、proxy服务

七、部署CNI网络组建

1、部署flannel

2、部署calico

3、node02节点部署

 八、部署core DNS

 九、master02 节点部署

十、负载均衡部署(lb01、lb02)

1、部署nginx服务、配置四层负载均衡

2、部署keepalived服务

十一、部署Dashboard(master01)

前言

Minikube

  • Minikube是一个工具,可以在本地快速运行一个单节点微型K8S,仅用于学习、预览K8S的一些特性使用。
  • 部署地址:https://kubernetes.io/docs/setup/minikube

Kubeadmin

  • Kubeadmin也是一个工具,提供kubeadm init和kubeadm join,用于快速部署K8S集群,相对简单。
  • https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/

二进制安装部署

  • 生产首选,从官方下载发行版的二进制包,手动部署每个组件和自签TLS证书,组成K8S集群,新手推荐。
  • https://github.com/kubernetes/kubernetes/releases

小结:Kubeadm降低部署门槛,但屏蔽了很多细节,遇到问题很难排查。如果想更容易可控,推荐使用二进制包部署Kubernetes集群,虽然手动部署麻烦点,期间可以学习很多工作原理,也利于后期维护。

端口介绍

实验环境:

主机名ip地址所需组件
master01192.168.247.10kube-apiserver、kubu-controller-manager、kube-scheduler、etcd
master02192.168.247.20kube-apiserver、kubu-controller-manager、kube-scheduler
node01192.168.247.30kubelet、kube-proxy、docker、flannel、etcd
node02192.168.247.40kubelet、kube-proxy、docker、flannel、etcd
负载均衡01192.168.247.50nginx+keepalive
负载均衡02192.168.247.60nginx+keepalive

一、操作系统初始化配置

一键部署环境脚本。

#!/bin/bash
# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X

# 关闭selinux
# 永久关闭
sed -i 's/enforcing/disabled/' /etc/selinux/config  
# 临时关闭
setenforce 0  

# 关闭swap
# 临时
swapoff -a 
# 永久关闭
sed -ri 's/.*swap.*/#&/' /etc/fstab


# 在master添加hosts
cat >> /etc/hosts << EOF
192.168.247.10 master01
192.168.247.20 master02
192.168.247.30 node01
192.168.247.40 node02
EOF


# 将桥接的IPv4流量传递到iptables的链
cat > /etc/sysctl.d/k8s.conf << EOF
#开启网桥模式,可将网桥的流量传递给iptables链
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
#关闭ipv6协议
net.ipv6.conf.all.disable_ipv6=1
net.ipv4.ip_forward=1
EOF


# 生效
sysctl --system  

# 时间同步
yum install ntpdate -y
ntpdate time.windows.com

crontab -e 
*/10 * * * * /usr/sbin/ntpdate/ntpdate time.windows.com

手动修改各个节点的主机名

# 根据规划设置主机名【master01节点上操作】
hostnamectl set-hostname master01

# 根据规划设置主机名【node01节点操作】
hostnamectl set-hostname node01

# 根据规划设置主机名【node02节点操作】
hostnamectl set-hostname node02

二、部署 etcd 集群

  • etcd分布式键值对的数据库,有服务发现系统。使用go语言开发,使用raft 一致性的算法,集群需要三台以上的奇数台组成。
  • 2379 端口对外(客户端)通讯的端口;2380端口对内(集群内部节点)通信的端口。
节点名称ip地址
etcd01192.168.247.10
etcd02192.168.247.30
etcd03192.168.247.40

注:为了节省机器,这里与 K8s 节点机器复用。也可以独立于 k8s 集群之外部署,只要apiserver 能连接到就行。

准备签发证书环境:

cfssl是一个开源的证书管理工具,使用配置文件生成证书,因此自签之前,需要生成它识别的 json 格式的配置文件,CFSSL 提供了方便的命令行生成配置文件。

CFSSL 用来为 etcd 提供 TLS 证书,它支持签三种类型的证书:

  • client 证书,服务端连接客户端时携带的证书,用于客户端验证服务端身份,如 kube-apiserver 访问 etcd;
  • server 证书,客户端连接服务端时携带的证书,用于服务端验证客户端身份,如 etcd 对外提供服务;
  • peer 证书,相互之间连接时使用的证书,如 etcd 节点之间进行验证和通信。

1、在master01节点操作

#准备cfssl证书生成工具

wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -O /usr/local/bin/cfssl
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -O /usr/local/bin/cfssljson
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -O /usr/local/bin/cfssl-certinfo

chmod +x /usr/local/bin/cfssl*


cfssl:证书签发的工具命令
cfssljson:将 cfssl 生成的证书(json格式)变为文件承载式证书
cfssl-certinfo:验证证书的信息
cfssl-certinfo -cert <证书名称>			#查看证书的信息

### 生成Etcd证书 ###

mkdir -p /opt/k8s

vim /opt/k8s/etcd-cert
#创建etcd证书的文件

#############自签CA,包含证书的过期时间10年 ###############
cat > ca-config.json << EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "www": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF

cat > ca-csr.json<< EOF 
{
    "CN": "etcd CA",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing"
        }
    ]
}
EOF

#######生成证书########################
cfssl gencert -initca ca-csr.json | cfssljson -bare ca 
##当前目录下会生成 ca.pem和ca-key.pem文件

#-----------------------
#生成 etcd 服务器证书和私钥
cat > server-csr.json <<EOF
{
    "CN": "etcd",
    "hosts": [
    "192.168.247.10",
    "192.168.247.30",
    "192.168.247.40"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "BeiJing",
            "ST": "BeiJing"
        }
    ]
}
EOF

#hosts:将所有 etcd 集群节点添加到 host 列表,需要指定所有 etcd 集群的节点 ip 或主机名不能使用网段,新增 etcd 服务器需要重新签发证书。

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www server-csr.json | cfssljson -bare server

#生成的文件:
#server.csr:服务器的证书请求文件
#server-key.pem:服务器的私钥
#server.pem:服务器的数字签名证书

#-config:引用证书生成策略文件 ca-config.json
#-profile:指定证书生成策略文件中的的使用场景,比如 ca-config.json 中的 www



vim /opt/k8s/etcd.sh

#!/bin/bash
#example: ./etcd.sh etcd01 192.168.247.10 etcd02=https://192.168.247.30:2380,etcd03=https://192.168.247.40:2380

#创建etcd配置文件/opt/etcd/cfg/etcd
ETCD_NAME=$1
ETCD_IP=$2
ETCD_CLUSTER=$3

WORK_DIR=/opt/etcd

cat > $WORK_DIR/cfg/etcd  <<EOF
#[Member]
ETCD_NAME="${ETCD_NAME}"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://${ETCD_IP}:2380"
ETCD_LISTEN_CLIENT_URLS="https://${ETCD_IP}:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://${ETCD_IP}:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://${ETCD_IP}:2379"
ETCD_INITIAL_CLUSTER="etcd01=https://${ETCD_IP}:2380,${ETCD_CLUSTER}"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
EOF

#Member:成员配置
#ETCD_NAME:节点名称,集群中唯一。成员名字,集群中必须具备唯一性,如etcd01
#ETCD_DATA_DIR:数据目录。指定节点的数据存储目录,这些数据包括节点ID,集群ID>,集群初始化配置,Snapshot文件,若未指定-wal-dir,还会存储WAL文件;如果不指定
会用缺省目录
#ETCD_LISTEN_PEER_URLS:集群通信监听地址。用于监听其他member发送信息的地址。ip为全0代表监听本机所有接口
#ETCD_LISTEN_CLIENT_URLS:客户端访问监听地址。用于监听etcd客户发送信息的地址>。ip为全0代表监听本机所有接口

#Clustering:集群配置
#ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址。其他member使用,其他member通
过该地址与本member交互信息。一定要保证从其他member能可访问该地址。静态配置方>式下,该参数的value一定要同时在--initial-cluster参数中存在
#ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址。etcd客户端使用,客户端通过该地址
与本member交互信息。一定要保证从客户侧能可访问该地址
#ETCD_INITIAL_CLUSTER:集群节点地址。本member使用。描述集群中所有节点的信息,
本member根据此信息去联系其他member
#ETCD_INITIAL_CLUSTER_TOKEN:集群Token。用于区分不同集群。本地如有多个集群要>设为不同
#ETCD_INITIAL_CLUSTER_STATE:加入集群的当前状态,new是新集群,existing表示加>入已有集群。


#创建etcd.service服务管理文件
cat > /usr/lib/systemd/system/etcd.service <<EOF
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
EnvironmentFile=${WORK_DIR}/cfg/etcd
ExecStart=${WORK_DIR}/bin/etcd \
--cert-file=${WORK_DIR}/ssl/server.pem \
--key-file=${WORK_DIR}/ssl/server-key.pem \
--trusted-ca-file=${WORK_DIR}/ssl/ca.pem \
--peer-cert-file=${WORK_DIR}/ssl/server.pem \
--peer-key-file=${WORK_DIR}/ssl/server-key.pem \
--peer-trusted-ca-file=${WORK_DIR}/ssl/ca.pem \
--logger=zap \
--enable-v2
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

#--enable-v2:开启 etcd v2 API 接口。当前 flannel 版本不支持 etcd v3 通信
#--logger=zap:使用 zap 日志框架。zap.Logger 是go语言中相对日志库中性能最高的
#--peer开头的配置项用于指定集群内部TLS相关证书(peer 证书),这里全部都使用同
一套证书认证
#不带--peer开头的的参数是指定 etcd 服务器TLS相关证书(server 证书),这里全部
都使用同一套证书认证


systemctl daemon-reload
systemctl enable etcd
systemctl restart etcd


chmod +x etcd-cert.sh etcd.sh

#创建用于生成CA证书、etcd 服务器证书以及私钥的目录

mkdir /opt/k8s/etcd-cert
mv etcd-cert.sh etcd-cert/
cd /opt/k8s/etcd-cert/
./etcd-cert.sh			#生成CA证书、etcd 服务器证书以及私钥

ls
ca-config.json  ca-csr.json  ca.pem        server.csr       server-key.pem
ca.csr          ca-key.pem   etcd-cert.sh  server-csr.json  server.pem

#上传 etcd-v3.4.9-linux-amd64.tar.gz 到 /opt/k8s 目录中,启动etcd服务

https://github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz

cd /opt/k8s/
tar zxvf etcd-v3.4.9-linux-amd64.tar.gz
ls etcd-v3.4.9-linux-amd64
Documentation  etcd  etcdctl  README-etcdctl.md  README.md  READMEv2-etcdctl.md
------------------------------------------------------------------------------------------
etcd就是etcd 服务的启动命令,后面可跟各种启动参数
etcdctl主要为etcd 服务提供了命令行操作

#创建用于存放 etcd 配置文件,命令文件,证书的目录

mkdir -p /opt/etcd/{cfg,bin,ssl}

cd /opt/k8s/etcd-v3.4.9-linux-amd64/
mv etcd etcdctl /opt/etcd/bin/
cp /opt/k8s/etcd-cert/*.pem /opt/etcd/ssl/

cd /opt/k8s/
./etcd.sh etcd01 192.168.247.10 etcd02=https://192.168.247.30:2380,etcd03=https://192.168.247.40:2380
#进入卡住状态等待其他节点加入,这里需要三台etcd服务同时启动,如果只启动其中一台后,服务会卡在那里,直到集群中所有etcd节点都已启动,可忽略这个情况

#可另外打开一个窗口查看etcd进程是否正常
ps -ef | grep etcd

#把etcd相关证书文件、命令文件和服务管理文件全部拷贝到另外两个etcd集群节点

scp -r /opt/etcd/ root@192.168.247.30:/opt/
scp -r /opt/etcd/ root@192.168.247.40:/opt/
scp /usr/lib/systemd/system/etcd.service root@192.168.247.30:/usr/lib/systemd/system/
scp /usr/lib/systemd/system/etcd.service root@192.168.247.40:/usr/lib/systemd/system/

2、在 node01 节点上操作

vim /opt/etcd/cfg/etcd
#[Member]
ETCD_NAME="etcd02"											#修改
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.10.18:2380"			#修改
ETCD_LISTEN_CLIENT_URLS="https://192.168.10.18:2379"		#修改

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.10.18:2380"		#修改
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.10.18:2379"				#修改
ETCD_INITIAL_CLUSTER="etcd01=https://192.168.10.80:2380,etcd02=https://192.168.10.18:2380,etcd03=https://192.168.10.19:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

#启动etcd服务
systemctl start etcd
systemctl enable etcd     ##systemctl enable --now etcd
systemctl在enable、disable、mask子命令里面增加了--now选项,可以激活同时启动服务,激活同时停止服务等。

systemctl status etcd

3、在 node02 节点上操作

vim /opt/etcd/cfg/etcd
#[Member]
ETCD_NAME="etcd03"											#修改
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.10.19:2380"			#修改
ETCD_LISTEN_CLIENT_URLS="https://192.168.10.19:2379"		#修改

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.10.19:2380"		#修改
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.10.19:2379"				#修改
ETCD_INITIAL_CLUSTER="etcd01=https://192.168.10.80:2380,etcd02=https://192.168.10.18:2380,etcd03=https://192.168.10.19:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

#启动etcd服务
systemctl start etcd
systemctl enable etcd
systemctl status etcd

4、查看集群状态

#检查etcd群集状态

ETCDCTL_API=3   /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.247.10:2379,https://192.168.247.30:2379,https://192.168.247.40:2379" endpoint health --write-out=table

ETCDCTL_API=3 /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.247.10:2379,https://192.168.247.30:2379,https://192.168.247.40:2379" endpoint status --write-out=table

------------------------------------------------------------------------------------------
--cert-file:识别HTTPS端使用SSL证书文件
--key-file:使用此SSL密钥文件标识HTTPS客户端
--ca-file:使用此CA证书验证启用https的服务器的证书
--endpoints:集群中以逗号分隔的机器地址列表
cluster-health:检查etcd集群的运行状况

#查看etcd集群成员列表

ETCDCTL_API=3 /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.247.10:2379,https://192.168.247.30:2379,https://192.168.247.40:2379" --write-out=table member list

二、部署docker引擎

//所有 node 节点部署docker引擎

yum install -y yum-utils device-mapper-persistent-data lvm2 
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 
yum install -y docker-ce docker-ce-cli containerd.io

systemctl start docker.service
systemctl enable docker.service 

三、部署 Master 组件

在 master01 节点上操作:
#上传 master.zip 和 k8s-cert.sh 到 /opt/k8s 目录中,解压 master.zip 压缩包

cd /opt/k8s/
unzip master.zip
chmod +x *.sh

mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}

1、准备证书

#创建用于生成CA证书、相关组件的证书和私钥的目录

mkdir /opt/k8s/k8s-cert

vim /opt/k8s/k8s-cert/k8s-cert.sh

#!/bin/bash
#配置证书生成策略,让 CA 软件知道颁发有什么功能的证书,生成用来签发其他组件证书的根证书
cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF

#生成CA证书和私钥(根证书和私钥)
cat > ca-csr.json <<EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing",
      	    "O": "k8s",
            "OU": "System"
        }
    ]
}
EOF

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


#-----------------------
#生成 apiserver 的证书和私钥(apiserver和其它k8s组件通信使用)
#hosts中将所有可能作为 apiserver 的 ip 添加进去,后面 keepalived 使用的 VIP 也要加入
cat > apiserver-csr.json <<EOF
{
    "CN": "kubernetes",
    "hosts": [
      "10.0.0.1",
      "127.0.0.1",
      "192.168.247.10",		
      "192.168.247.20",		
      "192.168.247.100",		
      "192.168.247.50",		
      "192.168.247.60",		
      "kubernetes",
      "kubernetes.default",
      "kubernetes.default.svc",
      "kubernetes.default.svc.cluster",
      "kubernetes.default.svc.cluster.local"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "BeiJing",
            "ST": "BeiJing",
            "O": "k8s",
            "OU": "System"
        }
    ]
}
EOF
//

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

#-----------------------
#生成 kubectl 连接集群的证书和私钥,具有admin权限
cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
EOF

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


#-----------------------
#生成 kube-proxy 的证书和私钥
cat > kube-proxy-csr.json <<EOF
{
  "CN": "system:kube-proxy",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF

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

此处一定要检查生成的证书配置文件是否到位

2、准备二进制文件、token

准备二进制文件

cp ca*pem apiserver*pem /opt/kubernetes/ssl/

#上传 kubernetes-server-linux-amd64.tar.gz 到 /opt/k8s/ 目录中,解压 kubernetes 压缩包

cd /opt/k8s/
tar zxvf kubernetes-server-linux-amd64.tar.gz

cd /opt/k8s/kubernetes/server/bin
cp kube-apiserver kubectl kube-controller-manager kube-scheduler /opt/kubernetes/bin/
ln -s /opt/kubernetes/bin/* /usr/local/bin/

 准备token令牌

#创建 bootstrap token 认证文件,apiserver 启动时会调用,然后就相当于在集群内创建了一个这个用户,接下来就可以用 RBAC 给他授权

cd /opt/k8s/
vim token.sh
#!/bin/bash
#获取随机数前16个字节内容,以十六进制格式输出,并删除其中空格
BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')
#生成 token.csv 文件,按照 Token序列号,用户名,UID,用户组 的格式生成
cat > /opt/kubernetes/cfg/token.csv <<EOF
${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF

chmod +x token.sh
./token.sh

cat /opt/kubernetes/cfg/token.csv

3、启动kube-apiserver服务

准备启动脚本

vim /opt/k8s/k8s-cert/apiserver.sh

#!/bin/bash
#例子: apiserver.sh 192.168.247.10 https://192.168.247.10 :2379,https://192.168.247.30:2379,https://192.168.247.40:2379
#创建 kube-apiserver 启动参数配置文件
MASTER_ADDRESS=$1
ETCD_SERVERS=$2

cat >/opt/kubernetes/cfg/kube-apiserver <<EOF
KUBE_APISERVER_OPTS="--logtostderr=false  \\
--v=2 \\
--log-dir=/opt/kubernetes/logs \\
--etcd-servers=${ETCD_SERVERS} \\
--bind-address=${MASTER_ADDRESS} \\
--secure-port=6443 \\
--advertise-address=${MASTER_ADDRESS} \\
--allow-privileged=true \\
--service-cluster-ip-range=10.0.0.0/24 \\
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \\
--authorization-mode=RBAC,Node \\
--enable-bootstrap-token-auth=true \\
--token-auth-file=/opt/kubernetes/cfg/token.csv \\
--service-node-port-range=30000-50000 \\
--kubelet-client-certificate=/opt/kubernetes/ssl/apiserver.pem \\
--kubelet-client-key=/opt/kubernetes/ssl/apiserver-key.pem \\
--tls-cert-file=/opt/kubernetes/ssl/apiserver.pem  \\
--tls-private-key-file=/opt/kubernetes/ssl/apiserver-key.pem \\
--client-ca-file=/opt/kubernetes/ssl/ca.pem \\
--service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \\
--service-account-issuer=api \\
--service-account-signing-key-file=/opt/kubernetes/ssl/apiserver-key.pem \\
--etcd-cafile=/opt/etcd/ssl/ca.pem \\
--etcd-certfile=/opt/etcd/ssl/server.pem \\
--etcd-keyfile=/opt/etcd/ssl/server-key.pem \\
--requestheader-client-ca-file=/opt/kubernetes/ssl/ca.pem \\
--proxy-client-cert-file=/opt/kubernetes/ssl/apiserver.pem \\
--proxy-client-key-file=/opt/kubernetes/ssl/apiserver-key.pem \\
--requestheader-allowed-names=kubernetes \\
--requestheader-extra-headers-prefix=X-Remote-Extra- \\
--requestheader-group-headers=X-Remote-Group \\
--requestheader-username-headers=X-Remote-User \\
--enable-aggregator-routing=true \\
--audit-log-maxage=30 \\
--audit-log-maxbackup=3 \\
--audit-log-maxsize=100 \\
--audit-log-path=/opt/kubernetes/logs/k8s-audit.log"
EOF

#--logtostderr=true:启用日志。输出日志到标准错误控制台,不输出到文件
#--v=4:日志等级。指定输出日志的级别,v=4为调试级别详细输出
#--etcd-servers:etcd集群地址。指定etcd服务器列表(格式://ip:port),逗号分隔
#--bind-address:监听地址。指定 HTTPS 安全接口的监听地址,默认值0.0.0.0
#--secure-port:https安全端口。指定 HTTPS 安全接口的监听端口,默认值6443
#--advertise-address:集群通告地址。通过该 ip 地址向集群其他节点公布 api server 的信息,必须能够被其他节点访问
#--allow-privileged=true:启用授权。允许拥有系统特权的容器运行,默认值false
#--service-cluster-ip-range:Service虚拟IP地址段。指定 Service Cluster IP 地址段
#--enable-admission-plugins:准入控制模块。kuberneres集群的准入控制机制,各控制模块以插件的形式依次生效,集群时必须包含ServiceAccount,运行在认证(Authentication)、授权(Authorization)之后,Admission Control是权限认证链上的最后一环, 对请求API资源对象进行修改和校验
#--authorization-mode:认证授权,启用RBAC授权和节点自管理。在安全端口使用RBAC,Node授权模式,未通过授权的请求拒绝,默认值AlwaysAllow。RBAC是用户通过角色与权限进行关联的模式;Node模式(节点授权)是一种特殊用途的授权模式,专门授权由kubelet发出的API请求,在进行认证时,先通过用户名、用户分组验证是否是集群中的Node节点,只有是Node节点的请求才能使用Node模式授权
#--enable-bootstrap-token-auth:启用TLS bootstrap机制。在apiserver上启用Bootstrap Token 认证
#--token-auth-file=/opt/kubernetes/cfg/token.csv:指定bootstrap token认证文件路径
#--service-node-port-range:指定 Service  NodePort 的端口范围,默认值30000-32767
#–-kubelet-client-xxx:apiserver访问kubelet客户端证书
#--tls-xxx-file:apiserver https证书
#1.20版本必须加的参数:–-service-account-issuer,–-service-account-signing-key-file
#--etcd-xxxfile:连接Etcd集群证书
#–-audit-log-xxx:审计日志
#启动聚合层相关配置:–requestheader-client-ca-file,–proxy-client-cert-file,–proxy-client-key-file,–requestheader-allowed-names,–requestheader-extra-headers-prefix,–requestheader-group-headers,–requestheader-username-headers,–enable-aggregator-routing


#创建 kube-apiserver.service 服务管理文件
cat >/usr/lib/systemd/system/kube-apiserver.service <<EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=-/opt/kubernetes/cfg/kube-apiserver
ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kube-apiserver
systemctl restart kube-apiserver

执行脚本、验证是否启动成功

cd /opt/k8s/k8s-cert
./apiserver.sh 192.168.247.10 https://192.168.247.10:2379,https://192.168.247.30:2379,https://192.168.247.40:2379
#执行创建的脚本,注意有两个位置变量,一个是确定本机的IP地址,一个是etcd集群的IP和端口

ps aux | grep kube-apiserver
#检查进程是否启动成功

netstat -natp | grep 6443   
#安全端口6443用于接收HTTPS请求,用于基于Token文件或客户端证书等认证

4、启动 scheduler 服务

准备启动脚本,脚本放在 /opt/k8s 中

vim /opt/k8s/k8s-cert/scheduler.sh

#!/bin/bash
##创建 kube-scheduler 启动参数配置文件
MASTER_ADDRESS=$1

cat >/opt/kubernetes/cfg/kube-scheduler <<EOF
KUBE_SCHEDULER_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/opt/kubernetes/logs \\
--leader-elect=true \\
--kubeconfig=/opt/kubernetes/cfg/kube-scheduler.kubeconfig \\
--bind-address=127.0.0.1"
EOF

#-–kubeconfig:连接 apiserver 用的配置文件,用于识别 k8s 集群
#--leader-elect=true:当该组件启动多个时,自动启动 leader 选举
#k8s中Controller-Manager和Scheduler的选主逻辑:k8s中的etcd是整个集群所有状态信息的存储,涉及数据的读写和多个etcd之间数据的同步,对数据的一致性要求严格,所以使用较复杂的 raft 算法来选择用于提交数据的主节点。而 apiserver 作为集群入口,本身是无状态的web服务器,多个 apiserver 服务之间直接负载请求并不需要做选主。Controller-Manager 和 Scheduler 作为任务类型的组件,比如 controller-manager 内置的 k8s 各种资源对象的控制器实时的 watch apiserver 获取对象最新的变化事件做期望状态和实际状态调整,调度器watch未绑定节点的pod做节点选择,显然多个这些任务同时工作是完全没有必要的,所以 controller-manager 和 scheduler 也是需要选主的,但是选主逻辑和 etcd 不一样的,这里只需要保证从多个 controller-manager 和 scheduler 之间选出一个 leader 进入工作状态即可,而无需考虑它们之间的数据一致和同步。


##生成kube-scheduler证书
cd /opt/k8s/k8s-cert/
#创建证书请求文件
cat > kube-scheduler-csr.json << EOF
{
  "CN": "system:kube-scheduler",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
EOF

#生成证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler

#生成kubeconfig文件
KUBE_CONFIG="/opt/kubernetes/cfg/kube-scheduler.kubeconfig"
KUBE_APISERVER="https://$MASTER_ADDRESS:6443"

kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=${KUBE_CONFIG}
kubectl config set-credentials kube-scheduler \
  --client-certificate=./kube-scheduler.pem \
  --client-key=./kube-scheduler-key.pem \
  --embed-certs=true \
  --kubeconfig=${KUBE_CONFIG}
kubectl config set-context default \
  --cluster=kubernetes \
  --user=kube-scheduler \
  --kubeconfig=${KUBE_CONFIG}
kubectl config use-context default --kubeconfig=${KUBE_CONFIG}


##创建 kube-scheduler.service 服务管理文件
cat >/usr/lib/systemd/system/kube-scheduler.service <<EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=-/opt/kubernetes/cfg/kube-scheduler
ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kube-scheduler
systemctl restart kube-scheduler

执行脚本、验证是否启动成功

cd /opt/k8s/k8s-cert

./scheduler.sh 192.168.247.10
#执行脚本,主要位置变量,需要写入本机的IP地址

ps aux | grep kube-scheduler

5、启动controller-manager 服务

准备启动脚本

vim /opt/k8s/k8s-cert/controller-manager.sh

#!/bin/bash
##创建 kube-controller-manager 启动参数配置文件
MASTER_ADDRESS=$1

cat >/opt/kubernetes/cfg/kube-controller-manager <<EOF
KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/opt/kubernetes/logs \\
--leader-elect=true \\
--kubeconfig=/opt/kubernetes/cfg/kube-controller-manager.kubeconfig \\
--bind-address=127.0.0.1 \\
--allocate-node-cidrs=true \\
--cluster-cidr=10.244.0.0/16 \\
--service-cluster-ip-range=10.0.0.0/24 \\
--cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \\
--cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \\
--root-ca-file=/opt/kubernetes/ssl/ca.pem \\
--service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \\
--cluster-signing-duration=87600h0m0s"
EOF

#––leader-elect:当该组件启动多个时,自动选举(HA)
#-–kubeconfig:连接 apiserver 用的配置文件,用于识别 k8s 集群
#--cluster-cidr=10.244.0.0/16:pod资源的网段,需与pod网络插件的值设置一致。通常,Flannel网络插件的默认为10.244.0.0/16,Calico插件的默认值为192.168.0.0/16
#--cluster-signing-cert-file/–-cluster-signing-key-file:自动为kubelet颁发证书的CA,与apiserver保持一致。指定签名的CA机构根证书,用来签名为 TLS BootStrapping 创建的证书和私钥
#--root-ca-file:指定根CA证书文件路径,用来对 kube-apiserver 证书进行校验,指定该参数后,才会在 Pod 容器的 ServiceAccount 中放置该 CA 证书文件
#--experimental-cluster-signing-duration:设置为 TLS BootStrapping 签署的证书有效时间为10年,默认为1年


##生成kube-controller-manager证书
cd /opt/k8s/k8s-cert/
#创建证书请求文件
cat > kube-controller-manager-csr.json << EOF
{
  "CN": "system:kube-controller-manager",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing", 
      "ST": "BeiJing",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
EOF

#生成证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

#生成kubeconfig文件
KUBE_CONFIG="/opt/kubernetes/cfg/kube-controller-manager.kubeconfig"
KUBE_APISERVER="https://$1:6443"

kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=${KUBE_CONFIG}
kubectl config set-credentials kube-controller-manager \
  --client-certificate=./kube-controller-manager.pem \
  --client-key=./kube-controller-manager-key.pem \
  --embed-certs=true \
  --kubeconfig=${KUBE_CONFIG}
kubectl config set-context default \
  --cluster=kubernetes \
  --user=kube-controller-manager \
  --kubeconfig=${KUBE_CONFIG}
kubectl config use-context default --kubeconfig=${KUBE_CONFIG}


##创建 kube-controller-manager.service 服务管理文件
cat >/usr/lib/systemd/system/kube-controller-manager.service <<EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=-/opt/kubernetes/cfg/kube-controller-manager
ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kube-controller-manager
systemctl restart kube-controller-manager

执行脚本、验证是否启动成功

cd /opt/k8s/k8s-cert

./controller-manager.sh 192.168.247.10
#启动脚本,并带上位置变量,位置变量为本机IP

ps aux | grep kube-controller-manager

6、生成kubectl连接集群的kubeconfig文件

准备执行脚本

vim /opt/k8s/k8s-cert/admin.sh

#!/bin/bash
mkdir /root/.kube
KUBE_CONFIG="/root/.kube/config"
KUBE_APISERVER="https://192.168.247.10:6443"

cd /opt/k8s/k8s-cert/

kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=${KUBE_CONFIG}
kubectl config set-credentials cluster-admin \
  --client-certificate=./admin.pem \
  --client-key=./admin-key.pem \
  --embed-certs=true \
  --kubeconfig=${KUBE_CONFIG}
kubectl config set-context default \
  --cluster=kubernetes \
  --user=cluster-admin \
  --kubeconfig=${KUBE_CONFIG}
kubectl config use-context default --kubeconfig=${KUBE_CONFIG}

执行脚本

./admin.sh

kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous

7、通过kubectl工具查看当前集群组件状态

kubectl get cs
#查看当前集群组件状态(都为健康状态即可,如果不健康,需要排错)

kubectl version
#查看版本信息(显示客户端和服务端的版本信息)

六、部署Worker node组件

1、在所有 node 节点上操作

mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}
#创建kubernetes的工作目录(提前创建号两个node的k8s的工作目录)

2、在master01上生成文件scp到node01

cd /opt/k8s/k8s-cert/kubernetes/server/bin
#在master01中进入到前面解压的用压缩文件,里面有node节点需要用到的两个组件,一个kubelet、一个lube-proxy组件

scp kubelet kube-proxy root@192.168.247.30:/opt/kubernetes/bin/
scp kubelet kube-proxy root@192.168.247.40:/opt/kubernetes/bin/
#把kubelet、kube-proxy 拷贝到 node 节点的工作目录中(因为是执行文件所以拷贝到bin目录下)

准备kubeconfig脚本文件

vim /opt/k8s/k8s-cert/kubeconfig.sh

#!/bin/bash
#example: kubeconfig 192.168.247.10 /opt/k8s/k8s-cert/
#创建bootstrap.kubeconfig文件
#该文件中内置了 token.csv 中用户的 Token,以及 apiserver CA 证书;kubelet 首次启动会加载此文件,使用 apiserver CA 证书建立与 apiserver 的 TLS 通讯,使用其中的用户 Token 作为身份标识向 apiserver 发起 CSR 请求

BOOTSTRAP_TOKEN=$(awk -F ',' '{print $1}' /opt/kubernetes/cfg/token.csv)
#获取token令牌

APISERVER=$1   #位置变量,获取本机ip
SSL_DIR=$2     #获取存放路径。

export KUBE_APISERVER="https://$APISERVER:6443"

# 设置集群参数
kubectl config set-cluster kubernetes \
  --certificate-authority=$SSL_DIR/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=bootstrap.kubeconfig
#--embed-certs=true:表示将ca.pem证书写入到生成的bootstrap.kubeconfig文件中

# 设置客户端认证参数,kubelet 使用 bootstrap token 认证
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

# 使用上下文参数生成 bootstrap.kubeconfig 文件
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig

#----------------------

#创建kube-proxy.kubeconfig文件
# 设置集群参数
kubectl config set-cluster kubernetes \
  --certificate-authority=$SSL_DIR/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-proxy.kubeconfig

# 设置客户端认证参数,kube-proxy 使用 TLS 证书认证
kubectl config set-credentials kube-proxy \
  --client-certificate=$SSL_DIR/kube-proxy.pem \
  --client-key=$SSL_DIR/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

# 使用上下文参数生成 kube-proxy.kubeconfig 文件
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

执行脚本文件

cd /opt/k8s/k8s-cert/

chmod +x kubeconfig.sh

./kubeconfig.sh 192.168.247.10 /opt/k8s/k8s-cert/
#执行脚本,需要添加两个位置变量,一个本机IP,一个生成的文件所在的目录
cd /opt/k8s/k8s-cert/
    
scp bootstrap.kubeconfig kube-proxy.kubeconfig root@192.168.247.30:/opt/kubernetes/cfg/

scp bootstrap.kubeconfig kube-proxy.kubeconfig root@192.168.247.40:/opt/kubernetes/cfg/
#把配置文件 bootstrap.kubeconfig、kube-proxy.kubeconfig 拷贝到 node 节点

#RBAC授权,使用户 kubelet-bootstrap 能够有权限发起 CSR 请求
kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap

#如想重新配置CSR可kubectl delete clusterrolebinding kubelet-bootstrap清空CSR请求

3、node01安装kubelet服务

准备kubelet服务的执行脚本文件

mkdir /opt/k8s-cert/
#创建一个目录用来存储脚本文件

vim /opt/k8s-cert/kubelet.sh

#!/bin/bash
#例子:./kubelet.sh 192.168.247.30
NODE_ADDRESS=$1
DNS_SERVER_IP=${2:-"10.0.0.2"}

#创建 kubelet 启动参数配置文件
cat >/opt/kubernetes/cfg/kubelet <<EOF
KUBELET_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/opt/kubernetes/logs \\
--hostname-override=${NODE_ADDRESS} \\
--network-plugin=cni \\
--kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \\
--bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \\
--config=/opt/kubernetes/cfg/kubelet.config \\
--cert-dir=/opt/kubernetes/ssl \\
--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0"
EOF

#--hostname-override:指定kubelet节点在集群中显示的主机名或IP地址,默认使用主机hostname;kube-proxy和kubelet的此项参数设置必须完全一致
#--network-plugin:启用CNI
#--kubeconfig:指定kubelet.kubeconfig文件位置,当前为空路径,会自动生成,用于如何连接到apiserver,里面含有kubelet证书,master授权完成后会在node节点上生成 kubelet.kubeconfig 文件
#--bootstrap-kubeconfig:指定连接 apiserver 的 bootstrap.kubeconfig 文件
#--config:指定kubelet配置文件的路径,启动kubelet时将从此文件加载其配置
#--cert-dir:指定master颁发的kubelet证书生成目录
#--pod-infra-container-image:指定Pod基础容器(Pause容器)的镜像。Pod启动的时候都会启动一个这样的容器,每个pod之间相互通信需要Pause的支持,启动Pause需要Pause基础镜像


#----------------------
#创建kubelet配置文件(该文件实际上就是一个yml文件,语法非常严格,不能出现tab键,冒号后面必须要有空格,每行结尾也不能有空格)
cat >/opt/kubernetes/cfg/kubelet.config <<EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: ${NODE_ADDRESS}
port: 10250
readOnlyPort: 10255
cgroupDriver: cgroupfs
clusterDNS:
- ${DNS_SERVER_IP} 
clusterDomain: cluster.local
failSwapOn: false
authentication:
  anonymous:
    enabled: true
EOF

#PS:当命令行参数与此配置文件(kubelet.config)有相同的值时,就会覆盖配置文件中的该值。


#----------------------
#创建 kubelet.service 服务管理文件
cat >/usr/lib/systemd/system/kubelet.service <<EOF
[Unit]
Description=Kubernetes Kubelet
After=docker.service
Requires=docker.service

[Service]
EnvironmentFile=/opt/kubernetes/cfg/kubelet
ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS
Restart=on-failure
KillMode=process

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kubelet
systemctl restart kubelet

执行脚本

cd /opt
chmod +x kubelet.sh
./kubelet.sh 192.168.247.30
#执行脚本,需要带上本机node节点的IP,此脚本,主要是触发关键kubelet的配置文件,启动kubelet的时候,也相当于给master01发送了CSR认证请求。

ps aux|grep kubelet
#查看是否启动

4、master01验证CSR请求

kubectl get csr
#检查到 node01 节点的 kubelet 发起的 CSR 请求,Pending 表示等待集群给该节点签发证书

kubectl certificate approve 【node-csr-duiobEzQ0R93HsULoS9NT9JaQylMmid_nBF3Ei3NtFE(这里面的根据实际的来)】
#通过CSR请求


kubectl get csr
#Approved,Issued 表示已授权 CSR 请求并签发证书

kubectl get node
#查看节点,由于网络插件还没有部署,节点会没有准备就绪,就会显示NotReady

5、node01安装proxy服务

准备执行脚本

for i in $(ls /usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs|grep -o "^[^.]*");do echo $i; /sbin/modinfo -F filename $i >/dev/null 2>&1 && /sbin/modprobe $i;done
#使用一个for循环,加载ip_vs模块
-------------------------------------------------------------------------------
vim /opt/k8s-cert/proxy.sh 

#!/bin/bash
#例子: ./proxy.sh 192.168.247.30
NODE_ADDRESS=$1

#创建 kube-proxy 启动参数配置文件
cat >/opt/kubernetes/cfg/kube-proxy <<EOF
KUBE_PROXY_OPTS="--logtostderr=true \\
--v=4 \\
--hostname-override=${NODE_ADDRESS} \\
--cluster-cidr=172.17.0.0/16 \\
--proxy-mode=ipvs \\
--kubeconfig=/opt/kubernetes/cfg/kube-proxy.kubeconfig"
EOF

#--hostnameOverride: 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 ipvs 规则
#--cluster-cidr:指定 Pod 网络使用的聚合网段,Pod 使用的网段和 apiserver 中指定的 service 的 cluster ip 网段不是同一个网段。 kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量,指定 --cluster-cidr 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT,即来自非 Pod 网络的流量被当成外部流量,访问 Service 时需要做 SNAT。
#--proxy-mode:指定流量调度模式为ipvs模式,可添加--ipvs-scheduler选项指定ipvs调度算法(rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq)
#--kubeconfig: 指定连接 apiserver 的 kubeconfig 文件	


#----------------------
#创建 kube-proxy.service 服务管理文件
cat >/usr/lib/systemd/system/kube-proxy.service <<EOF
[Unit]
Description=Kubernetes Proxy
After=network.target

[Service]
EnvironmentFile=-/opt/kubernetes/cfg/kube-proxy
ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kube-proxy
systemctl restart kube-proxy

执行脚本

cd /opt
chmod +x proxy.sh
./proxy.sh 192.168.247.30
#指向脚本,注意需要带入位置参数,为本机的IP地址,此脚本主要时配置proxy的配置文件,并启动它

ps aux |grep kube-proxy

6、node2启动安装kubelet、proxy服务

#将node01节点的kubelet.sh、proxy.sh文件复制到node02节点
[root@node01 opt]# scp kubelet.sh proxy.sh root@192.168.247.40:/opt/

#node02上启动kubelet服务
[root@node02 opt]# ./kubelet.sh 192.168.247.40


#启动proxy服务
[root@node02 opt]# ./proxy.sh 192.168.247.40
[root@node02 opt]# ps -aux |grep kube-proxy
在 node02 节点上操作
#加载 ip_vs 模块

for i in $(ls /usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs|grep -o "^[^.]*");do echo $i; /sbin/modinfo -F filename $i >/dev/null 2>&1 && /sbin/modprobe $i;done

kubectl get csr
#检查到 node02 节点的 kubelet 发起的 CSR 请求,Pending 表示等待集群给该节点签发证书

kubectl certificate approve 【node-csr-duiobEzQ0R93HsULoS9NT9JaQylMmid_nBF3Ei3NtFE(这里面的根据实际的来)】
#通过CSR请求


kubectl get csr
#Approved,Issued 表示已授权 CSR 请求并签发证书

kubectl get node
#查看节点,由于网络插件还没有部署,节点会没有准备就绪,就会显示NotReady

七、部署CNI网络组建

K8s的三种网络模式:

  1. Pod网络:在这种模式下,每个 Pod 都拥有独立的 IP 地址。并且可以通过容器之间直接访问。这种模式通常使用 Linux Bridge 网桥实现。
  2. Service网络:service是一个虚拟IP,是一个负载均衡集群IP,这种模式使用 Kubernetes Service 来公开应用程序服务,并为它们提供稳定的虚拟 IP 地址。
  3. 节点网络:CNI 模式通常通过 CNI 插件来管理容器网络。
  • Pod 内容器与容器之间的通信:用 localhost 地址访问彼此的端口。
  • 同一个 Node 内 Pod 之间的通信:每个 Pod 都有一个真实的全局 IP 地址,可以直接采用对方 Pod 的 IP 地址进行通信,Pod1 与 Pod2 都是通过 Veth 连接到同一个 docker0/cni0 网桥,网段相同,所以它们之间可以直接通信。
  • 不同 Node 上 Pod 之间的通信:
     

Flannel UDP 模式的工作原理:

数据从主机 A 上 Pod 的源容器中发出后,经由所在主机的 docker0/cni0 网络接口转发到 flannel0 接口,flanneld 服务监听在 flannel0 虚拟网卡的另外一端。
Flannel 通过 Etcd 服务维护了一张节点间的路由表。源主机 A 的 flanneld 服务将原本的数据内容封装到 UDP 报文中, 根据自己的路由表通过物理网卡投递给目的节点主机 B 的 flanneld 服务,数据到达以后被解包,然后直接进入目的节点的 flannel0 接口, 之后被转发到目的主机的 docker0/cni0 网桥,最后就像本机容器通信一样由 docker0/cni0 转发到目标容器。

Flannel VXLAN 模式跨主机的工作原理:

1、数据帧从主机 A 上 Pod 的源容器中发出后,经由所在主机的 docker0/cni0 网络接口转发到 flannel.1 接口
2、flannel.1 收到数据帧后添加 VXLAN 头部,封装在 UDP 报文中
3、主机 A 通过物理网卡发送封包到主机 B 的物理网卡中
4、主机 B 的物理网卡再通过 VXLAN 默认端口 4789 转发到 flannel.1 接口进行解封装
5、解封装以后,内核将数据帧发送到 cni0,最后由 cni0 发送到桥接到此接口的容器 B 中。

1、部署flannel

在node01节点上操作

cd /opt/k8s-cert
#上传 cni-plugins-linux-amd64-v0.8.6.tgz 和 flannel.tar 到 /opt 目录中
//一个为cni的各种执行文件,一个为docker镜像

docker load -i flannel.tar
#将镜像上传到容器本地中

mkdir  -p /opt/cni/bin
#创建一个存放cni插件的执行工作目录

tar zxvf cni-plugins-linux-amd64-v0.8.6.tgz -C /opt/cni/bin
#将有压缩包解压到cni的工作目录

在master01节点上操作

cd /opt/k8s/k8s-cert
#上传 kube-flannel.yml 文件到 /opt/k8s 目录中,部署 CNI 网络

kubectl apply -f kube-flannel.yml 
#基于yml文件创建pod。

kubectl get pods -n kube-system
#查看此pod是否正常运行(runing即可)

kubectl get nodes
#再次查看node节点的状态是否为就绪状态(ready即可)
------------------------------------------------------------
此步骤如果pod一直无法变成runing状态
可以使用 kubectl delete -f kube-flannel.yml  #删除然后重新创建,大部分原因是镜像问题,需要重新下

2、部署calico

k8s 组网方案对比(flannel与calico对比):

flannel

配置方便、功能简单,是基于ouverlay叠加实现网络,由于要进行封装和解封装的过程,对性能会有一定的影响,同时不具备网络策略配置能力

三种网络模式:UDP VXLAN HOST-GW 默认网段:10.244.0.0/16

calico

功能强大,没有封装和解封装的过程,对性能影响比较小,具有网络策略配置能力,但是路由表维护起来比较复杂。默认网段:192.168.0.0/16 模式:BGP

Calico 主要由三个部分组成:

  • Calico CNI插件:主要负责与kubernetes对接,供kubelet调用使用。
  • Felix:负责维护宿主机上的路由规则、FIB转发信息库等。
  • BIRD:负责分发路由规则,类似路由器。
  • Confd:配置管理组件。

Calico 工作原理:

Calico 是通过路由表来维护每个 pod 的通信。Calico 的 CNI 插件会为每个容器设置一个 veth pair 设备, 然后把另一端接入到宿主机网络空间,由于没有网桥,CNI 插件还需要在宿主机上为每个容器的 veth pair 设备配置一条路由规则, 用于接收传入的 IP 包。
有了这样的 veth pair 设备以后,容器发出的 IP 包就会通过 veth pair 设备到达宿主机,然后宿主机根据路由规则的下一跳地址, 发送给正确的网关,然后到达目标宿主机,再到达目标容器。
这些路由规则都是 Felix 维护配置的,而路由信息则是 Calico BIRD 组件基于 BGP 分发而来。
calico 实际上是将集群里所有的节点都当做边界路由器来处理,他们一起组成了一个全互联的网络,彼此之间通过 BGP 交换路由, 这些节点我们叫做 BGP Peer。
 

cd /opt/k8s/k8s-cert/
#上传 calico.yaml 文件到 /opt/k8s 目录中,部署 CNI 网络

vim calico.yaml
//修改里面定义 Pod 的网络(CALICO_IPV4POOL_CIDR),需与前面 kube-controller-manager 配置文件指定的 cluster-cidr 网段一样
    - name: CALICO_IPV4POOL_CIDR
      value: "10.244.0.0/16"      #(3878行)Calico 默认使用的网段为 192.168.0.0/16
      
kubectl apply -f calico.yaml
#基于calico.yml文件创建pod

kubectl get pods -n kube-system
#查看状态

kubectl get nodes
##等 Calico Pod 都 Running,节点也会准备就绪

--------------------------------------------------------------
此步骤如果pod一直无法变成runing状态
可以使用 kubectl delete -f calico.yml  #删除然后重新创建,大部分原因是镜像问题,需要重新下

3、node02节点部署

  • 在 node01 节点上操作
cd /opt/k8s-cert/
scp kubelet.sh proxy.sh root@192.168.27.40:/opt/
#将node01上面的两个组件的安装脚本拷贝到node02上面进行安装组件

scp -r /opt/cni root@192.168.247.40:/opt/
#将node01的cni里面的插件拷贝到node02上面
  • 在 node02节点上操作
cd /opt/
    
chmod +x kubelet.sh
./kubelet.sh 192.168.247.40
#执行kubelet.sh脚本,带入本机的IP地址,进行安装kubelet组件,这里也是相当于申请了CSR认证,需要在master01上进行认证。

systemctl status  kubelet
#查看kube组件是否启动
  • 在master01节点上操作
kubectl get csr

kubectl certificate approve 【node-csr-BbqEh6LvhD4R6YdDUeEPthkb6T_CJDcpVsmdvnh81y0】
#通过请求(两台都要通过,前面node01的已经通过)

kubectl get csr
  • 在node02 节点操作
for i in $(ls /usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs|grep -o "^[^.]*");do echo $i; /sbin/modinfo -F filename $i >/dev/null 2>&1 && /sbin/modprobe $i;done
#使用for循环,加载 ipvs 模块

cd /opt
chmod +x proxy.sh

./proxy.sh 192.168.247.40
##使用proxy.sh脚本启动proxy服务,位置变量为本机IP地址。

systemctl status kube-proxy
#查看kube-proxy组件服务是否启动
  • 在master节点操作
kubectl get nodes
#查看群集中的节点状态

kubectl get pods -n kube-system
#需要等里面的几个pod都启动起来,变成running(显示init可以稍微等下,或者PodInitializing,如果显示pullimage失败,可以安装上述办法重新创建)

 八、部署core DNS

CoreDNS:可以为集群中的 service 资源创建一个域名 与 IP 的对应关系解析

  • 在所有node节点上操作
cd /opt/
#上传coredns.tar 到/opt目录上

docker load -i coredns.tar
#上传到docker中
  • 在master01 节点上操作
cd /opt/k8s
#上传 coredns.yaml 文件到 /opt/k8s 目录中,部署 CoreDNS

kubectl apply -f coredns.yaml
#根据coredns.yml文件创建pod

kubectl get pods -n kube-system 
#再次查看创建的dns的pod是否运行成功

kubectl run -it --rm dns-test --image=busybox:1.28.4 sh
#运行一个容器,镜像为busybos:1.28.4,并且直接运行(网不好就多等会),然后解析kubernetes成功即可

注:

如果出现以下报错

[root@master01 k8s]# kubectl run -it  --image=busybox:1.28.4 sh
If you don't see a command prompt, try pressing enter.
Error attaching, falling back to logs: unable to upgrade connection: Forbidden (user=system:anonymous, verb=create, resource=nodes, subresource=proxy)
Error from server (Forbidden): Forbidden (user=system:anonymous, verb=get, resource=nodes, subresource=proxy) ( pods/log sh)

需要添加 rbac的权限  直接使用kubectl绑定  clusteradmin 管理员集群角色  授权操作权限

[root@master01 k8s]# kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous
clusterrolebinding.rbac.authorization.k8s.io/cluster-system-anonymous created

 九、master02 节点部署

需要安装步骤一的系统初始化脚本,并且更名为master02

  • 在master01上操作
scp -r /opt/etcd/ root@192.168.247.20:/opt/
#拷贝matser01上面的etcd证书到master02上面

scp -r /opt/kubernetes/ root@192.168.247.20:/opt
#拷贝master01上的整个kubernetes工作目录

scp -r /root/.kube root@192.168.247.20:/root
#拷贝运行master01上面的.kube运行文件kubect命令文件到master02上面

scp /usr/lib/systemd/system/{kube-apiserver,kube-controller-manager,kube-scheduler}.service root@192.168.247.20:/usr/lib/systemd/system/
#从 master01 节点上拷贝三大组件到master02上面

master02节点修改

vim /opt/kubernetes/cfg/kube-apiserver

KUBE_APISERVER_OPTS="--logtostderr=true \
--v=4 \
--etcd-servers=https://192.168.247.10:2379,https://192.168.247.30:2379,https://192.168.247.40:2379 \
--bind-address=192.168.247.20 \                 #修改
--secure-port=6443 \
--advertise-address=192.168.247.20 \			#修改
......

###修改apiserver组件指向的IP地址,修改监听地址为本即IP地址
systemctl start kube-apiserver.service
systemctl enable kube-apiserver.service
systemctl start kube-controller-manager.service
systemctl enable kube-controller-manager.service
systemctl start kube-scheduler.service
systemctl enable kube-scheduler.service
#在 master02 节点上启动各服务并设置开机自启

ln -s /opt/kubernetes/bin/* /usr/local/bin/


kubectl get nodes
kubectl get nodes -o wide			#-o=wide:输出额外信息;对于Pod,将输出Pod所在的Node名

##此时在master02节点查到的node节点状态仅是从etcd查询到的信息,而此时node节点实际上并未与master02节点建立通信连接,因此需要使用一个VIP把node节点与master节点都关联起来

至此,以上的两主两从,已安装完成,下面将配置负载均衡,通过nginx将负载均衡到两台master主机上。

十、负载均衡部署(lb01、lb02)

1、部署nginx服务、配置四层负载均衡

systemctl stop firewalld
systemctl disable firewalld
setenforce 0
#关闭防火墙

cat > /etc/yum.repos.d/nginx.repo << 'EOF'
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
EOF
#配置nginx的官方在线源,配置本地nginx的yum源

yum -y install nginx
#下载nginx

-----#修改nginx配置文件,配置四层反向代理负载均衡,指定k8s群集2台master的节点ip和6443端口-----
vim /etc/nginx/nginx.conf
events {
    worker_connections  1024;
}
#添加
stream {
    log_format  main  '$remote_addr $upstream_addr - [$time_local] $status $upstream_bytes_sent';
    
	access_log  /var/log/nginx/k8s-access.log  main;

    upstream k8s-apiserver {
        server 192.168.247.50:6443;
        server 192.168.247.60:6443;
    }
    server {
        listen 6443;
        proxy_pass k8s-apiserver;
    }
}

http {
......
---------------------------------------------------------------------
nginx -t
#检查语法

systemctl start nginx.service 
systemctl enable nginx.service
netstat -natp |grep nginx
#启动nginx服务,查看自己的监听6443端口
  • 创建nginx状态检查脚本

vim /etc/nginx/check_nginx.sh
#!/bin/bash
#egrep -cv "grep|$$" 用于过滤掉包含grep 或者 $$ 表示的当前Shell进程ID,即脚本运行的当前进程ID号
count=$(ps -ef | grep nginx | egrep -cv "grep|$$")

if [ "$count" -eq 0 ];then
    systemctl stop keepalived
fi


chmod +x /etc/nginx/check_nginx.sh

2、部署keepalived服务

yum install keepalived -y
#下载keepalived服务

#修改配置文件
vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   # 接收邮件地址
   notification_email {
     acassen@firewall.loc
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   # 邮件发送地址
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id NGINX_MASTER	#lb01节点的为 NGINX_MASTER,lb02节点的为 NGINX_BACKUP
}

#添加一个周期性执行的脚本
vrrp_script check_nginx {
    script "/etc/nginx/check_nginx.sh"	#指定检查nginx存活的脚本路径
}

vrrp_instance VI_1 {
    state MASTER			#lb01节点的为 MASTER,lb02节点的为 BACKUP
    interface ens33			#指定网卡名称 ens33
    virtual_router_id 51	#指定vrid,两个节点要一致
    priority 100			#lb01节点的为 100,lb02节点的为 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
       192.168.247.100/24	#指定 VIP
    }
    track_script {
        check_nginx			#指定vrrp_script配置的脚本
    }
}
  • 启动keepalived服务(一定要先启动了nginx服务,再启动keepalived服务)
systemctl start keepalived
systemctl enable keepalived
ip a				#查看VIP是否生成
  • 修改node节点上的bootstrap.kubeconfig,kubelet.kubeconfig配置文件为VIP
cd /opt/kubernetes/cfg/
vim bootstrap.kubeconfig 
server: https://192.168.10.100:6443
                      
vim kubelet.kubeconfig
server: https://192.168.10.100:6443
                        
vim kube-proxy.kubeconfig
server: https://192.168.10.100:6443

//重启kubelet和kube-proxy服务
systemctl restart kubelet.service 
systemctl restart kube-proxy.service
  • 在 lb01 上查看 nginx 和 node 、 master 节点的连接状态
netstat -natp | grep nginx
tcp        0      0 0.0.0.0:6443            0.0.0.0:*               LISTEN      84739/nginx: master 
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      84739/nginx: master 
tcp        0      0 192.168.10.21:60382     192.168.10.20:6443      ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.18:41650     ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.19:49726     ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.21:35234     192.168.10.80:6443      ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.18:41648     ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.19:49728     ESTABLISHED 84742/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.18:41646     ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.21:32786     192.168.10.20:6443      ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.18:41656     ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.21:60378     192.168.10.20:6443      ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.21:32794     192.168.10.20:6443      ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.19:49724     ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.21:35886     192.168.10.80:6443      ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.19:51372     ESTABLISHED 84742/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.19:49722     ESTABLISHED 84741/nginx: worker 
tcp        0      0 192.168.10.100:6443     192.168.10.19:49702     ESTABLISHED 84741/nginx: worker
  • 在 master01 节点上操作
//测试创建pod
kubectl run nginx --image=nginx

//查看Pod的状态信息
kubectl get pods
NAME                    READY   STATUS              RESTARTS   AGE
nginx-dbddb74b8-nf9sk   0/1     ContainerCreating   0          33s   #正在创建中

kubectl get pods
NAME                    READY   STATUS    RESTARTS   AGE
nginx-dbddb74b8-nf9sk   1/1     Running   0          80s  			#创建完成,运行中

kubectl get pods -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE            NOMINATED NODE
nginx-dbddb74b8-26r9l   1/1     Running   0          10m   172.17.36.2   192.168.80.15   <none>
//READY为1/1,表示这个Pod中有1个容器
  • 在对应网段的node节点上操作,可以直接使用浏览器或者curl命令访问
curl 172.17.36.2
  •  这时在master01节点上查看nginx日志
kubectl logs nginx-dbddb74b8-nf9sk

十一、部署Dashboard(master01)

Dashboard 介绍
仪表板是基于Web的Kubernetes用户界面。您可以使用仪表板将容器化应用程序部署到Kubernetes集群,对容器化应用程序进行故障排除,并管理集群本身及其伴随资源。您可以使用仪表板来概述群集上运行的应用程序,以及创建或修改单个Kubernetes资源(例如deployment,job,daemonset等)。例如,您可以使用部署向导扩展部署,启动滚动更新,重新启动Pod或部署新应用程序。仪表板还提供有关群集中Kubernetes资源状态以及可能发生的任何错误的信息。

上传文件并进行暴露端口

cd /opt/k8s/k8s-cert/
#上传 recommended.yaml 文件到 /opt/k8s 目录中

------------------------------------------------------------
vim recommended.yaml
#默认Dashboard只能集群内部访问,修改Service为NodePort类型,暴露到外部:
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 30001     #添加
  type: NodePort          #添加
  selector:
    k8s-app: kubernetes-dashboard
------------------------------------------------------------------

kubectl apply -f recommended.yaml
#基于yml文件创建pod

kubectl get pods -n kubernetes-dashboard
#查看是否创建成功

创建service account并绑定默认cluster-admin管理员集群角色

kubectl create serviceaccount dashboard-admin -n kube-system
#创建资源,并指定账号

kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin
#

kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
#查看信息
eyJhbGciOiJSUzI1NiIsImtpZCI6ImRyOUpLNnZNaUxfbHAySGpiV0xKdHlZaTJGU192V3VUX2RieXdqT1RRb2MifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4tOWNodjciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiZTE2ZWIwYzUtMDg2NC00MGIyLTg4MzAtZjAyODYxOTQxMTFiIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.HV7CM-5NFqzz99250Pou_qXrpDdzNbA7kUmYnl3gdcXHSLeUpAQXbrEH_JEkS5LTwp62Q_QCZlrYkO_wKXIzUpQI9_3Th3zZUec76SWlIVXf6rqEh2VZj45jdYXVqpZzwhI-FJ6NQ5H5sIxOQlyP1wKqwtb8XNB6Oj2uQsWEHuJFnKfm7wzF5n8-4WKQ0RVHWPcrCkScgY_POYv1utK55kVgrPk2dFH97eUDOuOv7ZaqiYJqupOluRpoVAqeks0z0i4BZNoZL_nxvuRlN-KBvuTkeWrr5Fb3WyI-BUZNurYFuIDhM6vNjSHuDyTQL2LQbkK2DRPepBFbniZWjMiu-w


最后网页进行登录

https://NodeIP:30001

例子:https://192.168.247.30:30001

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1137953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0基础学习PyFlink——用户自定义函数之UDTF

大纲 表值函数完整代码 在《0基础学习PyFlink——用户自定义函数之UDF》中&#xff0c;我们讲解了UDF。本节我们将讲解表值函数——UDTF 表值函数 我们对比下UDF和UDTF def udf(f: Union[Callable, ScalarFunction, Type] None,input_types: Union[List[DataType], DataTy…

macOS Sonoma 14.1正式版发布 改善Apple Music界面 新增保修状态显示

10月26日消息&#xff0c;苹果今天为 macOS Sonoma 推出了 14.1 版本更新&#xff0c;本更新主要改善了 Apple Music 界面&#xff0c;设置中新增保修状态&#xff0c;并修复了多项错误内容。 经过几周的用户测试&#xff0c;Apple 正式向所有 Mac 用户发布了 macOS Sonoma 14.…

【代码随想录01】数组总结

抄去吧&#xff0c;保存去吧&#xff01;

p5.js 3D图形-立方体

本文简介 带尬猴&#xff0c;我嗨德育处主任 前面写了几篇 p5.js 文章 都还没涉及到3D图形&#xff0c;但其实 p5.js 是提供了基础的3D图形的。 本文就从最简单的立方体讲起&#xff0c;并做几个小demo和各位工友一起掌握立方体的用法。 立方体的基础用法 在 p5.js 里使用 b…

智慧矿山AI算法助力煤矿安全:人员越界识别精准迅速

煤矿作为一种危险性较高的工业领域&#xff0c;安全管理一直是煤矿企业的重要任务。传统煤矿安全管理主要依靠人工巡逻及视频监控等手段&#xff0c;但这些方法往往存在人力不足、盲区多等问题&#xff0c;无法实时监控和预警&#xff0c;难以有效避免事故的发生。随着人工智能…

如何在linux服务器上安装Anaconda与pytorch,以及pytorch卸载

如何在linux服务器上安装Anaconda与pytorch&#xff0c;以及pytorch卸载 1&#xff0c;安装anaconda1.1 下载anaconda安装包1.2 安装anaconda1.3 设计环境变量1.4 安装完成验证 2 Anaconda安装pytorch2.1 创建虚拟环境2.2 查看现存环境2.3 激活环境2.4 选择合适的pytorch版本下…

FL Studio水果2023体验版如何破解?

FL Studio是一款非常专业的水果工具&#xff0c;软件功能齐全&#xff0c;拥有编曲、剪辑、录音、混音等功能&#xff0c;可以满足用户的各种音乐制作需求。软件已经成功破解&#xff0c;全中文的软件界面&#xff0c;去除了试用时间限制&#xff0c;有需要的快来下载吧&#x…

《动手学深度学习 Pytorch版》 10.6 自注意力和位置编码

在注意力机制中&#xff0c;每个查询都会关注所有的键&#xff0d;值对并生成一个注意力输出。由于查询、键和值来自同一组输入&#xff0c;因此被称为 自注意力&#xff08;self-attention&#xff09;&#xff0c;也被称为内部注意力&#xff08;intra-attention&#xff09;…

react-typescript-demo

1.使用 Context 来存储数据

rabbitMQ入门指南:管理页面全面指南及实战操作

文章目录 1. 引言2. RabbitMQ 管理页面的概览3. 探索 RabbitMQ 管理页面的主要功能3.1 连接3.2 通道3.3 交换机3.4 队列3.5 生产者3.6 消费者 4. RabbitMQ 的实战例子4.1 创建虚拟机4.2 连接和通道4.3 建立交换机4.3.1 交换机类型4.3.2 创建一个交换机 4.4 创建队列4.4.1 队列类…

海外广告投放保姆级教程,如何使用Quora广告开拓新流量市场?

虽然在Quora 上学习广告相对容易&#xff0c;但需要大量的试验和错误才能找出最有效的方法。一些广告技巧可以让您的工作更有效率。这篇文章将介绍如何有效进行quora广告投放与有价值的 Quora 广告要点&#xff0c;这将为您节省数万美元的广告支出和工作时间&#xff01;往下看…

五大绩效指标,为企业新媒体矩阵管理注入新动力

⭐关注矩阵通服务号&#xff0c;探索企业新媒体矩阵搭建与营销策略 新媒体矩阵的建设运营是为了能实现企业确定的业务目标。 那如何才能让推动最后业务目标的实现&#xff1f; 对于企业来说&#xff0c;可以将业务目标拆解为短期绩效目标&#xff0c;通过适当调整短期指标来保证…

【Java 进阶篇】Java HTTP 请求消息详解

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是一种用于传输超文本的应用层协议&#xff0c;广泛用于构建互联网应用。在Java中&#xff0c;我们经常需要发送HTTP请求来与远程服务器进行通信。本文将详细介绍Java中HTTP请求消息的各个部分&#xff0c;包括请求行、…

spring-初识spring

初识spring Spring简介 初识spring一、Spring 特性二、IOC容器(反转控制)1、IOC容器1.1、IOC思想1.2、IOC容器在Spring中的实现 2、基于XML管理bean2.1入门案例2.2获取bean2.3依赖注入(DI)2.3.1set方法注入2.3.1构造器注入 2.4 为类类型属性赋值2.4.1 引用外部已声明bean2.4.2 …

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件

说在前面 平时大家都是怎么管理自己的浏览器书签数据的呢&#xff1f;有没有过公司和家里的电脑浏览器书签不同步的情况&#xff1f;有没有过电脑突然坏了但书签数据没有导出&#xff0c;导致书签数据丢失了&#xff1f;解决这些问题的方法有很多&#xff0c;我选择自己写个chr…

数据结构与算法基础(青岛大学-王卓)(9)

终于迎来了最后一部分(排序)了&#xff0c;整个王卓老师的数据结构就算是一刷完成了&#xff0c;但是也才是数据结构的开始而已&#xff0c;以后继续与诸位共勉 &#x1f603; (PS.记得继续守护家人们的健康当然还有你自己的)。用三根美味的烤香肠开始吧。。。 文章目录 排序基…

高级深入--day41

用Pymongo保存数据 爬取豆瓣电影top250movie.douban.com/top250的电影数据&#xff0c;并保存在MongoDB中。 items.py class DoubanspiderItem(scrapy.Item):# 电影标题title scrapy.Field()# 电影评分score scrapy.Field()# 电影信息content scrapy.Field()# 简介info …

基于SSM的OA办公系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

nginx浏览器缓存和上流缓存expires指令_nginx配置HTTPS

1.nginx控制浏览器缓存是针对于静态资源[js,css,图片等] 1.1 expires指令 location /static {alias/home/imooc;#设置浏览器缓存10s过期expires 10s;#设置浏览器缓存时间晚上22:30分过期expires @22h30m;#设置浏览器缓存1小时候过期expires -1h;#设置浏览器不缓存expires …

【JavaEE初阶】 线程安全的集合类

文章目录 &#x1f340;前言&#x1f332;多线程环境使用 ArrayList&#x1f6a9;自己使用同步机制 (synchronized 或者 ReentrantLock)&#x1f6a9;Collections.synchronizedList(new ArrayList);&#x1f6a9;使用 CopyOnWriteArrayList &#x1f38d;多线程环境使用队列&am…