Kubernetes Pod控制器详解与最佳实践
1. Kubernetes Pod 控制器深度解析作为Kubernetes集群中最基础的调度单元Pod本身并不具备自我管理能力。这就好比一个刚出生的婴儿需要监护人照料一样Pod也需要专门的监护人来确保它们能够健康运行。在Kubernetes中这个监护人的角色就是Pod控制器。1.1 Pod控制器的核心职责Pod控制器本质上是一种Kubernetes资源对象它通过持续监控和调节确保集群中的Pod始终处于用户定义的期望状态。这种设计哲学体现了Kubernetes声明式API的精髓 - 你只需要告诉系统你想要什么而不需要关心具体如何实现。具体来说一个合格的Pod控制器需要具备以下核心能力副本数量维护就像严谨的仓库管理员确保货架上的商品数量永远符合库存记录。无论是节点故障导致Pod意外终止还是用户主动调整副本数量控制器都能迅速做出反应。健康状态监控通过内置的探针机制Liveness/Readiness Probe持续检查Pod的运行状况。这相当于给每个Pod配备了24小时值班的私人医生一旦发现异常立即采取救治措施。滚动更新支持实现无缝的应用版本升级就像飞机更换引擎时不需要停飞一样。通过精心设计的替换策略确保服务在整个更新过程中始终可用。版本回退能力当新版本出现问题时可以快速回退到已知良好的历史版本。这为运维人员提供了宝贵的后悔药大大降低了发布风险。1.2 控制器的工作原理Pod控制器通过Reconciliation Loop调和循环机制工作这个机制的核心是一个永不停止的监控-比较-调整循环监控当前状态通过Kubernetes API持续获取属于自己管理的所有Pod状态对比期望状态将实际状态与用户在YAML中声明的期望状态进行比对执行调整操作计算需要创建/删除/修改的Pod数量调用API执行相应操作等待下次循环经过一定间隔默认2秒后重新开始整个过程这个机制看似简单却非常强大。它使得Kubernetes能够自动应对各种异常情况包括节点宕机、网络分区、资源竞争等复杂场景。提示控制器使用Label Selector来确定哪些Pod归自己管理。这意味着你需要确保Pod模板中的labels与控制器spec.selector.matchLabels完全匹配否则控制器会丢失对Pod的追踪。2. 六大Pod控制器详解2.1 ReplicaSet副本守护者ReplicaSet是Kubernetes中最基础的Pod控制器它的设计目标非常简单 - 确保指定数量的Pod副本始终运行。虽然在实际生产中我们很少直接使用ReplicaSet通常用更高级的Deployment来管理但理解它的工作原理对掌握Kubernetes至关重要。核心特性精确副本控制通过spec.replicas字段定义期望的Pod数量控制器会持续调整使实际数量匹配期望值滚动扩缩容支持平滑地增加或减少Pod数量整个过程完全自动化选择器匹配使用label selector识别自己管理的Pod这个机制是所有控制器的基础典型使用场景ReplicaSet最适合管理完全无状态的应用实例比如简单的REST API服务无状态的Web前端实时数据处理worker示例配置apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: guestbook tier: frontend spec: replicas: 3 selector: matchLabels: tier: frontend template: metadata: labels: tier: frontend spec: containers: - name: php-redis image: gcr.io/google_samples/gb-frontend:v3在这个例子中我们定义了一个始终保持3个副本运行的frontend应用。如果某个Pod意外终止ReplicaSet会立即检测到并创建新的Pod来替代它。2.2 Deployment无状态应用之王Deployment是Kubernetes中最常用的控制器可以看作是ReplicaSet的升级版。它通过管理ReplicaSet来实现更高级的部署策略为无状态应用提供了完美的管理方案。核心优势声明式更新只需修改YAML文件中的镜像版本Deployment会自动处理所有更新细节滚动更新策略支持多种更新策略RollingUpdate/Recreate确保服务在更新期间不中断版本回滚内置版本历史记录可以一键回退到任意历史版本暂停/恢复支持暂停更新过程方便进行多步骤部署更新机制详解当您修改Deployment的Pod模板通常是更新容器镜像版本时Deployment会创建一个新的ReplicaSet然后按照以下步骤执行更新根据新模板创建Pod数量由maxSurge参数控制等待新Pod变为Ready状态逐步删除旧Pod数量由maxUnavailable参数控制重复上述过程直到所有Pod都更新完成这个过程确保了服务容量始终保持在可接受水平不会因为更新导致服务不可用。实战示例apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80关键操作命令# 查看更新状态 kubectl rollout status deployment/nginx-deployment # 暂停更新适用于多步骤部署 kubectl rollout pause deployment/nginx-deployment # 恢复更新 kubectl rollout resume deployment/nginx-deployment # 查看更新历史 kubectl rollout history deployment/nginx-deployment # 回滚到上一个版本 kubectl rollout undo deployment/nginx-deployment # 回滚到特定版本 kubectl rollout undo deployment/nginx-deployment --to-revision22.3 StatefulSet有状态应用的完美方案StatefulSet是专为有状态应用设计的控制器它解决了Deployment无法处理的几个关键问题稳定的网络标识、有序部署和持久化存储。核心特性稳定的Pod标识每个Pod获得一个固定的名称如web-0, web-1和对应的DNS记录有序部署策略Pod按顺序创建0→N-1和删除N-1→0确保主从架构的正确初始化持久化存储每个Pod可以拥有独立的PersistentVolumeClaim数据不会随Pod重建而丢失Headless Service通过无头服务为每个Pod提供唯一的DNS解析记录典型应用场景数据库集群MySQL, PostgreSQL, MongoDB分布式协调服务Zookeeper, etcd消息队列Kafka, RabbitMQ任何需要持久化数据或固定网络标识的应用配置示例apiVersion: v1 kind: Service metadata: name: mysql labels: app: mysql spec: ports: - port: 3306 name: mysql clusterIP: None selector: app: mysql --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql replicas: 3 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: initContainers: - name: init-mysql image: mysql:5.7 command: - bash - -c - | set -ex # 基于Pod序号生成server-id [[ hostname ~ -([0-9])$ ]] || exit 1 ordinal${BASH_REMATCH[1]} echo [mysqld] /mnt/conf.d/server-id.cnf echo server-id$((100 $ordinal)) /mnt/conf.d/server-id.cnf # 将配置复制到空目录 if [[ $ordinal -eq 0 ]]; then cp /mnt/config-map/master.cnf /mnt/conf.d/ else cp /mnt/config-map/slave.cnf /mnt/conf.d/ fi volumeMounts: - name: conf mountPath: /mnt/conf.d - name: config-map mountPath: /mnt/config-map containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password ports: - containerPort: 3306 name: mysql volumeMounts: - name: data mountPath: /var/lib/mysql - name: conf mountPath: /etc/mysql/conf.d volumes: - name: config-map configMap: name: mysql-config volumeClaimTemplates: - metadata: name: data spec: accessModes: [ReadWriteOnce] resources: requests: storage: 10Gi这个示例展示了一个完整的MySQL集群配置包含了以下关键点使用Headless Service提供DNS解析通过volumeClaimTemplates为每个Pod创建独立的持久化存储使用initContainer根据Pod序号配置不同的MySQL角色master/slave从Secret获取敏感信息root密码2.4 DaemonSet节点级守护者DaemonSet确保集群中的每个或指定节点上都运行一个Pod副本。当有新节点加入集群时DaemonSet会自动在新节点上创建Pod当节点从集群移除时对应的Pod也会被垃圾回收。典型使用场景集群存储守护进程如glusterd、ceph日志收集器如fluentd、logstash节点监控组件如Prometheus Node Exporter、Datadog agent网络插件如Calico、Weave Net配置示例apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers这个配置展示了典型的日志收集器部署有几个关键点值得注意使用hostPath挂载节点上的日志目录通过tolerations确保Pod也能在master节点上运行默认master节点不允许普通Pod运行设置了合理的资源限制避免DaemonSet占用过多节点资源2.5 Job一次性任务执行者Job创建一个或多个Pod并确保指定数量的Pod成功终止。当Pod成功完成后Job会跟踪完成状态不会自动删除Pod除非手动配置。核心特性任务完成保证确保指定数量的Pod成功运行完成并行控制通过parallelism参数控制并发数completions参数控制总完成数失败重试通过backoffLimit设置重试次数默认6定时清理通过ttlSecondsAfterFinished设置完成后自动删除的延迟时间使用场景数据库迁移批处理任务科学计算测试任务数据导出/备份示例配置apiVersion: batch/v1 kind: Job metadata: name: pi spec: ttlSecondsAfterFinished: 100 backoffLimit: 4 completions: 5 parallelism: 2 template: spec: containers: - name: pi image: perl command: [perl, -Mbignumbpi, -wle, print bpi(2000)] restartPolicy: Never这个Job会同时运行2个Podparallelism总共完成5个Podcompletions每个Pod计算π的2000位小数失败时最多重试4次backoffLimit完成后100秒自动删除ttlSecondsAfterFinished2.6 CronJob定时任务调度器CronJob基于时间表schedule创建Job类似于Linux系统中的crontab。它对于需要定期执行的任务非常有用。核心配置scheduleCron格式的时间表如*/1 * * * *表示每分钟执行jobTemplateJob的定义模板与普通Job配置相同concurrencyPolicy控制并发执行策略Allow/Forbid/ReplacestartingDeadlineSeconds启动截止时间超过此时限未启动的任务会被视为失败successfulJobsHistoryLimit保留的成功Job历史数量默认3failedJobsHistoryLimit保留的失败Job历史数量默认1示例配置apiVersion: batch/v1beta1 kind: CronJob metadata: name: hello spec: schedule: */1 * * * * successfulJobsHistoryLimit: 5 failedJobsHistoryLimit: 3 jobTemplate: spec: template: spec: containers: - name: hello image: busybox command: [/bin/sh, -c, date; echo Hello from Kubernetes] restartPolicy: OnFailure这个CronJob会每分钟执行一次保留最近5个成功Job和3个失败Job的记录每次执行时输出当前时间和问候语3. 配置资源管理实战3.1 Secret敏感信息管理Secret是Kubernetes中存储敏感信息的标准方式如密码、OAuth令牌、ssh密钥等。与直接写在Pod定义或容器镜像中相比使用Secret更安全、更灵活。Secret类型详解Opaque用户自定义的任意数据默认类型kubernetes.io/service-account-token服务账号令牌用于Pod访问API Serverkubernetes.io/dockerconfigjson用于私有Docker Registry认证kubernetes.io/tls存储TLS证书和私钥bootstrap.kubernetes.io/token节点引导令牌创建与管理从文件创建# 创建用户名和密码文件 echo -n admin username.txt echo -n S!B\*d$zDsb password.txt # 生成Secret kubectl create secret generic db-user-pass \ --from-fileusernameusername.txt \ --from-filepasswordpassword.txtYAML定义apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4 # echo -n admin | base64 password: UyFCXCpkJHpEc2I使用方式环境变量注入apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: mycontainer image: redis env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: mysecret key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password挂载为文件apiVersion: v1 kind: Pod metadata: name: secret-volume-pod spec: containers: - name: mycontainer image: redis volumeMounts: - name: secret-volume mountPath: /etc/secrets readOnly: true volumes: - name: secret-volume secret: secretName: mysecret items: - key: username path: my-group/my-username - key: password path: my-group/my-password3.2 ConfigMap配置管理利器ConfigMap用于存储非敏感的配置数据如配置文件、命令行参数、环境变量等。它允许你将配置与容器镜像解耦实现更灵活的部署。创建方式从文件创建# 创建配置文件 echo server.port8080 application.properties echo spring.profiles.activeprod application.properties # 生成ConfigMap kubectl create configmap game-config \ --from-fileapplication.properties从目录创建kubectl create configmap game-config-2 \ --from-fileconfigs/从字面值创建kubectl create configmap special-config \ --from-literalspecial.howvery \ --from-literalspecial.typegood使用方式挂载为文件apiVersion: v1 kind: Pod metadata: name: configmap-volume-pod spec: containers: - name: test-container image: busybox command: [/bin/sh, -c, cat /etc/config/special.how] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: special-config items: - key: special.how path: special.how环境变量注入apiVersion: v1 kind: Pod metadata: name: configmap-env-pod spec: containers: - name: test-container image: busybox command: [/bin/sh, -c, echo $SPECIAL_LEVEL echo $SPECIAL_TYPE] env: - name: SPECIAL_LEVEL valueFrom: configMapKeyRef: name: special-config key: special.how - name: SPECIAL_TYPE valueFrom: configMapKeyRef: name: special-config key: special.type热更新机制当ConfigMap更新时挂载为文件Kubernetes会定期约10秒同步更新Pod内的文件内容会自动更新环境变量不会自动更新需要重建Pod才能获取新值可以通过以下命令强制触发Deployment滚动更新kubectl patch deployment my-nginx --patch {spec: {template: {metadata: {annotations: {version/config: 20240301}}}}}4. 高级技巧与最佳实践4.1 Pod控制器选择指南选择标准ReplicaSetDeploymentStatefulSetDaemonSetJobCronJob是否需要副本管理✓✓✓×××是否需要滚动更新×✓✓✓××是否需要持久化存储××✓可选可选可选是否需要固定网络标识××✓×××是否需要有序部署××✓×××是否适合无状态应用✓✓×视情况视情况视情况是否适合一次性任务××××✓✓4.2 配置管理最佳实践敏感数据必须使用Secret即使数据已经过加密或哈希处理ConfigMap大小限制单个ConfigMap不能超过1MiB大配置应考虑挂载为文件命名规范使用有意义的名称如应用名-环境-config版本控制通过labels或annotations标记配置版本热更新测试确保应用能正确处理配置文件的动态更新默认值处理应用代码应处理配置项缺失的情况4.3 常见问题排查问题1Pod一直处于Pending状态可能原因没有满足节点选择器或亲和性规则资源不足CPU/内存持久卷声明无法绑定排查命令kubectl describe pod pod-name kubectl get events --sort-by.metadata.creationTimestamp问题2Pod不断重启可能原因应用崩溃检查退出代码存活探针失败资源限制被触发OOMKilled排查命令kubectl logs pod-name --previous kubectl describe pod pod-name问题3Service无法访问Pod可能原因Pod的labels与Service的selector不匹配Pod没有正确声明端口网络策略阻止了流量排查命令kubectl get endpoints service-name kubectl describe service service-name问题4ConfigMap更新未生效可能原因使用环境变量方式注入不支持热更新Pod缓存了旧配置某些应用需要重启才能加载新配置更新后时间太短Kubernetes同步需要时间解决方案对于文件挂载方式等待约10秒自动同步对于环境变量方式触发Pod重启如滚动更新检查应用自身的配置缓存机制5. 实战案例全栈应用部署让我们通过一个完整的示例部署一个包含前端、后端和数据库的三层应用综合运用各种Pod控制器和配置管理技术。5.1 应用架构前端Nginx服务使用Deployment管理3个副本后端Node.js API服务使用Deployment管理2个副本数据库MySQL主从集群使用StatefulSet管理1主1从日志收集Fluentd DaemonSet每个节点运行一个实例定时任务数据备份CronJob每天凌晨执行5.2 数据库部署# mysql-secret.yaml apiVersion: v1 kind: Secret metadata: name: mysql-secret type: Opaque data: root-password: UEBzc3cwcmQ # echo -n Pssw0rd | base64 replication-user: cmVwbA # echo -n repl | base64 replication-password: cmVwbFBhc3M # echo -n replPass | base64 # mysql-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: mysql-config data: master.cnf: | [mysqld] log-binmysql-bin server-id1 binlog_formatROW slave.cnf: | [mysqld] server-id2 log-binmysql-bin binlog_formatROW skip-slave-start # mysql-service.yaml apiVersion: v1 kind: Service metadata: name: mysql spec: ports: - name: mysql port: 3306 clusterIP: None selector: app: mysql # mysql-statefulset.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql replicas: 2 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: initContainers: - name: init-mysql image: mysql:5.7 command: - bash - -c - | set -ex [[ hostname ~ -([0-9])$ ]] || exit 1 ordinal${BASH_REMATCH[1]} echo [mysqld] /mnt/conf.d/server-id.cnf echo server-id$((100 $ordinal)) /mnt/conf.d/server-id.cnf if [[ $ordinal -eq 0 ]]; then cp /mnt/config-map/master.cnf /mnt/conf.d/ else cp /mnt/config-map/slave.cnf /mnt/conf.d/ fi volumeMounts: - name: conf mountPath: /mnt/conf.d - name: config-map mountPath: /mnt/config-map containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: root-password - name: MYSQL_REPLICATION_USER valueFrom: secretKeyRef: name: mysql-secret key: replication-user - name: MYSQL_REPLICATION_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: replication-password ports: - containerPort: 3306 name: mysql volumeMounts: - name: data mountPath: /var/lib/mysql - name: conf mountPath: /etc/mysql/conf.d volumes: - name: config-map configMap: name: mysql-config volumeClaimTemplates: - metadata: name: data spec: accessModes: [ReadWriteOnce] resources: requests: storage: 10Gi5.3 后端API服务# api-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: api-config data: application.properties: | server.port3000 db.hostmysql db.port3306 db.nameappdb logging.level.rootINFO # api-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: api-server spec: replicas: 2 selector: matchLabels: app: api-server template: metadata: labels: app: api-server spec: containers: - name: api image: my-registry/api-server:1.2.0 ports: - containerPort: 3000 env: - name: DB_USER valueFrom: secretKeyRef: name: mysql-secret key: replication-user - name: DB_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: replication-password volumeMounts: - name: config-volume mountPath: /app/config volumes: - name: config-volume configMap: name: api-config items: - key: application.properties path: application.properties # api-service.yaml apiVersion: v1 kind: Service metadata: name: api-service spec: selector: app: api-server ports: - protocol: TCP port: 80 targetPort: 30005.4 前端Web服务# frontend-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: frontend-config data: nginx.conf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } location /api { proxy_pass http://api-service; } } # frontend-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: frontend spec: replicas: 3 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: nginx image: nginx:1.19 ports: - containerPort: 80 volumeMounts: - name: config-volume mountPath: /etc/nginx/conf.d - name: html-volume mountPath: /usr/share/nginx/html volumes: - name: config-volume configMap: name: frontend-config items: - key: nginx.conf path: default.conf - name: html-volume emptyDir: {} # frontend-service.yaml apiVersion: v1 kind: Service metadata: name: frontend-service spec: type: LoadBalancer selector: app: frontend ports: - protocol: TCP port: 80 targetPort: 805.5 日志收集DaemonSetapiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd template: metadata: labels: name: fluentd spec: tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:v1.12.0-debian-elasticsearch7-1.0 env: - name: FLUENT_ELASTICSEARCH_HOST value: elasticsearch-logging - name: FLUENT_ELASTICSEARCH_PORT value: 9200 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers5.6 数据库备份CronJobapiVersion: batch/v1beta1 kind: CronJob metadata: name: mysql-backup spec: schedule: 0 3 * * * successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 1 jobTemplate: spec: template: spec: containers: - name: mysqldump image: mysql:5.7 command: - /bin/sh - -c - | mysqldump -h mysql-0.mysql -u root -p${MYSQL_ROOT_PASSWORD} --all-databases | gzip /backup/mysql-$(date %Y%m%d).sql.gz env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: root-password volumeMounts: - name: backup-volume mountPath: /backup restartPolicy: OnFailure volumes: - name: backup-volume persistentVolumeClaim: claimName: mysql-backup-pvc6. 性能优化与高级特性6.1 Pod调度优化节点选择器nodeSelectorspec: template: spec: nodeSelector: disktype: ssd亲和性与反亲和性affinityaffinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - api-server topologyKey: kubernetes.io/hostname资源请求与限制resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1Gi6.2 自动伸缩Horizontal Pod AutoscalerapiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: api-server-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api-server minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 506.3 Pod生命周期钩子postStart和preStoplifecycle: postStart: exec: command: [/bin/sh, -c, echo Hello from the postStart handler /usr/share/message] preStop: exec: command: [/bin/sh, -c, nginx -s quit; while killall -0 nginx; do sleep 1; done]6.4 Pod安全策略SecurityContextsecurityContext: runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 capabilities: add: [NET_ADMIN, SYS_TIME]7. 监控与日志7.1 监控指标Prometheus监控示例apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: api-server-monitor labels: team: backend spec: selector: matchLabels: app: api-server endpoints: - port: web interval: 30s7.2 日志收集Fluentd配置示例source type tail path /var/log