Kubernetes 实战教程:从基础到进阶
目录
3.2-创建 ClusterIP Service(内部访问)
5.1-安装 NGINX Ingress Controller
7.3-Horizontal Pod Autoscaler (HPA)
文件结构
k8s-practice/
├── 01-basic-pods
│ ├── busybox-pod.yaml
│ ├── nginx-pod-v1.yaml
│ └── nginx-pod-v2.yaml
├── 02-deployments-service
│ ├── nginx-deployment.yaml
│ ├── nginx-service-clusterip.yaml
│ └── nginx-service-nodeport.yaml
├── 03-configmaps-secrets-volume
│ ├── configmap.yaml
│ ├── pod-emptydir.yaml
│ ├── pod-hostpath.yaml
│ ├── pod-with-configmap.yaml
│ ├── pod-with-secret.yaml
│ └── secret.yaml
├── 04-ingress
│ ├── app1
│ │ └── app1-deployment.yaml
│ ├── app2
│ │ └── app2-deployment.yaml
│ ├── ingress-rules.yaml
│ ├── ingress-tls.yaml
│ ├── tls.crt
│ └── tls.key
├── 05-pods-probes
│ ├── deployment-readiness.yaml
│ ├── pod-liveness.yaml
│ ├── pod-sidecar.yaml
│ └── pod-startup.yaml
├── 06-AS-resource-management
│ ├── deployment-resources.yaml
│ ├── hpa.yaml
│ └── php-apache.yaml
├── 07-ENV-test
│ ├── allow-backend-to-db.yaml
│ ├── backend-policy.yaml
│ └── deny-all-ingress.yaml
└── 08-helm
├── custom-values.yaml
└── myapp
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── httproute.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
mkdir -p k8s-practice/{01-basic-pods,02-deployments-service,03-configmaps-secrets-volume,04-ingress/{app1,app2},05-pods-probes,06-AS-resource-management,07-ENV-test,08-helm/myapp}
1-基础环境搭建(都要做)
1.1-个人环境信息
Master: 192.168.132.100
Node: 192.168.132.101
Pod CIDR: 10.244.0.0/16
Service CIDR: 10.96.0.0/12
K8s Version: 1.34.1
修改containerd的镜像仓库为官方镜像仓库
# registry.k8s.io(Kubernetes 官方镜像仓库) mkdir -p /etc/containerd/certs.d/registry.k8s.io tee /etc/containerd/certs.d/registry.k8s.io/hosts.toml >/dev/null <<'EOM' server = "https://registry.k8s.io" EOM # docker.io(Docker Hub 官方域名建议使用 registry-1.docker.io) mkdir -p /etc/containerd/certs.d/docker.io tee /etc/containerd/certs.d/docker.io/hosts.toml >/dev/null <<'EOM' server = "https://registry-1.docker.io" EOM # quay.io(若使用) mkdir -p /etc/containerd/certs.d/quay.io tee /etc/containerd/certs.d/quay.io/hosts.toml >/dev/null <<'EOM' server = "https://quay.io" EOM # 确认 containerd 使用官方 pause(前文已设置为 registry.k8s.io/pause:3.10) sed -ri 's#^\s*sandbox_image = ".*"# sandbox_image = "registry.k8s.io/pause:3.10"#' /etc/containerd/config.toml systemctl restart containerd
生成默认/etc/containerd/config.toml文件用于修改
containerd config default > /etc/containerd/config.toml vim /etc/containerd/config.toml# 添加以下行 [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d" [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime] base_runtime_spec = "" [plugins."io.containerd.grpc.v1.cri".dns] servers = ["8.8.8.8", "1.1.1.1"]
1.2-配置环境
windows上(这里使用的是cv)
- 在windows上ipconfig查询IP(192.168.132.1),和虚拟机网段相同(我的虚拟机网关为192.168.132.2)
- 然后打开cv软件--->设置--->找到配置目录--->修改文件配置为如下(需要打开本地文件进行修改):
mixed-port: 7897 allow-lan: true log-level: info external-controller: 0.0.0.0:9097
在 systemd 为 containerd 添加完整的 proxy/no_proxy
内容如下(用你主机的真实 IP 替换):
sudo mkdir -p /etc/systemd/system/containerd.service.d sudo tee /etc/systemd/system/containerd.service.d/http-proxy.conf > /dev/null <<'EOF' [Service] Environment="HTTP_PROXY=http://192.168.132.1:7897" Environment="HTTPS_PROXY=http://192.168.132.1:7897" Environment="NO_PROXY=127.0.0.1,localhost,10.96.0.1,10.96.0.0/12,.svc,.cluster.local,192.168.132.0/24,192.168.132.100,192.168.132.101" EOF同样为 kubelet(必须)添加 NO_PROXY(kubelet 也会调用 CNI)
sudo mkdir -p /etc/systemd/system/kubelet.service.d sudo tee /etc/systemd/system/kubelet.service.d/http-proxy.conf > /dev/null <<'EOF' [Service] Environment="HTTP_PROXY=http://192.168.132.1:7897" Environment="HTTPS_PROXY=http://192.168.132.1:7897" Environment="NO_PROXY=127.0.0.1,localhost,10.96.0.1,10.96.0.0/12,.svc,.cluster.local,192.168.132.0/24,192.168.132.100,192.168.132.101" EOF
验证crictl代理是否完成
# 重新加载 systemd 并重启服务 systemctl daemon-reexec systemctl daemon-reload systemctl restart containerd systemctl restart kubelet重启 / 重建 Calico daemonset 的 pods(让 calico-node 重新尝试)
kubectl -n calico-system delete pod -l k8s-app=calico-node kubectl -n calico-system get pods# 期望输出:Connection to 192.168.132.1 7897 port [tcp/*] succeeded! nc -vz 192.168.132.1 7897# 如果返回 HTTP/1.1 200 OK 或 HTTP/2 200,就说明代理已成功。 curl -x http://192.168.132.1:7897 https://www.google.com -Icrictl pull docker.io/library/busybox:1.36
2-基础 Pod 操作(仅master)
2.1-创建最小 BusyBox Pod
代码解析
apiVersion: v1: 使用核心 API 组kind: Pod: 资源类型为 Podmetadata: 元数据,包含名称和标签spec.containers: 容器定义command: 覆盖镜像默认命令resources: 资源限制,防止资源耗尽
创建最小 BusyBox Pod
mkdir -p ~/k8s-practice/01-basic-pods && cd ~/k8s-practice/01-basic-pods cat > busybox-pod.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: busybox-test labels: app: busybox spec: containers: - name: busybox image: busybox:1.36 command: ['sh', '-c', 'echo "Hello Kubernetes!" && sleep 3600'] resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" EOF # 创建 Pod kubectl apply -f busybox-pod.yaml
如果 image 标签带有
:latest
默认拉取策略是Always,即每次都会尝试从远程仓库拉取镜像。如果 image 标签不是
:latest(比如 busybox:1.36)
默认拉取策略是IfNotPresent,即本地有就用本地的,没有才去远程仓库拉取。确保node节点上有镜像而不是master上有镜像(master节点不运行业务)
部署并验证
# 查看 Pod 状态 kubectl get pods kubectl get pods -w # 实时监控pod创建 kubectl get pods -o wide # 查看详细信息(IP、节点等)# 查看 Pod 详细描述(常用,用于观察pod创建过程中产生的Events) kubectl describe pod busybox-test
# 查看 Pod 日志(输出:Hello Kubernetes!) kubectl logs busybox-test# 进入 Pod 执行命令(测试pod) # kubectl exec -it busybox-test -- sh # 在容器内执行:ping -c 3 www.baidu.com kubectl exec busybox-test -- ping -c 3 www.baidu.com
# 删除 Pod kubectl delete pod busybox-test # 强制删除 pod kubectl delete pod busybox-test --grace-period=0 --force # 另一种方式删除资源(删除文件构建产生的资源) kubectl delete -f busybox-pod.yaml
2.2-Pod 滚动更新与镜像拉取策略
创建带版本标签的 Pod-v1
mkdir -p ~/k8s-practice/01-basic-pods && cd ~/k8s-practice/01-basic-pods cat > nginx-pod-v1.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: nginx-app-1 labels: app: nginx version: v1 spec: containers: - name: nginx image: nginx:1.23-alpine imagePullPolicy: IfNotPresent # 如果本地有则不拉取 ports: - containerPort: 80 name: http EOF # 部署 v1 kubectl apply -f nginx-pod-v1.yaml
部署并测试镜像拉取策略
# 查看pod kubectl get pods kubectl describe pod nginx-app-1 | sed -n '/Events/,$p' # 通过标签查询pod kubectl get pods -l app=nginx
# 查看当前镜像版本(输出:nginx:1.23-alpine) kubectl get pod nginx-app-1 -o jsonpath='{.spec.containers[0].image}' # 更新镜像(Pod 需要删除重建) kubectl delete pod nginx-app-1创建pod-2(可以不删除,用于后续做实验)
cat > nginx-pod-v2.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: nginx-app-2 labels: app: nginx version: v2 spec: containers: - name: nginx image: nginx:1.24-alpine imagePullPolicy: Always # 总是拉取最新镜像 ports: - containerPort: 80 EOF kubectl apply -f nginx-pod-v2.yaml kubectl get pods -w# 查看pod kubectl describe pod nginx-app-2 | sed -n '/Events/,$p' # 查看当前镜像版本(输出:nginx:1.24-alpine) kubectl get pod nginx-app-2 -o jsonpath='{.spec.containers[0].image}'
镜像拉取策略说明
Always: 每次都从仓库拉取IfNotPresent: 本地有则使用本地Never: 只使用本地镜像
问题处理(跳过)
直接kubectl apply -f busybox-pod.yaml无法直接创建busybox-pod,下面是解决方法
- 说明:你之前修改的
calico-cni-pluginClusterRole 被 operator 管理并可能回写覆盖。为避免 operator 干预,我们创建一个额外的、独立的 ClusterRole + ClusterRoleBinding(名字不同),单独授权projectcalico.orggroup 的clusterinformations给该 SA。这样即便 operator 回写原角色,你仍然有权限。# 为 calico-cni-plugin 旁路授权(不受 operator 回写影响) cat <<'EOF' | kubectl apply -f - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: calico-cni-plugin-extra rules: - apiGroups: ["projectcalico.org"] resources: - clusterinformations verbs: ["get","list","watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: calico-cni-plugin-extra-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: calico-cni-plugin-extra subjects: - kind: ServiceAccount name: calico-cni-plugin namespace: calico-system EOF重启 calico-node 并验证
# 删除 calico-node pods 让其重建(不涉及 operator 对 extra role): kubectl -n calico-system delete pod -l k8s-app=calico-node kubectl -n calico-system get pods -w # 等它们都 Running再次用节点上的 kubeconfig 验证:
KUBECONFIG=/etc/cni/net.d/calico-kubeconfig kubectl get clusterinformations default -o yaml最后重新启动/创建测试 Pod:
kubectl delete pod busybox-test --ignore-not-found kubectl run busybox-test --image=busybox:1.36 -- sh -c "echo Hello && sleep 3600" kubectl get pods -o wide
3-Deployment 与 Service
3.1 部署 Nginx Deployment(3副本)
创建 Deployment
mkdir -p ~/k8s-practice/02-deployments-service && cd ~/k8s-practice/02-deployments-service cat > nginx-deployment.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 # 3个副本 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.24-alpine ports: - containerPort: 80 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 10 periodSeconds: 5 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 3 EOF # 创建 Deployment kubectl apply -f nginx-deployment.yaml
部署并验证
# 查看 Deployment kubectl get deployment kubectl get deployment nginx-deployment -o wide
# 查看 ReplicaSet kubectl get rs # 查看 Pods(应该有3个) kubectl get pods -l app=nginx -o wide
# 查看某个 Pod 详情 kubectl describe pod nginx-deployment-5d8b9dbdd4-7r4h6 # 测试自愈能力:删除一个 Pod kubectl delete pod nginx-deployment-5d8b9dbdd4-7r4h6 kubectl get pods -l app=nginx # 会自动创建新的 Pod
# 删除deployments kubectl delete deployments nginx-deployment kubectl delete -f nginx-deployment.yaml
3.2-创建 ClusterIP Service(内部访问)
创建 ClusterIP Service
cat > nginx-service-clusterip.yaml <<EOF apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: ClusterIP # 默认类型,仅集群内访问 selector: app: nginx # 选择标签为 app=nginx 的 Pod ports: - protocol: TCP port: 80 # Service 端口 targetPort: 80 # Pod 端口 EOF # 创建 Service kubectl apply -f nginx-service-clusterip.yaml
测试 ClusterIP
# 查看 Service kubectl get svc nginx-service kubectl describe svc nginx-service # 查看 Endpoints(关联的 Pod IP) kubectl get endpoints nginx-service# 从集群内测试(创建临时 Pod) kubectl run test-client --rm -it --image=busybox:1.36 -- sh # 在容器内执行: wget -qO- http://nginx-service wget -qO- http://nginx-service.default.svc.cluster.local exit
# 删除 nginx-service kubectl delete -f nginx-service-clusterip.yaml
3.3-创建 NodePort Service(外部访问)
创建 NodePort Service
cat > nginx-service-nodeport.yaml <<EOF apiVersion: v1 kind: Service metadata: name: nginx-nodeport spec: type: NodePort selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 nodePort: 30080 # 指定 NodePort(范围:30000-32767) EOF # 创建 NodePort Service kubectl apply -f nginx-service-nodeport.yaml
测试 NodePort
# 查看 Service kubectl get svc nginx-nodeport kubectl get endpoints nginx-nodeport
# 从宿主机访问(任意节点 IP) curl http://192.168.132.100:30080 curl http://192.168.132.101:30080 # 如果失败,检查防火墙 sudo iptables -L -n | grep 30080 # 删除资源 kubectl delete -f nginx-service-nodeport.yaml
Service 类型对比
ClusterIP: 仅集群内访问,默认类型
NodePort: 通过节点 IP:Port 访问,端口范围 30000-32767
LoadBalancer: 云环境下使用外部负载均衡器
4-ConfigMap / Secret / Volume
4.1-ConfigMap 注入环境变量
创建 ConfigMap
# 命令行创建 app-config kubectl create configmap app-config \ --from-literal=APP_ENV=production \ --from-literal=APP_DEBUG=false \ --from-literal=DB_HOST=mysql.default.svc.cluster.local
# YAML 文件创建 app-config-file cat > configmap.yaml <<EOF apiVersion: v1 kind: ConfigMap metadata: name: app-config-file data: # 键值对形式 app.env: "production" log.level: "info" # 文件内容形式 nginx.conf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } } EOF kubectl apply -f configmap.yaml# 查看 ConfigMap kubectl get configmap kubectl describe configmap app-config-file kubectl get configmap app-config-file -o yaml
使用 ConfigMap
cat > pod-with-configmap.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: app-with-config spec: containers: - name: app image: busybox:1.36 command: ['sh', '-c', 'echo "ENV: \$APP_ENV, DEBUG: \$APP_DEBUG, DB: \$DB_HOST" && sleep 3600'] # 方式1:将 ConfigMap 所有键作为环境变量 envFrom: - configMapRef: name: app-config # 方式2:选择特定键作为环境变量 env: - name: LOG_LEVEL valueFrom: configMapKeyRef: name: app-config-file key: log.level # 方式3:挂载为文件 volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: app-config-file items: - key: nginx.conf path: nginx.conf EOF kubectl apply -f pod-with-configmap.yaml# 验证 kubectl logs app-with-config kubectl exec app-with-config -- cat /etc/config/nginx.conf
4.2-Secret 注入敏感信息
创建 Secret
# 方法1:命令行创建 db-secret kubectl create secret generic db-secret \ --from-literal=username=admin \ --from-literal=password=MyP@ssw0rd123
# 方法2:YAML 创建(需 base64 编码) echo -n 'admin' | base64 # YWRtaW4= echo -n 'MyP@ssw0rd123' | base64 # TXlQQHNzdzByZDEyMw== cat > secret.yaml <<EOF apiVersion: v1 kind: Secret metadata: name: db-secret-yaml type: Opaque data: username: YWRtaW4= password: TXlQQHNzdzByZDEyMw== EOF kubectl apply -f secret.yaml# 查看 Secret(data 不会直接显示) kubectl get secret kubectl describe secret db-secret kubectl describe secret db-secret-yaml
使用 Secret
cat > pod-with-secret.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: app-with-secret spec: containers: - name: app image: busybox:1.36 command: ['sh', '-c', 'echo "User: \$DB_USER, Pass: \$DB_PASS" && sleep 3600'] env: - name: DB_USER valueFrom: secretKeyRef: name: db-secret key: username - name: DB_PASS valueFrom: secretKeyRef: name: db-secret key: password # 挂载为文件 volumeMounts: - name: secret-volume mountPath: /etc/secret readOnly: true volumes: - name: secret-volume secret: secretName: db-secret EOF kubectl apply -f pod-with-secret.yaml# 验证 kubectl logs app-with-secret kubectl exec app-with-secret -- ls /etc/secret kubectl exec app-with-secret -- cat /etc/secret/username
4.3-Volume 持久化
emptyDir(临时存储)
cat > pod-emptydir.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: pod-emptydir spec: containers: - name: writer image: busybox:1.36 command: ['sh', '-c', 'while true; do echo $(date) >> /data/log.txt; sleep 5; done'] volumeMounts: - name: shared-data mountPath: /data - name: reader image: busybox:1.36 command: ['sh', '-c', 'tail -f /data/log.txt'] volumeMounts: - name: shared-data mountPath: /data volumes: - name: shared-data emptyDir: {} # Pod 删除后数据丢失 EOF kubectl apply -f pod-emptydir.yaml# 查看日志 kubectl logs pod-emptydir -c writer kubectl logs pod-emptydir -c reader
hostPath(宿主机路径)
注意:这里的192.168.132.101的主机名为k8s-node1(在写yaml的时候要注意)
# 在 node 节点上创建目录 ssh root@192.168.132.101 "mkdir -p /data/k8s-host"cat > pod-hostpath.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: pod-hostpath spec: nodeSelector: kubernetes.io/hostname: k8s-node1 # 指定运行在 node 节点 containers: - name: nginx image: nginx:1.24-alpine volumeMounts: - name: host-data mountPath: /usr/share/nginx/html volumes: - name: host-data hostPath: path: /data/k8s-host type: DirectoryOrCreate EOF kubectl apply -f pod-hostpath.yaml# 在 node 节点创建测试文件 ssh root@192.168.132.101 "echo '<h1>HostPath Test</h1>' > /data/k8s-host/index.html" # 验证 kubectl exec pod-hostpath -- cat /usr/share/nginx/html/index.html
# 删除资源 kubectl delete -f pod-hostpath.yaml kubectl delete -f pod-emptydir.yaml kubectl delete -f pod-with-secret.yaml kubectl delete -f secret.yaml kubectl delete secret db-secret kubectl delete -f pod-with-configmap.yaml kubectl delete -f configmap.yaml kubectl delete configmap app-config
5-Ingress 与负载均衡
5.1-安装 NGINX Ingress Controller
安装 Ingress Controller
mkdir -p ~/k8s-practice/04-ingress/{app1,app2} && cd ~/k8s-practice/04-ingress/# 下载官方 YAML (由于官方yaml无法安装) # kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/baremetal/deploy.yaml kubectl apply -f https://fastly.jsdelivr.net/gh/kubernetes/ingress-nginx@controller-v1.8.1/deploy/static/provider/baremetal/deploy.yaml # 等待 Pod 就绪 kubectl get pods -n ingress-nginx -w # 查看 Ingress Controller Service kubectl get svc -n ingress-nginx # 如果是 NodePort 类型,查看端口 kubectl get svc ingress-nginx-controller -n ingress-nginx # 记录 HTTP 和 HTTPS 端口(例如:80:32419/TCP, 443:30232/TCP)
5.2-部署两个应用并配置 Ingress
部署 App1(蓝色)
cd ~/k8s-practice/04-ingress/app1 cat > app1-deployment.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: app1 spec: replicas: 2 selector: matchLabels: app: app1 template: metadata: labels: app: app1 spec: containers: - name: nginx image: nginx:1.24-alpine ports: - containerPort: 80 volumeMounts: - name: html mountPath: /usr/share/nginx/html volumes: - name: html configMap: name: app1-html --- apiVersion: v1 kind: Service metadata: name: app1-service spec: selector: app: app1 ports: - port: 80 targetPort: 80 --- apiVersion: v1 kind: ConfigMap metadata: name: app1-html data: index.html: | <html> <body style="background-color: #3498db;"> <h1>App1 - Blue Service</h1> <p>Path: /app1</p> </body> </html> EOF kubectl apply -f app1-deployment.yaml# 创建的资源查询 kubectl get deployment app1 kubectl get service app1-service kubectl get configmap app1-html
部署 App2(绿色)
cd ~/k8s-practice/04-ingress/app2 cat > app2-deployment.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: app2 spec: replicas: 2 selector: matchLabels: app: app2 template: metadata: labels: app: app2 spec: containers: - name: nginx image: nginx:1.24-alpine ports: - containerPort: 80 volumeMounts: - name: html mountPath: /usr/share/nginx/html volumes: - name: html configMap: name: app2-html --- apiVersion: v1 kind: Service metadata: name: app2-service spec: selector: app: app2 ports: - port: 80 targetPort: 80 --- apiVersion: v1 kind: ConfigMap metadata: name: app2-html data: index.html: | <html> <body style="background-color: #2ecc71;"> <h1>App2 - Green Service</h1> <p>Path: /app2</p> </body> </html> EOF kubectl apply -f app2-deployment.yaml# 创建的资源查询 kubectl get deployment app2 kubectl get service app2-service kubectl get configmap app2-html
创建 Ingress 规则
cd ~/k8s-practice/04-ingress cat > ingress-rules.yaml <<EOF apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: multi-path-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: myapp.local # 自定义域名 http: paths: - path: /app1 pathType: Prefix backend: service: name: app1-service port: number: 80 - path: /app2 pathType: Prefix backend: service: name: app2-service port: number: 80 EOF kubectl apply -f ingress-rules.yaml# 查看 Ingress kubectl get ingress kubectl describe ingress multi-path-ingress
测试 Ingress
# 获取 Ingress Controller NodePort INGRESS_PORT=$(kubectl get svc ingress-nginx-controller -n ingress-nginx -o jsonpath='{.spec.ports[0].nodePort}') # 配置 hosts(在你的操作机器上) echo "192.168.132.100 myapp.local" | sudo tee -a /etc/hosts # 测试访问(在操作机器上) curl http://myapp.local:$INGRESS_PORT/app1 curl http://myapp.local:$INGRESS_PORT/app2 # 或在ubuntu中使用浏览器访问 # http://myapp.local:32419/app1 # http://myapp.local:32419/app2
5.3-配置 TLS 证书
生成自签名证书
# 生成私钥和证书 openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout tls.key -out tls.crt \ -subj "/CN=myapp.local/O=myapp" # 创建 Secret kubectl create secret tls myapp-tls --cert=tls.crt --key=tls.key # 查看 Secret kubectl get secret myapp-tls
更新 Ingress 添加 TLS
cat > ingress-tls.yaml <<EOF apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: multi-path-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx tls: - hosts: - myapp.local secretName: myapp-tls rules: - host: myapp.local http: paths: - path: /app1 pathType: Prefix backend: service: name: app1-service port: number: 80 - path: /app2 pathType: Prefix backend: service: name: app2-service port: number: 80 EOF kubectl apply -f ingress-tls.yaml# 测试 HTTPS(忽略证书警告) INGRESS_HTTPS_PORT=$(kubectl get svc ingress-nginx-controller -n ingress-nginx -o jsonpath='{.spec.ports[1].nodePort}') curl -k https://myapp.local:$INGRESS_HTTPS_PORT/app1
# 清理资源
kubectl delete -f ingress-tls.yaml
kubectl delete secret myapp-tls
kubectl delete -f app1/app1-deployment.yaml
kubectl delete -f app2/app2-deployment.yaml
6-多容器 Pod 与探针
6.1-Sidecar 模式(日志收集)
创建带 Sidecar 的 Pod
mkdir -p ~/k8s-practice/05-pods-probes && cd ~/k8s-practice/05-pods-probes cat > pod-sidecar.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: app-with-sidecar spec: containers: # 主容器:生成日志 - name: app image: busybox:1.36 command: ['sh', '-c'] args: - while true; do echo "$(date) - App log entry" >> /var/log/app.log; sleep 3; done volumeMounts: - name: log-volume mountPath: /var/log # Sidecar 容器:读取并处理日志 - name: log-collector image: busybox:1.36 command: ['sh', '-c'] args: - tail -f /var/log/app.log | while read line; do echo "[COLLECTED] $line"; done volumeMounts: - name: log-volume mountPath: /var/log volumes: - name: log-volume emptyDir: {} EOF kubectl apply -f pod-sidecar.yaml# 查看主容器日志 kubectl logs app-with-sidecar -c app # 查看 Sidecar 日志 kubectl logs app-with-sidecar -c log-collector -f
6.2-健康检查探针
Liveness Probe(存活探针)
cat > pod-liveness.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: pod-liveness-test spec: containers: - name: app image: busybox:1.36 command: ['sh', '-c'] args: - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600 livenessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 5 # 启动后5秒开始检查 periodSeconds: 5 # 每5秒检查一次 failureThreshold: 3 # 失败3次后重启 EOF kubectl apply -f pod-liveness.yaml# 观察 Pod 状态(30秒后会重启) kubectl get pod pod-liveness-test -w # 查看事件 kubectl describe pod pod-liveness-test
Readiness Probe(就绪探针)
cat > deployment-readiness.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: app-readiness spec: replicas: 3 selector: matchLabels: app: readiness-test template: metadata: labels: app: readiness-test spec: containers: - name: nginx image: nginx:1.24-alpine ports: - containerPort: 80 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 3 successThreshold: 1 # 成功1次即标记为就绪 failureThreshold: 2 # 失败2次标记为未就绪 livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 15 periodSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: readiness-service spec: selector: app: readiness-test ports: - port: 80 EOF kubectl apply -f deployment-readiness.yaml# 查看 Pod 就绪状态 kubectl get pods -l app=readiness-test # 查看 Service Endpoints(只包含就绪的 Pod) kubectl get endpoints readiness-service
Startup Probe(启动探针)
cat > pod-startup.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: slow-start-app spec: containers: - name: app image: busybox:1.36 command: ['sh', '-c', 'sleep 45 && touch /tmp/ready && sleep 3600'] startupProbe: exec: command: - cat - /tmp/ready initialDelaySeconds: 0 periodSeconds: 10 failureThreshold: 10 # 允许100秒启动时间 livenessProbe: exec: command: - cat - /tmp/ready periodSeconds: 5 EOF kubectl apply -f pod-startup.yaml kubectl get pod slow-start-app -w
# 删除资源
kubectl delete -f pod-startup.yaml
kubectl delete -f deployment-readiness.yaml
kubectl delete -f pod-liveness.yaml
kubectl delete -f pod-sidecar.yaml
7-自动扩缩容与资源管理
7.1-资源请求与限制
部署带资源限制的应用
mkdir -p ~/k8s-practice/06-AS-resource-management && cd ~/k8s-practice/06-AS-resource-management cat > deployment-resources.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: resource-demo spec: replicas: 2 selector: matchLabels: app: resource-demo template: metadata: labels: app: resource-demo spec: containers: - name: stress image: polinux/stress:1.0.4 command: ['stress'] args: - '--cpu' - '1' - '--vm' - '1' - '--vm-bytes' - '64M' resources: requests: memory: "64Mi" cpu: "100m" # 0.1 核 limits: memory: "128Mi" cpu: "200m" # 0.2 核 EOF kubectl apply -f deployment-resources.yaml# 查看资源使用(需要安装 metrics-server) # 下载命令如下:(下面小节也详细介绍如何下载) # kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml kubectl top pods -l app=resource-demo kubectl top nodes
7.2-安装 Metrics Server
# 方法一:下载 Metrics Server
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server.yaml
# 修改配置(绕过 TLS 验证)
cat >> metrics-server-patch.yaml <<EOF
spec:
template:
spec:
containers:
- name: metrics-server
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP
EOF
# 编辑 metrics-server.yaml,在 Deployment 的 spec.template.spec.containers[0].args 下添加:
# - --kubelet-insecure-tls
# - --kubelet-preferred-address-types=InternalIP
# 方法二:或直接替换
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl edit deployment metrics-server -n kube-system
# 添加上述参数
# 等待就绪
kubectl get pods -n kube-system -l k8s-app=metrics-server
# 测试
kubectl top nodes
kubectl top pods -A

7.3-Horizontal Pod Autoscaler (HPA)
部署测试应用
cat > php-apache.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: php-apache spec: replicas: 1 selector: matchLabels: app: php-apache template: metadata: labels: app: php-apache spec: containers: - name: php-apache image: registry.k8s.io/hpa-example ports: - containerPort: 80 resources: requests: cpu: 100m limits: cpu: 200m --- apiVersion: v1 kind: Service metadata: name: php-apache spec: selector: app: php-apache ports: - port: 80 EOF kubectl apply -f php-apache.yaml
创建 HPA
# 方法1:命令行 kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10 # 方法2:YAML cat > hpa.yaml <<EOF apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: php-apache-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache minReplicas: 1 maxReplicas: 3 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 50 EOF kubectl apply -f hpa.yaml# 查看 HPA kubectl get hpa kubectl describe hpa php-apache-hpa
负载测试
# 在新终端监控 HPA watch -n 1 kubectl get hpa # 生成负载 kubectl run load-generator --image=busybox:1.36 --restart=Never -- /bin/sh -c "while true; do wget -q -O- http://php-apache; done" # 观察 Pod 自动扩展 kubectl get pods -l app=php-apache -w # 停止负载 kubectl delete pod load-generator # 观察 Pod 自动缩减(需要等待约5分钟)
这里由于去官网拉取镜像导致部分pod创建时间过长导致不能很好的实现HPA,我的想法是将HPA的配置设置设置成本地拉取镜像,先将镜像拉取到本地后保证pod能够正常伸缩。
# 删除资源
kubectl delete -f hpa.yaml
kubectl delete -f php-apache.yaml
kubectl delete -f deployment-resources.yaml
8-网络策略与安全
检查当前 CNI
kubectl get pods -A | grep -E 'calico|flannel|weave'
8.1-创建测试环境
创建三个命名空间
mkdir ~/k8s-practice/07-ENV-test && cd ~/k8s-practice/07-ENV-test kubectl create namespace frontend kubectl create namespace backend kubectl create namespace database部署应用
# Frontend kubectl run frontend --image=nginx:1.24-alpine -n frontend --port=80 kubectl expose pod frontend --port=80 -n frontend # Backend kubectl run backend --image=nginx:1.24-alpine -n backend --port=80 kubectl expose pod backend --port=80 -n backend # Database kubectl run database --image=nginx:1.24-alpine -n database --port=80 kubectl expose pod database --port=80 -n database # 测试 Pod(用于测试连通性) kubectl run test-pod --image=busybox:1.36 -n frontend -- sleep 3600
测试默认连通性
# 从 frontend 访问其他服务(默认全通) kubectl exec -n frontend test-pod -- wget -qO- --timeout=2 backend.backend.svc.cluster.local kubectl exec -n frontend test-pod -- wget -qO- --timeout=2 database.database.svc.cluster.local
8.2-配置网络策略
策略1: 拒绝所有入站流量
cat > deny-all-ingress.yaml <<EOF apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-ingress namespace: database spec: podSelector: {} # 应用于所有 Pod policyTypes: - Ingress EOF kubectl apply -f deny-all-ingress.yaml # 测试(应该超时) kubectl exec -n frontend test-pod -- wget -qO- --timeout=2 database.database.svc.cluster.local
策略2:仅允许 backend 访问 database
cat > allow-backend-to-db.yaml <<EOF apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-backend-to-db namespace: database spec: podSelector: matchLabels: {} # 应用于 database 命名空间的所有 Pod policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: name: backend ports: - protocol: TCP port: 80 EOF # 给 backend 命名空间打标签 kubectl label namespace backend name=backend kubectl apply -f allow-backend-to-db.yaml# 从 backend 创建测试 Pod kubectl run test-backend --image=busybox:1.36 -n backend -- sleep 3600 # 测试(应该成功) kubectl exec -n backend test-backend -- wget -qO- --timeout=2 database.database.svc.cluster.local # 从 frontend 测试(应该失败) kubectl exec -n frontend test-pod -- wget -qO- --timeout=2 database.database.svc.cluster.local
策略3:限制 Pod 级别访问
cat > backend-policy.yaml <<EOF apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: backend-access namespace: backend spec: podSelector: matchLabels: run: backend policyTypes: - Ingress - Egress ingress: - from: - namespaceSelector: matchLabels: name: frontend - podSelector: matchLabels: role: api-gateway ports: - protocol: TCP port: 80 egress: - to: - namespaceSelector: matchLabels: name: database ports: - protocol: TCP port: 80 # 允许 DNS 查询 - to: - namespaceSelector: matchLabels: name: kube-system ports: - protocol: UDP port: 53 EOF kubectl label namespace frontend name=frontend kubectl label namespace kube-system name=kube-system kubectl apply -f backend-policy.yaml
# 删除资源
kubectl delete -f backend-policy.yaml
kubectl delete -f allow-backend-to-db.yaml
delete -f deny-all-ingress.yaml
kubectl delete pod test-pod -n frontend
kubectl delete pod frontend -n frontend
kubectl delete pod backend -n backend
kubectl delete pod database -n database
kubectl delete namespace frontend backend database
9-CI/CD & Helm
9.1-安装 Helm
mkdir ~/k8s-practice/08-helm && cd ~/k8s-practice/08-helm
# 下载 Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# 验证安装
helm version
# 添加常用仓库
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
# 添加 bitnami 镜像源(阿里云)
# helm repo add bitnami https://mirrors.aliyun.com/helm/bitnami/
helm repo update
# 搜索 Chart
helm search repo nginx

9.2-创建自定义 Helm Chart
创建 Chart 结构
helm create myapp # 查看生成的目录结构 tree myapp # myapp/ # ├── Chart.yaml # Chart 元数据 # ├── values.yaml # 默认配置 # ├── charts/ # 依赖 Chart # └── templates/ # K8s 资源模板 # ├── deployment.yaml # ├── service.yaml # ├── ingress.yaml # └── ...
编辑 Chart.yaml
cat > myapp/Chart.yaml <<EOF apiVersion: v2 name: myapp description: A simple web application type: application version: 1.0.0 appVersion: "1.0" EOF编辑 values.yaml
cat > myapp/values.yaml <<EOF replicaCount: 2 image: repository: nginx tag: "1.24-alpine" pullPolicy: IfNotPresent service: type: ClusterIP port: 80 ingress: enabled: true className: nginx hosts: - host: myapp.local paths: - path: / pathType: Prefix resources: limits: cpu: 200m memory: 256Mi requests: cpu: 100m memory: 128Mi autoscaling: enabled: true minReplicas: 2 maxReplicas: 5 targetCPUUtilizationPercentage: 70 serviceAccount: create: true automount: true name: "" httpRoute: enabled: false EOF
简化 templates/deployment.yaml
cat > myapp/templates/deployment.yaml <<'EOF' apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: {{- include "myapp.selectorLabels" . | nindent 6 }} template: metadata: labels: {{- include "myapp.selectorLabels" . | nindent 8 }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: 80 resources: {{- toYaml .Values.resources | nindent 10 }} EOF验证 Chart
# 语法检查 helm lint myapp # 渲染模板(不安装) helm template myapp ./myapp # 调试安装 helm install myapp ./myapp --dry-run --debug
9.3-部署和管理
安装 Chart
# 安装到 default 命名空间 helm install myapp-release-1 ./myapp # 安装到指定命名空间(需要先删除上面创建的) helm list -A helm uninstall myapp-release-1 helm install myapp-release-2 ./myapp -n myapp-ns --create-namespace # 覆盖 values helm list -A helm uninstall -n myapp-ns myapp-release-2 helm install myapp-release-3 ./myapp --set replicaCount=3 # 使用自定义 values 文件 cat > custom-values.yaml <<EOF replicaCount: 4 image: tag: "1.25-alpine" EOF helm uninstall myapp-release-3 helm install myapp-release-4 ./myapp -f custom-values.yaml # 查看部署 helm list helm status myapp-release-4 kubectl get all -l app.kubernetes.io/instance=myapp-release-4
升级 Chart
# 修改 values.yaml 或代码 # 升级 helm upgrade myapp-release-4 ./myapp # 升级并修改参数 helm upgrade myapp-release-4 ./myapp --set replicaCount=5 # 查看历史版本 helm history myapp-release-4
回滚
# 回滚到上一个版本 helm rollback myapp-release-4 # 回滚到指定版本 helm rollback myapp-release-4 1 # 查看回滚后状态 helm history myapp-release-4
卸载
# 卸载 Release helm uninstall myapp-release-4 # 保留历史记录的删除 helm uninstall myapp-release-4 --keep-history
9.4-Kubectl Rollout 管理
部署新版本
# 更新镜像 kubectl set image deployment/nginx-deployment nginx=nginx:1.25-alpine --record # 查看滚动更新状态 kubectl rollout status deployment/nginx-deployment # 查看更新历史 kubectl rollout history deployment/nginx-deployment # 查看特定版本详情 kubectl rollout history deployment/nginx-deployment --revision=2暂停和恢复
# 暂停滚动更新 kubectl rollout pause deployment/nginx-deployment # 进行多次更新 kubectl set image deployment/nginx-deployment nginx=nginx:1.26-alpine kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=500m,memory=512Mi # 恢复滚动更新 kubectl rollout resume deployment/nginx-deployment回滚
# 回滚到上一个版本 kubectl rollout undo deployment/nginx-deployment # 回滚到指定版本 kubectl rollout undo deployment/nginx-deployment --to-revision=2 # 验证回滚 kubectl get deployment nginx-deployment -o jsonpath='{.spec.template.spec.containers[0].image}'
总结与清理
清理所有资源
# 删除所有 Deployment kubectl delete deployment --all # 删除所有 Service kubectl delete service --all # 删除所有 Ingress kubectl delete ingress --all # 删除所有 ConfigMap kubectl delete configmap --all # 删除所有 Secret(系统 Secret 会保留) kubectl delete secret --all # 删除命名空间 kubectl delete namespace frontend backend database # 卸载 Helm Release helm uninstall $(helm list -q) # 卸载 Ingress Controller kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/baremetal/deploy.yaml
更多推荐


















































所有评论(0)