Kubernetes 集群

本文档讲解 Kubernetes(k8s)的基本概念、架构组成,以及使用 kubeadm 离线部署 Kubernetes v1.9.0 集群的完整过程,包含 Docker 与镜像准备、RPM 离线安装、Master 初始化、Flannel 网络插件、Dashboard 仪表盘、Node 节点加入、kubectl 常用命令、YAML 编排、服务暴露、Nginx / 投票应用 / MySQL 等部署示例与排错说明。

实验环境:CentOS Linux 7,内核 3.10.0-514.el7,Docker 1.13.1,Kubernetes v1.9.0。 集群规划:Master 192.168.224.10(server.cluster.com),Node 192.168.224.11(client1.example.com)。


一、Kubernetes 概述

Kubernetes(简称 k8s 或 kube)是一个开源平台,用于将容器的运维操作自动化。它消除了绝大部分已有的手工流程,包括部署、扩展以及容器化应用程序的管理。

使用 Kubernetes,可以将运行容器的主机组放到集群里,由 Kubernetes 帮助管理这些集群。

Kubernetes 由 Google 的工程师团队开发设计。Google 每周部署超过 20 亿个容器,这些都由其内部平台 Borg 支撑,Borg 是 Kubernetes 的先驱。Google 使用 Borg 多年积累的经验教训成为了 Kubernetes 的理念来源。

Kubernetes 简化了部署以及管理容器化应用程序相关的所有事情,将升级、回滚以及已部署服务的健康监控等工作自动化,让用户可以在问题变得严重之前避免有问题版本的升级。此外,Kubernetes 可以基于使用情况动态地对服务进行扩容缩容,确保只在需要的时候运行所需要的实例。和容器一样,Kubernetes 允许管理集群,做版本控制和复制。

Kubernetes 是容器化服务及应用的管理器。它易于扩展,可以管理大量容器;更重要的是,可以通过基于 YAML 的模板文件高度灵活地进行配置。


二、Kubernetes 架构

Kubernetes 集群由 Master(主节点) 和 Node(工作/成员节点) 两部分组成。

2.1 Master 组件

Master 负责集群的管理与控制,运行以下核心组件(在本实验中以 Static Pod 形式运行于 /etc/kubernetes/manifests/ 目录下):

组件 作用
kube-apiserver 集群的 API 入口,REST 接口的前端,所有组件通过它交互;负责认证、授权、准入控制等。
kube-controller-manager 运行各类控制器(如 Node Controller、Replication Controller、Endpoint Controller、ServiceAccount Controller 等),维护集群期望状态。
kube-scheduler 负责调度,将未分配节点的 Pod 绑定到合适的 Node 上运行。
etcd 分布式键值存储,用于保存集群的所有状态数据(ConfigMap、Secret、Pod、Node 等信息)。本实验使用 etcd-amd64:v3.1.10,以 Static Pod 形式与 Master 同机部署。

2.2 Node 组件

Node 是真正运行容器的工作节点,运行以下组件:

组件 作用
kubelet 每个 Node 上的核心代理,负责管理本节点上 Pod 的创建、启动、监控、销毁等生命周期,并向 Master 汇报节点和 Pod 状态。
kube-proxy 每个 Node 上的网络代理,负责实现 Service 的负载均衡与网络转发(iptables/用户空间模式),为 Pod 提供服务发现和访问入口。
容器运行时 负责拉取镜像和运行容器,本实验使用 Docker 1.13.1。

2.3 插件(Addons)

插件 作用
kube-dns 集群 DNS 服务(由 kube-dns、dnsmasq-nanny、sidecar 三个容器组成),为 Service 提供域名解析。
flannel 集群网络插件(DaemonSet 方式部署),通过 kube-flannel.yml 安装,为 Pod 提供跨节点互通的 Overlay 网络。
kubernetes-dashboard 集群 Web 管理控制台,通过 kubernetes-dashboard.yaml 安装,提供图形化工作负载管理界面。

三、核心概念

概念 说明
Pod Kubernetes 调度的最小单位,一个 Pod 包含一个或多个紧密相关的容器,共享网络和存储,运行在同一个 Node 上。
Label(标签) 键值对形式附加到资源(Pod、Service、Node 等)上的标识,用于资源的选择与关联(如 Service 通过 selector 匹配带特定 Label 的 Pod)。
Namespace(命名空间) 集群的虚拟分区,用于资源隔离。本实验默认使用 default,系统组件位于 kube-system,集群引导信息位于 kube-public。
ReplicaSet 确保在任何时间运行指定数量(replicas)的 Pod 副本,由 Deployment 管理,通常不直接创建。
Deployment 为 Pod 和 ReplicaSet 提供声明式更新,管理应用的部署、扩容、滚动升级与回滚。
Service 定义一组 Pod 的访问策略,通过 Label Selector 绑定后端 Pod,提供稳定的虚拟 IP(ClusterIP)和负载均衡。类型包括 ClusterIP、NodePort、LoadBalancer 等。
Volume(存储卷) Pod 中容器可访问的存储目录,生命周期与 Pod 绑定;通过 PersistentVolume(PV)和 PersistentVolumeClaim(PVC)可实现持久化存储。
ConfigMap / Secret 分别用于保存非敏感配置数据和敏感信息(密码、令牌等)。
DaemonSet 保证在每个(或指定)Node 上运行一个 Pod 副本,常用于日志、监控、网络插件(如 flannel)。

四、kubeadm 离线集群搭建

4.1 环境准备(所有节点执行)

4.1.1 关闭 Swap

Kubernetes 要求节点关闭 Swap:

swapoff -a
free -m          # 确认 Swap 为 0

4.1.2 设置网络规则

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

sysctl --system

4.1.3 安装 bzip2 解压工具

yum install -y bzip2

4.1.4 复制并解压离线镜像包

将 k8s_images.tar.bz2 复制到 /root/ 下并解压:

cp k8s_images.tar.bz2 /root/
cd /root/
tar -xjvf k8s_images.tar.bz2

解压后得到 k8s_images/ 目录,其中包含:

  • docker_images/:所有需要的 Docker 镜像 tar 包;
  • socat、kubernetes-cni、kubelet、kubectl、kubeadm 等 RPM 包;
  • kube-flannel.yml、kubernetes-dashboard.yaml 等部署文件。

4.2 安装与配置 Docker(所有节点执行)

4.2.1 安装 Docker

yum install -y docker

4.2.2 关闭 Docker SELinux

编辑 /etc/sysconfig/docker,将:

--selinux-enabled

修改为:

--selinux-enabled=false

4.2.3 启动 Docker

systemctl start docker
systemctl enable docker

确认版本:

docker version

输出示例:

Client:
 Version:         1.13.1
 API version:     1.26
 Package version: docker-1.13.1-75.git8633870.el7.centos.x86_64
 Go version:      go1.9.4
 Git commit:      8633870/1.13.1
 Built:           Fri Sep 28 19:45:08 2018
 OS/Arch:         linux/amd64

Server:
 Version:         1.13.1
 API version:     1.26 (minimum version 1.12)
 Package version: docker-1.13.1-75.git8633870.el7.centos.x86_64
 Go version:      go1.9.4
 Git commit:      8633870/1.13.1
 Built:           Fri Sep 28 19:45:08 2018
 OS/Arch:         linux/amd64
 Experimental:    false

4.2.4 配置 Docker 镜像加速

mkdir -p /etc/docker

tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://f7mxmtsw.mirror.aliyuncs.com"]
}
EOF

systemctl daemon-reload
systemctl restart docker

确认配置生效:

docker info

关键输出:

Server Version: 1.13.1
Storage Driver: overlay2
 Backing Filesystem: xfs
Cgroup Driver: systemd
 ...
Registry Mirrors:
 https://f7mxmtsw.mirror.aliyuncs.com

4.3 导入离线 Docker 镜像(所有节点执行)

将 k8s_images/ 中准备好的镜像逐个导入:

docker load < /root/k8s_images/docker_images/etcd-amd64_v3.1.10.tar
docker load < /root/k8s_images/docker_images/flannel_v0.9.1-amd64.tar
docker load < /root/k8s_images/docker_images/k8s-dns-dnsmasq-nanny-amd64_v1.14.7.tar
docker load < /root/k8s_images/docker_images/k8s-dns-kube-dns-amd64_1.14.7.tar
docker load < /root/k8s_images/docker_images/k8s-dns-sidecar-amd64_1.14.7.tar
docker load < /root/k8s_images/docker_images/kube-apiserver-amd64_v1.9.0.tar
docker load < /root/k8s_images/docker_images/kube-controller-manager-amd64_v1.9.0.tar
docker load < /root/k8s_images/docker_images/kube-scheduler-amd64_v1.9.0.tar
docker load < /root/k8s_images/docker_images/kube-proxy-amd64_v1.9.0.tar
docker load < /root/k8s_images/docker_images/pause-amd64_3.0.tar
docker load < /root/k8s_images/kubernetes-dashboard_v1.8.1.tar

镜像版本对应关系:

镜像 版本
etcd-amd64 v3.1.10
flannel v0.9.1-amd64
k8s-dns-dnsmasq-nanny-amd64 v1.14.7
k8s-dns-kube-dns-amd64 1.14.7
k8s-dns-sidecar-amd64 1.14.7
kube-apiserver-amd64 v1.9.0
kube-controller-manager-amd64 v1.9.0
kube-scheduler-amd64 v1.9.0
kube-proxy-amd64 v1.9.0
pause-amd64 3.0
kubernetes-dashboard v1.8.1

4.4 安装 Kubernetes RPM 包(所有节点执行)

cd /root/k8s_images
rpm -ivh socat-1.7.3.2-2.el7.x86_64.rpm
rpm -ivh kubernetes-cni-0.6.0-0.x86_64.rpm kubelet-1.9.9-9.x86_64.rpm
rpm -ivh kubectl-1.9.0-0.x86_64.rpm
rpm -ivh kubeadm-1.9.0-0.x86_64.rpm

重新加载 systemd 并启动 kubelet:

systemctl daemon-reload
systemctl enable kubelet && systemctl start kubelet

建议在此处对虚拟机做一次镜像/快照,方便后续节点加入失败时回滚。

4.5 初始化 Master 节点(仅 Master 执行)

kubeadm init --kubernetes-version=v1.9.0 --pod-network-cidr=10.244.0.0/16

参数说明:

参数 说明
--kubernetes-version=v1.9.0 指定 Kubernetes 版本,避免联网拉取版本信息。
--pod-network-cidr=10.244.0.0/16 指定 Pod 网络地址段,Flannel 默认使用 10.244.0.0/16。

完整输出示例:

[init] Using Kubernetes version: v1.9.0
[init] Using Authorization modes: [Node RBAC]
[preflight] Running pre-flight checks.
  [WARNING Hostname]: hostname "server.cluster.com" could not be reached
  [WARNING Hostname]: hostname "server.cluster.com" lookup server.cluster.com on 192.168.224.2:53: no such host
  [WARNING FileExisting-crictl]: crictl not found in system path
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [server.cluster.com kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.224.10]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "scheduler.conf"
[controlplane] Wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] Wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] Wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] Waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests".
[init] This might take a minute or longer if the control plane images have to be pulled.
[apiclient] All control plane components are healthy after 23.001206 seconds
[uploadconfig] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[markmaster] Will mark node server.cluster.com as master by adding a label and a taint
[markmaster] Master server.cluster.com tainted and labelled with key/value: node-role.kubernetes.io/master=""
[bootstraptoken] Using token: 4a5133.8736b2807e38b833
[bootstraptoken] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: kube-dns
[addons] Applied essential addon: kube-proxy

Your Kubernetes master 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

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 machines by running the following on each node
as root:

  kubeadm join --token 4a5133.8736b2807e38b833 192.168.224.10:6443 --discovery-token-ca-cert-hash sha256:69e87618c51090563484d6f7bd9046dc57c81a4f046a050fe3f65cefcdce277c

重要: 请记录 kubeadm init 输出的最后两行(即普通用户配置命令和 kubeadm join 命令)。kubeadm join 命令将在成员节点上执行,用于将其加入集群。每台机器初始化生成的 token 和 hash 都不同,必须复制自己 Master 输出中的命令,不能直接照抄本文示例。

4.5.1 配置 kubectl 环境变量

为了让 root 用户可以直接使用 kubectl,将 KUBECONFIG 写入 profile:

echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
source ~/.bash_profile

确认版本:

kubectl version
Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.0", GitCommit:"925c127ec6b946659ad0fd596fa959be43f0cc05", GitTreeState:"clean", BuildDate:"2017-12-15T21:07:38Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.0", GitCommit:"925c127ec6b946659ad0fd596fa959be43f0cc05", GitTreeState:"clean", BuildDate:"2017-12-15T20:55:30Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}

4.6 部署 Flannel 网络插件(仅 Master 执行)

Flannel 是集群的 Pod 网络插件,未安装网络插件时 Node 状态会持续 NotReady,集群 DNS 等组件也无法正常工作。

cd /root/k8s_images
kubectl create -f kube-flannel.yml

输出:

clusterrole "flannel" created
clusterrolebinding "flannel" created
serviceaccount "flannel" created
configmap "kube-flannel-cfg" created
daemonset "kube-flannel-ds" created

4.7 将 Node 加入集群(成员节点执行)

在每台成员节点上执行 Master kubeadm init 输出最后两行中的 kubeadm join 命令。

示例(务必使用自己 Master 实际输出的 token 和 hash):

kubeadm join --token 4a5133.8736b2807e38b833 192.168.224.10:6443 --discovery-token-ca-cert-hash sha256:69e87618c51090563484d6f7bd9046dc57c81a4f046a050fe3f65cefcdce277c

输出:

[preflight] Running pre-flight checks.
  [WARNING FileExisting-crictl]: crictl not found in system path
[discovery] Trying to connect to API Server "192.168.224.10:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://192.168.224.10:6443"
[discovery] Requesting info from "https://192.168.224.10:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "192.168.224.10:6443"
[discovery] Successfully established connection with API Server "192.168.224.10:6443"

This node has joined the cluster:
* Certificate signing request was sent to master and a response
  was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.

4.8 验证集群状态(Master 执行)

节点加入后需要等待一段时间(最快约 10 秒)才会变为 Ready:

kubectl get nodes
NAME                  STATUS     ROLES     AGE       VERSION
client1.example.com   NotReady   <none>    5s        v1.9.0
server.cluster.com    Ready      master    1m        v1.9.0

确认所有系统 Pod 都已经运行:

kubectl get pods --all-namespaces
NAMESPACE     NAME                                         READY     STATUS    RESTARTS   AGE
kube-system   etcd-server.cluster.com                      1/1       Running   0          13s
kube-system   kube-apiserver-server.cluster.com            0/1       Pending   0          1s
kube-system   kube-controller-manager-server.cluster.com   0/1       Pending   0          7s
kube-system   kube-dns-6f4fd4bdf-x796c                     3/3       Running   0          54s
kube-system   kube-flannel-ds-5pgnw                        1/1       Running   0          16s
kube-system   kube-flannel-ds-vgg6j                        1/1       Running   0          33s
kube-system   kube-proxy-tt4zr                             1/1       Running   0          16s
kube-system   kube-proxy-vm4mz                             1/1       Running   0          53s
kube-system   kube-scheduler-server.cluster.com            0/1       Pending   0          5s

刚加入时组件可能短暂处于 Pending,等待片刻后应全部变为 Running。


五、安装 Dashboard 仪表盘(Master 执行)

5.1 创建 Dashboard

cd /root/k8s_images
kubectl create -f kubernetes-dashboard.yaml

输出:

secret "kubernetes-dashboard-certs" created
serviceaccount "kubernetes-dashboard" created
role "kubernetes-dashboard-minimal" created
rolebinding "kubernetes-dashboard-minimal" created
deployment "kubernetes-dashboard" created
service "kubernetes-dashboard" created

5.2 配置登录账号密码

创建 basic auth 账号密码文件:

cat <<EOF > /etc/kubernetes/pki/basic_auth_file
admin,admin,2
EOF

文件格式为 用户名,密码,用户ID。

编辑 /etc/kubernetes/manifests/kube-apiserver.yaml,在 command 参数中加入:

- --basic_auth_file=/etc/kubernetes/pki/basic_auth_file

重启 kubelet 使配置生效:

systemctl restart kubelet

重新应用 apiserver 配置:

cd /etc/kubernetes/manifests
kubectl apply -f kube-apiserver.yaml
pod "kube-apiserver" created

踩坑说明: 如果执行 kubectl apply 时报错:

The connection to the server 192.168.224.10:6443 was refused - did you specify the right host or port?

请再次执行 kubectl apply -f kube-apiserver.yaml 和 systemctl restart kubelet,直到 apiserver 正常启动。

5.3 为 admin 用户绑定 cluster-admin 权限

kubectl get clusterrole/cluster-admin -o yaml
kubectl create clusterrolebinding login-on-dashboard-with-cluster-admin \
  --clusterrole=cluster-admin --user=admin
kubectl get clusterrolebinding/login-on-dashboard-with-cluster-admin -o yaml

5.4 确认 NodePort 端口

lsof -i:32666
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
kube-prox 5979 root    9u  IPv6  52713      0t0  TCP *:32666 (LISTEN)

查看内存(再次确认 Swap 已关闭):

free -m
              total        used        free      shared  buff/cache   available
Mem:           1823         719          76          10        1026         856
Swap:             0           0           0

5.5 登录 Dashboard

浏览器访问:

https://192.168.224.10:32666

在登录页选择「基本」认证方式,输入:

  • 账号:admin
  • 密码:admin

六、部署第一个应用:Nginx

6.1 YAML 编排文件

创建 deployment.yml:

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

字段说明:

字段 说明
apiVersion: apps/v1 API 版本,1.9.0 之前使用 apps/v1beta2。
kind: Deployment 资源类型为 Deployment。
metadata.name Deployment 名称 nginx-deployment。
spec.selector.matchLabels 选择器,匹配带有 app: nginx 标签的 Pod。
spec.replicas 期望的 Pod 副本数,这里为 2。
spec.template Pod 模板,定义副本的容器、镜像、端口等。
containers[].image 使用的容器镜像 nginx:1.7.9。
containers[].ports.containerPort 容器暴露端口 80。

6.2 两种部署方式

方法一:通过 Dashboard 控制台部署

进入 Dashboard → 工作负载 → 点击左上角「➕ 创建」,将上面的 YAML 复制粘贴,提交后会从镜像仓库自动拉取 nginx:1.7.9 镜像。

方法二:通过命令行部署

将 YAML 保存为 deployment.yml,然后执行:

kubectl create -f deployment.yml --save-config

6.3 验证 Pod

kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-6c54bd5869-7b655   1/1       Running   0          5m
nginx-deployment-6c54bd5869-8xc7p   1/1       Running   0          5m

查看 Deployment:

kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           3m

6.4 暴露服务

默认仅查看 Service:

kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   22m

将 Nginx Deployment 暴露为 LoadBalancer 类型服务:

kubectl expose deployment nginx-deployment --type="LoadBalancer"

再次查看 Service:

kubectl get services
NAME               TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes         ClusterIP      10.96.0.1       <none>        443/TCP        37m
nginx-deployment   LoadBalancer   10.109.70.217   <pending>     80:32517/TCP   10s

容器 80 端口被映射到节点的 32517 端口。通过浏览器或 curl 访问:

http://192.168.224.10:32517

6.5 查看资源详情

kubectl describe pods nginx
kubectl get pods -o wide
  • kubectl describe 用于查看资源详细信息(事件、挂载、环境变量等),是排错的重要手段。
  • kubectl get pods -o wide 会额外显示 Pod IP、所在节点等信息。

七、使用 kubectl run 部署应用与 API 访问

7.1 kubectl run 部署

kubectl run 用于快速创建一个 Deployment。需要提供部署名称、镜像地址,DockerHub 之外的镜像需要写全路径。通过 --port 指定容器端口:

kubectl run kubernetes-bootcamp \
  --image=docker.io/jocatalin/kubernetes-bootcamp:v1 \
  --port=8080

该命令在后台做了以下事情:

  1. 查找适合这个应用运行的 Node;
  2. 调度该应用在选定的 Node 上运行;
  3. 配置集群,在需要时为该应用调配新的 Node。

查看部署:

kubectl get deployments
NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp    1         1         1            1           5m

7.2 通过 kubectl proxy 访问 API

Kubernetes 中 Pod 运行在私有、隔离的网络内。默认情况下,Pod 对集群内其他 Pod 和 Service 可见,但对集群外不可见。使用 kubectl 时,实际是通过 API 端点(可以理解为 URL)与应用交互。

kubectl proxy 可以在本机与集群之间建立一条代理通道,让终端可以直接访问集群 API。重新打开一个终端运行:

kubectl proxy

代理运行过程中没有任何输出,按 Ctrl+C 可关闭代理。

代理默认监听 8001 端口(注:原文同时提到 8080 和 8001,实际默认为 8001)。通过 curl 访问:

curl http://localhost:8001/version
{
  "major": "1",
  "minor": "5",
  "gitVersion": "v1.5.2",
  "gitCommit": "08e099554f3c31f6e6f07b448ab3ed78d0520507",
  "gitTreeState": "clean",
  "buildDate": "1970-01-01T00:00:00Z",
  "goVersion": "go1.7.1",
  "compiler": "gc",
  "platform": "linux/amd64"
}

7.3 通过代理访问 Pod

API Server 会自动为每个 Pod 创建同名的 API 端点,且可通过代理访问。

先获取 Pod 名称并存入环境变量:

export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-390780338-x81xj

通过代理 URL 向 Pod 内的应用发起 HTTP 请求:

curl http://localhost:8001/api/v1/proxy/namespaces/default/pods/$POD_NAME/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-390780338-x81xj | v=1

注意:URL 中的 Pod 名称必须从 echo $POD_NAME 的实际输出中获取,不要照抄示例。


八、部署网站服务(示例投票应用 Result)

可以通过 Dashboard 创建,也可通过 kubectl create -f 创建。下面分别是 Service 和 Deployment 的 YAML。

8.1 Service(NodePort 类型)

apiVersion: v1
kind: Service
metadata:
  name: result
spec:
  type: NodePort
  ports:
    - name: "result-service"
      port: 5001
      targetPort: 80
      nodePort: 31001
  selector:
    app: result

8.2 Deployment

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: result
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: result
    spec:
      containers:
      - image: dockersamples/examplevotingapp_result:before
        name: result

8.3 验证 Pod 与 Service

kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5d7f968ccb-n5d4c   1/1       Running   0          9m
result-76784c98fb-nw6jp                1/1       Running   0          42s
kubectl get svc
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP          33m
nginx-service   NodePort    10.98.157.233   <none>        80:30001/TCP     13m
result          NodePort    10.106.86.50    <none>        5001:31001/TCP   2m

访问网站:

curl 192.168.224.10:31001

九、部署 MySQL(持久化存储示例)

使用 Kubernetes 官方示例中的 PV/PVC 与 Deployment:

kubectl create -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
kubectl create -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml

9.1 查看 Deployment 详情

kubectl describe deployment mysql
Name:               mysql
Namespace:          default
CreationTimestamp:  Tue, 30 Oct 2018 08:51:52 -0700
Labels:             app=mysql
Annotations:        deployment.kubernetes.io/revision=1
Selector:           app=mysql
Replicas:           1 desired | 1 updated | 1 total | 0 available | 1 unavailable
StrategyType:       Recreate
MinReadySeconds:    0
Pod Template:
  Labels:  app=mysql
  Containers:
   mysql:
    Image:  mysql:5.6
    Port:   3306/TCP
    Environment:
      MYSQL_ROOT_PASSWORD:  password
    Mounts:
      /var/lib/mysql from mysql-persistent-storage (rw)
  Volumes:
   mysql-persistent-storage:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  mysql-pv-claim
    ReadOnly:   false
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      False   MinimumReplicasUnavailable
  Progressing    True    ReplicaSetUpdated
OldReplicaSets:  <none>
NewReplicaSet:   mysql-544bbdcd6f (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  4s    deployment-controller  Scaled up replica set mysql-544bbdcd6f to 1

9.2 查看 MySQL Pod

kubectl get pods -l app=mysql
NAME                     READY     STATUS    RESTARTS   AGE
mysql-544bbdcd6f-8jbvq   1/1       Running   0          19s

9.3 查看 PVC

kubectl describe pvc mysql-pv-claim
Name:          mysql-pv-claim
Namespace:     default
StorageClass:  manual
Status:        Bound
Volume:        mysql-pv-volume
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed=yes
               pv.kubernetes.io/bound-by-controller=yes
Finalizers:    []
Capacity:      20Gi
Access Modes:  RWO
Events:
  Type     Reason              Age   From                         Message
  ----     ------              ----  ----                         -------
  Warning  ProvisioningFailed  32s   persistentvolume-controller  storageclass.storage.k8s.io "manual" not found

说明:该 Warning 是因为集群中没有名为 manual 的 StorageClass 动态供应器,但本示例通过 mysql-pv.yaml 预先创建了静态 PV 并与 PVC 绑定,因此 PVC 最终状态为 Bound,不影响使用。

9.4 临时客户端连接 MySQL

使用 kubectl run 启动一个临时 MySQL 客户端并连接到 mysql 服务:

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword
If you don't see a command prompt, try pressing enter.

进入 MySQL 后即可执行 SQL:

create database dbname default charset utf8;
use dbname;
show tables;
Database changed
Empty set (0.00 sec)

十、kubectl 常用命令速查

命令 作用
kubectl version 查看 Client/Server 版本。
kubectl get nodes 列出集群节点。
kubectl get pods [--all-namespaces] 列出 Pod(加 --all-namespaces 查看所有命名空间)。
kubectl get pods -o wide 查看 Pod 详情,包含 Pod IP、所在节点。
kubectl get pods -l app=mysql 按 Label 过滤 Pod。
kubectl get deployments 列出 Deployment。
kubectl get svc / kubectl get services 列出 Service。
kubectl get pvc 列出 PersistentVolumeClaim。
kubectl describe <资源类型> <资源名> 查看资源详细信息和事件,排错首选。
kubectl describe deployment mysql 查看名为 mysql 的 Deployment 详情。
kubectl describe pvc mysql-pv-claim 查看 PVC 详情。
kubectl create -f <file.yaml> 从 YAML/JSON 文件创建资源。
kubectl apply -f <file.yaml> 应用/更新资源配置(声明式)。
kubectl expose deployment <name> --type="LoadBalancer" 将 Deployment 暴露为 Service。
kubectl run <name> --image=<img> --port=<p> 快速创建一个 Deployment。
kubectl run -it --rm --image=<img> --restart=Never <name> -- <cmd> 启动临时交互式 Pod 并在退出后删除。
kubectl proxy 在本机与集群之间建立 API 代理。
kubectl get clusterrole/cluster-admin -o yaml 以 YAML 格式查看 cluster-admin 角色。
kubectl create clusterrolebinding <name> --clusterrole=cluster-admin --user=admin 将 cluster-admin 绑定到用户 admin。

10.1 常用参数

参数 作用
-o wide 输出额外信息(IP、节点等)。
-o yaml 以 YAML 格式输出资源定义。
-l <key>=<value> 通过 Label 选择器过滤资源。
--all-namespaces 跨所有命名空间查询资源。
--save-config 创建资源时保存当前配置,便于后续 apply 更新。
-it 以交互方式运行(分配 TTY 并保持 stdin 打开)。
--rm 命令退出后自动删除 Pod。
--restart=Never 不重启(用于一次性任务/临时客户端)。
--type=<类型> kubectl expose 指定 Service 类型(ClusterIP/NodePort/LoadBalancer)。

十一、常见排错思路

  1. 节点一直 NotReady

    • 检查 Flannel 是否正常运行:kubectl get pods -n kube-system | grep flannel。
    • 确认 --pod-network-cidr 与 Flannel 配置一致(本实验为 10.244.0.0/16)。
    • 在所有节点确认镜像 flannel_v0.9.1-amd64.tar 已成功 docker load。
  2. kubectl 无法连接 apiserver(connection refused)

    • 查看静态 Pod 清单 /etc/kubernetes/manifests/kube-apiserver.yaml 是否配置正确。
    • 重启 kubelet:systemctl restart kubelet。
    • 重新应用:kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml。
    • 若报错 The connection to the server 192.168.224.10:6443 was refused,重复执行上面两条命令直到 apiserver 启动。
  3. Pod 长时间 Pending

    • 使用 kubectl describe pod <pod-name> 查看 Events,常见原因包括:镜像未导入、资源不足、未调度到可用节点、PVC 未 Bound。
    • 在对应节点用 docker images 检查镜像是否存在。
  4. 成员节点 kubeadm join 失败

    • 确认 token 未过期,且使用的是当前 Master kubeadm init 输出中的完整命令(token 与 ca-cert-hash 必须匹配)。
    • 确认节点到 Master 的 6443 端口可达。
    • 确认节点已执行:关闭 Swap、加载内核网络参数、安装 socat、kubernetes-cni、kubelet、kubectl、kubeadm,导入必备镜像。
  5. Dashboard 无法登录或无权限

    • 确认 /etc/kubernetes/pki/basic_auth_file 已创建并配置到 kube-apiserver.yaml 的 --basic_auth_file 参数。
    • 确认已为 admin 用户创建 clusterrolebinding 绑定 cluster-admin。
    • 重启 kubelet 并重新 apply apiserver 清单。
  6. 外部无法访问服务

    • 用 kubectl get svc 查看 Service 类型和 NodePort/HostPort。
    • 使用 lsof -i:<端口> 或 netstat -tlnp 确认 kube-proxy 已监听对应端口。
    • 确认防火墙(firewalld/iptables)放行了相关端口,实验环境可关闭 firewalld。
  7. Swap 未关闭

    • free -m 中 Swap 必须为 0。若重启后 Swap 恢复,需要在 /etc/fstab 中注释掉 Swap 行,或确保每次启动前执行 swapoff -a。
  8. storageclass "manual" not found 警告

    • 这是因为没有动态 StorageClass,但使用静态 PV/PVC 绑定时可以忽略,只要 PVC 状态为 Bound 即可正常使用。
最后修改:2026 年 09 月 20 日
如果觉得我的文章对你有用,请随意赞赏