Kubernetes学习指北之单节点集群(Master)
一、 单节点集群(Single Node Cluster)说明
学习和开发利器,操作简单
特点
- Master 节点和 Worker 节点在同一台机器上
- kube-apiserver、kube-scheduler、kube-controller-manager、kubelet 都在同一台机器
- Pod 也在同一台机器上运行
- 资源有限:所有容器共享同一台机器的 CPU、内存和存储
- 高可用性低:Master 节点宕机,整个集群不可用
- 网络简单:只需一个节点,Pod 网络配置相对简单
适用场景
- 学习和测试 Kubernetes 核心概念
- 开发环境部署微服务
- 练习 kubectl 命令、Deployment、Service 等
优点
配置简单,一条命令就能搭建
资源占用低,不需要多台虚拟机
部署快速,便于练手
缺点
不适合生产环境
没有高可用性
无法模拟真实多节点调度
二、 准备环境和工具
- 这里我使用的CentOS7
- 做一些初始化
# 切换 root sudo -i # 关闭防火墙(实验环境) systemctl stop firewalld systemctl disable firewalld # 关闭 selinux setenforce 0 sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config # 关闭 swap swapoff -a sed -i '/swap/d' /etc/fstab # 调整内核参数(K8s 网络需要) cat <<EOF > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system
三 、 安装 Docker 或 Containerd
Kubernetes 需要一个容器运行时,这里推荐 containerd(更接近 K8s 原生)。
# 安装 containerd
yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加 Docker CE 源(阿里云加速)
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 更新缓存
yum makecache fast
# 安装 containerd
yum install -y containerd.io
# 生成默认配置
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
# 修改 SystemdCgroup = true(K8s 要求)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 启动并开机自启
systemctl enable --now containerd
systemctl status containerd
如果 yum install -y yum-utils device-mapper-persistent-data lvm2出现Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock error was 14: curl#6 - “Could not resolve host: mirrorlist.centos.org; Unknown error” 这类问题需要切换到可用镜像源
- 替换 CentOS 镜像源
# 备份旧的 repo 文件 sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak # 使用 vault 源(CentOS 官方归档) cat <<EOF | sudo tee /etc/yum.repos.d/CentOS-Base.repo [base] name=CentOS-7 - Base baseurl=http://vault.centos.org/7.9.2009/os/\$basearch/ gpgcheck=0 enabled=1 [updates] name=CentOS-7 - Updates baseurl=http://vault.centos.org/7.9.2009/updates/\$basearch/ gpgcheck=0 enabled=1 [extras] name=CentOS-7 - Extras baseurl=http://vault.centos.org/7.9.2009/extras/\$basearch/ gpgcheck=0 enabled=1 EOF # 清理缓存并更新 yum clean all yum makecache - 使用阿里云镜像(更快,推荐国内用户)
# 下载阿里云 repo 文件 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo # 清理缓存并更新 yum clean all yum makecache - 检查 DNS(如果还报 Could not resolve host)
- 说明虚拟机网络没配置好,先检查 DNS:
cat /etc/resolv.conf - 如果里面没有:
nameserver 8.8.8.8 nameserver 114.114.114.114 - 就加上:
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf echo "nameserver 114.114.114.114" | sudo tee -a /etc/resolv.conf
- 说明虚拟机网络没配置好,先检查 DNS:
四、安装 Kubernetes 工具
安装 kubeadm、kubelet、kubectl。
# 添加 Kubernetes 仓库
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
EOF
# 安装
yum install -y kubelet kubeadm kubectl
# 开机自启
systemctl enable kubelet
五、初始化单节点集群
# 使用 kubeadm 初始化(--pod-network-cidr 根据网络插件而定)
kubeadm init --apiserver-advertise-address=192.168.56.10 --pod-network-cidr=10.244.0.0/16
# 如果出现 /proc/sys/net/bridge/bridge-nf-call-iptables does not exist 和 /proc/sys/net/ipv4/ip_forward contents are not set to 1 执行下面的命令解决然后重新初始化:
modprobe br_netfilter
echo "br_netfilter" >> /etc/modules-load.d/k8s.conf
#配置内核参数
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 应用配置
sysctl --system
#验证
lsmod | grep br_netfilter
cat /proc/sys/net/bridge/bridge-nf-call-iptables
cat /proc/sys/net/ipv4/ip_forward
# 期望输出:
# br_netfilter 模块存在
# bridge-nf-call-iptables = 1
# ip_forward = 1
# 清理上一次初始化的残留
kubeadm reset -f # 清理上次失败的初始化
rm -rf /etc/cni/net.d # 清理 CNI 网络配置
# 清理 iptables(避免残留规则)
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X
# 清理 kubeconfig
rm -rf $HOME/.kube
# 重新初始化
kubeadm init --apiserver-advertise-address=192.168.56.10 --pod-network-cidr=10.244.0.0/16
# 如果出现拉取镜像失败问题,核心原因就是 虚拟机里根本访问不到 Google 的镜像仓库(k8s.gcr.io / registry.k8s.io),给 kubeadm 用国内的镜像源,指定 image-repository 重新初始化
# 阿里云: registry.aliyuncs.com/google_containers
# Azure China: gcr.azk8s.cn/google_containers
# : quay-mirror.qiniu.com
kubeadm init --apiserver-advertise-address=192.168.56.10 --pod-network-cidr=10.244.0.0/16 --image-repository=registry.aliyuncs.com/google_containers
# 如果出现 The kubelet is not running
# - The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)
# 这是 CentOS7 + kubelet 常见问题,主要原因是 cgroup 驱动没配置好 或 containerd 默认配置不对。
# 1. 检查 kubelet 状态
systemctl status kubelet -l
journalctl -xeu kubelet
# 大概率会看到类似:
# failed to run Kubelet: failed to run Kubelet: misconfiguration: kubelet cgroup driver
# 2. 修改 containerd 配置, 生成 containerd 默认配置:
containerd config default > /etc/containerd/config.toml
# 3. 编辑/etc/containerd/config.toml, 把 sandbox 镜像 改成阿里云 pause 镜像:
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"
# 4. 找到 SystemdCgroup 配置(默认是 false),改成:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
# 5. 重启 containerd & kubelet
systemctl daemon-reexec
systemctl restart containerd
systemctl restart kubelet
# 6. 清理并再次执行 kubeadm init
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d
sudo rm -rf /var/lib/etcd
sudo rm -rf /etc/kubernetes/manifests/*
sudo lsof -i :6443,10250,10259,10257,2379,2380
# 如果有进程占用,可以 kill
sudo systemctl restart kubelet
kubeadm init --apiserver-advertise-address=192.168.56.10 --pod-network-cidr=10.244.0.0/16 --image-repository=registry.aliyuncs.com/google_containers
执行成功后,会输出一段提示,包括:
- Your Kubernetes control-plane has initialized successfully!
- To start using your cluster, you need to run the following as a regular user: 配置 kubectl 的命令
kubeadm join ...(多节点才用到)
按照提示执行如:
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
六、安装网络插件(CNI)
最简单用 Flannel:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# 如果访问不了,可以改成使用 阿里云加速的镜像
# 先下载原始 yaml:
curl -O https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
# 替换国内镜像
sed -i 's#ghcr.io/flannel-io/flannel#registry.aliyuncs.com/google_containers/flannel#g' kube-flannel.yml
# 应用 Flannel
kubectl apply -f kube-flannel.yml
# 检查 Flannel Pod 状态
kubectl get pods -n kube-system -l app=flannel -w
# 等待 Pod 变为 Running
# 如果一直 Pending,可能是:
#节点有 NoSchedule 污点(需要移除)
#镜像拉取失败(网络或镜像源问题)
# 移除单节点控制平面的污点。 单节点控制平面默认不调度 Pod,需要允许调度:
kubectl taint nodes k8s-single node-role.kubernetes.io/control-plane-
# 再次查看节点状态
kubectl get nodes
# 节点 STATUS 应该从 NotReady 变为 Ready
# 网络插件就绪后,CoreDNS Pod 也会从 Pending 变为 Running
# 检查 CoreDNS
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 一旦网络就绪,CoreDNS 应该变为 Running
# 这是集群 DNS 功能是否正常的关键
七、允许单节点调度 Pod
默认 Master 节点不调度 Pod(但我们单机需要)。
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
八、验证集群
# 查看节点状态
kubectl get nodes -w
# 查看 Pod 状态
kubectl get pods -A
kubectl get pods -n kube-system
如果节点是 Ready,Pods 都是 Running,就说明单节点集群搭建完成
#如果节点是NotReady,说明 控制平面节点已经初始化,但网络插件(CNI)还没完全就绪。
#查看 kube-system Pod 状态
kubectl get pods -n kube-system
# 重点看 kube-flannel Pod 是否 Running。
#如果 Pod Pending 或 CrashLoopBackOff,节点就会 NotReady。
单节点 Kubernetes 就绪操作顺序
# 1️⃣ 移除控制平面污点,允许单节点调度 Pod
kubectl taint nodes k8s-single node-role.kubernetes.io/control-plane-
# 2️⃣ 删除旧的 Flannel 配置(可选,保证干净环境)
kubectl delete -f kube-flannel.yml
# 3️⃣ 重新应用 Flannel CNI
kubectl apply -f kube-flannel.yml
# 4️⃣ 等待 Flannel Pod 启动
kubectl get pods -n kube-system -l app=flannel -w
# 等 Pod 全部状态变为 Running 后,Ctrl+C 退出
# 5️⃣ 检查节点状态
kubectl get nodes -o wide
# 节点 STATUS 应该变为 Ready
# 6️⃣ 检查 CoreDNS 状态
kubectl get pods -n kube-system -l k8s-app=kube-dns
# CoreDNS Pod 应变为 Running
# 7️⃣ 测试 Pod 调度
kubectl run nginx --image=nginx
kubectl get pods
# Pod 应成功调度到单节点
其他问题:
systemctl status kubelet -l
- Flannel 的 CNI 插件 Pod 拉取镜像失败
- 手动拉取镜像到节点
ctr image pull registry.aliyuncs.com/google_containers/flannel:v0.27.3ctr image pull registry.aliyuncs.com/google_containers/flannel-cni-plugin:v1.7.1-flannel1
- 标记(Tag)镜像,让 Kubernetes 能找到。Kubelet/Flannel 期望的镜像名必须和 kube-flannel.yml 中一致:
ctr images tag registry.aliyuncs.com/google_containers/flannel:v0.27.3 ghcr.io/flannel-io/flannel:v0.27.3ctr images tag registry.aliyuncs.com/google_containers/flannel-cni-plugin:v1.7.1-flannel1 registry.aliyuncs.com/google_containers/flannel-cni-plugin:v1.7.1-flannel1
- 然后重新应用 Flannel:
kubectl delete -f kube-flannel.yml kubectl apply -f kube-flannel.yml
- 手动拉取镜像到节点
- 本地下载docker镜像,虚拟机导入
# 本机下载导出
docker save ghcr.io/flannel-io/flannel:v0.27.3 -o ./data/flannel.tar
docker save ghcr.io/flannel-io/flannel-cni-plugin:v1.7.1-flannel1 -o ./data/flannel-cni-plugin.tar
# 修改Vagrantfile共享目录
config.vm.synced_folder "./images", "/home/vagrant/flannel-images"
#重新挂载(虚拟机已经启动,可以直接 vagrant reload
vagrant reload
#重新ssh,切换root
vagrant ssh k8s-single -- -F NUL
sudo -i
# 虚拟机里访问共享目录:
ls /home/vagrant/flannel-images
sudo ctr -n k8s.io images import /home/vagrant/flannel-images/flannel.tar
sudo ctr -n k8s.io images import /home/vagrant/flannel-images/flannel-cni-plugin.tar
#验证
sudo ctr -n k8s.io images import /home/vagrant/flannel-images/flannel-cni-plugin.tar
# 修改 kube-flannel 主镜像
sed -i 's|image: registry.aliyuncs.com/google_containers/flannel:v0.*|image: ghcr.io/flannel-io/flannel:v0.27.3|' kube-flannel.yml
# 修改 cni 插件镜像
sed -i 's|image: registry.aliyuncs.com/google_containers/flannel-cni-plugin:v1.*|image: ghcr.io/flannel-io/flannel-cni-plugin:v1.7.1-flannel1|' kube-flannel.yml
# 然后重新应用 Flannel:
kubectl delete -f kube-flannel.yml
kubectl apply -f kube-flannel.yml
# 等待 Flannel Pod 启动
kubectl get pods -n kube-system -l app=flannel -
kubectl get nodes
# 单节点应该从 NotReady 变成 Ready。
# 如果 Flannel 容器 Running,但节点仍 NotReady,说明 kubelet 还没认到 CNI,可能需要重启 kubelet:
sudo systemctl restart kubelet
# 然后再观察状态:
kubectl get nodes -w
九、 测试 Pod 调度
# 查看系统命名空间的 Pod 状态
kubectl get pods -n kube-system
# 部署一个测试 Pod 验证网络:
kubectl run nginx-test --image=nginx --restart=Never
kubectl get pod nginx-test -o wide
kubectl get pods
# Pod IP 和节点 IP 是否在 Flannel 网段内(默认 10.244.0.0/
kubectl get pod nginx-test -o wide
- 确认 Pod 能调度到单节点
- 如果能运行,说明单节点 Kubernetes 已完全可用
十、Laravel on Kubernetes 单节点示例
准备 Laravel 项目镜像,
- Laravel 项目目录写一个 Dockerfile,示例如下:
FROM php:8.3-fpm # 安装 SQLite 和 Laravel 需要的扩展 RUN apt-get update && apt-get install -y libsqlite3-dev \ && docker-php-ext-install pdo pdo_sqlite bcmath # 安装 Composer COPY --from=composer:2 /usr/bin/composer /usr/bin/composer WORKDIR /var/www/html # 拷贝代码 COPY . . # 安装依赖(忽略 dev) RUN composer install --no-dev --optimize-autoloader \ && chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache - 构建镜像(假设名字叫 laravel-app:demo)
docker build -t laravel-app:demo . - 加载到 containerd(因为 kubelet 默认用 containerd 而不是 docker):
# 保存镜像 docker save laravel-app:demo -o laravel-app.tar # 导入到 containerd sudo ctr -n k8s.io images import laravel-app.tar
- Laravel 项目目录写一个 Dockerfile,示例如下:
Kubernetes YAML(最小示例)
创建一个 laravel-demo.yaml:
Secret 管理 MySQL 如:
# 会生成一个 Secret(存储在 etcd,K8s 内部是 base64 编码的,不是明文,但还是要注意权限管理) kubectl create secret generic mysql-secret \ --from-literal=username=laravel \ --from-literal=password=SuperSecret123 \ --from-literal=database=laravel_db \ --from-literal=host=mysql-servicelaravel-demo.yaml示例
# ========================== # Namespace,所有资源都放在 laravel-demo 命名空间 apiVersion: v1 kind: Namespace metadata: name: laravel-demo --- # ========================== # MySQL Secret # 用来存储数据库连接信息 # 假设之前已经用 kubectl create secret 创建了,也可以在 YAML 中声明 # ========================== apiVersion: v1 kind: Secret metadata: name: mysql-secret namespace: laravel-demo type: Opaque # 数据可以省略,已经创建过 secret --- # ========================== # PHP-FPM Deployment # 运行 Laravel 的 PHP 后端 # ========================== apiVersion: apps/v1 kind: Deployment metadata: name: laravel-php namespace: laravel-demo spec: replicas: 1 # 单节点只需一个副本 selector: matchLabels: app: laravel-php template: metadata: labels: app: laravel-php spec: containers: - name: php-fpm image: php:8.3-fpm ports: - containerPort: 9000 # PHP-FPM 默认端口 env: # 从 Secret 中读取数据库信息 - name: DB_USERNAME valueFrom: secretKeyRef: name: mysql-secret key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password - name: DB_DATABASE valueFrom: secretKeyRef: name: mysql-secret key: database - name: DB_HOST valueFrom: secretKeyRef: name: mysql-secret key: host volumeMounts: - name: laravel-code mountPath: /var/www/html # Laravel 项目挂载路径 volumes: - name: laravel-code hostPath: # 挂载宿主机代码目录 path: /home/vagrant/laravel-demo type: Directory --- # ========================== # Nginx Deployment # 处理 HTTP 请求,反向代理到 PHP-FPM # ========================== apiVersion: apps/v1 kind: Deployment metadata: name: laravel-nginx namespace: laravel-demo spec: replicas: 1 selector: matchLabels: app: laravel-nginx template: metadata: labels: app: laravel-nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 # HTTP 默认端口 volumeMounts: - name: laravel-code mountPath: /var/www/html # Laravel 项目代码挂载 - name: nginx-conf mountPath: /etc/nginx/conf.d # Nginx 配置挂载 volumes: - name: laravel-code hostPath: path: /home/vagrant/laravel-demo type: Directory - name: nginx-conf configMap: name: nginx-config # 配置来自 ConfigMap --- # ========================== # Nginx 配置 ConfigMap # 配置 Nginx 反向代理到 PHP-FPM # ========================== apiVersion: v1 kind: ConfigMap metadata: name: nginx-config namespace: laravel-demo data: default.conf: | server { listen 80; server_name localhost; root /var/www/html/public; # Laravel 公共目录 index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass laravel-php:9000; # PHP-FPM 服务名和端口 fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } --- # ========================== # Nginx Service # 暴露 HTTP 端口,NodePort 用于单节点测试 # ========================== apiVersion: v1 kind: Service metadata: name: laravel-nginx-service namespace: laravel-demo spec: selector: app: laravel-nginx # 关联 Nginx Pod ports: - protocol: TCP port: 80 targetPort: 80 type: NodePort # 可以通过节点 IP:NodePort 访问部署 & 使用
# 部署
kubectl apply -f laravel-demo.yaml
# 查看 Pod 状态:
kubectl get pods
等待 laravel-app 和 laravel-nginx 都是 Running。
然后在浏览器访问:
http://<你的宿主机IP>:30080
就能打开 Laravel 的首页
- 管理
应用生命周期管理
# 查看运行状态 kubectl get pods # 查看详细日志(Laravel PHP-FPM Pod) kubectl logs -f <laravel-app-pod> -c laravel # 查看 Nginx 的日志 kubectl logs -f <laravel-nginx-pod> -c nginx # 重启 Deployment(相当于重新部署) kubectl rollout restart deployment laravel-app kubectl rollout restart deployment laravel-nginx扩展和升级
Kubernetes 会 滚动更新,保证不中断服务。
# 扩容(增加副本数): kubectl scale deployment laravel-app --replicas=3 kubectl scale deployment laravel-nginx --replicas=2 # 升级镜像(比如新版本 Laravel 代码): kubectl set image deployment/laravel-app laravel=laravel-app:v2监控与调试
# 实时监控 Pod: kubectl get pods -w # 进入容器调试: kubectl exec -it <laravel-app-pod> -- bash
使用虚拟机说明
Vagrantfile 单机示例
Vagrant.configure("2") do |config|
config.vm.box = "generic/centos7"
config.vm.define "k8s-single" do |vm|
vm.vm.hostname = "k8s-single"
vm.vm.network "private_network", ip: "192.168.56.10"
vm.vm.provider "virtualbox" do |vb|
vb.memory = 2048
vb.cpus = 2
vb.name = "vagrant-k8s-single"
end
end
end
vagrant 命令:
vagrant up
vagrant status k8s-single
vagrant ssh k8s-single -- -F NUL
vagrant halt k8s-single
# 删除虚拟机
vagrant destroy k8s-single