[TOC]
0x00 前言简述 描述: 本章算是对前面Kubernetes学习的总结提炼以及最佳实践配置和K8s在持续集成、持续交互(CI/CD)中的应用方案。
学习了前面的几章我们发现Kubernetes的复杂性以及学习曲线都是比较陡峭的,而前面所学仅仅是一个基础打造,可以算是K8s真正入门和能搭建并且操作使用而已。所以在此之外我们也需进行不断的学习。
0x01 K8S 最佳实践 1.常规配置
(1) 将K8s集群搭建或者应用配置文件纳入到版本控制当中,便于后期集群的重建恢复以及应用服务回滚。
(2) 将构建、部署、测试与发布过程中相关的全部纳入到版本控制之中,包括但不限于应用代码、构建脚本、需求、设计、测试文档、代码库以及配置文件等等。
(3) 将应用代码与应用配置进行隔离,根据不同的环境进行响应的配置,例如开发、预生产以及生产环境中同一个应用配置不尽相同(比如连接的后端数据库的不同)。
2.资源清单
(1) 采用K8s部署应用时建议一组对象文件写一个单独文件(例如 Deployment 对象的yaml文件),因为单个文件更方便进行管理。
(2) 建议使用yaml格式而非JSON格式进行编写资源清单。
(3) 建议使用当前最新且稳定版本的API来定义对象。
(4) 建议把对象的描述信息写入到annotations中方便信息查找(例如在Kuboard 和 Nginx-Ingress 之中)。
3.Labels 配置
(1) 针对于创建的资源应当使用有意义的标签方便后期调度和管理。例如: {app: nginx-demo, tier: frontend, env: production}
4.Pod 配置
(1) 建议不要创建一个裸Pod除非您是在测试容器,最好使用Deployment资源对象(也可以根据业务选择其它的资源对象
)创建Pod,置于为什么我们在前面Controller讲解过。
5.Service 配置
(1) 通常情况下Service创建应当在后端负载创建成功后(Deployment或者StatefulSet资源控制器管理的Pod)
(2) 集群内部的服务发现建议使用k8s的DNS替代环境变量注入,便于先启动的Pod与后启动的Pod进行相互通信。
(3) 建议避免使用hostPort
和hostNetwork
。
6.镜像容器
(1) 根据业务需求设置imagePullPolicy
其拉取策略默认为ifNotPreset
, 如需每次获取到最新镜像则建议使用imagePullPolicy: Always
;
7.代码配置分离 描述: 这是一个应用部署最佳的实践之一是代码和配置之间的分离,因为在一般情况下部署在不同环境下的代码都是相同的,唯一不同的是应用的配置。例如在开发环境、预生产环境以及生产环境同一个应用配置不尽相同。
所以自K8s 1.2开始支持一个被称为ConfigMap的API资源,采用ConfigMap可以实现应用与代码之间的分离,例如可以简单复习一哈ConfigMap的作用:
1) special-config :英 [ˈspeʃl]
特殊的配置设置普通文本数据
2) env-config : 设置环境变量数据 (env 在对象消费)
3) file-config : 设置文件目录数据 (volumes 在对象消费)
8.简化配置方案 描述: 以下是正对于k8s的应用配置和部署简化的一些开源方案
Helm (模板和配置推荐) 便于升级与回滚以及指定历史版本回滚。
Kubecfg
OpenShift
Rancher
Jenkins X
描述: Kubernetes 自从1.7开始,可以在 pod 的container 内获取pod的spec,metadata 等源数据信息,实际上是使用 downward API 通过环境变量把自身的信息呈现给 Pod 中运行的容器。
pod一共有三种类型容器: • Infrastructure Container:基础容器,维护整个Pod网络空间 。 • InitContainers:初始化容器,先于业务容器开始执行 。 • Containers:业务容器,如果有多个通常是并行启动 。
需求 : 假如你有一个根据主机名词尾缀进行选择要使用GPU资源序号,或者是获取资源控制器生成的Pod相关IP或标签信息,此时都可以使用注入环境变量的方式(希望对大家有帮助)
目标 :通过使用 env 和 fieldRef,将 k8s 的源数据和容器字段变成环境变量注入到了容器中。
当前资源控制器env对象 (valueFrom.fieldRef.fieldPath
) 支持的注入字段信息如下:
[TOC]
0x00 前言简述 描述: 本章算是对前面Kubernetes学习的总结提炼以及最佳实践配置和K8s在持续集成、持续交互(CI/CD)中的应用方案。
学习了前面的几章我们发现Kubernetes的复杂性以及学习曲线都是比较陡峭的,而前面所学仅仅是一个基础打造,可以算是K8s真正入门和能搭建并且操作使用而已。所以在此之外我们也需进行不断的学习。
0x01 K8S 最佳实践 1.常规配置
(1) 将K8s集群搭建或者应用配置文件纳入到版本控制当中,便于后期集群的重建恢复以及应用服务回滚。
(2) 将构建、部署、测试与发布过程中相关的全部纳入到版本控制之中,包括但不限于应用代码、构建脚本、需求、设计、测试文档、代码库以及配置文件等等。
(3) 将应用代码与应用配置进行隔离,根据不同的环境进行响应的配置,例如开发、预生产以及生产环境中同一个应用配置不尽相同(比如连接的后端数据库的不同)。
2.资源清单
(1) 采用K8s部署应用时建议一组对象文件写一个单独文件(例如 Deployment 对象的yaml文件),因为单个文件更方便进行管理。
(2) 建议使用yaml格式而非JSON格式进行编写资源清单。
(3) 建议使用当前最新且稳定版本的API来定义对象。
(4) 建议把对象的描述信息写入到annotations中方便信息查找(例如在Kuboard 和 Nginx-Ingress 之中)。
3.Labels 配置
(1) 针对于创建的资源应当使用有意义的标签方便后期调度和管理。例如: {app: nginx-demo, tier: frontend, env: production}
4.Pod 配置
(1) 建议不要创建一个裸Pod除非您是在测试容器,最好使用Deployment资源对象(也可以根据业务选择其它的资源对象
)创建Pod,置于为什么我们在前面Controller讲解过。
5.Service 配置
(1) 通常情况下Service创建应当在后端负载创建成功后(Deployment或者StatefulSet资源控制器管理的Pod)
(2) 集群内部的服务发现建议使用k8s的DNS替代环境变量注入,便于先启动的Pod与后启动的Pod进行相互通信。
(3) 建议避免使用hostPort
和hostNetwork
。
6.镜像容器
(1) 根据业务需求设置imagePullPolicy
其拉取策略默认为ifNotPreset
, 如需每次获取到最新镜像则建议使用imagePullPolicy: Always
;
7.代码配置分离 描述: 这是一个应用部署最佳的实践之一是代码和配置之间的分离,因为在一般情况下部署在不同环境下的代码都是相同的,唯一不同的是应用的配置。例如在开发环境、预生产环境以及生产环境同一个应用配置不尽相同。
所以自K8s 1.2开始支持一个被称为ConfigMap的API资源,采用ConfigMap可以实现应用与代码之间的分离,例如可以简单复习一哈ConfigMap的作用:
1) special-config :英 [ˈspeʃl]
特殊的配置设置普通文本数据
2) env-config : 设置环境变量数据 (env 在对象消费)
3) file-config : 设置文件目录数据 (volumes 在对象消费)
8.简化配置方案 描述: 以下是正对于k8s的应用配置和部署简化的一些开源方案
Helm (模板和配置推荐) 便于升级与回滚以及指定历史版本回滚。
Kubecfg
OpenShift
Rancher
Jenkins X
描述: Kubernetes 自从1.7开始,可以在 pod 的container 内获取pod的spec,metadata 等源数据信息,实际上是使用 downward API 通过环境变量把自身的信息呈现给 Pod 中运行的容器。
pod一共有三种类型容器: • Infrastructure Container:基础容器,维护整个Pod网络空间 。 • InitContainers:初始化容器,先于业务容器开始执行 。 • Containers:业务容器,如果有多个通常是并行启动 。
需求 : 假如你有一个根据主机名词尾缀进行选择要使用GPU资源序号,或者是获取资源控制器生成的Pod相关IP或标签信息,此时都可以使用注入环境变量的方式(希望对大家有帮助)
目标 :通过使用 env 和 fieldRef,将 k8s 的源数据和容器字段变成环境变量注入到了容器中。
当前资源控制器env对象 (valueFrom.fieldRef.fieldPath
) 支持的注入字段信息如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 metadata.name metadata.namespace metadata.labels['' ] metadata.annotations['' ] spec.nodeName spec.serviceAccountName status.hostIP status.podIP status.podIPs
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 apiVersion: v1 kind: Pod metadata: name: dapi-envars-fieldref namespace: devtest labels: app: downwardAPI annotations: demo: dapi-envars spec: containers: - name: test-container image: busybox:latest command: [ "sh" , "-c" ] args: - while true ; do echo -en '\n' ; printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE; printenv MY_POD_IP MY_POD_IPS MY_POD_SERVICE_ACCOUNT; printenv MY_POD_LABELS_APP MY_POD_ANNOTATIONS_DEMO; printenv MY_CPU_REQUEST MY_CPU_LIMIT; printenv MY_MEM_REQUEST MY_MEM_LIMIT; sleep 10 ; done; resources: requests: memory: "32Mi" cpu: "125m" limits: memory: "64Mi" cpu: "250m" env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: MY_NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: HOST_IP valueFrom: fieldRef: fieldPath: status.hostIP - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: MY_POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: MY_POD_IPS valueFrom: fieldRef: fieldPath: status.podIPs - name: MY_POD_SERVICE_ACCOUNT valueFrom: fieldRef: fieldPath: spec.serviceAccountName - name: MY_POD_LABELS_APP valueFrom: fieldRef: fieldPath: metadata.labels['app'] - name: MY_POD_ANNOTATIONS_DEMO valueFrom: fieldRef: fieldPath: metadata.annotations['demo'] - name: MY_CPU_REQUEST valueFrom: resourceFieldRef: containerName: test-container resource: requests.cpu - name: MY_CPU_LIMIT valueFrom: resourceFieldRef: containerName: test-container resource: limits.cpu - name: MY_MEM_REQUEST valueFrom: resourceFieldRef: containerName: test-container resource: requests.memory - name: MY_MEM_LIMIT valueFrom: resourceFieldRef: containerName: test-container resource: limits.memory restartPolicy: Never
运行Pod后查看注入的环境变量:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ~$ kubectl apply -f test -container.yaml pod/dapi-envars-fieldref created ~$ kubectl logs -n devtest dapi-envars-fieldref dapi-envars-fieldref devtest 10.66.182.247 10.66.182.247 default downwardAPI dapi-envars 1 1 33554432 67108864 ~$ kubectl exec -n devtest dapi-envars-fieldref -- printenv HOSTNAME=dapi-envars-fieldref MY_MEM_REQUEST=33554432 HOST_IP=192.168.12.226 MY_POD_NAME=dapi-envars-fieldref MY_POD_NAMESPACE=devtest MY_POD_IP=10.66.182.247 MY_POD_IPS=10.66.182.247 MY_POD_SERVICE_ACCOUNT=default MY_POD_ANNOTATIONS_DEMO=dapi-envars NODE_NAME=weiyigeek-226 MY_POD_LABELS_APP=downwardAPI MY_CPU_REQUEST=1 MY_CPU_LIMIT=1 MY_MEM_LIMIT=67108864 ....
实践示例: 根据Pod名称截取最后一个-
字符后的数字来选择该Pod调用的GPU序号(即使用那一块gpu)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 apiVersion: v1 kind: Service metadata: name: healthcode namespace: devtest labels: app: healthcode use: gpu annotations: author: weiyigeek blog: blog.weiyigeek.top spec: type: NodePort ports: - name: http port: 8000 targetPort: 8000 protocol: TCP nodePort: 30000 selector: app: healthcode use: gpu --- apiVersion: apps/v1 kind: StatefulSet metadata: name: healthcode-0 namespace: devtest labels: app: healthcode spec: replicas: 6 selector: matchLabels: app: healthcode use: gpu serviceName: "healthcode" template: metadata: labels: app: healthcode use: gpu spec: volumes: - name: workdir emptyDir: {} - name: workspace hostPath: path: /storage/webapp/project/MultiTravelcodeocr type: DirectoryOrCreate - name: model hostPath: path: /storage/webapp/project/.EasyOCR type: DirectoryOrCreate - name: img hostPath: path: /storage/webapp/project/upfile type: DirectoryOrCreate initContainers: - name: init image: busybox:1.35.0 imagePullPolicy: IfNotPresent command: - /bin/sh - -c - "echo export CUDA_VISIBLE_DEVICES=${GPU_DEVICES##*-}> /app/${GPU_DEVICES}" env: - name: GPU_DEVICES valueFrom: fieldRef: fieldPath: metadata.name volumeMounts: - name: workdir mountPath: /app/ containers: - name: app image: harbor.weiyigeek.top/python/easyocr-healthcode:v1.6.2 command: ['/bin/bash', '-c' ,'source /app/${HOSTNAME}; echo ${CUDA_VISIBLE_DEVICES}; python ./setup.py --imgdir=/imgs --logdir= /logs --gpu=True'] imagePullPolicy: IfNotPresent resources: limits: {} volumeMounts: - name: workdir mountPath: /app/ - name: workspace mountPath: /workspace - name: model mountPath: /root/.EasyOCR - name: img mountPath: /imgs ports: - name: http protocol: TCP containerPort: 8000
执行结果:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 $ kubectl get pod -n devtest NAME READY STATUS RESTARTS AGE healthcode-0-5 1/1 Running 0 15h healthcode-0-4 1/1 Running 0 15h healthcode-0-3 1/1 Running 0 15h healthcode-0-2 1/1 Running 0 15h healthcode-0-1 1/1 Running 0 15h healthcode-0-0 1/1 Running 0 15h $ nvidia-smi Fri Dec 9 10:08:32 2022 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 465.19.01 Driver Version: 465.19.01 CUDA Version: 11.3 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 NVIDIA Tesla V1... Off | 00000000:1B:00.0 Off | 0 | | N/A 41C P0 36W / 250W | 6697MiB / 32510MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ | 1 NVIDIA Tesla V1... Off | 00000000:1D:00.0 Off | 0 | | N/A 51C P0 53W / 250W | 9489MiB / 32510MiB | 14% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ | 2 NVIDIA Tesla V1... Off | 00000000:3D:00.0 Off | 0 | | N/A 53C P0 42W / 250W | 5611MiB / 32510MiB | 20% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ | 3 NVIDIA Tesla V1... Off | 00000000:3F:00.0 Off | 0 | | N/A 37C P0 35W / 250W | 10555MiB / 32510MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ | 4 NVIDIA Tesla V1... Off | 00000000:40:00.0 Off | 0 | | N/A 45C P0 51W / 250W | 5837MiB / 32510MiB | 5% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ | 5 NVIDIA Tesla V1... Off | 00000000:41:00.0 Off | 0 | | N/A 37C P0 37W / 250W | 10483MiB / 32510MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| | 0 N/A N/A 167660 C python 6693MiB | | 1 N/A N/A 166790 C python 9485MiB | | 2 N/A N/A 165941 C python 5607MiB | | 3 N/A N/A 165032 C python 10551MiB | | 4 N/A N/A 164226 C python 5833MiB | | 5 N/A N/A 163344 C python 10479MiB | +-----------------------------------------------------------------------------+
参考文章: