K8S + PV + PVC + Logrotate
一、认识
本文基于 Kubernetes
部署 Node.js
服务,具体方案如下:
-
Secret
: 安全管理敏感信息,如数据库密码。 -
ConfigMap for Application
: 存储后端服务器的非敏感配置数据。 -
Service
: 使用NodePort
服务将后端服务器暴露在3000
端口。 -
Deployment
: 管理后端服务器的部署,包括环境变量、资源限制和卷挂载。 -
使用
PersistentVolume
和PersistentVolumeClaim
持久化存储日志, 通过hostPath
或者NFS
的方式写入到本地。 -
基于
Logrotate
来管理Node
开发、测试环境下的日志文件,定期轮转、压缩、删除,避免日志文件过大占用过多磁盘空间。在Kubernetes
中,通过ConfigMap
将logrotate
配置文件挂载到容器中, 通过CronJob
定期执行logrotate
命令。
1.1 logrotate
logrotate
是一个用于管理日志文件的工具,它可以自动轮转、压缩、删除和邮件通知。它通常用于日志文件较大的系统或应用程序,可以帮助系统管理员减少磁盘空间的占用,并保持日志文件的整洁和可管理性。 Logrotate
的功能:
-
轮转(
Rotate
):当日志文件达到指定大小时,会创建一个新的日志文件,并将当前日志文件重命名为备份文件。 -
压缩(
Compress
):轮转后的旧日志文件会进行压缩,减少磁盘空间的占用。 -
删除(
Remove
):删除历史日志文件,保留一定数量的日志文件,防止占满磁盘空间。 -
邮件通知(
Mail
):在日志文件轮转时发送通知邮件。
我们在 Kubernetes
中使用 Logrotate
来管理 Node
开发、测试环境下的日志文件,定期轮转、压缩、删除,避免日志文件过大占用过多磁盘空间。在 Kubernetes
中,通过 ConfigMap
将 logrotate
配置文件挂载到容器中, 通过 CronJob
定期执行 logrotate
命令 。具体过程如下:
-
创建
logrotate
配置文件,并将其存储在ConfigMap
中 -
将
ConfigMap
挂载到容器中,使容器能够读取该配置文件 -
使用
CronJob
定期执行logrotate
命令,确保日志文件按预定规则进行轮转
二、构建镜像
构建 Node.js
服务镜像
2.1 目录结构
backend-server
│── node_modules
│── .dockerignore
│── Dockerfile
│── index.js
└── package.json
2.2 index.js
const Koa = require("koa");
const pino = require("pino");
const KoaRouter = require("koa-router");
const port = 3000;
const app = new Koa();
const router = new KoaRouter();
const logFilePath =
process.env.APP_ENV === "development" || !process.env.APP_ENV
? "logs/backend-server.log"
: "/logs/backend-server.log";
const logFile = pino.destination(logFilePath);
const logger = pino(logFile);
const PORT = process.env.APP_PORT || 3000;
const DB_PASSWORD = process.env.DB_PASSWORD;
console.log("------PORT------", PORT);
console.log("-------DB_PASSWORD--------", DB_PASSWORD);
router.get("/", async (ctx) => {
logger.info("Hello again distributed logs");
ctx.body = {
code: 200,
message: "Hello Koa Server 001",
};
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(port, () => {
console.log(`Koa HTTP Server is running on port ${port}`);
});
访问环境变量: 通过 process.env
可以访问 Dockerfile
以及 Deployment env
环境变量。在 Kubernetes
部署中,环境变量的优先级如下:
-
ConfigMap
和Secret
注入的环境变量(最高优先级) -
容器镜像(
Dockerfile
)中的ENV
默认值 -
应用程序代码中的默认值(如 ||
3000
)
日志写入路径: 如果本地开发,可以将 logs
通过相对路径写入到 logs/xxx.log = ./logs/xxx.log
。如果在 K8S
环境中,需要使用绝对路径 /logs/xx.log
, 来确保它匹配 volumeMounts
中的 mountPath
。
2.3 Dockerfile
FROM registry.cn-hangzhou.aliyuncs.com/bolawen/node:22.7.0-linux-arm64
WORKDIR /server
COPY package*.json /server
RUN npm install --registry=https://registry.npmmirror.com
COPY . /server
ENV APP_ENV=test
ENV APP_PORT=3000
ENV DB_PASSWORD=defaultpassword
EXPOSE 3000
CMD ["node","index.js"]
ENV
: ENV
指令设置镜像的默认环境变量值。在 Kubernetes
中运行时,Deployment env
的环境变量会覆盖 Dockerfile
中的默认值。
2.4 .dockerignore
dist
node_modules
2.5 构建并推送镜像
# 构建镜像
docker build -t backend-server:1.0.0 .
# 推送镜像
docker tag backend-server:1.0.0 registry.cn-hangzhou.aliyuncs.com/bolawen/backend-server:1.0.0
docker push registry.cn-hangzhou.aliyuncs.com/bolawen/backend-server:1.0.0
三、部署服务
3.1 Service
创建 backend-server-service.yaml
文件
apiVersion: v1
kind: Service
metadata:
name: backend-server-service
spec:
type: NodePort
selector:
app: backend-server
ports:
- protocol: TCP
port: 3000
targetPort: 3000
3.2 Deployment
创建 backend-server-deployment.yaml
文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-server-deployment
spec:
replicas: 2
selector:
matchLabels:
app: backend-server
template:
metadata:
labels:
app: backend-server
spec:
containers:
- name: backend-server
image: registry.cn-hangzhou.aliyuncs.com/bolawen/backend-server:1.0.3
ports:
- containerPort: 3000
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: backend-server-config-map
key: APP_ENV
- name: APP_PORT
valueFrom:
configMapKeyRef:
name: backend-server-config-map
key: APP_PORT
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: backend-server-secret
key: DB_PASSWORD
volumeMounts:
- name: backend-server-logs-volume
mountPath: /logs
- name: backend-server-logrotate-config-volume
mountPath: /etc/logrotate.d
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
volumes:
- name: backend-server-logs-volume
persistentVolumeClaim:
claimName: backend-server-logs-pvc
- name: backend-server-logrotate-config-volume
configMap:
name: backend-server-logrotate-config-map
3.3 Development Secret
创建 backend-server-secret.yaml
文件
apiVersion: v1
kind: Secret
metadata:
name: backend-server-secret
type: Opaque
data:
DB_PASSWORD: cm9vdA==
3.4 Development ConfigMap
创建 backend-server-config-map.yaml
文件
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-server-config-map
data:
APP_PORT: "3000"
APP_ENV: "test"
3.5 logrotate ConfigMap 和 logrotate CronJob
backend-server-logrotate-config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-server-logrotate-config-map
data:
app-logrotate.conf: |
/logs/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0644 root root
postrotate
kill -HUP $(pidof node) || true
endscript
-
app-logrotate.conf
:Logrotate
的配置文件通常分为两个部分:主配置文件 通常位于/etc/logrotate.conf
,控制全局设置, 单个日志文件配置:通常位于/etc/logrotate.d/
目录下,每个应用或服务的日志都有一个独立的配置文件。在logrotate
中,app-logrotate.conf
文件(例如/etc/logrotate.d/app-logrotate.conf
)是你为应用程序或服务单独配置的日志轮转规则文件。这个文件通常位于/etc/logrotate.d/
目录下,用来为特定应用或日志文件定义轮转行为。这个文件并不是自动创建的,通过ConfigMap
以及CronJob volumeMounts
手动创建。 -
/logs/*.log
: 日志文件的路径, 表示轮转/logs
目录下所有.log
后缀的日志文件。 -
daily
:日志文件每天轮转一次。你也可以设置为weekly
或monthly
,根据实际需求。 -
rotate 7
:保留最近的7
个日志文件。旧的日志文件会被删除。可以根据需求调整为其他数字。 -
compress
:将轮转后的日志文件压缩。压缩格式通常是 .gz,可以节省磁盘空间。 -
delaycompress
:延迟压缩。即不压缩最后一个轮转的日志文件,等下次轮转时才压缩。 -
missingok
:如果日志文件丢失,logrotate
会忽略,不报错。 -
notifempty
:如果日志文件为空,则不进行轮转。 -
create 0644 root root
:为新创建的日志文件设置权限和属主,确保应用能正常写入。 -
postrotate
:在日志轮转之后,kill -HUP $(pidof node) || true
会发送SIGHUP
信号给node
进程,从而重新打开日志文件。
backend-server-logrotate-cron-job.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: backend-server-logrotate-cron-job
spec:
schedule: "0 0 * * *" # 每天午夜执行一次
jobTemplate:
spec:
template:
spec:
containers:
- name: logrotate
image: registry.cn-hangzhou.aliyuncs.com/bolawen/alpine:3.21.0-linux-arm64
command:
- "/bin/sh"
- "-c"
- "logrotate -f /etc/logrotate.d/app-logrotate.conf"
volumeMounts:
- name: logrotate-config
mountPath: /etc/logrotate.d
- name: logs-volume
mountPath: /logs
restartPolicy: OnFailure
volumes:
- name: logrotate-config
configMap:
name: backend-server-logrotate-config-map
- name: logs-volume
persistentVolumeClaim:
claimName: backend-server-logs-pvc
-
logrotate-config
: 挂载了ConfigMap backend-server-logrotate-config-map
,并将其挂载到容器的/etc/logrotate.d
目录。因此,容器会加载你在ConfigMap
中指定的app-logrotate.conf
配置文件。 -
logs-volume
: 这个卷挂载了一个PVC (backend-server-logs-pvc)
,并将其挂载到容器的/logs
目录,这意味着容器访问的是通过PVC
持久化存储的日志文件。logrotate
会执行日志轮转时,指定的路径是/logs/*.log
,所以它会轮转挂载到/logs
目录下的日志文件。所以,logrotate
轮转的是logs-volume
指向的通过PVC
持久化存储的日志文件。
调试 logrotate
: 在主容器中通过 kubectl exec
调试执行 logrotate
命令
kubectl exec -it <pod-name> -- logrotate -f /etc/logrotate.d/app-logrotate.conf
3.6 PersistentVolume 和 PersistentVolumeClaim
backend-server-logs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: backend-server-logs-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt/logs
backend-server-logs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: backend-server-logs-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
3.7 应用以上配置文件
kubectl apply -f backend-server-config-map.yaml
kubectl apply -f backend-server-secret.yaml
kubectl apply -f backend-server-service.yaml
kubectl apply -f backend-server-logs-pv.yaml
kubectl apply -f backend-server-logs-pvc.yaml
kubectl apply -f backend-server-logrotate-config-map.yaml
kubectl apply -f backend-server-logrotate-cron-job.yaml
kubectl apply -f backend-server-deployment.yaml
四、访问服务
4.1 检测资源状态
# 查看 Pod 状态
kubectl get pods
# 查看 Deployment 状态
kubectl get deployments
# 查看 Service 状态
kubectl get services
4.2 NodePort 访问服务
1. 通过 kubectl describe pod [Pod Name]
获取 NodeIP
kubectl describe pod [Pod Name]
# 输出
Name: backend-server-deployment-64df744d7c-mcns7
Namespace: default
Priority: 0
Service Account: default
Node: k8s-node1/192.168.105.133
Start Time: Wed, 04 Dec 2024 20:16:41 +0800
Labels: app=backend-server
2. 通过 kubectl get services
获取 NodePort
端口
kubectl get services
# 输出
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
node-app-service NodePort 10.108.73.245 <none> 80:30007/TCP 2m
3. 使用集群节点的 IP
地址访问服务
http://<Node-IP>:<NodePort>
# 比如:
http://192.168.105.133:32490/
4.3 验证 Node 服务日志
方案一、直接在宿主机上查看日志目录 /mnt/logs
是否有写入内容
# 查看宿主机的日志目录
ls -la /mnt/logs
cat /mnt/logs/backend-server.log
方案二、进入 Pod
内部,直接写入 /logs
目录进行测试, 然后在宿主机 /mnt/logs
查看是否写入
kubectl exec -it <pod-name> -- /bin/sh
echo "Test log entry" >> /logs/test.log
4.4 验证 Logrotate CronJob
获取 Cronjob
列表
kubectl get cronjob
或者
kubectl get cronjobs
获取 Cronjob
生成的 Pod
: 找到具体的 Pod
名称后,你可以使用 kubectl logs
查看该 Pod
的日志
kubectl get pods --selector=job-name=<job-name>
// 例如
kubectl get pods --selector=job-name=backend-server-logrotate-cron-job
查看 Cronjob
生成的 Pod
日志: 找到具体的 Pod
名称后,你可以使用 kubectl logs
查看该 Pod
的日志
kubectl logs <Cronjob Pod Name>
获取 Cronjob
生成的 Job
: CronJob
会定期执行任务,每次执行都会创建一个新的 Job
。你可以查看 CronJob
创建的所有 Job
和它们对应的 Pod
kubectl get jobs
查看 Cronjob
执行历史和详细日志: 如果你需要检查 CronJob
执行的历史记录,可以使用 kubectl describe
命令来查看 CronJob
以及其 Job
运行的详细信息。会显示 CronJob
任务的调度信息、执行历史以及最近运行的 Job
。
kubectl describe cronjob <cronjob-name>
4.4 访问 PV+ PVC 持久化存储日志文件
tail -l /mnt/logs/backend-server.log