K8s学习总结——【一】 搭建本地集群
1 概念总结
1.1 kubernetes(k8s)
- kubernetes(k8s)是用于管理云平台中的多个容器化应用的工具。
- 理解:
- 现在的大型程序被分割为多个服务,各自运行在单独的容器里。
- 为了提升性能和可用性,多个容器需要分散在多个物理机器上,并拥有备份。
- 多个服务和容器需要通信
- k8s用于自动化管理此系统。
1.2 组成
物理层面
- Master节点
- 物理服务器
- kubectl:操作k8s的命令行工具。添加deployment、service等。
- apiServer:对象的请求和调用操作通过此模块提供的接口进行。
- kube-scheduler:资源调度
- kube-controller-manager:维护集群状态
- etcd分布式存储:保存集群状态
- Worker节点
- 物理服务器
- kubelet:创建和管理pod;与master通讯
- kube-proxy:负责不同pod通信。
- 资源(Pod等)
逻辑层面
-
主要资源对象
- Container:容器化应用。
- Pod:k8s创建或销毁的最小单位。一个pod包含一个或多个容器。Pod中容器共享网络、存储和计算资源。
- Service:通过labels绑定pod,提供Cluster IP地址和服务名来访问Pod资源。好处是可以实现多个Pod负载均衡,并固定访问的url,Pod IP或Cluster IP变动时不会产生影响。
- Deployment:管理和控制Pod的数量,确保每时每刻有用户要求数量的 Pod 在工作,某Pod出现问题就重新拉起。
-
为了更好的提供开放、扩展、规范等能力的规范
- CNI:容器网络接口。定义通信规范,屏蔽底层使用不同网络插件的差异。
- CSI:容器存储接口。将任意存储系统规范地暴露给容器化应用程序。
- CRI:容器运行时接口。定义规范,使k8s可以兼容多种容器引擎,如docker、frakti等。
2 实验操作
2.1 环境准备
- 宿主机:Windows下通过vmware运行的Ubuntu。磁盘:30GB,内存:5.2G
- 选择用kind搭建集群
2.2 自制服务镜像
- go 服务
请求ip:8080/test返回字符串"hello world"
package main
import (
"fmt"
"net/http"
)
func helloWorld(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet && r.URL.Path == "/test" {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "hello world")
return
}
http.NotFound(w, r)
}
func main() {
http.HandleFunc("/test", helloWorld)
// 监听并在 8080 端口启动服务
fmt.Println("Server is listening on port 8080...")
http.ListenAndServe(":8080", nil)
}
- Dockerfile
ARG IMAGE_VERSION=v0.0.1
# 使用官方的Golang基础镜像作为父镜像
FROM golang:latest as builder
# 设置工作目录
WORKDIR /app
# 将项目文件复制到容器的工作目录中
COPY . .
ENV CGO_ENABLED=0
# 构建应用
RUN go build -o main .
# 使用一个新的轻量级基础镜像,例如Alpine
FROM alpine:latest
RUN apk update
# 安装curl和其他可能需要的依赖
RUN apk add --no-cache curl
WORKDIR /app
# 复制编译好的二进制文件到新镜像中
COPY --from=builder /app/main /app/
# 设置工作目录和提供运行时环境
WORKDIR /app
CMD ["./main"]
# 声明运行时容器提供的服务端口
EXPOSE 8080
- 运行命令
docker build -t go-hello-world-image:v0.0.1 .
2.3 kind创建集群
- 文件结构
├── go-hello
│ ├── app
│ ├── Dockerfile
│ ├── go.mod
│ └── main.go
├── hello-deploy.yaml
├── hello-service.yaml
├── kind-config.yaml
├── nginx-deployment.yaml
└── nginx-service.yaml
- 集群配置文件:kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
image: kindest/node:v1.29.0 # 指定worknode镜像
- role: worker
image: kindest/node:v1.29.0
- role: worker
image: kindest/node:v1.29.0
- deployment配置文件:hello-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-hello-world-deployment
spec:
replicas: 3
selector:
matchLabels:
app: go-hello-world
template:
metadata:
labels:
app: go-hello-world
spec:
containers:
- name: go-hello-world-container
image: go-hello-world-image:v0.0.1 # 替换为您的镜像名称和标签
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080 # 假设服务监听在容器内部的8080端口
- service配置文件:hello-service.yaml
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
type: NodePort # 或者 LoadBalancer 如果在模拟云环境中
selector:
app: go-hello-world
ports:
- port: 30007
targetPort: 8080
nodePort: 30008 # 如果是NodePort类型,设置一个可用端口
- 搭建集群
# 创建集群
kind create cluster --name hello-cluster --config kind-config.yaml
# 加载镜像
kind load docker-image go-hello-world-image:v0.0.1 --name hello-cluster
# 创建 deployment
kubectl apply -f hello-deploy.yaml
# 创建 service
kubectl apply -f hello-service.yaml
- Docker信息概览
yzq@ubuntu:~/Documents/k8s-proj/hello$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a20802131005 kindest/node:v1.29.0 "/usr/local/bin/entr…" 4 hours ago Up 4 hours hello-cluster-worker
710f6226c8ab kindest/node:v1.29.0 "/usr/local/bin/entr…" 4 hours ago Up 4 hours 127.0.0.1:35839->6443/tcp hello-cluster-control-plane
c21dc643b049 kindest/node:v1.29.0 "/usr/local/bin/entr…" 4 hours ago Up 4 hours hello-cluster-worker2
bc232db8553e go-hello-world-image:v0.0.1 "./main" 4 hours ago Up 4 hours 0.0.0.0:10086->8080/tcp, :::10086->8080/tcp hello-local
- nodes信息
yzq@ubuntu:~/Documents/k8s-proj/hello$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
hello-cluster-control-plane Ready control-plane 3h41m v1.29.0 172.18.0.2 <none> Debian GNU/Linux 11 (bullseye) 5.15.0-91-generic containerd://1.7.1
hello-cluster-worker Ready <none> 3h41m v1.29.0 172.18.0.4 <none> Debian GNU/Linux 11 (bullseye) 5.15.0-91-generic containerd://1.7.1
hello-cluster-worker2 Ready <none> 3h41m v1.29.0 172.18.0.3 <none> Debian GNU/Linux 11 (bullseye) 5.15.0-91-generic containerd://1.7.1
- pods信息
yzq@ubuntu:~/Documents/k8s-proj/hello$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
go-hello-world-deployment-58569cd74c-85hw5 1/1 Running 0 3h33m 10.244.1.5 hello-cluster-worker <none> <none>
go-hello-world-deployment-58569cd74c-bd8ht 1/1 Running 0 3h33m 10.244.2.3 hello-cluster-worker2 <none> <none>
go-hello-world-deployment-58569cd74c-qgfm7 1/1 Running 0 3h33m 10.244.1.4 hello-cluster-worker <none> <none>
- service信息
yzq@ubuntu:~/Documents/k8s-proj/hello$ kubectl get service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
hello-service NodePort 10.96.2.242 <none> 8080:30008/TCP 3h13m app=go-hello-world
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h42m <none>
2.4 结果与问题
使用Kind创建了集群。自定义的服务监听 port = 8080
,service监听 sport = 30007
,在workNode上开放 NodePort=30008
。
结果
1 进入到pod内部
yzq@ubuntu:~/Documents/k8s-proj/hello$ kubectl exec -it go-hello-world-deployment-58569cd74c-85hw5 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/app # curl localhost:8080/test
hello world
/app # curl 10.244.1.4:8080/test
hello world
/app # curl 10.96.2.242:30007/test
hello world
/app # curl hello-service:30007/test
hello world
- 可以用
localhost:port
访问本pod服务 - 可以用
pod ip:port
访问对应pod的服务 - 创建Service后,可以用
clusterIP:sport
可以访问服务 - 创建Service后,无法通过
hello-service:sport
访问服务 [1]
2 进入到workNode内部
docker exec -it hello-cluster-worker sh
- 使用
localhost:NodePort
可以访问本workNode上的服务; - 使用
WorkNodeIp:NodePort
和其他WorkNodeIp:NodePort
无法访问服务 [2]
问题与解决
[1] 检查kube-dns,无明显错误,重启后解决。
[2] 排查过程如下:
-
检查
ufw
防火墙:未开启防火墙 -
检查CNI日志,workNode拥有子网段,可以分配给pod
Node hello-cluster-control-plane has CIDR [10.244.0.0/24
]
Node hello-cluster-worker has CIDR [10.244.1.0/24
]
Node hello-cluster-worker2 has CIDR [10.244.2.0/24
] -
检查
ping
:可以ping通IP -
通过
curl -v
查看详细信息,发现走了代理192.168.115.1
# Windows的IP为192.168.115.1;在这个windows里面,我运行了一个ubuntu虚拟机,IP为192.168.115.128; # windows上的7890端口设置了代理服务器,用于访问资源; # 为了让ubuntu虚拟机同样能够访问资源,我配置ubuntu的代理服务器为192.168.115.1:7890; curl -v 172.18.0.3:30008 * Uses proxy env variable no_proxy == '172.18.0.0/16,fc00:f853:ccd:e793::/64,localhost,127.0.0.1,10.96.0.0/12,192.168.59.0/24,192.168.49.0/24,192.168.39.0/24,::1,10.96.0.0/16,10.244.0.0/16,hello-cluster-control-plane,hello-cluster-worker,hello-cluster-worker2,.svc,.svc.cluster,.svc.cluster.local' * Uses proxy env variable http_proxy == 'http://192.168.115.1:7890/' * Trying 192.168.115.1:7890... * Connected to 192.168.115.1 (192.168.115.1) port 7890 (#0) > GET http://172.18.0.3:30008/ HTTP/1.1 > Host: 172.18.0.3:30008 > User-Agent: curl/7.74.0 > Accept: */* > Proxy-Connection: Keep-Alive > * Mark bundle as not supporting multiuse < HTTP/1.1 502 Bad Gateway < Connection: keep-alive < Keep-Alive: timeout=4 < Proxy-Connection: keep-alive < Content-Length: 0 <
-
curl --noproxy
:强制使用curl不走代理,访问成功# curl -v --noproxy '*' 172.18.0.3:30008/test hello world
-
检查workNode上的环境变量:确实存在no_proxy环境变量
# env | grep no_proxy no_proxy=172.18.0.0/16,fc00:f853:ccd:e793::/64,localhost,127.0.0.1,10.96.0.0/12,192.168.59.0/24,192.168.49.0/24,192.168.39.0/24,::1,10.96.0.0/16,10.244.0.0/16,hello-cluster-control-plane,hello-cluster-worker,hello-cluster-worker2,.svc,.svc.cluster,.svc.cluster.local'
-
使用其他工具
wget
访问接口:也走了代理# wget -O - http://172.18.0.3:30008/test
-
现在问题已经在于:在kind创建的这个workNode中,path包含了proxy和no_proxy,但no_proxy没有起作用,在curl和wget都出现这个现象。
-
回到vmware ubuntu界面,把整个虚拟机的代理关闭。
-
重新创建集群,使用
workNode:NodePort
可以访问。