第74课:Go 应用 Kubernetes 部署
学习目标
- 理解 Kubernetes 部署的核心概念(Deployment, Service, ConfigMap)
- 学会为 Go Web 应用编写基本的 Deployment 和 Service YAML 文件
- 掌握使用 ConfigMap 管理应用配置的方法
- 能够使用
kubectl命令完成应用的部署、更新和访问 - 了解 Kubernetes 部署的基本流程和最佳实践
核心概念
想象一下,你开发了一个非常棒的 Go Web 服务。现在,你想让它稳定地运行在成千上万的用户面前。直接在一台服务器上运行 go run main.go 是不可靠的,服务器可能会宕机,用户量增长也需要扩展。Kubernetes (K8s) 就是这样一个容器编排平台,它能帮你自动化地管理、扩展和修复你的应用。
在 K8s 中部署一个应用,我们主要和以下几个“角色”打交道:
-
Deployment(部署):这是你的“应用管理员”。你告诉 Deployment 你希望同时运行多少个应用副本(比如 3 个),它负责保证始终有 3 个健康的实例在运行。如果某个实例挂了,它会自动拉起一个新的。你还可以用它来滚动更新你的应用版本,实现零停机升级。
-
Service(服务):这是你的应用的“门卫”和“负载均衡器”。Pod(由 Deployment 创建的运行实例)的 IP 地址是动态变化的,不固定。Service 为你的应用提供一个稳定的访问入口(例如,一个固定的域名或 IP)。它将用户的流量均匀地分发到后端健康的 Pod 上。
-
ConfigMap(配置图):这是应用的“外部配置中心”。通常我们把数据库连接串、API 密钥等配置信息从代码中分离出来。ConfigMap 可以以文件或环境变量的形式挂载到 Pod 中,这样修改配置就无需重新构建镜像。
简单流程:你编写 YAML 文件 -> kubectl 提交给 K8s API Server -> K8s 根据 YAML 创建 Deployment -> Deployment 创建和管理 Pod -> Service 为这些 Pod 创建统一入口。
代码示例
我们假设有一个简单的 Go Web 应用,它从环境变量读取一个欢迎语。
1. Go 应用代码 (main.go)
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
// 从环境变量获取欢迎语,如果没有则使用默认值
greeting := os.Getenv("GREETING")
if greeting == "" {
greeting = "Hello from my Go app!"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, greeting)
})
log.Println("Server starting on port 8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
2. 构建 Docker 镜像 (Dockerfile)
# 构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# 运行阶段
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
构建并推送镜像到镜像仓库(如Docker Hub):
docker build -t your-dockerhub-user/go-greeting-app:v1 .
docker push your-dockerhub-user/go-greeting-app:v1
3. Kubernetes 配置文件
-
configmap.yaml - 定义应用的配置
apiVersion: v1 kind: ConfigMap metadata: name: go-app-config # ConfigMap 的名字 data: # 键值对,键名将成为环境变量名 GREETING: "Welcome to the cloud-native Go world!" -
deployment.yaml - 定义如何运行应用
apiVersion: apps/v1 kind: Deployment metadata: name: go-greeting-deployment # Deployment 的名字 labels: app: go-greeting # 用于关联 Service 的标签 spec: replicas: 3 # 我们希望同时运行 3 个副本 selector: matchLabels: app: go-greeting # 选择器,用于找到它管理的 Pod template: # 这是 Pod 的模板 metadata: labels: app: go-greeting # Pod 的标签,必须与 selector 匹配 spec: containers: - name: go-greeting-container image: your-dockerhub-user/go-greeting-app:v1 # 使用我们构建的镜像 ports: - containerPort: 8080 # 容器暴露的端口 env: - name: GREETING # 从 ConfigMap 设置环境变量 valueFrom: configMapKeyRef: name: go-app-config # 引用哪个 ConfigMap key: GREETING # 引用 ConfigMap 中的哪个键 -
service.yaml - 定义如何访问应用
apiVersion: v1 kind: Service metadata: name: go-greeting-service # Service 的名字 spec: type: LoadBalancer # 在云环境中通常会提供外部负载均衡器 selector: app: go-greeting # 必须与 Deployment 中 Pod 的标签匹配! ports: - protocol: TCP port: 80 # Service 在集群内部暴露的端口 targetPort: 8080 # 流量转发到 Pod 的哪个端口
4. 部署应用
# 1. 创建 ConfigMap
kubectl apply -f configmap.yaml
# 2. 创建 Deployment
kubectl apply -f deployment.yaml
# 3. 创建 Service
kubectl apply -f service.yaml
# 4. 查看状态
kubectl get deployments # 查看 Deployment 状态
kubectl get pods -l app=go-greeting # 通过标签查看关联的 Pod
kubectl get services # 查看 Service,获取外部 IP (EXTERNAL-IP)
# 5. 访问应用 (替换 EXTERNAL-IP)
# 在浏览器或使用 curl 访问 http://<EXTERNAL-IP>
# 你应该会看到:”Welcome to the cloud-native Go world!”
实践练习
-
基础部署:使用上面的 YAML 模板,修改镜像名为你自己的镜像仓库地址,将你的 Go 应用部署到 Kubernetes 集群(如 Minikube、Kind 或云厂商的 K8s 服务)。验证
kubectl get pods显示 3 个运行中的 Pod。- 预期输出:3 个状态为
Running的 Pod。
- 预期输出:3 个状态为
-
配置热更新:在不重新构建和部署镜像的情况下,更新 ConfigMap 中的
GREETING值,然后重启 Pod 以使新配置生效。最后验证应用输出已改变。- 提示:使用
kubectl edit configmap go-app-config或重新kubectl apply -f configmap.yaml(需提前修改文件),然后用kubectl rollout restart deployment/go-greeting-deployment重启 Pod。 - 预期输出:访问应用后看到新的欢迎语。
- 提示:使用
-
滚动更新:修改 Go 应用代码(例如,在欢迎语后添加一个版本号如
v2),构建新镜像v2并推送。然后更新deployment.yaml中的镜像标签为v2并执行kubectl apply -f deployment.yaml。观察 Pod 的更新过程。- 预期输出:
kubectl get pods -w会显示旧 Pod 逐渐终止,新 Pod 逐渐启动,整个过程应用服务不中断。
- 预期输出:
常见错误
- 标签选择器不匹配:这是最常见的错误!
Deployment.spec.selector.matchLabels、Pod.template.metadata.labels和Service.spec.selector三者必须使用完全相同的标签键值对。如果不匹配,Service 将无法找到 Pod,Deployment 也无法正确管理 Pod。 - 端口混淆:需要区分三种端口:
containerPort:容器内部监听的端口(代码中ListenAndServe的端口)。port:Service 在集群内部暴露的端口。targetPort:Service 将流量转发到 Pod 的哪个端口(通常等于containerPort)。
- ConfigMap 未先创建:如果
Deployment引用了一个还不存在的ConfigMap,Pod 会启动失败。务必确保kubectl apply的顺序,或使用--dry-run=client检查。 - 镜像拉取失败:确保镜像名正确无误,并且 K8s 集群的节点能够访问到你的镜像仓库(如果是私有仓库,需要配置
imagePullSecrets)。
小结
本节课,我们学习了使用 Kubernetes 部署 Go 应用的三大核心组件:
- Deployment:负责维护应用的副本数量,并管理 Pod 的生命周期,是实现高可用和滚动更新的基石。
- Service:为动态的 Pod 集合提供一个稳定的网络访问入口,并实现负载均衡。
- ConfigMap:将应用的配置信息与容器镜像解耦,便于管理和更新配置。
掌握这三个组件的 YAML 编写和 kubectl 的基本操作,你就具备了将 Go 应用部署到生产级 Kubernetes 环境的基础能力。记住,Kubernetes 的强大之处在于其声明式 API:你只需告诉它“你想要什么状态”,它就会自动努力达成并维持这个状态。接下来,你可以进一步探索 Ingress(管理外部 HTTP 访问)、Secret(管理敏感信息)和 PersistentVolume(管理存储)等更高级的概念。