前言:
这套部署是典型的 Kubernetes 集群架构,包含了 Nginx + Keepalived、Etcd、Docker 和 Harbor 这些组件,各自扮演着不同的角色,共同构建了一个稳定、高可用的容器化环境。以下是对这些组件的介绍和作用:
-
Nginx + Keepalived:
-
Nginx: 作为一个高性能的反向代理服务器和负载均衡器,Nginx 负责接收来自外部用户的请求,并将这些请求转发给后端的 Kubernetes API Server 或者其他服务。
-
Keepalived: 用于实现高可用性和故障转移。通过 Keepalived 的配置,可以将多个 Nginx 实例组成一个虚拟 IP(VIP)集群,确保即使其中一个 Nginx 节点出现故障,服务仍然可用。
-
-
Etcd:
-
Etcd 是一个分布式键值存储系统,主要用于存储 Kubernetes 集群的各种配置信息、状态信息和资源对象的数据。它保证了集群中各个节点之间的数据一致性,并支持高可用部署,确保集群的稳定性和可靠性。
-
-
Docker:
-
Docker 是一个开源的容器化平台,用于打包、分发和运行应用程序。在 Kubernetes 集群中,Docker 负责管理和运行各个容器,提供了轻量级、快速部署的容器化解决方案。
-
-
Harbor:
-
Harbor 是一个企业级的容器镜像仓库,用于存储和管理 Docker 镜像。在 Kubernetes 集群中,Harbor 可以作为集中式的镜像仓库,提供镜像的版本管理、权限控制、安全扫描等功能,简化了容器镜像的管理和使用。
-
这套部署架构的优点包括:
-
高可用性: 使用了 Nginx + Keepalived 和 Etcd 实现了集群的高可用性和故障转移,确保服务的连续性和稳定性。
-
容器化管理: 使用 Docker 将应用程序打包成容器,实现了轻量级、快速部署的容器化管理。
-
镜像管理: 使用 Harbor 作为容器镜像仓库,实现了容器镜像的集中管理、安全扫描和权限控制。
综合来看,这套部署结合了容器化技术和高可用架构,为 Kubernetes 集群提供了稳定、可靠的运行环境,适用于生产环境的部署和管理。
硬件环境
4台虚机 : 2c2g,20g
一、基础环境配置
1.系统安装已经IP配置:
下载CentOS Linux release 7.9.2009 (Core)镜像
安装完成之后,进行IP配置:
vi /etc/sysconfig/network-scripts/ifcfg-ens33 ##修改 BOOTPROTO="static" #这表示网络接口使用静态 IP 地址配置,而不是动态获取 IP 地址(比如通过 DHCP) ONBOOT="yes" #这表示在系统启动时自动启用这个网络接口 #添加 IPADDR="99.99.10.10" #静态 IP 地址 GATEWAY="99.99.10.2" #网关地址,用于指示该网络接口的数据包应该通过哪个网关发送到其他网络 NETMASK="255.255.255.0" #子网掩码,用于确定该网络接口所在网络的范围 DNS1=99.99.10.2 #DNS 服务器的 IP 地址,用于系统解析域名 :wq
重启network:
systemctl restart netwok
ping baidu.com测试网络:
[root@k8s-master1 ~]# ping 99.99.10.14 PING 99.99.10.14 (99.99.10.14) 56(84) bytes of data. 64 bytes from 99.99.10.14: icmp_seq=1 ttl=64 time=0.104 ms 64 bytes from 99.99.10.14: icmp_seq=2 ttl=64 time=0.080 ms 64 bytes from 99.99.10.14: icmp_seq=3 ttl=64 time=0.048 ms 64 bytes from 99.99.10.14: icmp_seq=4 ttl=64 time=0.038 ms 64 bytes from 99.99.10.14: icmp_seq=5 ttl=64 time=0.041 ms [root@k8s-master1 data]# ping baidu.com PING baidu.com (39.156.66.10) 56(84) bytes of data. 64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=1 ttl=128 time=11.8 ms 64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=2 ttl=128 time=16.0 ms 64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=3 ttl=128 time=12.7 ms 64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=4 ttl=128 time=12.1 ms
2.集群名称规划:
在每台的/etc/hosts上加入 cat >> /etc/hosts << EOF 99.99.10.10 k8s-master1 99.99.10.11 k8s-master2 99.99.10.12 k8s-node1 99.99.10.13 k8s-harbor.com EOF
3.基础环境配置
#关闭防火墙 systemctl stop firewalld && systemctl disable firewalld #关闭NetworkManager,NetworkManager会和network冲突 systemctl stop NetworkManager && systemctl disable NetworkManager #暂时关闭 SELinux,并将其配置为禁用状态 setenforce 0 sed -i s/SELINUX=enforcing/SELINUX=disabled/ /etc/selinux/config #关闭swap 分区,并将 /etc/fstab 文件中关于 swap 分区的条目注释,以防止系统在下次启动时重新启用 swap 分区 swapoff -a sed -ri 's/.*swap.*/#&/' /etc/fstab cat > /etc/sysctl.d/k8s.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system yum install ntpdate -y #使用 ntpdate 工具来同步系统时间 ntpdate time.windows.com
4.设置免密
ssh-keygen #连续回车即可 ssh-copy-id -i ~/.ssh/id_rsa.pub root@99.99.10.11 ssh-copy-id -i ~/.ssh/id_rsa.pub root@99.99.10.12 ssh-copy-id -i ~/.ssh/id_rsa.pub root@99.99.10.13
二、部署Nginx+Keepalived负载均衡器
1.安装nginx和keepalived
yum install epel-release -y yum install nginx keepalived -y #这样安装nginx缺少stream模块,需要手动安装 yum install -y nginx-mod-stream
2.修改nginx配置文件
cat > /etc/nginx/nginx.conf << "EOF" user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } # 四层负载均衡,为两台Master apiserver组件提供负载均衡 # 这个strem是为nginx4层负载均衡的一个模块,不使用的https 7层的负载均衡; 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 99.99.10.10:6443; # Master1 APISERVER IP:PORT server 99.99.10.11:6443; # Master2 APISERVER IP:PORT } server { listen 16443; # 由于nginx与master节点复用,这个监听端口不能是6443,否则会冲突 proxy_pass k8s-apiserver; } } http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; } EOF
3.修改keepalived配置文件
cat > /etc/keepalived/keepalived.conf << EOF 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 } vrrp_script check_nginx { script "/etc/keepalived/check_nginx.sh" } vrrp_instance VI_1 { state MASTER interface ens33 # 修改为实际网卡名 virtual_router_id 51 # VRRP 路由 ID实例,每个实例是唯一的 priority 100 # 优先级,备服务器设置 90 advert_int 1 # 指定VRRP 心跳包通告间隔时间,默认1秒 authentication { auth_type PASS auth_pass 1111 } # 虚拟IP virtual_ipaddress { 99.99.10.14/16 } track_script { check_nginx } } EOF
cat > /etc/keepalived/keepalived.conf << EOF 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_BACKUP } vrrp_script check_nginx { script "/etc/keepalived/check_nginx.sh" } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 51 # VRRP 路由 ID实例,每个实例是唯一的 priority 90 #backup这里为90 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 99.99.10.14/16 #VIP的IP } track_script { check_nginx } } EOF
4.启动并设置开机自启
systemctl daemon-reload systemctl start nginx systemctl start keepalived systemctl enable nginx systemctl enable keepalived
如果出现:nginx: [error] invalid PID number "" in "/run/nginx.pid" 再确认安装了nginx的stream模块后
[root@k8s-master1 data]# cat /run/nginx.pid [root@k8s-master1 data]# nginx [root@k8s-master1 data]# cat /run/nginx.pid 2787
如果出现报错:
[root@k8s-master2 data]# systemctl start nginx Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details. [root@k8s-master2 data]# systemctl status nginx ● nginx.service - The nginx HTTP and reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Mon 2024-04-15 14:09:47 CST; 7s ago Process: 17914 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=1/FAILURE) Process: 17912 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS) Apr 15 14:09:46 k8s-master2 systemd[1]: Starting The nginx HTTP and reverse proxy server... Apr 15 14:09:47 k8s-master2 nginx[17914]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok Apr 15 14:09:47 k8s-master2 nginx[17914]: nginx: [emerg] bind() to 0.0.0.0:16443 failed (13: Permission denied) Apr 15 14:09:47 k8s-master2 nginx[17914]: nginx: configuration file /etc/nginx/nginx.conf test failed Apr 15 14:09:47 k8s-master2 systemd[1]: nginx.service: control process exited, code=exited status=1 Apr 15 14:09:47 k8s-master2 systemd[1]: Failed to start The nginx HTTP and reverse proxy server. Apr 15 14:09:47 k8s-master2 systemd[1]: Unit nginx.service entered failed state. Apr 15 14:09:47 k8s-master2 systemd[1]: nginx.service failed.
-
检查SELinux的状态。如果SELinux处于强制模式,可能会阻止Nginx在非标准端口上监听。你可以使用以下命令来检查SELinux的状态:
getenforce
如果显示的是Enforcing
,你可能需要调整SELinux的策略允许Nginx在该端口上绑定,或者你可以临时将SELinux设置为Permissive
(宽容模式)进行测试:
sudo setenforce 0
-
检查是否有防火墙规则可能在阻止Nginx绑定指定的端口。如果你的系统使用的是
firewalld
,可以用下列命令来允许端口16443:
sudo firewall-cmd --permanent --zone=public --add-port=16443/tcp
-
创建nginx.pid
vim /run/nginx.pid
-
重新启动nginx
[root@k8s-master2 data]# sudo systemctl stop nginx [root@k8s-master2 data]# sudo systemctl start nginx [root@k8s-master2 data]# systemctl status nginx ● nginx.service - The nginx HTTP and reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled) Active: active (running) since Mon 2024-04-15 14:13:20 CST; 7s ago Process: 17977 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS) Process: 17973 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS) Process: 17972 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS) Main PID: 17979 (nginx) CGroup: /system.slice/nginx.service ├─17979 nginx: master process /usr/sbin/nginx ├─17980 nginx: worker process └─17981 nginx: worker process Apr 15 14:13:20 k8s-master2 systemd[1]: Starting The nginx HTTP and reverse proxy server... Apr 15 14:13:20 k8s-master2 nginx[17973]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok Apr 15 14:13:20 k8s-master2 nginx[17973]: nginx: configuration file /etc/nginx/nginx.conf test is successful Apr 15 14:13:20 k8s-master2 systemd[1]: Started The nginx HTTP and reverse proxy server.
5.查看keepalived状态
[root@k8s-master1 ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 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 group default qlen 1000 link/ether 00:0c:29:36:37:bd brd ff:ff:ff:ff:ff:ff inet 99.99.10.10/24 brd 99.99.10.255 scope global noprefixroute ens33 valid_lft forever preferred_lft forever inet 99.99.10.14/32 scope global ens33 valid_lft forever preferred_lft forever inet6 fe80::e4b9:35be:db90:55b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8df6:8f68:1ae:e989/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::72b5:c53b:5302:401d/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:4c:bc:c4:7f brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever
这里网卡ens33成功绑定了99.99.10.14虚拟IP
6.测试nginx+keepalived的高可用性
关闭主节点的nginx,观察备份节点状态
[root@k8s-master2 ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 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 group default qlen 1000 link/ether 00:0c:29:e9:c5:15 brd ff:ff:ff:ff:ff:ff inet 99.99.10.11/24 brd 99.99.10.255 scope global noprefixroute ens33 valid_lft forever preferred_lft forever inet6 fe80::8df6:8f68:1ae:e989/64 scope link noprefixroute valid_lft forever preferred_lft forever inet6 fe80::e4b9:35be:db90:55b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:52:87:eb:11 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever
[root@k8s-master1 nginx]# systemctl status nginx ● nginx.service - The nginx HTTP and reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled) Active: inactive (dead) since Mon 2024-04-15 14:30:57 CST; 11s ago Process: 2637 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS) Process: 2634 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS) Process: 2632 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS) Main PID: 2639 (code=exited, status=0/SUCCESS) Apr 15 14:02:13 k8s-master1 systemd[1]: Starting The nginx HTTP and reverse proxy server... Apr 15 14:02:13 k8s-master1 nginx[2634]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok Apr 15 14:02:13 k8s-master1 nginx[2634]: nginx: configuration file /etc/nginx/nginx.conf test is successful Apr 15 14:02:13 k8s-master1 systemd[1]: Started The nginx HTTP and reverse proxy server. Apr 15 14:30:57 k8s-master1 systemd[1]: Stopping The nginx HTTP and reverse proxy server... Apr 15 14:30:57 k8s-master1 systemd[1]: Stopped The nginx HTTP and reverse proxy server. [root@k8s-master1 ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 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 group default qlen 1000 link/ether 00:0c:29:36:37:bd brd ff:ff:ff:ff:ff:ff inet 99.99.10.10/24 brd 99.99.10.255 scope global noprefixroute ens33 valid_lft forever preferred_lft forever inet 99.99.10.14/32 scope global ens33 valid_lft forever preferred_lft forever inet6 fe80::e4b9:35be:db90:55b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8df6:8f68:1ae:e989/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::72b5:c53b:5302:401d/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:4c:bc:c4:7f brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever
注意:keepalived在切换时会丢一个包,可以尝试ping 99.99.10.14虚拟IP,然后再尝试切换就可以看到切换过程中会丢一个包。
7.keepalived介绍:
Keepalived 是一个用于实现高可用性和负载均衡的开源软件。它主要用于在多个服务器之间实现故障转移,确保在其中一个服务器故障时服务仍然可用。下面是 Keepalived 的一些主要特点和用途:
-
故障检测:Keepalived 可以监控服务器的健康状态,如检测服务是否可用、服务器负载等。一旦发现故障,它可以自动切换到备用服务器,实现故障转移。
-
虚拟 IP(VIP):Keepalived 可以为一组服务器提供一个虚拟 IP 地址(VIP),对外提供服务。如果主服务器出现故障,Keepalived 会将 VIP 转移到备用服务器,确保服务的连续性。
-
负载均衡:除了故障转移外,Keepalived 还可以实现负载均衡,将请求均匀分配到多个服务器上,提高系统的整体性能和可扩展性。
-
配置简单:Keepalived 的配置相对简单,通常使用简洁的文本配置文件即可实现基本的功能。它支持多种配置选项,可以根据具体需求进行灵活配置。
-
支持多种协议:Keepalived 支持多种协议,如VRRP(Virtual Router Redundancy Protocol)、SMTP(Simple Mail Transfer Protocol)、HTTP(HyperText Transfer Protocol)等,可以用于不同类型的应用场景。
总体而言,Keepalived 是一个强大的工具,可以帮助系统管理员轻松实现高可用性和负载均衡,提高系统的稳定性和可靠性。
三、部署Etcd集群
1.etcd在k8s集群的作用:
Etcd 在 Kubernetes 集群中的作用包括:
-
配置存储: Etcd 存储了 Kubernetes 集群的各种配置信息,如节点信息、网络配置、存储配置、安全配置等。这些配置对于集群的正常运行和管理都至关重要。
-
状态监控: Etcd 还负责监控集群中各个组件的状态,包括 API Server、Controller Manager、Scheduler 等组件的运行状态,以及节点的健康状态等。通过监控这些状态,可以及时发现并处理集群中的问题。
-
资源存储: Kubernetes 中的各种资源对象,如 Pod、Service、Replication Controller、ConfigMap 等都存储在 Etcd 中。这些对象的创建、更新、删除等操作都会反映在 Etcd 中,确保集群中各个组件的一致性和可靠性。
-
分布式一致性: Etcd 使用 Raft 算法来实现分布式一致性,确保集群中的数据在多个节点之间保持一致。这样可以防止单点故障,并提高集群的可用性和稳定性。
总体来说,Etcd 是 Kubernetes 集群的核心组件之一,承担着存储和管理集群配置、状态和资源数据的重要任务,对于集群的正常运行和高可用性至关重要。
2.安装cfssl证书生成工具
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 chmod +x cfssl* for x in cfssl*; do mv $x ${x%*_linux-amd64}; done mv cfssl* /usr/bin
3.生成证书
mkdir -p ~/etcd_tls cd ~/etcd_tls
自签CA:
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 - [root@k8s-master1 etcd_tls]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca - 2024/04/15 14:52:37 [INFO] generating a new CA key and certificate from CSR 2024/04/15 14:52:37 [INFO] generate received request 2024/04/15 14:52:37 [INFO] received CSR 2024/04/15 14:52:37 [INFO] generating key: rsa-2048 2024/04/15 14:52:37 [INFO] encoded CSR 2024/04/15 14:52:37 [INFO] signed certificate with serial number 106940532372652583388643549123311178715600450059
可以看到生成了ca.pem和ca-key.pem
[root@k8s-master1 etcd_tls]# ll total 20 -rw-r--r--. 1 root root 287 Apr 15 14:52 ca-config.json -rw-r--r--. 1 root root 956 Apr 15 14:52 ca.csr -rw-r--r--. 1 root root 209 Apr 15 14:52 ca-csr.json -rw-------. 1 root root 1675 Apr 15 14:52 ca-key.pem -rw-r--r--. 1 root root 1265 Apr 15 14:52 ca.pem
使用自签CA签发Etcd HTTPS证书
cat > server-csr.json << EOF { "CN": "etcd", "hosts": [ "99.99.10.10", "99.99.10.11", "99.99.10.12", "99.99.10.14", "99.99.10.2" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "BeiJing", "ST": "BeiJing" } ] } EOF
生成证书:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www server-csr.json | cfssljson -bare server [root@k8s-master1 etcd_tls]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www server-csr.json | cfssljson -bare server 2024/04/15 14:54:04 [INFO] generate received request 2024/04/15 14:54:04 [INFO] received CSR 2024/04/15 14:54:04 [INFO] generating key: rsa-2048 2024/04/15 14:54:04 [INFO] encoded CSR 2024/04/15 14:54:04 [INFO] signed certificate with serial number 84218250271244398651603471024412481684503411417 2024/04/15 14:54:04 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); specifically, section 10.2.3 ("Information Requirements").
可以看到生成server.pem和server-key.pem文件
[root@k8s-master1 etcd_tls]# ll total 36 -rw-r--r--. 1 root root 287 Apr 15 14:52 ca-config.json -rw-r--r--. 1 root root 956 Apr 15 14:52 ca.csr -rw-r--r--. 1 root root 209 Apr 15 14:52 ca-csr.json -rw-------. 1 root root 1675 Apr 15 14:52 ca-key.pem -rw-r--r--. 1 root root 1265 Apr 15 14:52 ca.pem -rw-r--r--. 1 root root 1021 Apr 15 14:54 server.csr -rw-r--r--. 1 root root 319 Apr 15 14:53 server-csr.json -rw-------. 1 root root 1675 Apr 15 14:54 server-key.pem -rw-r--r--. 1 root root 1346 Apr 15 14:54 server.pem
4.安装Etcd
下载地址:https://link.zhihu.com/?target=https%3A//github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz #解压 tar -zxvf etcd-v3.4.9-linux-amd64.tar.gz mkdir /opt/etcd/{bin,cfg,ssl} -p mv etcd-v3.4.9-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/
5.修改etcd配置文件
cat > /opt/etcd/cfg/etcd.conf << EOF #[Member] ETCD_NAME="etcd-1" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://99.99.10.10:2380" #2380是 集群通信的端口; ETCD_LISTEN_CLIENT_URLS="https://99.99.10.10:2379" #2379是指它的数据端口,其他客户端要访问etcd数据库的读写都走的是这个端口; #[Clustering] ETCD_INITIAL_ADVERTISE_PEER_URLS="https://99.99.10.10:2380" ETCD_ADVERTISE_CLIENT_URLS="https://99.99.10.10:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://99.99.10.10:2380,etcd-2=https://99.99.10.11:2380,etcd-3=https://99.99.10.12:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #一种简单的认证机制,网络里可能配置了多套k8s集群,防止误同步; ETCD_INITIAL_CLUSTER_STATE="new" EOF
6.systemd管理etcd
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=/opt/etcd/cfg/etcd.conf ExecStart=/opt/etcd/bin/etcd \ --cert-file=/opt/etcd/ssl/server.pem \ --key-file=/opt/etcd/ssl/server-key.pem \ --trusted-ca-file=/opt/etcd/ssl/ca.pem \ --peer-cert-file=/opt/etcd/ssl/server.pem \ --peer-key-file=/opt/etcd/ssl/server-key.pem \ --peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \ --logger=zap Restart=on-failure LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
7.给etcd上传证书
cp ~/etcd_tls/ca*pem ~/etcd_tls/server*pem /opt/etcd/ssl/
8.将配置好的etcd复制到其它两个节点
scp -r /opt/etcd/ root@99.99.10.11:/opt/ scp -r /opt/etcd/ root@99.99.10.12:/opt/ scp /usr/lib/systemd/system/etcd.service root@99.99.10.11:/usr/lib/systemd/system/ scp /usr/lib/systemd/system/etcd.service root@99.99.10.12:/usr/lib/systemd/system/
在其它连个节点修改/opt/etcd/cfg/etcd.conf
[root@k8s-master2 data]# cat /opt/etcd/cfg/etcd.conf #[Member] ETCD_NAME="etcd-2" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://99.99.10.11:2380" #2380是 集群通信的端口; ETCD_LISTEN_CLIENT_URLS="https://99.99.10.11:2379" #2379是指它的数据端口,其他客户端要访问etcd数据库的读写都走的是这个端口; #[Clustering] ETCD_INITIAL_ADVERTISE_PEER_URLS="https://99.99.10.11:2380" ETCD_ADVERTISE_CLIENT_URLS="https://99.99.10.11:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://99.99.10.10:2380,etcd-2=https://99.99.10.11:2380,etcd-3=https://99.99.10.12:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #一种简单的认证机制,网络里可能配置了多套k8s集群,防止误同步; ETCD_INITIAL_CLUSTER_STATE="new"
[root@k8s-master2 data]# cat /opt/etcd/cfg/etcd.conf #[Member] ETCD_NAME="etcd-3" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://99.99.10.12:2380" #2380是 集群通信的端口; ETCD_LISTEN_CLIENT_URLS="https://99.99.10.12:2379" #2379是指它的数据端口,其他客户端要访问etcd数据库的读写都走的是这个端口; #[Clustering] ETCD_INITIAL_ADVERTISE_PEER_URLS="https://99.99.10.12:2380" ETCD_ADVERTISE_CLIENT_URLS="https://99.99.10.12:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://99.99.10.10:2380,etcd-2=https://99.99.10.11:2380,etcd-3=https://99.99.10.12:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #一种简单的认证机制,网络里可能配置了多套k8s集群,防止误同步; ETCD_INITIAL_CLUSTER_STATE="new"
9.启动etcd
systemctl daemon-reload systemctl start etcd systemctl enable etcd
如果启动etcd的时候发现一直在进行主节点选举,可以查看端口是否被防火墙拦截了
[root@k8s-master1 ssl]# systemctl status etcd ● etcd.service - Etcd Server Loaded: loaded (/usr/lib/systemd/system/etcd.service; enabled; vendor preset: disabled) Active: activating (start) since Mon 2024-04-15 15:30:56 CST; 22s ago Main PID: 3439 (etcd) CGroup: /system.slice/etcd.service └─3439 /opt/etcd/bin/etcd --cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem --trusted-ca-file=/opt/etcd/ssl/ca.pem --peer-cert-file=/opt/etcd/s... Apr 15 15:31:17 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:17.609+0800","caller":"raft/raft.go:713","msg":"bd58f91ef2ee2335 became candidate at term 260"} Apr 15 15:31:17 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:17.609+0800","caller":"raft/raft.go:824","msg":"bd58f91ef2ee2335 received MsgVoteResp from...t term 260"} Apr 15 15:31:17 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:17.609+0800","caller":"raft/raft.go:811","msg":"bd58f91ef2ee2335 [logterm: 1, index: 3] se...t term 260"} Apr 15 15:31:17 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:17.609+0800","caller":"raft/raft.go:811","msg":"bd58f91ef2ee2335 [logterm: 1, index: 3] se...t term 260"} Apr 15 15:31:18 k8s-master1 etcd[3439]: {"level":"warn","ts":"2024-04-15T15:31:18.016+0800","caller":"etcdserver/server.go:2065","msg":"failed to publish local member to cluster thr... Apr 15 15:31:18 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:18.609+0800","caller":"raft/raft.go:923","msg":"bd58f91ef2ee2335 is starting a new election at term 260"} Apr 15 15:31:18 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:18.609+0800","caller":"raft/raft.go:713","msg":"bd58f91ef2ee2335 became candidate at term 261"} Apr 15 15:31:18 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:18.609+0800","caller":"raft/raft.go:824","msg":"bd58f91ef2ee2335 received MsgVoteResp from...t term 261"} Apr 15 15:31:18 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:18.609+0800","caller":"raft/raft.go:811","msg":"bd58f91ef2ee2335 [logterm: 1, index: 3] se...t term 261"} Apr 15 15:31:18 k8s-master1 etcd[3439]: {"level":"info","ts":"2024-04-15T15:31:18.609+0800","caller":"raft/raft.go:811","msg":"bd58f91ef2ee2335 [logterm: 1, index: 3] se...t term 261"} Hint: Some lines were ellipsized, use -l to show in full.
解决方法:
[root@k8s-master2 data]# firewall-cmd --zone=public --add-port=2379/tcp --permanent success [root@k8s-master2 data]# firewall-cmd --zone=public --add-port=2380/tcp --permanent success [root@k8s-master2 data]# firewall-cmd --reload success [root@k8s-master2 data]# systemctl status etcd ● etcd.service - Etcd Server Loaded: loaded (/usr/lib/systemd/system/etcd.service; disabled; vendor preset: disabled) Active: active (running) since Mon 2024-04-15 15:34:54 CST; 10s ago Main PID: 18206 (etcd) CGroup: /system.slice/etcd.service └─18206 /opt/etcd/bin/etcd --cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem --trusted-ca-file=/opt/etcd/ssl/ca.pem --peer-cert-file=/opt/etcd/...
10.查看集群状态
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://99.99.10.10:2379,https://99.99.10.11:2379,https://99.99.10.12:2379" endpoint health --write-out=table [root@k8s-master1 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://99.99.10.10:2379,https://99.99.10.11:2379,https://99.99.10.12:2379" endpoint health --write-out=table +--------------------------+--------+-------------+-------+ | ENDPOINT | HEALTH | TOOK | ERROR | +--------------------------+--------+-------------+-------+ | https://99.99.10.11:2379 | true | 11.274216ms | | | https://99.99.10.10:2379 | true | 10.090251ms | | | https://99.99.10.12:2379 | true | 15.350268ms | | +--------------------------+--------+-------------+-------+ [root@k8s-master2 keepalived]# 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://99.99.10.10:2379,https://99.99.10.11:2379,https://99.99.10.12:2379" endpoint health --write-out=table +--------------------------+--------+-------------+-------+ | ENDPOINT | HEALTH | TOOK | ERROR | +--------------------------+--------+-------------+-------+ | https://99.99.10.12:2379 | true | 13.877137ms | | | https://99.99.10.11:2379 | true | 13.310408ms | | | https://99.99.10.10:2379 | true | 13.64291ms | | +--------------------------+--------+-------------+-------+ [root@k8s-node1 ssl]# 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://99.99.10.10:2379,https://99.99.10.11:2379,https://99.99.10.12:2379" endpoint health --write-out=table +--------------------------+--------+-------------+-------+ | ENDPOINT | HEALTH | TOOK | ERROR | +--------------------------+--------+-------------+-------+ | https://99.99.10.12:2379 | true | 12.405176ms | | | https://99.99.10.10:2379 | true | 10.765985ms | | | https://99.99.10.11:2379 | true | 12.413707ms | | +--------------------------+--------+-------------+-------+
四、安装docker和harbor
1.安装docker-ce
yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7 containerd.io #配置加速器文件 mkdir -p /etc/docker cat >> /etc/docker/daemon.json <<-EOF { "registry-mirrors":["https://dockerhub.azk8s.cn", "http://hub-mirror.c.163.com", "http://qtid6917.mirror.aliyuncs.com", "http://74f21445.m.daocloud.io", "https://registry.docker-cn.com", "http://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn" ], "insecure-registries": ["k8s-harbor.com"], "exec-opts": ["native.cgroupdriver=systemd"] } EOF
vim /etc/sysctl.conf net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1 sysctl -p systemctl daemon-reload systemctl restart docker
2.安装cri-docker
下载cri-docker mkdir /data/softs && cd /data/softs wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.2/cri-dockerd-0.3.2.amd64.tgz 解压软件 tar xf cri-dockerd-0.3.2.amd64.tgz mv cri-dockerd/cri-dockerd /usr/local/bin/ 检查效果 cri-dockerd --version
定制配置文件 cat > /etc/systemd/system/cri-dockerd.service<<-EOF [Unit] Description=CRI Interface for Docker Application Container Engine Documentation=https://docs.mirantis.com After=network-online.target firewalld.service docker.service Wants=network-online.target [Service] Type=notify ExecStart=/usr/local/bin/cri-dockerd --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9 --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin --container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --cri-dockerd-root-directory=/var/lib/dockershim --docker-endpoint=unix:///var/run/docker.sock --cri-dockerd-root-directory=/var/lib/docker ExecReload=/bin/kill -s HUP $MAINPID TimeoutSec=0 RestartSec=2 Restart=always StartLimitBurst=3 StartLimitInterval=60s LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity TasksMax=infinity Delegate=yes KillMode=process [Install] WantedBy=multi-user.target EOF
定制配置 cat > /etc/systemd/system/cri-dockerd.socket <<-EOF [Unit] Description=CRI Docker Socket for the API PartOf=cri-docker.service [Socket] ListenStream=/var/run/cri-dockerd.sock SocketMode=0660 SocketUser=root SocketGroup=docker [Install] WantedBy=sockets.target EOF
设置服务开机自启动 systemctl daemon-reload systemctl enable cri-dockerd.service systemctl restart cri-dockerd.service
3.安装harbor
安装docker-compose yum install -y docker-compose 下载软件 mkdir /data/{softs,server} -p && cd /data/softs wget https://github.com/goharbor/harbor/releases/download/v2.5.0/harbor-offline-installer-v2.5.0.tgz 解压软件 tar -zxvf harbor-offline-installer-v2.5.0.tgz -C /data/server/ cd /data/server/harbor/ 加载镜像 docker load < harbor.v2.5.0.tar.gz docker images 备份配置 cp harbor.yml.tmpl harbor.yml
修改配置 [root@kubernetes-register /data/server/harbor]# vim harbor.yml.tmpl # 修改主机名 hostname: k8s-harbor.com http: port: 80 #https: 注释ssl相关的部分 # port: 443 # certificate: /your/certificate/path # private_key: /your/private/key/path # 修改harbor的登录密码 harbor_admin_password: 123456 # 设定harbor的数据存储目录 data_volume: /data/server/harbor/data
配置harbor ./prepare 启动harbor ./install.sh 检查效果 docker-compose ps
定制启动文件
定制服务启动文件 /etc/systemd/system/harbor.service [Unit] Description=Harbor After=docker.service systemd-networkd.service systemd-resolved.service Requires=docker.service Documentation=http://github.com/vmware/harbor [Service] Type=simple Restart=on-failure RestartSec=5 #需要注意harbor的安装位置 ExecStart=/usr/bin/docker-compose --file /data/server/harbor/docker-compose.yml up ExecStop=/usr/bin/docker-compose --file /data/server/harbor/docker-compose.yml down [Install] WantedBy=multi-user.target
加载服务配置文件 systemctl daemon-reload 启动服务 systemctl start harbor 检查状态 systemctl status harbor 设置开机自启动 systemctl enable harbor
4.访问harbor
浏览器访问域名,用户名: admin, 密码:123456 创建用户michelxct 创建michaelxct用户专用的项目仓库,名称为 michaelxct,权限为公开的
在所有节点上登录仓库 # docker login k8s-harbor.com -u michaelxct Password: # 输入登录密码 下载镜像 docker pull busybox 定制镜像标签 docker tag busybox kubernetes-register.sswang.com/sswang/busybox:v0.1 推送镜像 docker push kubernetes-register.sswang.com/sswang/busybox:v0.1
五、k8s集群初始化
1安装kubeadm+kubelet+kunectl
添加阿里yum源
cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF
yum install -y kubelet-1.20.7 kubeadm-1.20.7 kubectl-1.20.7 systemctl enable kubelet
2.初始化k8s-master1
cat > kubeadm-config.yaml << EOF apiVersion: kubeadm.k8s.io/v1beta2 bootstrapTokens: - groups: - system:bootstrappers:kubeadm:default-node-token token: 9037x2.tcaqnpaqkra9vsbw ttl: 24h0m0s usages: - signing - authentication kind: InitConfiguration localAPIEndpoint: advertiseAddress: 99.99.10.10 bindPort: 6443 nodeRegistration: criSocket: /var/run/dockershim.sock name: k8s-master1 taints: - effect: NoSchedule key: node-role.kubernetes.io/master --- apiServer: certSANs: # 包含所有Master/LB/VIP IP,一个都不能少!为了方便后期扩容可以多写几个预留的IP。 - k8s-master1 - k8s-master2 - 99.99.10.10 - 99.99.10.11 - 99.99.10.14 - 99.99.10.2 - 127.0.0.1 extraArgs: authorization-mode: Node,RBAC timeoutForControlPlane: 4m0s apiVersion: kubeadm.k8s.io/v1beta2 certificatesDir: /etc/kubernetes/pki clusterName: kubernetes controlPlaneEndpoint: 99.99.10.14:16443 # 负载均衡虚拟IP(VIP)和端口 controllerManager: {} dns: type: CoreDNS etcd: external: # 使用外部etcd endpoints: - https://99.99.10.10:2379 # etcd集群3个节点 - https://99.99.10.11:2379 - https://99.99.10.12:2379 caFile: /opt/etcd/ssl/ca.pem # 连接etcd所需证书 certFile: /opt/etcd/ssl/server.pem keyFile: /opt/etcd/ssl/server-key.pem imageRepository: registry.aliyuncs.com/google_containers # 由于默认拉取镜像地址k8s.gcr.io国内无法访问,这里指定阿里云镜像仓库地址 kind: ClusterConfiguration kubernetesVersion: v1.20.7 # K8s版本,与上面安装的一致 networking: dnsDomain: cluster.local podSubnet: 10.244.0.0/16 # Pod网络,与下面部署的CNI网络组件yaml中保持一致 serviceSubnet: 10.96.0.0/12 # 集群内部虚拟网络,Pod统一访问入口 scheduler: {} EOF
初始化k8s-master1前检查nginx和keepalived,确保keepalived正常,不然会出现找不到k8s-master1的情况
systemctl restart network systemctl restart keepalived systemctl restart nginx systemctl status network systemctl status keepalived systemctl status nginx [root@k8s-master1 ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 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 group default qlen 1000 link/ether 00:0c:29:36:37:bd brd ff:ff:ff:ff:ff:ff inet 99.99.10.10/24 brd 99.99.10.255 scope global noprefixroute ens33 valid_lft forever preferred_lft forever inet 99.99.10.14/32 scope global ens33 valid_lft forever preferred_lft forever inet6 fe80::e4b9:35be:db90:55b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8df6:8f68:1ae:e989/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::72b5:c53b:5302:401d/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:4c:bc:c4:7f brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
[root@k8s-master1 ~]# kubeadm init --config kubeadm-config.yaml [init] Using Kubernetes version: v1.20.7 [preflight] Running pre-flight checks [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.7. Latest validated version: 19.03 [preflight] Pulling images required for setting up a Kubernetes cluster [preflight] This might take a minute or two, depending on the speed of your internet connection [preflight] You can also perform this action in beforehand using 'kubeadm config images pull' [certs] Using certificateDir folder "/etc/kubernetes/pki" [certs] Generating "ca" certificate and key [certs] Generating "apiserver" certificate and key [certs] apiserver serving cert is signed for DNS names [k8s-master1 k8s-master2 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 99.99.10.10 99.99.10.14 99.99.10.11 99.99.10.2 127.0.0.1] [certs] Generating "apiserver-kubelet-client" certificate and key [certs] Generating "front-proxy-ca" certificate and key [certs] Generating "front-proxy-client" certificate and key [certs] External etcd mode: Skipping etcd/ca certificate authority generation [certs] External etcd mode: Skipping etcd/server certificate generation [certs] External etcd mode: Skipping etcd/peer certificate generation [certs] External etcd mode: Skipping etcd/healthcheck-client certificate generation [certs] External etcd mode: Skipping apiserver-etcd-client certificate generation [certs] Generating "sa" key and public key [kubeconfig] Using kubeconfig folder "/etc/kubernetes" [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [kubeconfig] Writing "admin.conf" kubeconfig file [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [kubeconfig] Writing "kubelet.conf" kubeconfig file [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [kubeconfig] Writing "controller-manager.conf" kubeconfig file [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [kubeconfig] Writing "scheduler.conf" kubeconfig file [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Starting the kubelet [control-plane] Using manifest folder "/etc/kubernetes/manifests" [control-plane] Creating static Pod manifest for "kube-apiserver" [control-plane] Creating static Pod manifest for "kube-controller-manager" [control-plane] Creating static Pod manifest for "kube-scheduler" [wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s [apiclient] All control plane components are healthy after 11.661086 seconds [upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace [kubelet] Creating a ConfigMap "kubelet-config-1.20" in namespace kube-system with the configuration for the kubelets in the cluster [upload-certs] Skipping phase. Please see --upload-certs [mark-control-plane] Marking the node k8s-master1 as control-plane by adding the labels "node-role.kubernetes.io/master=''" and "node-role.kubernetes.io/control-plane='' (deprecated)" [mark-control-plane] Marking the node k8s-master1 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule] [bootstrap-token] Using token: 9037x2.tcaqnpaqkra9vsbw [bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles [bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes [bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials [bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token [bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster [bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace [kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key [addons] Applied essential addon: CoreDNS [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [addons] Applied essential addon: kube-proxy Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config Alternatively, if you are the root user, you can run: export KUBECONFIG=/etc/kubernetes/admin.conf You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ You can now join any number of control-plane nodes by copying certificate authorities and service account keys on each node and then running the following as root: kubeadm join 99.99.10.14:16443 --token 9037x2.tcaqnpaqkra9vsbw \ --discovery-token-ca-cert-hash sha256:aaf2dbf6d24b07f9cacd72c971beb9880a10e2bb30ca07a2ae7e15acf45f3944 \ --control-plane Then you can join any number of worker nodes by running the following on each as root: kubeadm join 99.99.10.14:16443 --token 9037x2.tcaqnpaqkra9vsbw \ --discovery-token-ca-cert-hash sha256:aaf2dbf6d24b07f9cacd72c971beb9880a10e2bb30ca07a2ae7e15acf45f3944
拷贝kubectl使用的连接k8s认证文件到默认路径:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config [root@k8s-master1 ~]#kubectl get node NAME STATUS ROLES AGE VERSION k8s-master1 NotReady control-plane,master 7m34s v1.20.0 [root@k8s-master1 ~]#
3.初始化k8s-master2
[root@k8s-master2 .kube]# kubeadm join 99.99.10.14:16443 --token 9037x2.tcaqnpaqkra9vsbw --discovery-token-ca-cert-hash sha256:aaf2dbf6d24b07f9cacd72c971beb9880a10e2bb30ca07a2ae7e15acf45f3944 --control-plane [preflight] Running pre-flight checks [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 26.0.1. Latest validated version: 19.03 [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml' error execution phase preflight: One or more conditions for hosting a new control plane instance is not satisfied. failure loading certificate for CA: couldn't load the certificate file /etc/kubernetes/pki/ca.crt: open /etc/kubernetes/pki/ca.crt: no such file or directory Please ensure that: * The cluster has a stable controlPlaneEndpoint address. * The certificates that must be shared among control plane instances are provided. To see the stack trace of this error execute with --v=5 or higher
可以看到缺少证书:
将k8s-master1的证书复制到k8s-master2:
scp -r /etc/kubernetes/pki/ 99.99.10.11:/etc/kubernetes/
重新尝试加入:
[root@k8s-master2 .kube]# kubeadm join 99.99.10.14:16443 --token 9037x2.tcaqnpaqkra9vsbw --discovery-token-ca-cert-hash sha256:aaf2dbf6d24b07f9cacd72c971beb9880a10e2bb30ca07a2ae7e15acf45f3944 --control-plane [preflight] Running pre-flight checks [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 26.0.1. Latest validated version: 19.03 [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml' [preflight] Running pre-flight checks before initializing the new control plane instance [preflight] Pulling images required for setting up a Kubernetes cluster [preflight] This might take a minute or two, depending on the speed of your internet connection [preflight] You can also perform this action in beforehand using 'kubeadm config images pull' [certs] Using certificateDir folder "/etc/kubernetes/pki" [certs] Using the existing "front-proxy-client" certificate and key [certs] Using the existing "apiserver" certificate and key [certs] Using the existing "apiserver-kubelet-client" certificate and key [certs] Valid certificates and keys now exist in "/etc/kubernetes/pki" [certs] Using the existing "sa" key [kubeconfig] Generating kubeconfig files [kubeconfig] Using kubeconfig folder "/etc/kubernetes" [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [kubeconfig] Writing "admin.conf" kubeconfig file [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [kubeconfig] Writing "controller-manager.conf" kubeconfig file [endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address [kubeconfig] Writing "scheduler.conf" kubeconfig file [control-plane] Using manifest folder "/etc/kubernetes/manifests" [control-plane] Creating static Pod manifest for "kube-apiserver" [control-plane] Creating static Pod manifest for "kube-controller-manager" [control-plane] Creating static Pod manifest for "kube-scheduler" [check-etcd] Skipping etcd check in external mode [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... [control-plane-join] using external etcd - no local stacked instance added [upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace [mark-control-plane] Marking the node k8s-master2 as control-plane by adding the labels "node-role.kubernetes.io/master=''" and "node-role.kubernetes.io/control-plane='' (deprecated)" [mark-control-plane] Marking the node k8s-master2 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule] This node has joined the cluster and a new control plane instance was created: * Certificate signing request was sent to apiserver and approval was received. * The Kubelet was informed of the new secure connection details. * Control plane (master) label and taint were applied to the new node. * The Kubernetes control plane instances scaled up. To start administering your cluster from this node, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config Run 'kubectl get nodes' to see this node join the cluster.
拷贝kubectl使用的连接k8s认证文件到默认路径:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config [root@k8s-master2 ~]#kubectl get node NAME STATUS ROLES AGE VERSION k8s-master1 NotReady control-plane,master 14m v1.20.0 k8s-master2 NotReady control-plane,master 30s v1.20.0 [root@k8s-master2 ~]#
注意:由于网络插件还没有部署,还没有准备就绪 NotReady。
出现的问题:
[root@k8s-master2 .kube]# kubeadm join 99.99.10.14:16443 --token 9037x2.tcaqnpaqkra9vsbw --discovery-token-ca-cert-hash sha256:aaf2dbf6d24b07f9cacd72c971beb9880a10e2bb30ca07a2ae7e15acf45f3944 --control-plane [preflight] Running pre-flight checks [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 26.0.1. Latest validated version: 19.03 error execution phase preflight: couldn't validate the identity of the API Server: Get "https://99.99.10.14:16443/api/v1/namespaces/kube-public/configmaps/cluster-info?timeout=10s": dial tcp 99.99.10.14:16443: connect: connection refused To see the stack trace of this error execute with --v=5 or higher
systectl restart network systectl restart keepalived ip a
若遇到token过期的,执行下面的指令重新生成token
[root@k8s-master1 data]# kubeadm token create --print-join-command kubeadm join 99.99.10.14:16443 --token mj1v1g.g0im25mdfhs88ovc --discovery-token-ca-cert-hash sha256:c8adf5ec97e9b68190ab6de03f5a7bf1e5766504d06591099aa8943a7d185613
4.测试负载均衡
[root@k8s-master2 softs]# curl -k https://99.99.10.14:16443/version { "major": "1", "minor": "20", "gitVersion": "v1.20.7", "gitCommit": "132a687512d7fb058d0f5890f07d4121b3f0a2e2", "gitTreeState": "clean", "buildDate": "2021-05-12T12:32:49Z", "goVersion": "go1.15.12", "compiler": "gc", "platform": "linux/amd64" }
可以正确获取到K8s版本信息,说明负载均衡器搭建正常。该请求数据流程:curl -> vip(nginx) -> apiserver
5.初始化k8s-node
[root@k8s-node1 opt]# kubeadm join 99.99.10.14:16443 --token 9037x2.tcaqnpaqkra9vsbw \ > --discovery-token-ca-cert-hash sha256:aaf2dbf6d24b07f9cacd72c971beb9880a10e2bb30ca07a2ae7e15acf45f3944 [preflight] Running pre-flight checks [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.7. Latest validated version: 19.03 [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml' [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... This node has joined the cluster: * Certificate signing request was sent to apiserver and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
六、部署网络组件
1.部署calico
Calico是一个纯三层的数据中心网络方案,是目前Kubernetes主流的网络方案。
kubectl apply -f calico.yaml kubectl get pod -A [root@k8s-master1 data]# kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-kube-controllers-97769f7c7-crtkp 0/1 ContainerCreating 0 9m52s kube-system calico-node-bshw6 0/1 Running 4 9m53s kube-system calico-node-dbpb2 1/1 Running 0 9m53s kube-system calico-node-ss4kl 0/1 CrashLoopBackOff 6 9m53s kube-system coredns-7f89b7bc75-kbs6h 0/1 ContainerCreating 0 17h kube-system coredns-7f89b7bc75-kv5sz 0/1 ContainerCreating 0 17h kube-system kube-apiserver-k8s-master1 1/1 Running 0 85m kube-system kube-apiserver-k8s-master2 1/1 Running 0 84m kube-system kube-controller-manager-k8s-master1 1/1 Running 0 85m kube-system kube-controller-manager-k8s-master2 1/1 Running 0 84m kube-system kube-proxy-4j7p7 1/1 Running 0 85m kube-system kube-proxy-89z8r 1/1 Running 0 84m kube-system kube-proxy-fxpkq 1/1 Running 0 84m kube-system kube-scheduler-k8s-master1 1/1 Running 0 85m kube-system kube-scheduler-k8s-master2 1/1 Running 0 84m kubernetes-dashboard dashboard-metrics-scraper-7b59f7d4df-jvzlc 0/1 ContainerCreating 0 2m40s kubernetes-dashboard kubernetes-dashboard-74d688b6bc-n4r5n 0/1 ContainerCreating 0 2m40s
2.部署 Dashboard
Dashboard是官方提供的一个UI,可用于基本管理K8s资源。
kubectl apply -f kubernetes-dashboard.yaml [root@k8s-master1 data]# kubectl apply -f kubernetes-dashboard.yaml namespace/kubernetes-dashboard created serviceaccount/kubernetes-dashboard created service/kubernetes-dashboard created secret/kubernetes-dashboard-certs created secret/kubernetes-dashboard-csrf created secret/kubernetes-dashboard-key-holder created configmap/kubernetes-dashboard-settings created role.rbac.authorization.k8s.io/kubernetes-dashboard created clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created deployment.apps/kubernetes-dashboard created service/dashboard-metrics-scraper created deployment.apps/dashboard-metrics-scraper created
[root@k8s-master1 data]# kubectl create serviceaccount dashboard-admin -n kube-system serviceaccount/dashboard-admin created [root@k8s-master1 data]# kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin clusterrolebinding.rbac.authorization.k8s.io/dashboard-admin created [root@k8s-master1 data]# kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}') Name: dashboard-admin-token-t6wd6 Namespace: kube-system Labels: <none> Annotations: kubernetes.io/service-account.name: dashboard-admin kubernetes.io/service-account.uid: 95be708e-d111-47e3-b697-7ef4a1d3954e Type: kubernetes.io/service-account-token Data ==== token: eyJhbGciOiJSUzI1NiIsImtpZCI6IlFNUDY3SGxOZEY3eDNKcUZaOWRGSTV1c2ZhTmdzeGRQMS10c0ZSTlpUV3MifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4tdDZ3ZDYiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiOTViZTcwOGUtZDExMS00N2UzLWI2OTctN2VmNGExZDM5NTRlIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.bKoEfyd1H8_92DRasWFog45G8zCm8mXu8honFYf27F7gtMeaCFTTlrs9-3mKCcOj1Nr8-rUyV9MKzHAflHuGE0AR_Ng8z5YbFGGCiQnCc9eVsB57HsVMHp-h0bWGB0oLR8O0Ztaf5SPD6efiu-2pPLLF547GSDu_mpInJD1HLSsZRXVCiU-ZV6Fvy93aDFlWlISX1YDl14Lgp6RkhO9YPjbaUwHwlts-lgAqhdyvjWL76DaRCEzTNscGTSYZczp3qLJK2I-CGWJsTCan7R-ykiMyRJqaNVCV78OhsY1CLGQ3gCedsHEy7heS_ZhVPMFkumZAFO0kgXbw_UdncW-U8w ca.crt: 1066 bytes namespace: 11 bytes
浏览器登录node:30001