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),Node192.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
该命令在后台做了以下事情:
- 查找适合这个应用运行的 Node;
- 调度该应用在选定的 Node 上运行;
- 配置集群,在需要时为该应用调配新的 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)。 |
十一、常见排错思路
-
节点一直
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。
- 检查 Flannel 是否正常运行:
-
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 启动。
- 查看静态 Pod 清单
-
Pod 长时间
Pending- 使用
kubectl describe pod <pod-name>查看Events,常见原因包括:镜像未导入、资源不足、未调度到可用节点、PVC 未 Bound。 - 在对应节点用
docker images检查镜像是否存在。
- 使用
-
成员节点
kubeadm join失败- 确认 token 未过期,且使用的是当前 Master
kubeadm init输出中的完整命令(token 与 ca-cert-hash 必须匹配)。 - 确认节点到 Master 的 6443 端口可达。
- 确认节点已执行:关闭 Swap、加载内核网络参数、安装
socat、kubernetes-cni、kubelet、kubectl、kubeadm,导入必备镜像。
- 确认 token 未过期,且使用的是当前 Master
-
Dashboard 无法登录或无权限
- 确认
/etc/kubernetes/pki/basic_auth_file已创建并配置到kube-apiserver.yaml的--basic_auth_file参数。 - 确认已为 admin 用户创建
clusterrolebinding绑定cluster-admin。 - 重启 kubelet 并重新 apply apiserver 清单。
- 确认
-
外部无法访问服务
- 用
kubectl get svc查看 Service 类型和 NodePort/HostPort。 - 使用
lsof -i:<端口>或netstat -tlnp确认 kube-proxy 已监听对应端口。 - 确认防火墙(firewalld/iptables)放行了相关端口,实验环境可关闭 firewalld。
- 用
-
Swap 未关闭
free -m中 Swap 必须为 0。若重启后 Swap 恢复,需要在/etc/fstab中注释掉 Swap 行,或确保每次启动前执行swapoff -a。
-
storageclass "manual" not found警告- 这是因为没有动态 StorageClass,但使用静态 PV/PVC 绑定时可以忽略,只要 PVC 状态为
Bound即可正常使用。
- 这是因为没有动态 StorageClass,但使用静态 PV/PVC 绑定时可以忽略,只要 PVC 状态为
此处评论已关闭