写在开篇
继续接上篇,《一文了解K8S的ConfigMap》。上篇聊过,官方文档中提到的可以使用下面4种方式来使用 ConfigMap 配置 Pod 中的容器:
- 容器的环境变量:可以将 ConfigMap 中的键值对作为容器的环境变量。
- 在只读卷里面添加一个文件,让应用来读取:可以将 ConfigMap 中的内容作为一个只读卷挂载到 Pod 中的容器内部,然后在容器内读取挂载的文件。
- 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap:可以在 Pod 中运行自定义代码,使用 Kubernetes API 来读取 ConfigMap 中的内容。
- 在容器命令和参数内:可以在容器的启动命令中通过引用环境变量的方式来使用 ConfigMap。
为了控制篇幅,计划分4篇进行分享,本篇分享以使用“容器的环境变量”的方式进行实战。 关于使用ConfigMap的更多详情,可参考官方文档:https://kubernetes.io/zh-cn/docs/concepts/configuration/configmap/#using-configmaps
开发示例应用
- goweb项目目录结构
[root@workhost goweb]# tree . ├── Dockerfile ├── go.mod ├── main.go ├── README.md └── static └── login.html
- main.go
package main import ( "fmt" "log" "net/http" "os" "text/template" ) type Message struct { Msg string } func home(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { w.Header().Set("Location", "/login") w.WriteHeader(http.StatusFound) } } func login(w http.ResponseWriter, r *http.Request) { t, _ := template.ParseFiles("./static/login.html") if r.Method == "GET" { t.Execute(w, nil) } } func main() { http.HandleFunc("/", home) http.HandleFunc("/login", login) args := os.Args if args[1] == "-p" { port := args[2] listenAddr := fmt.Sprintf(":%v", port) log.Println("ListenAndserve", listenAddr) err := http.ListenAndServe(listenAddr, nil) if err != nil { log.Println(err) } } }
本次代码在上次的基础上做了点小改造:接受命令行参数,使用 os.Args 获取程序运行时的参数。如果传入的参数中包含 -p,则说明需要指定监听的端口,将端口值读取出来并使用 http.ListenAndServe 启动 HTTP 服务。
- login.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Login K8S</title> <style> body { background-color: #f2f2f2; font-family: Arial, sans-serif; } #login-box { background-color: #fff; border-radius: 5px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); margin: 100px auto; max-width: 400px; padding: 20px; } h1 { font-size: 24px; margin: 0 0 20px; text-align: center; } label { display: block; font-size: 14px; font-weight: bold; margin-bottom: 5px; } input[type="text"], input[type="password"] { border-radius: 3px; border: 1px solid #ccc; box-sizing: border-box; font-size: 14px; padding: 10px; width: 100%; } input[type="submit"] { background-color: #007bff; border: none; border-radius: 3px; color: #fff; cursor: pointer; font-size: 14px; padding: 10px; width: 100%; } input[type="submit"]:hover { background-color: #0069d9; } .error-message { color: #dc3545; font-size: 14px; margin: 10px 0; text-align: center; } </style> </head> <body> <div id="login-box"> <h1>容器云管理平台</h1> <form> <label for="username">账号:</label> <input type="text" id="username" name="username"> <label for="password">密码:</label> <input type="password" id="password" name="password"> <input type="submit" value="登录"> </form> <div class="error-message"></div> </div> <script> const form = document.querySelector('form'); const errorMessage = document.querySelector('.error-message'); form.addEventListener('submit', function(event) { event.preventDefault(); const username = document.getElementById('username').value; const password = document.getElementById('password').value; if (username === '' || password === '') { errorMessage.textContent = 'Please enter a username and password.'; } else if (username !== 'admin' || password !== 'password') { errorMessage.textContent = 'Incorrect username or password.'; } else { alert('Login successful!'); } }); </script> </body> </html>
实战:以使用“容器的环境变量”的方式
- 制作镜像
编写Dockerfile:
FROM alpine:latest WORKDIR /app COPY static /app/static COPY main /app ENV PORT 80 # 设置默认端口号为80,这个值将在容器启动时被覆盖 CMD ["/bin/sh", "-c", "./main -p $PORT"]
构建镜像和推送到私有harbor:
docker build -t 192.168.11.254:8081/webdemo/goweb:20230515v2 . docker push 192.168.11.254:8081/webdemo/goweb:20230515v2
- 测试
[root@workhost goweb]# docker run --rm -it -p 80:9090 -e PORT=9090 192.168.11.254:8081/webdemo/goweb:20230515v2 2023/05/15 02:08:43 ListenAndserve :9090
使用 -p 参数将本地主机的 80 端口映射到容器内部的 9090 端口,使用 -e 参数设置环境变量 PORT 的值为 9090,可以正常启动,说明在启动时已经覆盖掉了默认端口80,且能正常访问:
- 创建configmap
kubectl create configmap goweb --from-literal=port=9091
执行命令后将会创建一个名为 goweb 的 ConfigMap,其中包含一个名为 port 的键,值为 9091。这样,在 Pod 中使用 $PORT 环境变量时,就可以将其设置为 9091。
说明:--from-literal=port=9091 表示要将 port 这个键的值设置为 9091,这里使用 --from-literal 标志表示将文本作为字面量值创建 ConfigMap。
- 创建pod
apiVersion: v1 kind: Pod metadata: name: goweb spec: containers: - name: goweb image: 192.168.11.254:8081/webdemo/goweb:20230515v2 env: - name: PORT valueFrom: configMapKeyRef: name: goweb key: port
上面yaml中,通过设置 env 字段,将 ConfigMap 中的 port 键值对作为环境变量注入到容器中的应用程序中。使用了 valueFrom 字段指定了 ConfigMap 的名称和键,从而将 ConfigMap 中的 port 值注入到容器的 PORT 环境变量中。这样,在容器启动后,应用程序就可以通过读取 PORT 环境变量的值来获取应该监听的端口,实现了将 ConfigMap 的值注入到容器的环境变量中的功能。
- 进入pod验证
[root@k8s-b-master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE goweb 1/1 Running 0 29s [root@k8s-b-master ~]# kubectl exec -it goweb -- sh /app # ps PID USER TIME COMMAND 1 root 0:00 ./main -p 9091 11 root 0:00 sh 17 root 0:00 ps /app #
- 完整的yaml
apiVersion: v1 metadata: name: goweb data: port: "9091" kind: ConfigMap --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: goweb name: goweb spec: replicas: 3 selector: matchLabels: app: goweb template: metadata: labels: app: goweb spec: containers: - name: goweb image: 192.168.11.254:8081/webdemo/goweb:20230515v2 env: - name: PORT valueFrom: configMapKeyRef: name: goweb key: port