使用管理员登录 JumpServer 堡垒机时遇到错误的处理
# 进入容器内部 docker exec -it jumpserver /bin/bash # 进入目录 cd /opt/jumpserver/apps/ # 如果遇到错误：ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment? # 可以执行以下命令激活 python 环境（可能 v4 容器需要） source /opt/py3/bin/activate # 进入 python 环境 python manage.py shell # 或 python3 manage.py shell # 重置用户密码 from users.models import User u = User.objects.get(username='admin') u.reset_password('admin') u.save() # 解锁登录限制 from django.core.cache import cache cache.delete_pattern('_LOGIN_BLOCK_*') cache.delete_pattern('_LOGIN_LIMIT_*') # 启用禁用的用户： from users.models import User User.objects.filter(username='admin').update(is_active=True)
存储分类元数据◆configMap:用于保存配置数据(明文)◆Secret:用于保存敏感性数据(编码)◆Downward API:容器在运行时从 Kubernetes API服务器获取有关它们自身的信息真实数据◆Volume:用于存储临时或者持久性数据◆PersistentVolume:申请制的持久化存储VolumeKubernetes 里的 Volume（卷） 是用来给容器提供 数据存储 的机制，主要解决两个问题：容器存储临时性：容器文件系统在容器退出或 Pod 删除后会被清空，Volume 可以保存数据。多容器数据共享：同一个 Pod 中多个容器可以通过 Volume 共享文件。常见 Volume 类型1. emptyDir节点本地的临时存储，Pod 删除数据也删除适合临时缓存、中间文件存储位置可以是磁盘，也可以是内存（medium: Memory）apiVersion: v1 kind: Pod metadata: name: emptydir-demo spec: containers: - name: app image: busybox command: ["/bin/sh", "-c"] args: - | echo "写入数据到 /cache/test.txt" echo "Hello from emptyDir" > /cache/test.txt sleep 3600 volumeMounts: - name: cache-volume mountPath: /cache - name: sidecar image: busybox command: ["/bin/sh", "-c"] args: - | echo "读取 /cache/test.txt" cat /cache/test.txt || echo "文件不存在" sleep 3600 volumeMounts: - name: cache-volume mountPath: /cache volumes: - name: cache-volume emptyDir: {} #使用内存介质 #- name: cache-volume # emptyDir: # medium: Memory # sizeLimit: "64Mi"在 kubelet 的工作目录 ( root-dir 参数控制)，默认为 /var/lib/kubelet, 会为每个使用了 emptyDir: {} 的 pod 创建一个目录，格式如 /var/lib/kubelet/pods/{podid}/volumes/kubernetes.io~empty-dir/, 所有放在 emptyDir 中数据，最终都是落在了 node 的上述路径中2. hostPath挂载宿主机的目录/文件到容器适合需要访问宿主机日志、配置文件等场景可移植性差，不适合多节点部署常用的类型apiVersion: v1 kind: Pod metadata: name: hostpath-pod spec: containers: - image: nginx name: myapp volumeMounts: - mountPath: /test-pd name: test-volume volumes: - name: test-volume hostPath: # directory location on host path: /data # this field is optional type: Directorynfs / glusterfs / cephfs网络存储卷，可实现多节点共享数据projected将多个 Volume（如 ConfigMap、Secret、DownwardAPI）合并到一个挂载点PV/PVC通过声明式申请持久化存储（PV）容量：PV 的值不小于 PVC 要求，可以大于最好一致 读写策略：完全匹配单节点读写 - ReadWriteOnce - RWO多节点只读 - ReadOnlyMany - ROX多节点读写 - ReadWriteMany - RWX存储类：PV 的类与 PVC 的类必须一致，不存在包容降级关系后端可以是 NFS、Ceph、云盘等数据独立于 Pod 生命周期回收策略：Retain（保留）：手动回收Recycle（回收）：基本擦除（rm -rf /thevolume/*）Delete（删除）：关联的存储资产（例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷）将被删除 当前，只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略当启用 PVC 保护功能时，如果用户删除了一个 pod 正在使用的 PVC，则该 PVC 不会被立即删除。PVC 的删除将被推迟，直到 PVC 不再被任何 pod 使用部署 PVapiVersion: v1 kind: PersistentVolume metadata: name: nfspv1 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /data/nfs server: 10.66.66.10创建服务并使用 PVCapiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx serviceName: "nginx" replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: wangyanglinux/myapp:v1.0 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/local/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "nfs" resources: requests: storage: 1Gi storageClassStorageClass 是一种资源对象，用于定义持久卷（Persistent Volumes）的动态供给（Dynamic Provisioning）策略。StorageClass 允许管理员定义不同类型的存储，并指定如何动态创建持久卷以供应用程序使用。这使得 Kubernetes 集群中的存储管理更加灵活和自动化。nfs-client-provisionernfs-client-provisioner 是一个 Kubernetes 供应商，用于动态提供由 NFS（Network File System）共享支持的持久卷。在 Kubernetes 中，持久卷是独立于 pod 存在的存储资源，可以在 pod 重新启动或重新调度时持久地存储数据。nfs-client-provisioner 自动化了根据需要创建持久卷的过程，通过与 NFS 服务器交互。在需要在 Kubernetes 集群中为应用程序动态分配存储而无需手动管理 NFS 共享和持久卷创建的情况下，这尤其有用。部署 nfs-client-provisionerkind: Deployment apiVersion: apps/v1 metadata: name: nfs-client-provisioner namespace: nfs-storageclass spec: replicas: 1 selector: matchLabels: app: nfs-client-provisioner strategy: type: Recreate template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner # image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 image: k8s.dockerproxy.com/sig-storage/nfs-subdir-external-provisioner:v4.0.2 volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: k8s-sigs.io/nfs-subdir-external-provisioner - name: NFS_SERVER # value: <YOUR NFS SERVER HOSTNAME> value: 192.168.66.11 - name: NFS_PATH # value: /var/nfs value: /nfsdata/share volumes: - name: nfs-client-root nfs: # server: <YOUR NFS SERVER HOSTNAME> server: 192.168.66.11 # share nfs path path: /nfsdata/shareapiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: nfs-storageclass --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-client-provisioner-runner rules: - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: run-nfs-client-provisioner subjects: - kind: ServiceAccount name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: nfs-storageclass roleRef: kind: ClusterRole name: nfs-client-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: nfs-storageclass rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: nfs-storageclass subjects: - kind: ServiceAccount name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: nfs-storageclass roleRef: kind: Role name: leader-locking-nfs-client-provisioner apiGroup: rbac.authorization.k8s.ioapiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-client namespace: nfs-storageclass provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: pathPattern: ${.PVC.namespace}/${.PVC.name} onDelete: delete 创建名字空间$ kubectl create ns nfs-storageclass测试 Podkind: PersistentVolumeClaim apiVersion: v1 metadata: name: test-claim annotations: spec: accessModes: - ReadWriteMany resources: requests: storage: 1Mi storageClassName: nfs-client --- kind: Pod apiVersion: v1 metadata: name: test-pod spec: containers: - name: test-pod image: wangyanglinux/myapp:v1.0 volumeMounts: - name: nfs-pvc mountPath: "/usr/local/nginx/html" restartPolicy: "Never" volumes: - name: nfs-pvc persistentVolumeClaim: claimName: test-claim
存储分类元数据◆configMap:用于保存配置数据(明文)◆Secret:用于保存敏感性数据(编码)◆Downward API:容器在运行时从 Kubernetes API服务器获取有关它们自身的信息真实数据◆Volume:用于存储临时或者持久性数据◆PersistentVolume:申请制的持久化存储configMap：通过文件创建：kubectl create configmap --from-file=file.suffix 如果文件中以key=value，那么文件名将作为key,，文件内容将作为value。 kubectl create configmap --from-file=file.suffix 可作为环境变量。通过命令创建：kubectl create configmap literal-config --from-literal=name=dave --from-literal=password=pass 以key=value的形式创建。在pad中使用configMap作为环境变量：方法 1：envFrom（一次性导入所有键值）：apiVersion: v1 kind: Pod metadata: name: demo-pod spec: containers: - name: demo-container image: nginx envFrom: - configMapRef: name: my-config方法 2：env（按键导入）apiVersion: v1 kind: Pod metadata: name: demo-pod spec: containers: - name: demo-container image: nginx env: - name: APP_MODE valueFrom: configMapKeyRef: name: my-config key: APP_MODE在 Pod 中以Volume挂载ConfigMap，会将configMap中的key作为文件名，value作为文件内容。--- apiVersion: v1 kind: Pod metadata: name: configmap-volume-pod spec: containers: - name: my-container image: busybox command: ["sleep", "3600"] volumeMounts: - name: config-volume mountPath: /etc/config # 挂载路径 volumes: - name: config-volume configMap: name: my-configconfigMap更新不会触发pad更新，可以通过修改pod annotations的方式强制触发更新。kubectl patch deployment nginx-test --patch '{"spec": {"template":{"metadata":{"annotations":{"version/config":"66666666"}}}}}'更新 ConfigMap 后:使用该 ConfigMap 挂载的 Env 不会同步更新使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新环境变量是 Pod 启动时 从 ConfigMap 读取一次的，之后不会变。所以必须 重建 Pod 才能重新读取最新值。滚动更新（无论是 patch annotation 还是 kubectl rollout restart）都会让 Deployment 逐个替换 Pod，新的 Pod 会拿到更新后的环境变量值。SecretSecret在使用时需要进行bash64编码，因为 Pod 使用 Secret 挂载成环境变量或文件卷时，Kubelet 会自动进行 Base64 解码，容器内部拿到的就是原始值。# 编码 echo -n "admin" | base64 # 解码 echo -n "YWRtaW4=" | base64 --decodeKubernetes 通过仅仅将 Secret 分发到需要访问 Secret 的 Pod 所在的机器节点来保障其安全性。Secret 只会存储在节点的内存中，永不写入物理存储，这样从节点删除 secret 时就不需要擦除磁盘数据。创建：apiVersion: v1 kind: Secret metadata: name: my-secret type: Opaque data: username: YWRtaW4= # "admin" 的 Base64 password: cGFzc3dvcmQ= # "password" 的 Base64环境变量使用：env: - name: USER_NAME valueFrom: secretKeyRef: name: my-secret key: username文件使用：volumes: - name: secret-volume secret: secretName: my-secret containers: - name: app volumeMounts: - name: secret-volume mountPath: "/etc/secret"当你在 Pod 里用 Secret（或 ConfigMap）以 subPath 方式挂载时：它在容器启动时，会将对应文件从临时目录 拷贝到容器文件系统的指定位置，而不是做一个动态挂载。拷贝过去的文件与原来的 Secret 文件没有链接关系，所以 Secret 更新时，subPath 挂载的内容不会自动刷新。Kubernetes 给不可变的 ConfigMap 和 Secret 提供了一种可选配置，可以设置各个 Secret 和ConfigMap 为不可变的。对于大量使用 configmap 的集群(至少有成千上万各不相同的 configmap 供Pod 挂载)，禁止变更它们的数据有下列好处:防止意外(或非预期的)更新导致应用程序中断通过将 configmap 标记为不可变来关闭 kube-apiserver 对其的监视，从而显著降低 kube-apiserver的负载，提升集群性能。immutable: trueDownward APIDownward API 是 Kubernetes 中的一个功能，它允许容器在运行时从 Kubernetes API 服务器获取有关它们自身的信息。这些信息可以作为容器内部的环境变量或文件注入到容器中，以便容器可以获取有关其运行环境的各种信息，如 Pod 名称、命名空间、标签等。提供容器元数据、动态配置、与K8S集成。以环境变量提供元数据apiVersion: v1 kind: Pod metadata: name: downward-api-env-example spec: containers: - name: my-container image: nginx env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: CPU_REQUEST valueFrom: resourceFieldRef: resource: requests.cpu - name: CPU_LIMIT valueFrom: resourceFieldRef: resource: limits.cpu - name: MEMORY_REQUEST valueFrom: resourceFieldRef: resource: requests.memory - name: MEMORY_LIMIT valueFrom: resourceFieldRef: resource: limits.memory restartPolicy: Never以文件形式提供：apiVersion: v1 kind: Pod metadata: name: downward-api-volume-example spec: containers: - name: my-container image: nginx resources: limits: cpu: "1" memory: "512Mi" requests: cpu: "0.5" memory: "256Mi" volumeMounts: - name: downward-api-volume mountPath: /etc/podinfo volumes: - name: downward-api-volume downwardAPI: items: - path: "annotations" fieldRef: fieldPath: metadata.annotations - path: "labels" fieldRef: fieldPath: metadata.labels - path: "name" fieldRef: fieldPath: metadata.name - path: "namespace" fieldRef: fieldPath: metadata.namespace - path: "uid" fieldRef: fieldPath: metadata.uid - path: "cpuRequest" resourceFieldRef: containerName: my-container resource: requests.cpu - path: "memoryRequest" resourceFieldRef: containerName: my-container resource: requests.memory - path: "cpuLimit" resourceFieldRef: containerName: my-container resource: limits.cpu - path: "memoryLimit" resourceFieldRef: containerName: my-container resource: limits.memory restartPolicy: NeverKubernetes API[root@k8s-master01 apiServer]# cat 1.RBAC.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: test-api-cluster-admin-binding subjects: - kind: ServiceAccount name: test-api namespace: default roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io [root@k8s-master01 apiServer]# cat 2.pod.yaml apiVersion: v1 kind: Pod metadata: name: curl spec: serviceAccountName: test-api containers: - name: main image: tutum/curl command: ["sleep", "9999"] root@curl:/# TOKEN=$( cat /var/run/secrets/kubernetes.io/serviceaccount/token ) root@curl:/# CAPATH="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" root@curl:/# NS=$( cat /var/run/secrets/kubernetes.io/serviceaccount/namespace ) root@curl:/# curl -H "Authorization: Bearer $TOKEN" --cacert $CAPATH https://kubernetes/api/v1/namespaces/$NS/pods获取Api文档kubectl proxy --port=8080 curl localhost:8080/openapi/v2 > k8s-swagger.json docker run \ --rm \ -d \ -p 80:8080 \ -e SWAGGER_JSON=/k8s-swagger.json \ -v $(pwd)/k8s-swagger.json:/k8s-swagger.json \ swaggerapi/swagger-ui
Service 的主要作用：服务发现 (Service Discovery): Service 提供了一个稳定的名称和 IP 地址，其他 Pod 或外部客户端可以通过这个名称或 IP 地址来发现并访问服务。Kubernetes DNS 会为 Service 创建一个 DNS 记录。负载均衡 (Load Balancing): Service 将到来的请求分发到其背后的一个或多个健康的 Pod 上。Kubernetes 支持多种负载均衡算法（取决于 kube-proxy 的模式）。隐藏 Pod 的动态性: Service 将客户端与后端 Pod 解耦，客户端不需要关心后端 Pod 的创建、删除、IP 地址变化等细节。提供不同的访问方式: Service 提供了多种不同的方式来暴露服务，满足不同的访问需求（集群内部、集群外部）。这张图片概括了 Kubernetes 中 Service 的核心迭代，特别是 kube-proxy 组件的代理模式演进。图片中的文字内容总结如下：Service - 核心迭代在 Kubernetes 集群中，每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP (虚拟 IP) 的形式。在 Kubernetes v1.0 版本，代理完全在 userspace (用户空间)。在 Kubernetes v1.1 版本，新增了 iptables 代理，但并不是默认的运行模式。从 Kubernetes v1.2 起，默认就是 iptables 代理。在 Kubernetes v1.8.0-beta.0 中，添加了 ipvs 代理。Userspace: 最早的实现方式，性能较差。Iptables: 成为主要的代理模式，提高了性能，但存在可伸缩性问题。IPVS: 作为新的高性能代理模式被引入，解决了 iptables 在大规模集群中的性能瓶颈。Service 的主要类型 (spec.type):ClusterIP (默认类型):特点: Service 会被分配一个集群内部的 IP 地址（ClusterIP）。访问方式: 只能在集群内部访问 Service，通过 Service 的名称或 ClusterIP。用途: 主要用于集群内部的服务间通信。NodePort:特点: 在所有节点上打开一个固定的端口（NodePort），并将流量转发到 Service 的 ClusterIP。访问方式: 可以通过集群中任何一个节点的 IP 地址加上 NodePort 来访问服务，例如 :。用途: 方便从集群外部访问服务，常用于开发环境或简单的外部暴露需求。但 NodePort 端口范围有限 (默认为 30000-32767)，且需要所有节点都开放该端口，不利于生产环境扩展和管理。LoadBalancer:特点: 在云提供商环境中使用时，会自动创建一个外部负载均衡器，并将流量导入到 NodePort Service。访问方式: 客户端通过云提供商分配的外部负载均衡器 IP 地址访问服务。用途: 在云环境中将服务暴露给外部世界，提供公网访问和高级负载均衡功能。注意: 非云环境需要额外的组件来实现 LoadBalancer 功能 (如 MetalLB)。ExternalName:特点: 将 Service 映射到外部的 DNS 名称。访问方式: 客户端通过 Service 的名称访问，Kubernetes DNS 会将其解析到外部 DNS 名称。不会创建 ClusterIP 或 NodePort。用途: 方便地将集群内部的服务调用重定向到外部服务，而无需修改应用程序代码。会话亲和性会话亲和性是指将来自同一个客户端的请求始终转发到同一个后端 Pod 的能力。配置 Service 的会话亲和性：您可以在 Service 的 .spec 中配置 sessionAffinity 字段来启用会话亲和性。sessionAffinity (string): 可选字段。指定 Service 的会话亲和性类型。目前支持两种类型：None (默认值): 不启用会话亲和性，请求会根据 Service 的调度算法（如轮询、最少连接等）在后端 Pod 之间随机或均匀分发。ClientIP: 基于客户端的 IP 地址实现会话亲和性。来自同一个客户端 IP 地址的所有请求都会被转发到同一个后端 Pod。apiVersion: v1 kind: Service metadata: name: nginx namespace: nginx spec: selector: app: nginx type: NodePort ports: - port: 80 name: http targetPort: 80 nodePort: 30080 - port: 443 name: https targetPort: 443 nodePort: 30443 sessionAffinity: ClientIP #会话亲和性 sessionAffinityConfig: clientIP: timeoutSeconds: 3600 # 1 小时Endpoints Endpoints 是一组实际服务的端点集合。一个 Endpoint 是一个可访问的服务端点，即一个状态为 running 的 pod 的可访问访问端点。一般 Pod 都不是一个独立存在，所以一组 Pod 的端点合在一起称为 EndPoints。只有被 Service Selector 匹配选中并且状态为 Running 的才会被加入到和 Service 同名的 Endpoints 中。apiVersion: v1 kind: Endpoints metadata: name: nginx-service # 与 Service 同名 namespace: default # 与 Service 同命名空间 subsets: - addresses: # 这是 Service Controller 自动填充的 Pod IP 地址列表 - hostname: nginx-deployment-xxxxxxxxxx-yyyyy # Pod 的主机名 (可选) ip: 10.244.0.10 # 第一个 Nginx Pod 的实际 IP 地址 nodeName: <node-name-1> # Pod 所在的节点 (可选) - hostname: nginx-deployment-xxxxxxxxxx-zzzzz # Pod 的主机名 (可选) ip: 10.244.0.11 # 第二个 Nginx Pod 的实际 IP 地址 nodeName: <node-name-2> # Pod 所在的节点 (可选) ports: # 这是 Service 的 targetPort - port: 80 # 对应 Service 定义的 targetPort: 80 protocol: TCPpublishNotReadyAddresses认情况下，Service 的 Endpoints 中只包含那些处于 Ready 状态的 Pod 的地址。Ready 状态通常是由 Pod 的 readiness probe 决定的。这意味着如果一个 Pod 正在启动中、正在执行启动前的初始化任务、或者 readiness probe 失败，它就不会被认为是可用的端点，Service 也不会将流量转发给它。publishNotReadyAddresses 参数是在 Service 的 .spec 中配置的，与 selector、ports 等字段处于同一级别。
Kubernetes 控制器详解在 Kubernetes（K8s）中，控制器（Controller）是实现集群自动化管理的核心组件。它们通过持续监控集群的实际状态，并将其与期望状态进行对比，从而确保集群资源的正确配置和稳定运行。以下是对 Kubernetes 控制器的详细解释：1. 控制器的核心概念期望状态与实际状态：用户通过声明式 API 定义资源的“期望状态”。控制器负责监控资源的“实际状态”。当两者不一致时，控制器会执行操作，使实际状态向期望状态靠拢，这个过程称为“调谐”（Reconcile）。控制循环：控制器通过控制循环（Control Loop）不断地执行以下步骤：观察：监控集群中资源的状态变化。比较：将实际状态与期望状态进行比较。调整：如果两者不一致，则执行必要的操作进行调整。2. 控制器的主要功能维护期望状态： 确保集群中资源的状态与用户定义的期望状态一致。自动化管理： 自动化执行资源的创建、更新、删除等操作，减少人工干预。故障恢复： 在资源发生故障时，自动进行恢复操作，保证集群的稳定性和可用性。弹性伸缩： 根据负载情况，自动调整资源的副本数量，实现弹性伸缩。3. 常见的 Kubernetes 控制器ReplicationController（复制控制器）/ReplicaSet（副本集）:确保指定数量的 Pod 副本始终运行。当 Pod 发生故障或节点失效时，自动创建新的 Pod 副本。ReplicaSet对比ReplicationController支持匹配表达式：selector.matchExpressions rs 在标签选择器上，除了可以定义键值对的选择形式，还支持 matchExpressions 字段，可以提供多种选择。 目前支持的操作包括： In：label 的值在某个列表中 NotIn：label 的值不在某个列表中 Exists：某个 label 存在 DoesNotExist：某个 label 不存在Deployment（部署控制器）:管理 ReplicaSet 的创建和更新。支持滚动更新和回滚等功能，实现应用程序的平滑升级。StatefulSet（有状态副本集）:管理有状态应用程序的 Pod 副本。为每个 Pod 分配唯一的标识符和稳定的网络标识。DaemonSet（守护进程集）:确保每个节点上都运行一个 Pod 副本。常用于部署日志收集、监控等守护进程。Job/CronJob:Job：创建一次性运行的Pod。CronJob：基于时间的Job，用来做定时任务。Node Controller（节点控制器）:负责监控节点的状态，在节点不可用时执行相应的操作，例如将Pod 标记为不可调度、在节点无法通信时驱逐Pod。ServiceController（服务控制器）:负责监听service和对应的pod副本的变化，如果检测到service被删除，则删除和该service同名的endpoints对象。 如果检测到新的service被创建或者修改，则根据该service的信息获取到相关的pod列表，然后创建或者更新service对应的endpoints对象。IngressController（入口控制器）:管理Ingress 资源，提供外部访问到集群内部服务的方式，通过HTTP/HTTPS 路由流量。4. 控制器管理器（Controller Manager）Kubernetes 控制器管理器（kube-controller-manager）是运行 Kubernetes 控制器的组件。它将多个控制器组合在一起，作为一个进程运行，减少了资源消耗。总结Kubernetes 控制器是实现集群自动化管理的关键组件。它们通过持续监控和调整资源状态，确保集群的稳定性和可靠性。理解控制器的原理和功能，有助于更好地管理和运维 Kubernetes 集群。
initinit容器与普通的容器非常像，除了如下两点：init容器总是运行到成功完成为止每个init容器都必须在下一个init容器启动之前成功完成如果 Pod 的 Init 容器失败Kubernetes 会不断地重启该Pod，直到 Init 容器成功为止。然而，如果Pod对应的restartpolicy 为 Never，它不会重新启动。（restartpolicy默认Always）init容器特点及特点：Init容器与应用容器具备不同的镜像，可以把一些危险的工具放置在init容器中，进行使用。init容器多个之间是线性启动的，所以可以做一些延迟性的操作。init容器无法定义readinessProbe，其它以外同应用容器定义无异。Kubernetes 探针类型Kubernetes 支持三种类型的探针（Probes）来检查容器的健康状态和启动情况，分别是 readinessProbe、livenessProbe 和 startupProbe。每种探针有不同的用途和配置选项，下面是对这些探针类型的详细说明：1. Readiness Probe（就绪探针）用途：检查容器是否已经准备好处理流量。当容器处于启动阶段或临时不可用时，它可能还不能服务请求。就绪探针帮助 Kubernetes 确定容器是否可以接受流量。配置方式：httpGet：容器通过 HTTP 请求检查一个 URL 是否返回成功的状态（如 200）。exec：通过执行一个命令，如果命令返回 0，则认为容器就绪。tcpSocket：容器检查某个端口是否能通过 TCP 连接。常见配置项：initialDelaySeconds：容器启动后，探针第一次执行的延迟时间，。periodSeconds：探针执行的频率（单位：秒），默认是 10 秒。failureThreshold：连续失败多少次后认为容器不可用， 默认值为3。successThreshold：连续成功多少次后认为容器变为就绪状态，默认值为1。示例：readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3或者使用 exec 命令：readinessProbe: exec: command: ["test","-e","/usr/share/nginx/html/index0.html"] initialDelaySeconds: 1 periodSeconds: 10 failureThreshold: 202. Liveness Probe（存活探针）用途：检查容器是否仍然存活。如果存活探针失败，Kubernetes 会重启该容器。通常用来检查容器是否卡住或死锁。示例：livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 15 periodSeconds: 20 failureThreshold: 33. Startup Probe（启动探针）用途：用于检查容器是否已经成功启动。通常适用于启动时间较长的应用或容器。如果启动探针失败，Kubernetes 会认为容器启动失败并重新启动容器。与 readinessProbe 和 livenessProbe 不同，startupProbe 在容器启动期间生效，容器启动后会停止使用。示例：startupProbe: httpGet: path: /startup port: 8080 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 30探针总结探针类型用途适用场景readinessProbe判断容器是否准备好接收流量容器启动后，服务准备好接收请求时livenessProbe判断容器是否存活，如挂起、死锁等容器问题时进行重启容器可能进入死锁状态，需重启startupProbe判断容器是否成功启动，启动过程中检测，失败时重启容器容器启动时间较长，常用于长时间初始化的容器生命周期钩子（Lifecycle Hooks）在 Kubernetes 中，容器的生命周期管理可以通过 生命周期钩子（Lifecycle Hooks） 来控制，主要包括 启动钩子（postStart） 和 结束钩子（preStop）。这些钩子可以在容器的启动和结束过程中执行特定的命令或操作，以确保容器在这两个关键时刻完成某些任务。1. 启动钩子 postStart用途：postStart 钩子在容器启动后立即执行。它是在容器启动并运行容器内的主进程之前调用的。你可以在这个阶段执行一些额外的操作，比如初始化任务、通知服务、进行预处理等。触发时机：在容器启动后、主进程开始执行之前。常见应用：容器启动时创建必要的文件、目录或网络设置。启动时通知其他服务或进行资源初始化。在容器启动时，执行一些健康检查、数据迁移等操作。配置方式：postStart 是 lifecycle 的一部分，它的执行通常是基于 命令执行，可以使用 exec 或 httpGet 的方式。示例：apiVersion: v1 kind: Pod metadata: name: poststart-example spec: containers: - name: my-container image: nginx:1.14.2 lifecycle: postStart: exec: command: ["/bin/sh", "-c", "echo 'Container Started' > /tmp/start.txt"]用途：preStop 钩子在容器终止之前执行，用于在容器退出前执行一些清理操作或确保容器退出时能够优雅地停机。它在容器的主进程开始终止之前被调用，通常用于处理资源释放、通知外部服务等任务。触发时机：在容器终止前，即主进程被杀死之前。此时可以进行一些清理操作，确保容器以优雅的方式关闭。常见应用：在容器退出前清理临时文件、关闭网络连接。在容器关闭前，通知其他服务停止相关任务。提供优雅的停机过程，确保不会丢失数据或中断服务。配置方式：2. 结束钩子 preStoppreStop 钩子同样是 lifecycle 的一部分，通常是一个 命令执行，可以使用 exec 或 httpGet。示例：apiVersion: v1 kind: Pod metadata: name: prestop-example spec: containers: - name: my-container image: nginx:1.14.2 lifecycle: preStop: exec: command: ["/bin/sh", "-c", "echo 'Container is stopping' > /tmp/stop.txt"]启动钩子与结束钩子的配置位置钩子通常作为 lifecycle 的一部分配置在容器的规范中。注意事项执行时机：postStart 在容器启动后立即执行，但不会等待它完成；它和主进程并行执行。preStop 在容器关闭前执行，但它的执行时间有限制。如果 preStop 钩子执行时间超过了terminationGracePeriodSeconds，则容器会被强制终止。超时设置：Kubernetes 对钩子的执行有默认的超时时间，postStart 和 preStop 钩子都有 timeoutSeconds 配置项，默认值为 1 秒，表示钩子执行时如果超过此时间，则会被强制中止。配置超时的示例：lifecycle: postStart: exec: command: ["/bin/sh", "-c", "echo 'Container Started'"] timeoutSeconds: 5 # 配置超时为5秒 preStop: exec: command: ["/bin/sh", "-c", "echo 'Container is stopping'"] timeoutSeconds: 5 # 配置超时为5秒钩子失败处理：如果 postStart 或 preStop 钩子失败，容器仍然会继续执行或终止。postStart 失败时不会阻止容器启动，容器会继续运行。preStop 失败时不会阻止容器终止，但是它的退出会被记录。总结钩子类型触发时机用途配置方式postStart容器启动后、主进程开始执行之前在容器启动时执行初始化操作或启动任务exec 或 httpGetpreStop容器终止前、主进程终止之前在容器退出前执行清理操作或优雅停机exec 或 httpGet启动钩子和结束钩子提供了在容器生命周期的关键时刻执行额外操作的能力，能够帮助容器优雅地启动和关闭，减少数据丢失或不一致的情况。
一、kubectl1. 查看所有padkubectl get pod-A,--all-namespaces 查看当前所有名称空间的资源-n 指定名称空间，默认值 default，kube-system 空间存放是当前组件资源--show-labels 查看当前的标签-l 筛选资源，key、key=value-o wide 详细信息包括 IP、分配的节点-w 监视，打印当前的资源对象的变化部分2. 查看指定命名空间podkubectl get pod -n namespace3. 查看指定命名空间pod及明细kubectl get pod -o wide -n namespace4. 进而容器内部docker exec -it nginx /bin/bashkubectl exec -it nginx-0 -c nginx -n nginx -- /bin/bash-it以交互终端模式打开pad-c指定容器名-n指定命名空间--执行的命令5. 通过标签查找podkubectl get pod -l app=nginx -n nginx6. 查看pod容器日志kubectl logs nginx-0 -n nginx -c nginx 7. 查看pod详细描述kubectl describe pod podName8. 查看pod标签kubectl get pod --show-labels9. 查看集群的servicekubectl get service 10. 查看集群的deploymentkubectl get deployment选项说明kubectl get获取 Kubernetes 中的资源对象deployment资源类型，这里是 Deploymenttest-deployment具体的 Deployment 名称-n test指定命名空间为 test，也可以写成 --namespace=test-o yaml输出格式为 YAML，完整显示 Deployment 的定义，包括系统自动生成的字段11. kubectl create 、apply 、replace*kubectl create：创建资源（如果已存在会失败）kubectl apply：推荐使用，声明式管理资源（存在则更新，不存在则创建）kubectl replace：替换资源整体定义（全量替换）命令用途是否创建是否更新如果资源已存在kubectl create创建新资源✅❌❌ 报错kubectl apply推荐：声明式管理✅✅✅ 合并更新kubectl replace覆盖式更新❌（需存在）✅✅ 覆盖替换12. 快速修改资源使用 kubectl edit（临时改，手动编辑）kubectl edit deployment <名称> -n <命名空间>快速修改镜像kubectl set image deployment/<名称> <容器名>=<新镜像> -n <命名空间>快速修改副本数kubectl scale <资源类型>/<资源名称> --replicas=<数量> [-n 命名空间]13. 自动伸缩deploymentkubectl autoscale deployment ndeployment名称 --min=最少副本数 --max=最大副本数 --cpu-percent=伸缩界限14. 管理资源的滚动更新kubectl rollout history 资源类型/资源名称 查看历史版本kubectl rollout undo 资源类型/资源名称 回滚上一个版本kubectl rollout undo 资源类型/资源名称 --to-revision=版本 回滚指定版本kubectl rollout status 资源类型/资源名称 查看滚动更新状态 有返回码成功返回0 echo $?控制保留版本.spec.revisionHistoryLimit 是一个重要的 Deployment 清理策略配置项，它允许你控制保留的更新历史版本数量，从而管理存储资源并决定是否允许回滚操作。15. 资源补丁kubectl patch 资源类型 资源名称 -p '{}'-p: 表示使用 patch 策略来更新资源。金丝雀部署示例：$ kubectl patch deployment nginx-deployment -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}''{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}}': 这是一个 JSON 格式的 patch 内容，用于更新 Deployment 的 spec.strategy.rollingUpdate 字段。"strategy": {"rollingUpdate": ...}: 指定 Deployment 的更新策略为滚动更新 (RollingUpdate)."rollingUpdate": {"maxSurge": 1, "maxUnavailable": 0}: 配置滚动更新的参数："maxSurge": 1: 表示在更新过程中，可以额外创建一个新的 Pod，使得总 Pod 数量最多比期望的 Pod 数量多一个。"maxUnavailable": 0: 表示在更新过程中，不允许任何旧的 Pod 变得不可用。这意味着在启动新 Pod 之前，不会停止旧 Pod。$ kubectl patch deployment nginx-deployment --patch '{"spec": {"template": {"spec": {"containers": [{"name": "nginx-deployment-container", "image": "wangyanglinux/myapp:v2.0"}]}}}}}' && kubectl rollout pause deploy nginx-deployment这个命令的目的是将 nginx-deployment 中的一部分 Pod 更新到新版本 (wangyanglinux/myapp:v2.0)，并暂停 rollout。由于之前配置了 maxSurge: 1 和 maxUnavailable: 0，Kubernetes 会先启动一个新的 v2.0 Pod，然后暂停，此时集群中会同时存在一部分运行旧版本和一部分运行新版本的 Pod。$ kubectl rollout resume deploy nginx-deployment使用 kubectl rollout resume 命令继续完成剩余 Pod 的更新，逐步将所有流量切换到新版本。命令补全安装 bash-completion apt install bash-completion -y添加到 .bashrc echo "source <(kubectl completion bash)" >> ~/.bashrc加载source ~/.bashrc技巧在k8s的资源清单yaml文件可以通过---编写多个资源清单
方法教程1. 首先去控制面板-终端机和SNMP中启用ssh2. SSH连接群晖这里我使用的是Aechoterm，当然你们也可以用putty之类的工具，具体方法自行搜索吧，站内有很多3. 获取root权限sudo -i4. 可以先来查看一下设备里的全部机械硬盘情况可以看到命名规则是sda、sdb、sdc、sdd....每个sd又分为sd1、sd2和sd3ls /dev/sd*楼主有三块硬盘分别是sda sdc sdd楼主有三块硬盘分别是sda sdc sdd楼主有三块硬盘分别是sda sdc sdd5. 查看这几块硬盘的具体情况例如我的sdc就是这块HGST的盘，可以看到list确实是/dec/sdc1、/dec/sdc2、/dec/sdc3。这里顺便说一下，sdc1和sdc2是群晖的系统分区，sdc3才是我们存储数据的分区，记住它fdisk -l /dev/sd*sdc为目标硬盘sdc为目标硬盘sdc为目标硬盘6. 创建分区只创建目标硬盘执行这一行就可以了：synopartition --part /dev/sdc 12因为我想把sdc和sdd组raid0，因此我还执行了这行：synopartition --part /dev/sdd 12如果出提示，输入y然后回车确认，等待完成7. 查看现有存储池md0和md1不用管，这俩是系统分区。剩下的md2和md3就是你的群晖里已经存在的存储池，新建存储池的话序号就是md4，记住md4这个数字cat /proc/mdstat查看现有的存储池查看现有的存储池查看现有的存储池8. 建立新存储池并格式化如果只需要用sdc创建，直接这两行就可以（注意第一行里的/dev/md4和/dev/sdc3需要分别替换为实际的值）mdadm --create /dev/md4 --level=1 --raid-devices=1 --force /dev/sdc3mkfs.ext4 -F /dev/md4如果有提示就按y并回车，等待完成（第二行格式化命令，如果要格式化为btrfs，就把ext4改成btrfs即可）因为我要把sdc和sdd组raid0，因此输了以下的命令mdadm --create /dev/md4 --level=0 --raid-devices=2 --force /dev/sdc3 /dev/sdd3mkfs.ext4 -F /dev/md49. 重启群晖，一定要重启才生效！10. 进入存储空间查看，应该就可以看到创建好的新存储池了如果是dsm7.0可能需要点击一下“在线重组”，等待完成就好原文章
前言：k8s集群是目前高端运维需要掌握的必备技能之一，工作中你可以不用k8s，但是简历你不能没有k8s；面试造火箭，工作打螺丝；话不多说，直接上操作，这里就不过多阐述k8s的原理和作用了。部署前工作机器设备：MacBook Pro m1pro虚拟系统：Ubuntu 22.04.3 LTSDocker： v24.0.7 - 直接安装最新的即可k8s： v1.26.2 - 根据最近两年的新版安装，旧版可能会存在很多bug虚拟配置：稳妥起见，k8s至少需要2个CPU，4G运行IP CPU 内存192.168.10.91 2 4G192.168.10.91 2 4G192.168.10.91 2 4G配置ssh传输无密码传输，方便文件之间的传输root@k8s-master-90:~# ssh-keygen -t rsa root@k8s-master-91:~# ssh-keygen -t rsa root@k8s-master-92:~# ssh-keygen -t rsa root@k8s-master-90:~# ssh-copy-id 192.168.10.91 root@k8s-master-90:~# ssh-copy-id 192.168.10.92 root@k8s-master-91:~# ssh-copy-id 192.168.10.90 root@k8s-master-91:~# ssh-copy-id 192.168.10.92 root@k8s-master-92:~# ssh-copy-id 192.168.10.90 root@k8s-master-90:~# ssh-copy-id 192.168.10.91系统上的基本配置以下操作，都在每台主机上配置1. 填写hosts域名映射root@k8s-master-90:~# vim /etc/hosts 19.168.10.90 k8s-master-90 19.168.10.91 k8s-work-91 19.168.10.91 k8s-work-922. 关闭防火墙root@k8s-master-90:~# systemctl status ufw.service root@k8s-master-91:~# systemctl stop ufw.service root@k8s-master-92:~# systemctl disable ufw.service3、临时关闭swaproot@k8s-master-90:~# systemctl status swap.target root@k8s-master-91:~# systemctl stop swap.target root@k8s-master-92:~# systemctl disable swap.target // 永久关闭swap root@k8s-master-92:~# vim /etc/default/armbian-zram-config root@k8s-master-92:~# systemctl restart armbian-zram-config.service4. 文件系统的静态信息的文件中/etc/fstab，注释掉swaproot@k8s-master-90:~# vim /etc/fstab #/swap.img none swap sw 0 05、修改内核，开启ipv4转发功能root@k8s-master-90:~# tee /etc/sysctl.d/k8s.conf <<-'EOF' net.ipv4.ip_forward = 1 EOF6、查看是否开启转发功能成功，有输出即可root@k8s-master-90:~# sysctl -p /etc/sysctl.d/k8s.conf 7、安装ipvsadmroot@k8s-master-90:~# apt-get install -y ipvsadm root@k8s-master-90:~# cat > /etc/modules-load.d/ipvs.conf << EOF ip_vs_dh ip_vs_fo ip_vs_ftp ip_vs ip_vs_lblc ip vs_lblcr ip_vs_lc ip_vs_mh ip_vs_nq ip_vs_ovf ip_vs_pe_sip ip_vs_rr ip_vs_sed ip_vs_sh ip_vs_wlc ip_vs_wrr nf_conntrack EOF8、开机自动systemd-modules-load.serviceroot@k8s-master-90:~# systemctl enable --now systemd-modules-load.service9、重启查看是否安装ipvsadm成功root@k8s-master-90:~# lsmod | grep ip_vs root@k8s-master-90:~# reboot root@k8s-master-90:~# lsmod | grep ip_vs注意：以上操作，均需要在每台主机上操作！！部署docker同样的，这也需要在每台机器上安装docker，在这只演示一台主机安装流程1、更新软件包root@k8s-master-90:~# apt-get update2、安装依赖包root@k8s-master-90:~# apt-get install \ ca-certificates \ curl \ gnupg \ lsb-release3、创建目录和docker下载文件root@k8s-master-90:~# mkdir -p /etc/apt/keyrings root@k8s-master-90:~# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg root@k8s-master-90:~# echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 4、安装docker-engineroot@k8s-master-90:~# apt-get update root@k8s-master-90:~# apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin5、查看docker是否安装成功，并设置开机自启root@k8s-master-90:~# docker images root@k8s-master-90:~# systemctl status docker root@k8s-master-90:~# systemctl enable docker root@k8s-master-90:~# systemctl start docker root@k8s-master-90:~# docker images 6、配置docker镜像与cgroupdriver ，配置阿里云加速root@k8s-master-90:~# cat > /etc/docker/daemon.json <<-EOF { "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn", "https://registry.docker-cn.com", "https://registry.docker-cn.com" ], "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2" } EOF7、重置systemd，重启docker读取最新配置root@k8s-master-90:~# systemctl daemon-reload root@k8s-master-90:~# systemctl restart docker root@k8s-master-90:~# docker images安装cri-dockerk8s版本1.24以上就不在支持docker，需要手动安装1、在GitHub下载二进制文件，cri-dockerd-0.3.8.arm64.tgz链接：https://github.com/Mirantis/cri-dockerd/releases2、解压文件root@k8s-master-90:~# tar -xf cri-dockerd-0.3.8.arm64.tgz 3、复制执行文件到/usr/binroot@k8s-master-90:~/cri-dockerd# cp cri-dockerd /usr/bin/4、创建文件cri-docker.socket 和cri-docker.serviceroot@k8s-master-90:/etc/systemd/system# vim cri-docker.service [Unit] Description=CRI Interface for Docker Application Container Engine Documentation=https://docs.mirantis.com After=network-online.target firewalld.service docker.service Wants=network-online.target Requires=cri-docker.socket [Service] Type=notify ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9 ExecReload=/bin/kill -s HUP $MAINPID TimeoutSec=0 RestartSec=2 Restart=always # Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229. # Both the old, and new location are accepted by systemd 229 and up, so using the old location # to make them work for either version of systemd. StartLimitBurst=3 # Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230. # Both the old, and new name are accepted by systemd 230 and up, so using the old name to make # this option work for either version of systemd. StartLimitInterval=60s # Having non-zero Limit*s causes performance problems due to accounting overhead # in the kernel. We recommend using cgroups to do container-local accounting. LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity # Comment TasksMax if your systemd version does not support it. # Only systemd 226 and above support this option. TasksMax=infinity Delegate=yes KillMode=process [Install] WantedBy=multi-user.targetroot@k8s-master-90:/etc/systemd/system# vim cri-docker.socket [Unit] Description=CRI Docker Socket for the API PartOf=cri-docker.service [Socket] ListenStream=%t/cri-dockerd.sock SocketMode=0660 SocketUser=root SocketGroup=docker [Install] WantedBy=sockets.target5、重置systemdroot@k8s-master-90:/etc/systemd/system# systemctl daemon-reload 6、产生systemd文件root@k8s-master-90:/etc/systemd/system# systemctl enable cri-docker Created symlink /etc/systemd/system/multi-user.target.wants/cri-docker.service → /etc/systemd/system/cri-docker.service.7、启动cri-dockerroot@k8s-master-90:/etc/systemd/system# systemctl start cri-docker8、查看是否启动成功root@k8s-master-90:/etc/systemd/system# systemctl status cri-docker安装k8s(kubernetes)1. 安装kubeadm、kubectl、kubelet阿里云教程链接：https://developer.aliyun.com/mirror/kubernetes?spm=a2c6h.13651102.0.0.3fac1b11P65H2N此步骤1，需要在每台机器上安装kubectl 客户端工具kubeadm 管理集群程序kubelet 所有节点需要安装的 代理服务root@k8s-master-90:~# apt-get update && apt-get install -y apt-transport-https root@k8s-master-90:~# curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - root@k8s-master-90:~# cat <<EOF >/etc/apt/sources.list.d/kubernetes.list deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main EOF root@k8s-master-90:~# apt-get update root@k8s-master-90:~# apt-get install kubelet=1.26.2-00 kubeadm=1.26.2-00 kubectl=1.26.2-00 # 可以根据版本进行安装，不指定默认最新版启动kubelet 服务，并允许开机自启动root@k8s-master-90:~# systemctl start kubelet root@k8s-master-90:~#​​​​​​​ systemctl enable kubelet root@k8s-master-90:~# systemctl status kubelet2. 在主节点设置master，初始化集群，此处使用90 当做master根据自己的IP，版本进行修改root@k8s-master-90:~# kubeadm init \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.26.2 --apiserver-advertise-address 192.168.10.90 \ --pod-network-cidr=10.244.0.0/16 \ --cri-socket unix:///var/run/cri-dockerd.sock \3. 初始化完成后，会有要求配置，按照要求教程进行配置以及记住 kubeadm join 的那一条命令，node节点加入集群需要用到root@k8s-master-90:~# mkdir -p $HOME/.kube root@k8s-master-90:~# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config root@k8s-master-90:~# sudo chown $(id -u):$(id -g) $HOME/.kube/config4. root用户操作的话，可以设置一个root 的环境变量root@master-90 ~# export KUBECONFIG=/etc/kubernetes/admin.conf root@master-90 ~# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> /etc/profile5. 初始化后，查看相关容器已经全部启动root@master-90 ~# docker ps | awk '{print $NF}' NAMES k8s_kube-proxy_kube-proxy-hv8j9_kube-system_34a0e9d4-061e-4508-b270-17d43676b6eb_0 k8s_POD_kube-proxy-hv8j9_kube-system_34a0e9d4-061e-4508-b270-17d43676b6eb_0 k8s_kube-apiserver_kube-apiserver-k8s-master-90_kube-system_c50e991b430e64e8ec4726cad2f89510_0 k8s_etcd_etcd-k8s-master-90_kube-system_56b2f7c51f38056b60778da6aedd5aa2_0 k8s_kube-scheduler_kube-scheduler-k8s-master-90_kube-system_8981bc9d0b59c08f641d39fe6e9c0060_0 k8s_kube-controller-manager_kube-controller-manager-k8s-master-90_kube-system_26a10ba4e20f3c9dace25d2a4c9dfe10_0 k8s_POD_kube-apiserver-k8s-master-90_kube-system_c50e991b430e64e8ec4726cad2f89510_0 k8s_POD_etcd-k8s-master-90_kube-system_56b2f7c51f38056b60778da6aedd5aa2_0 k8s_POD_kube-controller-manager-k8s-master-90_kube-system_26a10ba4e20f3c9dace25d2a4c9dfe10_0 k8s_POD_kube-scheduler-k8s-master-90_kube-system_8981bc9d0b59c08f641d39fe6e9c0060_06. 默认没有tab键没有命令补齐，配置命令补齐root@master-90 ~# kubectl completion bash > /etc/bash_completion.d/kubelet root@master-90 ~# kubeadm completion bash > /etc/bash_completion.d/kubeadm root@master-90 ~# exit #重新登录，启动bash生效7. 在master初始化后提供的命令，直接复制粘贴在node节点上运行，加入主集群root@master-90 ~# kubeadm join 192.168.10.90:6443 --token r48gje.bbg8fns58hrkg2zz \ --discovery-token-ca-cert-hash sha256:06a3255c64901c6ef61d1516dea897542b8f5a54441bd87ccd717c16038308d2 --cri-socket unix:///var/run/cri-dockerd.sock8. 在主节点查看是否加入成功，此时 NotReady 是正常的root@k8s-master-90:~# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master-90 NotReady control-plane 10m v1.26.2 k8s-work-91 NotReady <none> 4m33s v1.26.2 k8s-work-92 NotReady <none> 3m55s v1.26.2部署网络flannel部署流程：1. 下载文件：flannel-v0.24.0-linux-arm64.tar.gz下载地址：https://github.com/flannel-io/flannel/releases/2. 下载kube-flannel.yml文件root@k8s-master-90:~# mkdir -p /root/flannel root@k8s-master-90:~/flannel# wget https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml3、查看要下载的镜像root@k8s-master-90:~# grep image kube-flannel.yml image: docker.io/flannel/flannel:v0.24.0 image: docker.io/flannel/flannel-cni-plugin:v1.2.0 image: docker.io/flannel/flannel:v0.24.04. 预先下载镜像root@k8s-master-90:~# docker pull docker.io/flannel/flannel:v0.24.0 root@k8s-master-90:~# docker pull docker.io/flannel/flannel-cni-plugin:v1.2.0 root@k8s-master-90:~# docker pull docker.io/flannel/flannel:v0.24.05. node节点上也需要下载镜像root@k8s-work-91:~# docker pull docker.io/flannel/flannel:v0.24.0 root@k8s-work-91:~# docker pull docker.io/flannel/flannel-cni-plugin:v1.2.0 root@k8s-work-91:~# docker pull docker.io/flannel/flannel:v0.24.0 root@k8s-work-92:~# docker pull docker.io/flannel/flannel:v0.24.0 root@k8s-work-92:~# docker pull docker.io/flannel/flannel-cni-plugin:v1.2.0 root@k8s-work-92:~# docker pull docker.io/flannel/flannel:v0.24.06. 应用yaml文件root@k8s-master-90:~/flannel# kubectl apply -f kube-flannel.yml7. 验证flannel应用是否成功root@k8s-master-90:~/flannel# kubectl get pods -n kube-flannel NAME READY STATUS RESTARTS AGE kube-flannel-ds-7x7rg 1/1 Running 0 39s kube-flannel-ds-dwmtg 1/1 Running 0 39s kube-flannel-ds-pqvdf 1/1 Running 0 39s9. 查看节点是否OK，Ready说明可以连接成功root@k8s-master-90:~/flannel# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master-90 Ready control-plane 176m v1.26.2 k8s-work-91 Ready <none> 170m v1.26.2 k8s-work-92 Ready <none> 169m v1.26.2 root@k8s-master-90:~/flannel# 有一点需要注意：因为机器是arm架构的，关于arm的软件包有的非常少，目前还没有找到部署k8s可视化kuboard管理的软件包，官网也没有相关arm包，如果诸位前辈有部署的软件包，如果可以分享的话，十分感激！！到此，k8s集群部署完成，后续还会支持更新！！！Docker实现CICD的流程k8s实现CICD的流程原文链接：https://blog.csdn.net/2301_76868495/article/details/140591572
本文将介绍使用官方源和第三方源在 Debian 12 和 Ubuntu 22.04 安装最新版 Apache 2 + PHP + MySQL 的教程，并且可以自行选择 PHP 版本。PS：本文同时适用于 Debian 11 Bullseye，Debian 10 Buster，Ubuntu 20.04 Focal 以及 Ubuntu 22.04 Jammy以下操作需要在 root 用户下完成，请使用 sudo -i 或 su root 切换到 root 用户进行操作。[](#1、更新系统并安装部分必要软件)1、更新系统并安装部分必要软件apt update apt upgrade -y apt dist-upgrade -y apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates如果您通过 iso 方式安装 Debian 11 并且设置了 root 密码，则默认不带 sudo 包，使用 apt install sudo 安装即可[](#2、增加-ondrej-sury-大神打包的-php-源并安装-php-8-x)2、增加 Ondřej Surý 大神打包的 PHP 源并安装 PHP 8.x和 LEMP 安装方法一样，我们还是使用 Ondřej Surý 大佬打包的 PHP 源。至于为啥先装 PHP 再装 Apache 2，因为装了 PHP 以后 Apache 2 会识别你 PHP 版本然后生成对应的配置文件？[](#2-1-debian-和-ubuntu-安装-lamp-区别)2.1 Debian 和 Ubuntu 安装 LAMP 区别唯一区别就是 PHP 和 Apache 2 的安装添加源方法不一样，其他的步骤都一毛一样。[](#2-2-加入大神做好的源)2.2 加入大神做好的源wget -O /usr/share/keyrings/php.gpg https://packages.sury.org/php/apt.gpg echo "deb [signed-by=/usr/share/keyrings/php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.listadd-apt-repository ppa:ondrej/php[](#2-3-更新系统源)2.3 更新系统源apt update apt upgrade -y[](#2-4-安装自己需要的-php-版本)2.4 安装自己需要的 PHP 版本这个源目前默认的 PHP 是 8.3.x，如果您需要其他版本，那么请修改对应的 PHP 版本号 (注意配置文件哦)。这里举例 WordPress 需要的部分 PHP 包安装 PHP 8.3.x：apt install php8.3-{fpm,cli,mysql,curl,gd,mbstring,xml,zip,imap,opcache,soap,gmp,bcmath} -y安装 PHP 8.2.x：apt install php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-imap php8.2-opcache php8.2-soap php8.2-gmp php8.2-bcmath -y安装 PHP 8.1.xapt install php8.1-fpm php8.1-cli php8.1-mysql php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-zip php8.1-imap php8.1-opcache php8.1-soap php8.1-gmp php8.1-bcmath -y以下版本 PHP 已经 EOL，PHP 官方不再提供支持，请尽快更新您的程序兼容最新的 PHP，如果您的程序还未兼容，建议鞭策开发者安装 PHP 8.0.xapt install php8.0-fpm php8.0-cli php8.0-mysql php8.0-curl php8.0-gd php8.0-mbstring php8.0-xml php8.0-zip php8.0-imap php8.0-opcache php8.0-soap php8.0-gmp php8.0-bcmath -y安装 PHP 7.4.xapt install php7.4-fpm php7.4-cli php7.4-mysql php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-xmlrpc php7.4-zip php7.4-json php7.4-imap php7.4-opcache php7.4-soap php7.4-gmp php7.4-bcmath -y安装 PHP 7.3.xapt install php7.3-fpm php7.3-mysql php7.3-curl php7.3-gd php7.3-mbstring php7.3-xml php7.3-xmlrpc php7.3-zip php7.3-opcache安装 PHP 7.2.x (PHP 7.2 开始已经不支持 mcrypt 组件)apt install php7.2-fpm php7.2-mysql php7.2-curl php7.2-gd php7.2-mbstring php7.2-xml php7.2-xmlrpc php7.2-zip php7.2-opcache安装 PHP 7.1.xapt install php7.1-fpm php7.1-mysql php7.1-curl php7.1-gd php7.1-mbstring php7.1-mcrypt php7.1-xml php7.1-xmlrpc php7.1-zip php7.1-opcache安装 PHP 7.0.xapt php7.0-fpm php7.0-mysql php7.0-curl php7.0-gd php7.0-mbstring php7.0-mcrypt php7.0-xml php7.0-xmlrpc php7.0-zip php7.0-opcache安装 PHP 5.6.xapt install php5.6-fpm php5.6-mysql php5.6-curl php5.6-gd php5.6-mbstring php5.6-mcrypt php5.6-xml php5.6-xmlrpc php5.6-zip php5.6-opcache如果希望安装其他组件，可以通过搜索看看有没有对应的包apt-cache search php8.3* | grep php修改 php.ini 防止跨目录攻击，如果安装的 PHP 8.2.x 请相应修改 /etc/php/8.3/fpm/php.ini PHP 7.4.x 请相应修改 /etc/php/7.4/fpm/php.inised -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/' /etc/php/8.3/fpm/php.ini 修改 php.ini 增加上传大小限制sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 10M/' /etc/php/8.3/fpm/php.ini sed -i 's/post_max_size = 8M/post_max_size = 10M/' /etc/php/8.3/fpm/php.ini您也可以同时安装多个 PHP 版本，然后使用以下命令选择系统默认的 PHP 版本：update-alternatives --config php[](#3、增加-ondrej-sury-大神打包的-apache-2-源并安装)3、增加 Ondřej Surý 大神打包的 Apache 2 源并安装这里我们推荐 Ondřej Surý 打包的 Apache 2 源。[](#3-1-debian-和-ubuntu-安装-lamp-区别)3.1 Debian 和 Ubuntu 安装 LAMP 区别唯一区别就是 Apache 2 和 PHP 的安装添加源方法不一样，其他的步骤都一毛一样。[](#3-2-首先增加-apache-源)3.2 首先增加 Apache 源wget -O /usr/share/keyrings/apache2.gpg https://packages.sury.org/apache2/apt.gpg echo "deb [signed-by=/usr/share/keyrings/apache2.gpg] https://packages.sury.org/apache2/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/apache2.listadd-apt-repository ppa:ondrej/apache2[](#3-3-接着更新并接安装-apache-2-4)3.3 接着更新并接安装 Apache 2.4apt update apt upgrade -y apt install apache2 -y安装完毕后，我们可以使用 apache2 -v 命令看到 Apache 2 已经是最新的 2.4 版本了root@debian ~ # apache2 -v Server version: Apache/2.4.54 (Debian) Server built: 2022-06-08T16:00:36[](#3-4-apache-2-开启-php-fpm-支持)3.4 Apache 2 开启 PHP-FPM 支持首先，需要开启 Apache 2 的 PHP-FPM 支持，我们以 PHP 8.3 为例，按照自己的需求，可以开启如下模块a2enconf php8.3-fpm a2enmod proxy_fcgi a2enmod headers a2enmod http2 a2enmod remoteip a2enmod ssl a2enmod rewrite a2enmod expires a2enmod deflate a2enmod mime a2enmod setenvif然后我们重启 PHP-FPM 服务systemctl restart php8.3-fpm对应 PHP 7.4.x 命令如下systemctl restart php7.4-fpmApache 2 参考配置文件如下，因为默认 Debian 的 Apache 2 默认配置已经使用了 example.com 这个域名，所以我们以 example.org 为例，新建立个 /etc/apache2/sites-available/example.org.confcat >> /etc/apache2/sites-available/example.org.conf << EOF <VirtualHost *:80> ServerName example.org DocumentRoot /var/www/example.org DirectoryIndex index.php index.html index.htm ErrorLog ${APACHE_LOG_DIR}/example.org.error.log CustomLog ${APACHE_LOG_DIR}/example.org.access.log combined <Directory /var/www/example.org> Options FollowSymLinks AllowOverride All Require all granted </Directory> </VirtualHost> EOF然后使用 a2ensite 命令使其生效a2ensite example.org.conf如果不需要这个 vhost 的时候可以使用 a2dissite example.org.conf 命令移除检测是否已经软链接到 /etc/apache2/sites-enabled 目录root@debian ~ # ls /etc/apache2/sites-enabled 000-default.conf example.org.conf到这里基本没有问题，我们可以执行 apache2ctl configtest 命令检查配置文件是否出错root@debian ~ # apache2ctl configtest Syntax OK显示 Syntax OK 则说明所有配置文件均无问题，可以重启 Apache 2 使我们的配置生效systemctl restart apache2我们的目录在 /var/www/example.org，我们先创建这个目录mkdir -p /var/www/example.org然后创建一个 phpinfo.php 并输入 phpinfo() 函数cat >> /var/www/example.org/phpinfo.php << EOF <?php phpinfo(); ?> EOF好了，此时在浏览器输入 http://example.org/phpinfo.php，如果看到经典的 phpinfo 页面则说明安装成功，如果不成功，请仔细对比步骤查找哪里出错或在烧饼博客下方留言。[](#4、安装-mariadb)4、安装 MariaDB这里我们使用 MariaDB 作为 MySQL 的代替[](#4-1-首先，添加并导入-mariadb-的官方源)4.1 首先，添加并导入 Mariadb 的官方源按照官方的教程导入 GPG下载 GPG Key：curl -sSL https://mariadb.org/mariadb_release_signing_key.asc | gpg --dearmor > /usr/share/keyrings/mariadb.gpg然后添加 MariaDB 的源echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirror-cdn.xtom.com/mariadb/repo/10.11/debian $(lsb_release -sc) main" > /etc/apt/sources.list.d/mariadb.listecho "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirror-cdn.xtom.com/mariadb/repo/10.11/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/mariadb.list国内可以用清华 TUNA 的源echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/10.6/debian $(lsb_release -sc) main" > /etc/apt/sources.list.d/mariadb.listecho "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/10.6/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/mariadb.list您也可以在这儿找到更多的 MariaDB 源[](#4-2-接着更新一下系统)4.2 接着更新一下系统[](#4-3-然后直接安装最新稳定版-mariadb)4.3 然后直接安装最新稳定版 MariaDBapt install mariadb-server mariadb-client安装完毕后强烈推荐使用 mariadb-secure-installation 命令做一次安全设置[](#4-4-创建数据库并测试)4.4 创建数据库并测试开启数据库之前，您可以使用 pwgen 这个小工具或者随机密码生成器生成一个强大的随机密码，比如 32 位，然后随意挑选一个使用apt install pwgen pwgen 32使用 MySQL root 用户登陆，因为默认使用 Unix domain socket 模式，所以本机不需要 MySQL root 密码即可登录创建数据库 example_databaseCREATE DATABASE example_database DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;创建用户名 example_user 并赋予权限GRANT ALL ON example_database.* TO 'example_user'@'localhost' IDENTIFIED BY '这里改成您要设置的强大的没人能猜出来的随机的密码';刷新 MySQL 权限没问题以后就可以退出了新建立一个 /var/www/example.org/mysql-test.php 文件并测试cat >> /var/www/example.org/mysql-test.php << EOF <?php \$dbname = 'example_database'; //MySQL 数据库名 \$dbuser = 'example_user'; //MySQL 用户名 \$dbpass = '您的强大的没人可以猜出来的密码'; \$dbhost = 'localhost'; //安装在本地就用 localhost \$link = mysqli_connect(\$dbhost, \$dbuser, \$dbpass) or die("Unable to Connect to '\$dbhost'"); mysqli_select_db(\$link, \$dbname) or die("Could not open the db '\$dbname'"); \$test_query = "SHOW TABLES FROM \$dbname"; \$result = mysqli_query(\$link, \$test_query); \$tblCnt = 0; while(\$tbl = mysqli_fetch_array(\$result)) { \$tblCnt++; #echo \$tbl[0]." \n"; } if (!\$tblCnt) { echo "MySQL is working fine. There are no tables."; } else { echo "MySQL is working fine. There are \$tblCnt tables."; } ?> EOF创建完毕后访问 http://example.org/mysql-test.php 如果出现 MySQL is working fine. There are no tables. 则说明 MySQL 工作正常。[](#5、安装-mysql-8-0-可选)5、安装 MySQL 8.0 (可选)如果您必须使用某些 MySQL 8.0 才有的功能，那么可以按照 MySQL 官网的教程安装 MySQL 8.0*注意：*除非您知道您在做什么，否则不要同时安装 MySQL 和 MariaDB[](#5-1-添加-apt-源)5.1 添加 apt 源wget https://repo.mysql.com/mysql-apt-config_0.8.29-1_all.deb dpkg -i mysql-apt-config_0.8.29-1_all.deb国内的机器可以在添加完成后修改为清华 TUNA 源，您可以修改 /etc/apt/sources.list.d/mysql-community.list 文件，替换成如下内容deb https://mirrors.tuna.tsinghua.edu.cn/mysql/apt/debian $(lsb_release -sc) mysql-5.6 mysql-5.7 mysql-8.0 mysql-tools[](#5-2-安装-mysql-8-0)5.2 安装 MySQL 8.0apt update apt install mysql-server -y默认 MySQL 会安装最新的 8.0 版本，如果您需要更低的版本，比如 5.6 或 5.7，可以使用如下命令：dpkg-reconfigure mysql-apt-config您可能需要设置一个强大的 root 密码，接下来的步骤和 MariaDB 基本相同，把 mariadb 命令换成 mysql 命令即可，本文不再赘述。好了，以上就是基本的 Debian 11.x “Bullseye” 安装最新版 LAMP 的教程，如有问题可以随时发评论留言讨论。