我每条代码的都有知识拓展,能让大家在实验中理解底层逻辑
一.实验主机配置
harbor主机:eth0-172.25.254.254
master主机:eth0-172.25.254.100
node1主机:eth0-172.25.254.10
node2主机:eth0-172.25.254.20
1.创建 docker.repo 阿里云源
[root@harbor ~]# cat > /etc/yum.repos.d/docker.repo <<EOF
[docker]
name = docker
baseurl = https://mirrors.aliyun.com/docker-ce/linux/rhel/9.6/x86_64/stable/
gpgcheck = 0
EOF#创建 docker.repo 阿里云源 ,dnf 才有 docker 软件仓库[root@harbor ~]# dnf install httpd createrepo-y#安装httpd、createrepo
[root@harbor ~]# mkdir /var/www/html/docker/ -p#创建仓库存放目录
[root@harbor ~]# vim /etc/httpd/conf/httpd.conf
Listen 4444#将 httpd 监听端口由默认 80 改为 4444
搭建本地 HTTP 私有 dnf 源仓库,把 docker 的 rpm 包做成内网 yum 源,httpd 提供网页访问,端口 4444
[root@harbor ~]# systemctl enable --now httpd
[root@harbor ~]# dnf install docker-ce --downloadonly --destdir /mnt/ -y#如果直接 dnf install docker-ce:本机 harbor 自己装上 docker,rpm 包下载完系统缓存会被清理,别的机器拿不到 rpm 文件,收集 rpm 包,搭建离线源给别的机器用
[root@harbor ~]# mv /mnt/*.rpm /var/www/html/docker/# 将下载好的rpm移动到http网页仓库目录,rpm 包一开始下载到 /mnt 目录,/mnt 这个目录,httpd 网页服务访问不到,内网其他机器无法通过 http 拿到里面的 rpm 文件
[root@harbor ~]# createrepo -v /var/www/html/docker/#-v verbose 输出详细过程,生成仓库元数据,createrepo 扫描目录下全部 rpm,生成一堆 xml 索引文件,存放在提前建好的索引目录repodata/,这样才是可用软件仓库,执行 dnf 安装
[root@harbor ~]# cat > /etc/yum.repos.d/docker.repo <<EOF
[docker]
name = docker
baseurl = http://172.25.254.254:4444/docker#地址指向本机 httpd 服务提供的本地仓库,harbor 本机后续 dnf 不再访问阿里云,改为访问本机 http://172.25.254.254:4444/docker
gpgcheck = 0
EOF
下载完 rpm 包之后,模拟 “不再依赖外网”,本机也使用离线源,harbor 本机自己写这个 repo,本机就会从自己的 http 源安装 docker,harbor可以访问外网就可以不用配置这个,但内网其他节点必须配置这个 repo,因为内网节点没有外网,访问不了公网 docker 仓库,同时rpm 包不在内网节点本地磁盘上只能通过网络 HTTP 协议去 harbor 机器拿 rpm 包,如果用本地包,还可以直接用 file:// 本地源(baseurl=file:///var/www/html/docker)
[root@harbor ~]# dnf install docker-ce -y
tips:所有主机都要配置开机自动加载br_netfilter内核模块
2.安装docker在harbor仓库节点
[root@harbor ~]# echo br_netfilter > /etc/modules-load.d/docker_mod.conf# 配置开机自动加载br_netfilter内核模块
[root@harbor ~]# modprobe -a br_netfilter#立即加载br_netfilter模块,不用重启机器
[root@harbor ~]# vim /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-iptables = 1# 网桥流量交给iptables处理,k8s/容器网络必须
net.bridge.bridge-nf-call-ip6tables = 1# ipv6网桥交给ip6tables
net.ipv4.ip_forward = 1# 开启ip转发,容器之间、容器访问外网需要
[root@harbor ~]# systemctl restart systemd-modules-load.service # 重新加载模块配置文件
[root@harbor ~]# sysctl --system# 加载所有sysctl.d下面的内核参数,生效
[root@harbor ~]# vim /lib/systemd/system/docker.service# 修改docker.service启动参数
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
[root@harbor ~]# systemctl daemon-reload# 重载systemd单元配置
[root@harbor ~]# systemctl enable --now docker
3.生成key
[root@harbor ~]# mkdir /data/certs -p
[root@harbor ~]# mkdir /data/certs -p
[root@harbor ~]# openssl req -newkey rsa:4096 \
-nodes -sha256 -keyout /data/certs/LLJtiminglee.org.key \
-addext "subjectAltName = DNS:reg.LLJ.org" \
-x509 -days 365 -out /data/certs/LLJ.org.crt
[root@harbor harbor]# ./install.sh --with-chartmuseum
4.编辑harbor配置文件
[root@harbor ~]# tar zxf harbor-offline-installer-v2.5.4.tgz -C /opt/
[root@harbor ~]# cd /opt/harbor/
[root@harbor harbor]# ls
common.sh harbor.v2.5.4.tar.gz harbor.yml.tmpl install.sh LICENSE prepare
[root@harbor harbor]# cp harbor.yml.tmpl harbor.yml
[root@harbor harbor]# vim harbor.yml
hostname: reg.LLJ.org
certificate: /data/certs/LLJ.org.crt
private_key: /data/certs/LLJ.org.key
harbor_admin_password: llj
[root@harbor harbor]# ./install.sh --with-chartmuseum
5.编写启动脚本
[root@harbor ~]# vim /lib/systemd/system/harbor.service
[Unit]
Description=harbor with Docker Compose
Documentation=https://reg.LLJ.com/compose/
After=docker.service network-online.target
Requires=docker.service
[Service]
Type=oneshot
WorkingDirectory=/opt/harbor
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
[root@harbor harbor]# docker compose down
[root@harbor ~]# systemctl enable --now harbor
Created symlink /etc/systemd/system/multi-user.target.wants/harbor.service → /usr/lib/systemd/system/harbor.service.
[root@harbor harbor]# docker compose ps
6.关闭swap
只要节点开启 swap,后续执行 kubeadm init / kubeadm join 会报错、集群启动异常。
1. 立刻关闭当前所有swap(实时生效!你之前漏掉这条)
swapoff -a
2. 注释fstab,禁止开机挂载
sed -i '/^[[:space:]]*[^#].*swap/s/^/#/' /etc/fstab
3. 查看真实swap设备单元名称
systemctl --type swap
输出类似:dev-dm\x2d1.swap
4. mask真实swap单元(重中之重,只mask swap.target不够)
systemctl mask dev-dm\x2d1.swap
5. 重载systemd配置
systemctl daemon-reload
临时关闭(立刻生效):swapoff -a
永久禁止开机自动启用:注释 fstab + mask 对应.swap单元
7.给 Docker 配置 HTTPS 私有仓库证书信任
mkdir /etc/docker/certs.d/reg.LLJ.org/ -p
[root@harbor ~]# for i in 100 10 20#在harbor主机中分发证书到所有主机
> do
> scp /data/certs/LLJ.org.crt root@172.25.254.$i:/etc/docker/certs.d/reg.LLJ.org/ca.crt
> done#文件名必须叫 ca.crt,docker 固定识别这个文件名systemctl enable docker
systemctl restart dockertips:Harbor 是自签名证书,docker 默认不信任;把签发 Harbor 的 CA 根证书给到每一台机器 docker,docker 就认为这个 HTTPS 仓库是合法可信,这是 HTTPS 自签仓库标准配置,与insecure‑registries 的 HTTP 跳过校验方案有区别
8.配置docker加速器(所有主机)
#配置 mirrors 之后,docker 会优先访问这个镜像代理地址去拉取镜像,加速下载
cat >/etc/docker/daemon.json <<EOF
{
"registry-mirrors":["https://reg.LLJ.org"]
}
EOF
systemctl restart docker
8.所有主机彼此建立解析
vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.100 k8s-master
172.25.254.10 node1
172.25.254.20 node2
172.25.254.254 reg.LLJ.org
9.配置kubernetes安装源(所有主机)
vim /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name = kubernetes
baseurl = https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.35/rpm/
gpgcheck = 0
dnf list kubelet#检测
以上操作完成后重启主机检测swap分区,swapon -s 没有任何输出表示ok
二.kubernetes的部署
1.安装cri-dockerd(所有主机中安装)
[root@docker-node1 ~]# wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.4/cri-dockerd-0.4.4.amd64.tgz
[root@docker-node1 ~]# tar zxf cri-dockerd-0.4.4.amd64.tgz [root@docker-node1 ~]# cd cri-dockerd/
[root@docker-node1 cri-dockerd]# ls
cri-dockerd cri-docker.service cri-docker.socket
[root@docker-node1 cri-dockerd]install -o root -g root -m 0755 cri-dockerd /usr/local/bin/cri-dockerd #把编译 / 下载好的二进制程序 cri‑dockerd 安装到系统可执行目录 /usr/local/bin/,同时设置属主、属组、权限
[root@docker-node1 cri-dockerd]vim /etc/systemd/system/cri-docker.service
[Service]
Type=notify
ExecStart=/usr/local/bin/cri-dockerd --network-plugin=cni --pod-infra-container-image=reg.LLJ.org/k8s/pause:3.10.1 --container-runtime-endpoint fd://
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
[root@docker-node1 cri-dockerd]# systemctl daemon-reload [root@docker-node1 cri-dockerd]# systemctl enable --now cri-docker.service
2.安装构建kubernetes 集群所需软件
master节点: dnf install kubelet kubeadm kubectl -y
systemctl enable --now kubelet.service node节点: dnf install kubelet kubeadm -y
systemctl enable --now kubelet.service master节点中 kubectl 和kubeadm 补齐: [root@master ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@master ~]# echo "source <(kubeadm completion bash)" >> ~/.bashrc
[root@master ~]# source ~/.bashrc
3.下载kubernetes集群所需镜像
[root@master ~]# kubeadm config images pull \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.35.7 \
--cri-socket=unix:///var/run/cri-dockerd.sock
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-apiserver:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-controller-manager:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-scheduler:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-proxy:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/coredns:v1.13.1
[config/images] Pulled registry.aliyuncs.com/google_containers/pause:3.10.1
[config/images] Pulled registry.aliyuncs.com/google_containers/etcd:3.6.6-0
[root@master ~]# docker login reg.LLJ.org -u admin
[root@master ~]# docker images --format "{{.Repository}}:{{.Tag}}" | awk -F "/" '/google/{system("docker tag "$0" reg.LLJ.org/k8s/"$3)}'
[root@master ~]# docker images --format "{{.Repository}}:{{.Tag}}" | awk -F "/" '/LLJ/{system("docker push "$0)}'
4.在master中初始化kubernetes集群
[root@k8s-master .docker]# kubeadm init --pod-network-cidr=10.244.0.0/16 \
--image-repository reg.LLJ.org/k8s \
--kubernetes-version v1.35.7 \
--cri-socket=unix:///var/run/cri-dockerd.sock
上述有警告,可以忽略
[root@k8s-master .docker]# kubeadm token create --print-join-command#查看节点
kubeadm join 172.25.254.100:6443 --token jlhgvo.644uzakl2upno99y --discovery-token-ca- cert-hash sha256:fd60ad5adf7aa93ef0e845d0b1230f786bb2f08c387f5d3b5db0c56f505679e2
#如果初始化出问题可以重置集群设定,kubeadm reset在多 CRI 环境偶尔清理不干净,需要手动删残余 kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock rm -rf /etc/kubernetes/*
rm -rf /var/lib/kubelet/*
docker ps -a | grep k8s | awk '{print $1}' | xargs -r docker rm -f#列出所有容器筛选出 k8s 创建的容器(pause、业务 pod、组件)只打印第一列,也就是容器 ID,把前面输出的容器 ID 传给 docker rm 强制删除,-r如果前面没有输出(没有 k8s 容器),就不执行 rm,避免报错
5.其他主机加入凭证
每个人的值不一样,接下来其他主机要加入本集群的凭证, [root@k8s-node1 ~]# kubeadm join 172.25.254.100:6443 --token jlhgvo.644uzakl2upno99y --discovery-token-ca-cert-hash sha256:fd60ad5adf7aa93ef0e845d0b1230f786bb2f08c387f5d3b5 db0c56f505679e2 --cri-socket=unix:///run/cri-dockerd.sock
[root@k8s-node2 ~]# kubeadm join 172.25.254.100:6443 --token jlhgvo.644uzakl2upno99y --discovery-token-ca-cert-hash sha256:fd60ad5adf7aa93ef0e845d0b1230f786bb2f08c387f5d3b5 db0c56f505679e2 --cri-socket=unix:///run/cri-dockerd.sock
cat /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf #kubelet必须是连接cri‑dockerd 套接字
6.添加kubernets环境变量到本机
[root@k8s-master .docker]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" > ~/.bash_profile [root@k8s-master .docker]# source ~/.bash_profile#source 让当前终端立刻加载生效
#kubectl 寻找认证配置文件,优先读取环境变量 KUBECONFIG 指定的路径(强制命名大写,kubectl只读这个)配置环境变量就是为省去每次敲长长的--kubeconfig指定文件参数tips:如果新开一个 ssh 窗口,要确保加载.bash_profile;有些系统 root 登录加载.bashrc,会导致环境变量失效,kubectl 又报错,更推荐kubeadm init 成功后复制 admin.conf, k8s 超级管理员的 kubeconfig 配置文件是/etc/kubernetes/admin.conf,有了这套东西,kubectl 才知道:去哪里访问 API、用什么证书身份去登录集群,/etc/kubernetes/admin.conf是kubeadm init 初始化完成后自动生成,是 k8s 集群最高权限的认证配置文件,kubectl 工具必须读取这个配置文件才能连接、操作kube‑apiserver
7.复制管理员 kubeconfig
mkdir -p /root/.kube
cp -i /etc/kubernetes/admin.conf /root/.kube/config
chown root:root /root/.kube/config原理: kubectl 默认读取路径:~/.kube/config(当前用户家目录下)
kubeadm init 只会把 admin.conf 生成到 /etc/kubernetes/admin.conf,不会自动拷贝到 root 家目录。root 用户家目录 /root/.kube/ 默认是空,没有 config 文件。执行 kubectl get ns → kubectl 找不到配置文件,直接报错
[root@k8s-master .docker]# kubectl get nodes#查看集群中所有节点的简要信息
(NotReady 是正常现象:CNI 网络插件 flannel 还没部署)
[root@k8s-master ~]# tar zxf kube-flannel-v0.28.9.tar.gz
[root@k8s-master ~]# ls
公共 图片 音乐 cri-dockerd kube-flannel-v0.28.9.tar.gz
模板 文档 桌面 cri-dockerd-0.4.4.amd64.tgz
视频 下载 anaconda-ks.cfg flannel
[root@k8s-master ~]# cd flannel/
[root@k8s-master flannel]# docker load -i kube-flannel-v0.28.9.tar#写的相对路径
[root@k8s-master flannel]# docker login reg.LLJ.org -u admin -pllj [root@k8s-master flannel]# docker tag ghcr.io/flannel-io/flannel-cni-plugin:v1.9.1-flannel3 reg.LLJ.org/flannel/flannel-cni-plugin:v1.9.1-flannel3 [root@k8s-master flannel]# docker push reg.LLJ.org/flannel/flannel-cni-plugin:v1.9.1-flannel3 [root@k8s-master flannel]# docker tag ghcr.io/flannel-io/flannel:v0.28.9 reg.LLJ.org/flannel/ flannel:v0.28.9
[root@k8s-master flannel]# docker push reg.LLJ.org/flannel/flannel:v0.28.9#打标签起别名,push到私有仓库tips:版本号必须load把离线 tar 格式的镜像包一样,然后导入到本机 Docker 本地镜像库,load使用一般要么是本地有tar包,要么就是正常联网机器用 docker pull 镜像地址,从外网仓库拉镜像,docker pull ghcr.io/flannel‑io/flannel‑cni‑plugin --> docker save -o kube‑flannel‑v0.28.9.tar 镜像名--> scp 把 tar 传到内网 k8s‑master --> docker load -i这个包
8.为k8s声明拉取镜像地址
改成你自己 Harbor 地址,修改 yaml 的 image 字段,告诉 k8s:不要去外网 ghcr.io 拉,去我们内网 harbor 仓库拉取镜像我的 Harbor 域名:reg.LLJ.org,项目名:flannel
[root@k8s-master flannel]# vim kube-flannel.yml
[root@k8s-master flannel]# kubectl apply -f kube-flannel.yml
#通过kubectl apply -f声明指定在文件里描述 “我期望集群最终是什么样子”
[root@k8s-master flannel]# kubectl get nodes#查看集群全部节点状态
tips:节点 Ready 的几个关键条件
1.kubelet 定期上报,全部满足才是 Ready:
2.kubelet 自身正常运行,能够和 apiserver 通信
内存、磁盘没有压力(磁盘没满)
3.CNI 网络插件就绪(最重要!)
4.节点时间正常