前言 个人对 kubesphere 删库闭源这件事很FXXK。 目前部署 kubesphere 的难度更多来源于镜像缺失。 docker.io 上只有4.1.3的相关镜像，部分镜像甚至没有。 现在开放的最新版是 4.1.3,但该版本有点点小bug，还是推荐 4.1.2。 其中一个bug点在，某镜像中调用的命令是 ks-controller-manager,但镜像中实际只有controller-manager，需要手动修改yaml纠正。 前置条件 你得先有个 kubernetes 集群 helm ks-core 文件 kubesphere 相关镜像 helm 部署 这里要指定扩展中心镜像ksExtensionRepository.image.tag，默认镜像里很多扩展都被移除了。 另外可以指定仓库源。 ks-core-1.1.2.tgz 在 github 项目，config/ks-core，将该目录打包为tgz，可指定为该本地tgz文件。 helm upgrade --install \ -n kubesphere-system --create-namespace ks-core https://mirrors.xyh.moe/linux/kubernetes/ks-core-1.1.2.tgz \ --set global.imageRegistry=docker.xyh.moe \ --set extension.imageRegistry=docker.xyh.moe \ --set ksExtensionRepository.image.tag=v1.1.5 \ --debug \ --wait WEB访问 部署成功后会有如下提示 NOTES: Thank you for choosing KubeSphere Helm Chart. Please be patient and wait for several seconds for the KubeSphere deployment to complete. Wait for Deployment Completion Confirm that all KubeSphere components are running by executing the following command: kubectl get pods -n kubesphere-system Access the KubeSphere Console Once the deployment is complete, you can access the KubeSphere console using the following URL: http://xxx.xxx.xxx.xxx:30880 Login to KubeSphere Console Use the following credentials to log in: Account: admin Password: P@88w0rd NOTE: It is highly recommended to change the default password immediately after the first login. For additional information and details, please visit https://kubesphere.io. 部署完成后访问 http://ip:30880 用户名：admin 密码：P@88w0rd 登陆后会要求修改初始密码 扩展 WhizardTelemetry 平台服务 WhizardTelemetry 数据流水线 WhizardTelemetry 监控 OpenSearch 分布式检索与分析引擎 WhizardTelemetry 日志 Metrics Server WhizardTelemetry 通知 WhizardTelemetry 事件 WhizardTelemetry 告警 KubeSphere 服务网格 Grafana for WhizardTelemetry 基本上改下仓库源就能直接部署了，需要注意的是部分容器需要PV,请提前置备，不然会卡住，也不会有明显的提示。 提前置备的话会自动使用，后置置备的话就需要手动分配给PVC。
K3S 部署 文件 k3s # 可执行文件 k3s-airgap-images-amd64.tar # 镜像离线包 install.sh # 安装脚本 mkdir -p /usr/local/bin mkdir -p /var/lib/rancher/k3s/agent/images curl -fsSL https://github.com/k3s-io/k3s/releases/download/v1.30.14%2Bk3s2/k3s-airgap-images-amd64.tar.gz -o /var/lib/rancher/k3s/agent/images/k3s-airgap-images-amd64.tar.gz curl -fsSL https://github.com/k3s-io/k3s/releases/download/v1.30.14%2Bk3s2/k3s -o /usr/local/bin/k3s curl -fsfL https://get.k3s.io -o /usr/local/bin/install.sh chmod +x /usr/local/bin/k3s # 安装 # Server 节点 INSTALL_K3S_SKIP_DOWNLOAD=true ./install.sh # Agent 节点 # K3S_TOKEN cat /var/lib/rancher/k3s/server/node-token INSTALL_K3S_SKIP_DOWNLOAD=true K3S_URL=https://<SERVER_IP>:6443 K3S_TOKEN=<YOUR_TOKEN> ./install.sh # 在线执行 # --write-kubeconfig-mode 644 普通用户可执行 curl -fsSL https://get.k3s.io | \ INSTALL_K3S_SKIP_DOWNLOAD=true \ INSTALL_K3S_EXEC="--node-name master1 --write-kubeconfig-mode 644" \ sh - curl -fsSL https://mirrors.xyh.moe:8888/linux/k3s/install.sh | \ INSTALL_K3S_SKIP_DOWNLOAD=true K3S_URL=https://192.168.8.244:6443 \ INSTALL_K3S_EXEC="--node-name agent2" \ K3S_TOKEN="K103cd36c3188b95b44347317a2957384223e0ba7706828b4b932a32d2d1998ccff::server:cea5c67aaa2f16986e86881ba8be718a" sh - # 检查 systemctl status k3s k3s kubectl get nodes Rancher-Docker name: rancher services: rancher: image: rancher/rancher:latest hostname: rancher restart: unless-stopped privileged: true ports: - 9876:80 - 9877:443 配置代理 /etc/systemd/system/k3s.service.env HTTP_PROXY="http://127.0.0.1:10809" HTTPS_PROXY="http://127.0.0.1:10809" NO_PROXY="localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.example.com"
❄ RuntimeClass | gVisor https://kubernetes.io/zh-cn/docs/concepts/containers/runtime-class/ 该集群用 containerd 作为 CRI 运行时。 containerd 的默认运行时处理程序是 runc。 containerd 已准备好支持额外的运行时处理程序 runsc（gVisor） 使用名为 runsc 的现有运行时处理程序，创建一个名为 untrusted 的 RuntimeClass 更新 namespace server 中的所有 Pod 以在 gVisor 上运行。您可以在 /cks/gVisor/rc.yaml 中找到一个模版清单。 # /cks/gVisor/rc.yaml apiVersion: node.k8s.io/v1 kind: RuntimeClass metadata: 创建 RuntimeClass # gvisorRuntimeClass.yml apiVersion: node.k8s.io/v1 kind: RuntimeClass metadata: name: untrusted handler: runsc kubectl create -f gvisorRuntimeClass.yaml 修改 pod 的 RuntimeClass kubectl edit pod podname -n server ... metadata: namespace: server spec: runtimeClassName: untrusted containers: - name: foo image: janedoe/awesomeapp:v1 ... 验证 kubectl -n client exec podname -- dmesg | grep gVisor ❄ ServiceAccount 知识点文档：https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-service-account 清单文件 /cks/sa/pod1.yaml 中指定的 Pod 由于 ServiceAccount 指定错误而无法调度。 在现有 namespace qa 中创建一个名为 backend-sa 的新 ServiceAccount，确保此 ServiceAccount 不自动挂载 API 凭据。 使用 /cks/sa/pod1.yaml 中的清单文件来创建一个Pod。 最后，清理 namespace qa 中任何未使用的 ServiceAccount。 # /cks/sa/pod1.yaml apiVersion: v1 kind: Pod metadata: name: backend namespace: qa spec: serviceAccountName: backend-sa containers: - image: nginx:1.9 imagePullPolicy: IfNotPresent name: backend apiVersion: v1 kind: ServiceAccount metadata: name: backend-sa namespace: qa automountServiceAccountToken: false #不自动挂载API --- apiVersion: v1 kind: Pod metadata: name: backend namespace: qa spec: serviceAccountName: backend-sa #给pod配置serviceaccount containers: - image: nginx:1.9 imagePullPolicy: IfNotPresent name: backend # 查看所有sa kubectl get sa -n qa # 查看已经被使用的sa kubectl get pod -n qa -o yaml | grep -i serviceaccount # 删除不用的sa kubectl delete sa -n qa test01 ❄ TLS通信配置 知识点文档：https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-apiserver/ 修改 apiserver 通过 TLS 加强 kube-apiserver 安全配置，要求 kube-apiserver 除了 TLS 1.3 及以上的版本可以使用，其他版本都不允许使用。 密码套件（Cipher suite）为 TLS_AES_128_GCM_SHA256 修改 etcd 通过 TLS 加强 ETCD 安全配置，要求 密码套件（Cipher suite）为 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 # /etc/kubernetes/manifests/kube-apiserver.yaml - --tls-cipher-suites=TLS_AES_128_GCM_SHA256 - --tls-min-version=VersionTLS13 # 如果题目要求 TLS 1.2，则就写 VersionTLS12 # /etc/kubernetes/manifests/etcd.yaml - --cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 # 更改配置后将暂时get不到信息 # 等能get到信息即表示正常 kubectl get pod -n kube-system ❄ RBAC 授权 知识点文档：https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/ 一个名为 web-pod 的现有 Pod 已在 namespace db 中运行。 编辑绑定到 Pod 的 ServiceAccount service-account-web 的现有 Role，仅允许对 services 类型的资源执行 get 操作 在 namespace db 中创建一个名为 role-2，并仅允许对 namespaces 类型的资源执行 delete 操作的新 Role。 创建一个名为 role-2-binding 的新RoleBinding，将新创建的 Role 绑定到 Pod 的 ServiceAccount 注意：请勿删除现有的 RoleBindinbg # 查找 web-pod 所绑定的 ServiceAccount kubectl get pod -n db -o yaml | grep -i serviceaccount # 查找对应 role kubectl get rolebindings.rbac.authorization.k8s.io -n db -o yaml | grep -i service-account-web -C 2 修改role对应的权限 kubectl edit role -n db web-role ... rules: - apiGroups: - "" resources: - services verbs: - get ... 在 namespace db 中创建 role-2，并配置对 namespaces 类型进行 update 操作 kubectl create role role-2 --verb=update --resource=namespaces -n db 创建新的 RoleBinding role-2-binding，将 role-2 绑定到 Pod 的 ServiceAccount kubectl create rolebinding role-2-binding \ -n db \ --role=role-2 \ --serviceaccount=db:service-account-web 检查 kubectl describe rolebindings -n db role-2-binding ❄ 禁止匿名访问 重新配置 cluster 的 Kubernetes API 服务以确保只允许经过身份验证和授权的 REST 请求 使用授权模式 Node , RBAC 和准入控制器 NodeRestriction 删除用户 system:anonymous 的 ClusterRoleBinding 来进行清理 注意：所有 kubectl 配置环境/文件也被配置为使用未经身份验证和未授权的访问。您不必更改它，但请注意，一旦完成 cluster 的安全加固，kubectl 的配置将无法工作 你可以使用位于 Cluster 的 master 节点上，cluster 原本的 kubectl 配置文 /etc/kubernetes/admin.conf ，以确保经过身份验证和授权的请求任然被允许 更改授权模式和添加 NodeRestriction 准入控制器 # /etc/kubernetes/manifests/kube-apiserver.yaml ... - --authorization-mode=Node,RBAC # 修改这里 - --client-ca-file=/etc/kubernetes/pki/ca.crt - --enable-admission-plugins=NodeRestriction # 修改这里 ... 删除 clusterrolebinding kubectl delete clusterrolebinding ❄ Secret 知识点文档：https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/ 在 namespace istio-system 中获取名为 db1-test 的现有 secret 的内容 将 username 字段存储在名为 /cks/sec/user.txt 的文件中，并将 password 字段存储在名为 /cks/sec/pass.txt 的文件中。 注意：你必须创建以上两个文件，他们还不存在。 注意：不要在以下步骤中使用/修改先前创建的文件，如果需要，可以创建新的临时文件。 在 namespace istio-system 中创建一个名为 db2-test 的新 secret，内容如下： username : production-instance password : KvLftKgs4aVH 最后，创建一个新的 Pod，它可以通过卷访问 secret db2-test ： Pod 名称 secret-pod Namespace istio-system 容器名 dev-container 镜像 nginx 卷名 secret-volume 挂载路径 /etc/secret 查看 namespace istio-system 中的 db1-test 的内容 kubectl get secrets -n istio-system db1-test -o yaml ... password: aGVsbG8= username: ZGIx ... 解密用户名和密码，并放到对应的文件中 # 解密用户名 echo -n 'ZGIx' | base64 -d > /cks/sec/user.txt # 解密密码 echo -n 'aGVsbG8=' | base64 -d > /cks/sec/pass.txt 创建新secret kubectl create secret generic db2-test \ --from-literal=username=production-instance \ --from-literal=password=KvLftKgs4aVH \ -n istio-system 创建pod，使用挂载 # secret-pod.yaml apiVersion: v1 kind: Pod metadata: name: secret-pod namespace: istio-system spec: containers: - image: nginx name: dev-container volumeMounts: - name: secret-volume mountPath: /etc/secret volumes: - name: secret-volume secret: secretName: db2-test kubectl create -f secret-pod.yaml ❄ Dockerfile 检测 分析和编辑给定的 Dockerfile /cks/docker/Dockerfile（基于 ubuntu:16.04 镜像），并修复在文件中拥有的突出的安全/最佳实践问题的两个指令 分析和编辑给定的清单文件 /cks/docker/deployment.yaml ，并修复在文件中拥有突出的安全/最佳实践问题的两个字段 注意： 请勿添加或删除配置设置；只需修改现有的配置设置让以上两个配置设置都不再有安全/最佳实践问题 如果您需要非特权用户来执行任何项目，请使用用户 ID 65535 的用户 nobody 只修改文件即可，不需要创建。 # /cks/docker/Dockerfile FROM ubuntu:latest RUN apt-get install -y wget curl gcc gcc-c++ make openssl-devel pcre-devel gd-devel \ iproute net-tools telnet && \ yum clean all && \ rm -rf /var/cache/apt/* USER root COPY sunnydale.sh . ADD nginx-1.15.5.tar.gz / RUN cd nginx-1.15.5 && \ ./configure --prefix=/usr/local/nginx \ --with-http_ssl_module \ --with-http_stub_status_module && \ make -j 4 && make install && \ mkdir /usr/local/nginx/conf/vhost && \ cd / && rm -rf nginx* && \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime USER root CMD ["./sunnydale.sh"] ENV PATH $PATH:/usr/local/nginx/sbin COPY nginx.conf /usr/local/nginx/conf/nginx.conf WORKDIR /usr/local/nginx EXPOSE 80 CMD ["nginx","-g","daemon off;"] # /cks/docker/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: couchdb namespace: default labels: app: couchdb version: stable spec: replicas: 1 revisionHistoryLimit: 3 selector: matchLabels: app: couchdb version: stable template: metadata: labels: app: couchdb version: stable spec: containers: - name: couchdb image: demo:v1 imagePullPolicy: IfNotPresent livenessProbe: httpGet: path: /healthCheck port: 8080 scheme: HTTP initialDelaySeconds: 30 timeoutSeconds: 5 periodSeconds: 30 successThreshold: 1 failureThreshold: 5 readinessProbe: httpGet: path: /healthCheck port: 8080 scheme: HTTP initialDelaySeconds: 30 timeoutSeconds: 5 periodSeconds: 10 successThreshold: 1 failureThreshold: 5 ports: - name: http containerPort: 8080 protocol: TCP volumeMounts: - name: database-storage mountPath: /var/lib/database securityContext: {'capabilities': {'add': ['NET_BIND_SERVICE'], 'drop': ['all']}, 'privileged': True, 'readOnlyRootFilesystem': False, 'runAsUser': 65535} resources: limits: cpu: 300m memory: 500Mi requests: cpu: 100m memory: 100Mi volumes: - name: database-storage emptyDir: {} Dockerfile 修改 # /cks/docker/Dockerfile FROM ubuntu:16.04 # 镜像版本修改为16.04 ... USER nobody # 用户修改为 nobody ... USER nobody # 用户修改为 nobody ... deployment.yaml 修改 # /cks/docker/deployment.yaml ... securityContext: {'capabilities': {'add': ['NET_BIND_SERVICE'], 'drop': ['all']}, 'privileged': False, 'readOnlyRootFilesystem': True, 'runAsUser': 65535} # 修改 privileged 为 False # 修改 readOnlyRootFilesystem 为 True ... ❄ SecurityContext 知识点文档：https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/security-context/ 按照如下要求修改 namespace sec-ns 里的 Deployment secdep 用 ID 为 30000 的用户启动容器（设置用户 ID 为: 30000） 不允许进程获得超出其父进程的特权（禁止 allowPrivilegeEscalation） 以只读方式加载容器的根文件系统（对根文件的只读权限） kubectl edit deployments.apps -n sec-ns secdep 在 spec.template.containers 加入参数 注意：可能有多个容器 ... spec: ... template: ... spec: containers: - image: nginx name: nginx securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 30000 ... containers: - image: tomcat name: tomcat resources: {} securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 30000 ... ... ❄ 容器安全 删除特权 Pod 查看 namespace production 下所有 pod 是否有特权 Privileged 或者挂载 volume 删除包含 Volumes 或 privileged 的 pod kubectl get pods PODNAME -n production -o yaml | grep -i hostpath kubectl get pods PODNAME -n production -o yaml | grep -i "privi.*: true" kubectl delete pod PODNAME ❄ kube-bench 修复不安全项 针对 kubeadm 创建的 cluster 运行 CIS 基准测试工具时，发现了多个必须立即解决的问题 通过配置修复所有问题并重新启动受影响的组件以确保新的设置生效。 修复针对 API 服务器发现的所有以下违规行为： 1.2.7 Ensure that the --authorization-mode argument is not set to AlwaysAllow FAIL 1.2.8 Ensure that the --authorization-mode argument includes Node FAIL 1.2.9 Ensure that the --authorization-mode argument includes RBAC FAIL 1.2.18 Ensure that the --insecure-bind-address argument is not set FAIL 修复针对 kubelet 发现的所有以下违规行为： Fix all of the following violations that were found against the kubelet: 4.2.1 Ensure that the anonymous-auth argument is set to false FAIL 4.2.2 Ensure that the --authorization-mode argument is not set to AlwaysAllow FAIL 注意：尽可能使用 Webhook 身份验证/授权。 修复针对 etcd 发现的所有以下违规行为： Fix all of the following violations that were found against etcd: 2.2 Ensure that the --client-cert-auth argument is set to true FAIL kube-apiserver # /etc/kubernetes/manifests/kube-apiserver.yaml ... - --authorization-mode=Node,RBAC # 修改这行 ... - --insecure-bind-address=0.0.0.0 # 注释这行 注释！！！！！！！ ... - --insecure-port=0 # 添加 ... kubelet # /var/lib/kubelet/config.yaml ... authentication: anonymous: enabled: false # 设定为 false webhook: ... enabled: true # 设定为 true authorization: mode: Webhook # 设定为 Webhook ... etcd # /etc/kubernetes/manifests/etcd.yaml - --client-cert-auth=true # 设定为 true 重启服务 systemctl restart kubelet systemctl status kubelet ❄ 日志审计 知识点文档：https://kubernetes.io/zh-cn/docs/tasks/debug/debug-cluster/audit/ 在 cluster 中启用审计日志。为此，请启用日志后端，并确保： 日志存储在 /var/log/kubernetes/audit-logs.txt 日志文件能保留 10 天 最多保留2个旧审计日志文件 /etc/kubernetes/logpolicy/sample-policy.yaml 提供了基本策略。它仅指定不记录内容 注意：基本策略位于 cluster 的 master 节点上。 编辑和扩展基本策略以记录： RequestResponse 级别的 persistentvolumes 更改 namespace front-apps 中 configmaps 更改的 请求体 Metadata 级别的所有 namespace 中的 ConfigMap 和 Secret 的更改 此外，添加一个全方位的规则以在 Metadata 级别记录所有其他请求 注意：不要忘记应用修改后的策略 # /etc/kubernetes/logpolicy/sample-policy.yaml apiVersion: audit.k8s.io/v1 kind: Policy omitStages: - "RequestReceived" rules: - level: RequestResponse resources: - group: "" resources: ["persistentvolumes"] - level: Request resources: - group: "" resources: ["configmaps"] namespaces: ["front-apps"] - level: Metadata resources: - group: "" resources: ["secrets", "configmaps"] - level: Metadata omitStages: - "RequestReceived" # /etc/kubernetes/manifests/kube-apiserver.yaml # 添加以下配置 ... spec: containers: - command: - --audit-policy-file=/etc/kubernetes/logpolicy/sample-policy.yaml - --audit-log-path=/var/log/kubernetes/audit-logs.txt - --audit-log-maxage=10 - --audit-log-maxbackup=2 ... # 检查一下是否挂载这两个配置文件，如果有挂载就不用管，如果未挂载则需要手动添加 volumeMounts: - mountPath: /etc/kubernetes/logpolicy/sample-policy.yaml name: audit readOnly: true - mountPath: /var/log/kubernetes name: audit-log readOnly: false ... volumes: - hostPath: path: /etc/kubernetes/logpolicy/sample-policy.yaml type: File name: audit - hostPath: path: /var/log/kubernetes type: DirectoryOrCreate name: audit-log ... 检查/var/log/kubernetes/audit/audit.log日志文件是否有日志产生 tail -f /var/log/kubernetes/audit/audit.log ❄ NetworkPolicy 创建一个名为 pod-restriction 的 NetworkPolicy 限制对在 namespace dev-team 中运行的 Pod products-service 的访问 只允许以下 Pod 连接到 Pod products-service namespace qa 中的 Pod 位于任何 namespace，带有标签 environment: testing 的 Pod 你可以在/cks/net/po.yaml 找到一个模板清单文件 # /cks/net/po.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: # NetworkPolicy.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: pod-restriction namespace: dev-team spec: podSelector: matchLabels: kubernetes.io/metadata.name: dev-team # 指定 namespace policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: qa # 指定 namespace - from: - namespaceSelector: {} # 表示所有 namespace - podSelector: matchLabels: environment: testing ❄ NetworkPolicy DenyAll https://kubernetes.io/zh-cn/docs/concepts/services-networking/network-policies/ 在 namespace testing 中创建一个名为 denypolicy NetworkPolicy，拒绝所有类型为 Ingress + Egress 的流量。 此新的 NetworkPolicy 必须拒绝 namespace testing 中的所有的 Ingress + Egress 流量。 将新创建的 NetworkPolicy 应用在 namespace testing 中运行的所有 Pod 中。 # defaultdeny.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: denypolicy namespace: testing spec: podSelector: {} # 默认匹配本命名空间 policyTypes: - Ingress # 不指定则不允许所有 - Egress # 不指定则不允许所有 ❄ trivy 检测镜像 使用 Trivy 开源容器扫描器检测 namespace kamino 中 Pod 使用的具有严重漏洞的镜像。 查找具有 High 或 Critical 严重性漏洞的镜像，并删除使用这些镜像的 Pod。 注意：Trivy 仅安装在 cluster 的 master 节点上 # 获取 pod image kubectl describe pod -n kamino | grep "Image:" # 或 kubectl get pods -n kamino -o yaml | grep "\- image:" # 或 kubectl get pods -n kamino -o json | grep "\"image\"" # 或 kubectl get pods -n kamino -o custom-columns="NAME:.metadata.name,IMAGE:.spec.containers[*].image" # 检测 trivy image -s HIGH,CRITICAL busybox:1.33.1 trivy image -s HIGH,CRITICAL nginx:1.14.2 #HIGH and CRITICAL trivy image -s HIGH,CRITICAL amazonlinux:2 trivy image -s HIGH,CRITICAL amazonlinux:1 trivy image -s HIGH,CRITICAL centos:7 #HIGH and CRITICAL 删除 kubectl delete pod -n kamino PODNAME ❄ AppArmor 知识点文档：https://kubernetes.io/zh-cn/docs/tutorials/security/apparmor/ APPArmor 已在 cluster 的工作节点 node02 上被启用。一个 APPArmor 配置文件已存在，但尚未被实施。 在 cluster 的工作节点 node02 上，实施位于 /etc/apparmor.d/nginx_apparmor 的现有 APPArmor 配置文件。 编辑位于 /cks/KSSH00401/nginx-deploy.yaml 的现有清单文件以应用 AppArmor 配置文件。 最后，应用清单文件并创建其中指定的 Pod 切换到 apparmor 的目录 cat /etc/apparmor.d/nginx_apparmor # 其中 profile nginx-profile-3 flags=(attach_disconnected) 这一行的第二个单词 nginx-profile-3 就是这个配置文件的名字 执行 apparmor 策略模块 # 加载启用这个配置文件 apparmor_parser /etc/apparmor.d/nginx_apparmor # 检查是否成功 apparmor_status | grep nginx-profile-3 配置 Pod 使用策略 # /cks/KSSH00401/nginx-deploy.yaml apiVersion: v1 kind: Pod metadata: name: hello-apparmor annotations: # 告知 Kubernetes 去应用 AppArmor 配置 "k8s-apparmor-example-deny-write"。 # 请注意，如果节点上运行的 Kubernetes 不是 1.4 或更高版本，此注解将被忽略。 # Note: annotations, container and apparmor profile to be edited # container.apparmor.security.beta.kubernetes.io/<<container name>>: localhost/<<profile name>> container.apparmor.security.beta.kubernetes.io/hello: localhost/nginx-profile-3 spec: containers: - name: hello image: busybox:1.28 command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ] 创建pod kubectl apply -f /cks/KSSH00401/nginx-deploy.yaml ❄ sysdig & Falco 使用运行时检测工具来检测 Pod redis123 单个容器中频发生成和执行的异常进程。 有两种工具可供使用： sysdig falco 注： 这些工具只预装在 cluster 的工作节点 node02 上，不在 master 节点。 使用工具至少分析 30 秒 ，使用过滤器检查生成和执行的进程，将事件写到 /opt/KSR00101/incidents/summary 文件中，其中包含检测的事件， 格式如下： timestamp,uid/username,processName 01:40:19.601363716,root,init 01:40:20.606013716,nobody,bash 01:40:21.137163716,1000,tar 注： 保持工具的原始时间戳格式不变。 注： 确保事件文件存储在集群的工作节点上。 查看容器 container id # containerd crictl ps # docker docker ps sysdig # user.name sysdig -M 45 -p "%evt.time,%user.name,%proc.name" container.id=46183d7281e15 >> /opt/KSR00101/incidents/summary # user.uid sysdig -M 45 -p "%evt.time,%user.uid,%proc.name" container.id=46183d7281e15 >> /opt/KSR00101/incidents/summary Falco # /home/cloud_user/monitor_rules.yaml - rule: rule1 desc: rule1 condition: container.name = "redis123" output: "%evt.time,%user.uid,%proc.name" priority: WARNING falco -M 45 -r /home/cloud_user/monitor_rules.yaml >> /opt/KSR00101/incidents/summary ❄ ImagePolicyWebhook 容器镜像扫描 cluster 上设置了容器镜像扫描器，但尚未完全集成到 cluster 的配置中。 完成后，容器镜像扫描器应扫描并拒绝易受攻击的镜像的使用。 注意：你必须在 cluster 的 master 节点上完成整个考题，所有服务和文件都已被准备好并放置在该节点上。 给定一个目录 /etc/kubernetes/epconfig 中不完整的配置， 以及具有 HTTPS 端点 https://image-bouncer-webhook.default.svc:1323/image_policy 的功能性容器镜像扫描器： 启用必要的插件来创建镜像策略 校验控制配置并将其更改为隐式拒绝（implicit deny） 编辑配置以正确指向提供的 HTTPS 端点 最后，通过尝试部署易受攻击的资源 /cks/img/web1.yaml 来测试配置是否有效。 # /etc/kubernetes/epconfig/admission_configuration.json { "imagePolicy": { "kubeConfigFile": "/etc/kubernetes/epconfig/kubeconfig.yaml", "allowTTL": 50, "denyTTL": 50, "retryBackoff": 500, "defaultAllow": false } } # /etc/kubernetes/epconfig/kubeconfig.yaml ... clusters: - cluster: server: https://image-bouncer-webhook.default.svc:1323/image_policy # 添加 webhook server 地址 name: bouncer_webhook ... # /etc/kubernetes/manifests/kube-apiserver.yaml ... spec: containers: - command: - --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook - --admission-control-config-file=/etc/kubernetes/epconfig/admission_configuration.json ... volumeMounts: - mountPath: /etc/kubernetes/epconfig name: epconfig ... volumes: - name: epconfig hostPath: path: /etc/kubernetes/epconfig 等待 apiserver 自动重启，并恢复 创建易受攻击的资源 /cks/img/web1.yaml 来测试配置是否有效。因为模拟环境里的 image_policy 策略是镜像 tag 是 latest 的不允许创建，则验证成功。 kubectl apply -f /cks/img/web1.yaml ❄ PodSecurityPolicy 创建一个可以阻止创建特权 pod 的名为 prevent-psp-policy 的 PodSecurityPolicy。 创建一个使用 PodSecurityPolicy prevent-psp-policy 的 名为 restrict-access-role 的 ClusterRole。 在 namespace development 创建一个名为 pspdenial-sa 的 serviceAccount。 创建一个 ClusterRole restrict-access-role 与 serviceAccount pspdenial-sa 绑定的名为 dany-access-bind 的 clusterRoleBinding。 确认策略启用 # /etc/kubernetes/manifests/kube-apiserver.yaml - --enable-admission-plugins=NodeRestriction,PodSecurityPolicy # prevent-psp-policy.yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: prevent-psp-policy namespace: development spec: privileged: false runAsUser: rule: "RunAsAny" fsGroup: rule: "RunAsAny" seLinux: rule: "RunAsAny" supplementalGroups: rule: "RunAsAny" # restrict-access-role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: restrict-access-role rules: - apiGroups: - policy resources: - podsecuritypolicies verbs: - use resourceNames: - prevent-psp-policy # pspdenial-sa # kubectl create sa pspdenial-sa -n development apiVersion: v1 kind: ServiceAccount metadata: name: pspdenial-sa namespace: development # dany-access-bind.yaml # kubectl create clusterrolebinding dany-access-bind \ # --clusterrole=restrict-access-role \ # --serviceaccount=development:pspdenial-sa apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: dany-access-bind roleRef: kind: ClusterRole name: restrict-access-role apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: pspdenial-sa namespace: development
☀ 获取信息 列出状态为运行，命名空间为 kube-system 的 pods kubectl get pods --field-selector=status.phase=Running -n kube-system 列出所有 pv 并以 name 字段排序 kubectl get pv --sort-by=.metadata.name 列出所有 pv 并以容量排序，并输出到指定文件 kubectl get pv -A --sort-by={.spec.capacity.storage} > xxx.txt 列出指定 pod 的日志中状态为 Error 的行，并记录在指定的文件上 kubectl logs <podname> | grep Error > xxx.txt 列出 Service 名为 test 下的 pod，并找出使用 CPU 使用率最高的一个，将 pod 名称写入文件中 kubectl get svc test --show-labels kubectl get pod -l app=xxx kubectl top pod # 手动写入文件 列出可用节点，不包含不可调度的 和 NoReachable 的节点，并把数字写入到文件里 kubectl get nodes # 数了手动写入文件 ☀ 创建并调度 pod 1 创建一个 pod，名为 nginx，并将其调度到有 disktype=ssd 标签的节点上 https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-pods-nodes/ apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx nodeSelector: disktype: ssd ☀ 创建并调度 pod 2 创建一个 pod，名为 nginx，并将其调度到名为 foo-node 的节点上 apiVersion: v1 kind: Pod metadata: name: nginx spec: nodeName: foo-node containers: - name: nginx image: nginx ☀ 创建多容器 pod 在命名空间 cka 内创建一个 pod，名为 podx4，内含四个指定的镜像 nginx、``redis、memcached、busybox` apiVersion: v1 kind: Pod metadata: name: podx4 namespace: cka spec: containers: - name: nginx image: nginx - name: redis image: redis - name: memcached image: memcached - name: busybox image: busybox ☀ 创建带存储 pod 创建一个 pod，名为 pod-npv，镜像为 nginx 创建 non-Persistent 类型 Volume，名为 cache-volume，挂载在 /data 目录 apiVersion: v1 kind: Pod metadata: name: pod-npv spec: containers: - name: nginx image: nginx volumeMounts: - name: cache-volume mountPath: /data volumes: - name: cache-volume emptyDir: {} ☀ 创建关联服务 pod 创建一个 Pod，名为 nginx-app，镜像为 nginx，并根据 pod 创建名为 nginx-app 的 Service，type 为 NodePort --- apiVersion: v1 kind: Pod metadata: name: nginx-app labels: run: nginx-app spec: containers: - name: nginx image: nginx --- apiVersion: v1 kind: Service metadata: name: nginx-app spec: selector: run: nginx-app ports: - name: http protocol: TCP port: 80 - name: https protocol: TCP port: 443 type: NodePort ☀ 创建包含 Init 容器的 Pod 创建一个 pod 的 yaml，要求添加 Init Container， Init Container的作用是创建一个空文件 pod 的 Containers 判断文件是否存在，不存在则退出 https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-pod-initialization/#creating-a-pod-that-has-an-init-container apiVersion: v1 kind: Pod metadata: name: pod-with-init labels: app: pod-with-init spec: volumes: - name: workdir emptyDir: {} initContainers: - name: touch image: busybox:1.28 command: ['sh', '-c', 'touch /workdir/emptyfile'] volumeMounts: - name: workdir mountPath: /workdir containers: - name: pwi image: busybox:1.28 command: ['sh', '-c', 'ls /opt/emptyfile || exit 1'] volumeMounts: - name: workdir mountPath: /opt ☀ 创建 DaemonSet pod 创建一个 nginx 的 DaemonSet，保证其在每个节点上运行 apiVersion: apps/v1 kind: DaemonSet metadata: name: nginx-daemonset spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: containers: - name: nginx-daemonset image: nginx ☀ 静态 pod 在节点上配置 kubelet 托管启动一个静态 pod # 查看配置文件地址 systemctl status kubelet | grep -e "--config" # 查看静态pod地址 cat /var/lib/kubelet/config.yaml | grep staticPodPath: apiVersion: v1 kind: Pod metadata: name: static-pod-nginx labels: role: myrole spec: containers: - name: nginx image: nginx ports: - name: web containerPort: 80 protocol: TCP ☀ 创建关联服务 deployment 创建一个 deployment 并暴露 Service 方法一 kubectl create deployment my-deploy --image=nginx kubectl expose deployment my-deploy --port=80 --target-port=80 --type=NodePort 方法二 apiVersion: apps/v1 kind: Deployment metadata: name: my-deploy labels: app: my-deploy spec: replicas: 1 selector: matchLabels: app: my-deploy-tp template: metadata: labels: app: my-deploy-tp spec: containers: - name: nginx image: nginx --- apiVersion: v1 kind: Service metadata: name: my-deploy-svc labels: app: my-deploy-svc spec: selector: app: my-deploy-tp ports: - port: 80 targetPort: 80 protocol: TCP name: http type: NodePort ☀ deployment 弹性伸缩 将名为 nginx-app 的 deployment 的副本数变成 4 kubectl scale deployment nginx-app --replicas=4 ☀ 镜像升级回滚 创建一个 deployment，名为 deploy-rollout，镜像为 nginx:1.11.0-alpine 修改镜像版本为 nginx:1.11.3-alpine，并记录升级 再使用回滚，将镜像回滚至 nginx:1.11.0-alpine # 创建 deployment kubectl create deployment deploy-rollout --image=nginx:1.11.0-alpine # 修改镜像 kubectl set image deployment deploy-rollout nginx=nginx:1.11.3-alipne # [可选] 升级记录添加注释 kubectl annotate deployment [deployment_name] kubernetes.io/change-cause="[notes]" # 回滚 kubectl rollout undo deployment nginx-rollout # 查看历史 kubectl rollout history deployment nginx-rollout ☀ dns 解析 创建一个 pod，名为 nginx-nslookup，镜像为 nginx 创建对应的 svc，名为 nginx-nslookup-svc 并使用 nslookup 查找出 service 和 pod 的 dns 记录，并写入到指定的文件中 # 创建 busybox 用于执行 nslookup kubectl run busybox-nslookup --image=busybox --command -- sleep 3600 # 创建对象 kubectl run nginx-nslookup --image=nginx # 暴露端口 方法一 kubectl expose pod nginx-nslookup --type=NodePort --port=80 --target-port=80 --name=nginx-nslookup-svc # 暴露端口 方法二 kubectl create svc nodeport nginx-nslookup-svc --tcp=80:80 #获取pod的ip地址 kubeclt get pod -o wide # 解析 kubectl exec busybox -- nslookup [ip] kubectl exec busybox -- nslookup [hostname] ☀ Secret 创建一个 secret 包含 username 和 password 创建一个 pod，挂载该 secret 到 /etc/foo 创建另一个 pod，使用环境变量引用该 secret --- apiVersion: v1 kind: Secret metadata: name: x-secret data: username: dWdseQo= password: YmVhdXRpZnVsCg== --- apiVersion: v1 kind: Pod metadata: name: secret-1 labels: run: secret-1 spec: containers: - name: secret-1 image: nginx volumeMounts: - name: secret-volume mountPath: /etc/foo dnsPolicy: ClusterFirst restartPolicy: Always volumes: - name: secret-volume secret: secretName: x-secret --- apiVersion: v1 kind: Pod metadata: name: secret-2 labels: run: secret-2 spec: containers: - name: secret-2 image: nginx env: - name: XXA valueFrom: secretKeyRef: name: x-secret key: username - name: XXB valueFrom: secretKeyRef: name: x-secret key: password dnsPolicy: ClusterFirst restartPolicy: Always # 手动创建 secret 方法 k create secret generic x-secret --from-literal=password=password --from-literal=username=username # 验证 kubectl exec secret-1 -- /bin/sh -c 'cat /etc/foo/username' kubectl exec secret-1 -- /bin/sh -c 'cat /etc/foo/password' kubectl exec secret-2 -- /bin/sh -c 'echo $XXA' kubectl exec secret-2 -- /bin/sh -c 'echo $XXB' ☀ RBAC 创建一个名为 deployment-clusterrole 的 clusterrole 且仅允许创建 Deployment，StatefulSet，DaemonSet 创建名为 cicd-token 的 ServiceAccount 将 ClusterRole 与 ServiceAccount 绑定 # 创建 clusterrole kubectl create clusterrole deployment-clusterrole --verb=create --resource=Deployment,StatefulSet,DaemonSet # 创建 ServiceAccount kubectl create serviceaccount cicd-token # 绑定 kubectl create rolebinding xxx --clusterrole=deployment-clusterrole --serviceaccount=cicd-token:[namespace] ☀ 节点调度 将 ek8s-node-0 节点设置为不可用，并重新调度该节点上所有运行的 pods # 禁止Pod调度到该节点 kubectl cordon ek8s-node-0 # 驱逐节点所有Pod kubectl drain ek8s-node-0 --ignore-daemonsets ☀ 集群升级 现有 kubernetes 集群正在运行的版本是 1.21.0 仅将主节点 k8sm1 上的所有 kubernetes 控制平面和节点组件升级到版本 1.21.1 请不要升级 etcd 在主节点上升级 kubelet 和 kubectl 确保再升级前drain主节点，并在升级后uncordon主节点 # 禁止调度到该节点 kubectl cordon k8sm1 # 驱逐节点pod kubectl drain k8sm1 --ignore-daemonsets # 安装新版 kubeadm yum install kubeadm-1.21.1 # 查看升级计划 kubeadm upgrade plan # 升级 kubeadm upgrade apply v1.21.1 -etcd-upgrade=false # 升级 kubelet kubectl yum install kubelet-1.21.1 kubectl-1.21.1 # 重启 systemctl daemon-reload systemctl restart kubelet kubectl uncordon k8sm1 # 检查版本 kubeadm upgrade plan ☀ 日志监控 监控Pod foobar的日志，并提取错误“unable-to-access-website”对应的日志行。把它们写到/opt/KULM00612/foobar。 kubectl logs foobar | grep "unable-to-access-website" > /opt/KULM00612/foobar ☀ 节点 Debug 给出一个失联节点的集群，排查节点故障，要保证改动是永久的。 多半就是 kubelet 服务未启动 https://kubernetes.io/docs/tasks/debug-application-cluster/debug-cluster/ #查看集群状态 kubectl get nodes #查看故障节点信息 kubectl describe node node1 #Message显示kubelet无法访问（记不清了） #进入故障节点 ssh node1 #查看节点中的kubelet进程 ps -aux | grep kubelete #没找到kubelet进程，查看kubelet服务状态 systemctl status kubelet.service #kubelet服务没启动，启动服务并观察 systemctl start kubelet.service #启动正常，enable服务 systemctl enable kubelet.service #回到考试节点并查看状态 exit kubectl get nodes #正常 ☀ 创建 hostPath PV 创建一个 pv，类型是 hostPath，位于 /data，大小 1Gi，模式为 ReadOnlyMany 参考： https://kubernetes.io/docs/concepts/storage/persistent-volumes/ apiVersion: v1 kind: PersistentVolume metadata: name: pv-host spec: capacity: storage: 1Gi volumeMode: Filesystem accessModes: - ReadOnlyMany persistentVolumeReclaimPolicy: Recycle storageClassName: xxzc hostPath: path: /data ☀ 使用边车容器运行日志代理 https://kubernetes.io/zh-cn/docs/concepts/cluster-administration/logging/#sidecar-container-with-logging-agent apiVersion: v1 kind: Pod metadata: name: counter spec: containers: - name: count image: busybox:1.28 args: - /bin/sh - -c - > i=0; while true; do echo "$i: $(date)" >> /var/log/1.log; echo "$(date) INFO $i" >> /var/log/2.log; i=$((i+1)); sleep 1; done volumeMounts: - name: varlog mountPath: /var/log - name: count-log-1 image: busybox:1.28 args: [/bin/sh, -c, 'tail -n+1 -F /var/log/1.log'] volumeMounts: - name: varlog mountPath: /var/log - name: count-log-2 image: busybox:1.28 args: [/bin/sh, -c, 'tail -n+1 -F /var/log/2.log'] volumeMounts: - name: varlog mountPath: /var/log volumes: - name: varlog emptyDir: {} ☀ xxxxxxxxxxxxxxxxxxxetcd 使用etcd 备份功能备份etcd（提供enpoints，ca、cert、key） ETCD备份恢复 ETCDCTL_API=3 etcdctl --endpoints https://127.0.0.1:2379 \ --cacert=ca.pem --cert=cert.pem --key=key.pem \ snapshot save snapshotdb export ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert="/opt/xxxxxx" 备份etcd yum -y install etcd 查看etcd密钥位置 ls /etc/kubernetes/pki/etcd/ ca.crt ca.key healthcheck-client.crt healthcheck-client.key peer.crt peer.key server.crt server.key 查看etcd的帮助信息 ETCDCTL_API=3 etcdctl -h snapshot save Stores an etcd node backend snapshot to a given file snapshot restore Restores an etcd member snapshot to an etcd directory snapshot status Gets backend snapshot status of a given file --cacert="" verify certificates of TLS-enabled secure servers using this CA bundle --cert="" identify secure client using this TLS certificate file --endpoints=[127.0.0.1:2379] gRPC endpoints --key="" identify secure client using this TLS key file 备份etcd ETCDCTL_API=3 etcdctl --endpoints https://127.0.0.1:2379 snapshot save snapshotdb --cacert="/etc/kubernetes/pki/etcd/ca.crt" --cert="/etc/kubernetes/pki/etcd/server.crt" --key="/etc/kubernetes/pki/etcd/server.key" Snapshot saved at snapshotdb 查看备份的文件 ls snapshotdb 查看文件的状态 ETCDCTL_API=3 etcdctl --endpoints https://127.0.0.1:2379 snapshot status snapshotdb --cacert="/etc/kubernetes/pki/etcd/ca.crt" --cert="/etc/kubernetes/pki/etcd/server.crt" --key="/etc/kubernetes/pki/etcd/server.key" 593dda57, 480925, 1532, 3.3 MB 恢复etcd mv /etc/kubernetes/manifests/ /etc/kubernetes/manifests.bak mv /var/lib/etcd/ /var/lib/etcd.bak ETCDCTL_API=3 etcdctl snapshot restore snapshotdb --data-dir=/var/lib/etcd 2020-12-20 15:30:29.156579 I | mvcc: restore compact to 479751 2020-12-20 15:30:29.176899 I | etcdserver/membership: added member 8e9e05c52164694d [http://localhost:2380] to cluster cdf818194e3a8c32 启动etcd和apiserver mv /etc/kubernetes/manifests.bak/ /etc/kubernetes/manifests xxx创建一个Pod使用PV自动供给 使用hostpath方式提供pv（首先在所有的node节点上面创建/mnt/data目录） sudo mkdir /mnt/data sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/index.html" 创建pv（一般运维人员创建） apiVersion: v1 kind: PersistentVolume metadata: labels: app: my-pv name: my-pv spec: #storageClassName: manual capacity: storage: 5Gi accessModes: - ReadWriteOnce hostPath: path: /mnt/data/ kubectl create -f pv.yaml 创建pvc（一般开发人员创建） apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: type: local name: my-pvc spec: #storageClassName: manual accessModes: - ReadWriteOnce resources: requests: storage: 5Gi kubectl create -f pvc.yaml 使用pvc创建pod apiVersion: v1 kind: Pod metadata: labels: run: my-pod name: my-pod spec: volumes: - name: test-pvc persistentVolumeClaim: claimName: my-pvc containers: - image: nginx name: my-pod volumeMounts: - name: test-pvc mountPath: "/usr/share/nginx/html" kubectl create -f my-pod.yaml 测试 kubectl exec -i -t my-pod -- curl localhost Hello from Kubernetes storage Bootstrap Token方式增加一台Node https://www.cnblogs.com/hlc-123/articles/14163603.html 给出一个集群，将节点node1添加到集群中，并使用TLS bootstrapping 参考：https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/#kube-controller-manager-configuration 创建一个ingress apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tls-example-ingress spec: tls: - hosts: - https-example.foo.com secretName: testsecret-tls rules: - host: https-example.foo.com http: paths: - path: / pathType: Prefix backend: service: name: service1 port: number: 80 kubectl create -f ingress.yaml kubectl get ingress default命名空间下所有pod可以互相访问，也可以访问其他命名空间Pod，但其他命名空间不能访问default命名空间Pod apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress namespace: default spec: podSelector: {} policyTypes: - Ingress ingress: - from: - podSelector: {} kubectl create -f network_policy.yaml 必考的题目 etcd 的备份和还原 network policy 的创建 ingress 的创建
☀ 安装部署 ★☀❄❂✪☁ 系统信息 操作系统：CentOS 7/8/9 x64 kubernetes: v1.23.14 docker-ce: v20.10.7 系统调整 # 停止防火墙 systemctl stop firewalld # 禁用防火墙 systemctl disable firewalld # 停止SELinux setenforce 0 # 禁用SELinux sed -i.bak -e 's|^SELINUX=.*|SELINUX=disabled|' /etc/selinux/config # 卸载swap swapoff -a # 禁用swap sed -i.bak -e 's|^[^#].*swap|#&|' /etc/fstab # 时间同步 (sudo crontab -l;echo "@reboot chronyc -a makestep") | sudo crontab (sudo crontab -l;echo "23 * * * * chronyc -a makestep") | sudo crontab 内核调整 将桥接的IPv4流量传递到iptables的链 cat > /etc/sysctl.d/kubernetes.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl -p /etc/sysctl.d/kubernetes.conf 容器运行时 dockershim 安装Docker-CE # 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加docker-ce官方源 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 修改为docker-ce清华源 sed -i.bak 's|^baseurl=https://download.docker.com|baseurl=https://mirrors.tuna.tsinghua.edu.cn/docker-ce|g' /etc/yum.repos.d/docker-ce.repo # 安装指定版本docker-ce # CentOS 7 yum install -y docker-ce-20.10.7-3.el7.x86_64 # CentOS 9 yum install -y docker-ce-20.10.21-3.el9.x86_64 # 启用docker服务 systemctl enable docker&& systemctl start docker docker 非root运行 如果要使用 Docker 作为非 root 用户，则应考虑使用类似以下方式将用户添加到 docker 组： usermod -aG docker your-user [可选]配置docker镜像仓库源 # docker镜像仓库源 # 配置 kubelet 使用的 cgroup 驱动程序 cat > /etc/docker/daemon.json << EOF { "exec-opts": ["native.cgroupdriver=systemd"] "registry-mirrors": ["https://hub.c.163.com/","https://mirror.baidubce.com/"] } EOF # 启用镜像仓库源配置 systemctl daemon-reload systemctl restart docker cri-docker # 安装 libcgroup https://rpmfind.net/linux/rpm2html/search.php?query=libcgroup(x86-64) https://rpmfind.net/linux/centos/8-stream/BaseOS/x86_64/os/Packages/libcgroup-0.41-19.el8.x86_64.rpm https://rpmfind.net/linux/centos/7.9.2009/os/x86_64/Packages/libcgroup-0.41-21.el7.x86_64.rpm # 安装cri-docker https://github.com/Mirantis/cri-dockerd/releases/tag/v0.3.8 https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.8/cri-dockerd-0.3.8-3.el8.x86_64.rpm https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.8/cri-dockerd-0.3.8-3.el7.x86_64.rpm containerd 安装Kubernetes # 添加Kubernetes清华源 echo "[kubernetes]" > /etc/yum.repos.d/kubernetes.repo echo "name=kubernetes" >> /etc/yum.repos.d/kubernetes.repo echo "baseurl=https://mirrors.cloud.tencent.com/kubernetes/yum/repos/kubernetes-el7-\$basearch" >> /etc/yum.repos.d/kubernetes.repo echo "enabled=1" >> /etc/yum.repos.d/kubernetes.repo echo "gpgcheck=0" >> /etc/yum.repos.d/kubernetes.repo # 安装 kubeadm kubelet kubectl yum install -y kubelet-1.23.14 kubectl-1.23.14 kubeadm-1.23.14 # 启用 kubelet systemctl enable kubelet && systemctl start kubelet 初始化 # Master 主节点初始化 # k8s.gco.io 镜像站国内无法访问，需替换为国内镜像站 kubeadm init \ --apiserver-advertise-address=192.168.8.30 \ --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \ --kubernetes-version v1.23.14 `#版本`\ --service-cidr=10.96.0.0/12 `#服务网段`\ --service-dns-domain=k8s.xxz.moe `#服务域名`\ --pod-network-cidr=10.244.0.0/16 `#Pod网段`\ --node-name=master `#节点名 默认为主机名` USER="$(logname)" USERUID=$(id $USER -u) USERGID=$(id $USER -g) HOME="$(getent passwd $USER 2>/dev/null | cut -d: -f6)" # 继续初始化 mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $USERUID:$USERGID $HOME/.kube/config 网络插件 未加载网络插件节点会处于NotReady状态 重新指定 apiserver-advertise-address kubeadm init phase certs apiserver --apiserver-advertise-address 192.168.8.30 重置初始化 sudo kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock 加入节点 # 从节点加入 kubeadm join 192.168.8.30:6443 \ --token 3bq7fr.orzotbz2b9rcsgkv \ --discovery-token-ca-cert-hash \ sha256:ccff364618768d355efb12cbc979a57ef4dc34607cbf8db5ddbf2aa36bd86adc \ --cri-socket=unix:///var/run/cri-dockerd.sock # 重新生成加入节点 token kubeadm token create --print-join-command etcd # 下载程序 wget https://github.com/etcd-io/etcd/releases/download/v3.5.5/etcd-v3.5.5-linux-amd64.tar.gz # 解压文件 tar -xzvf etcd-v3.5.5-linux-amd64.tar.gz # 复制文件到 /usr/bin cp etcd-v3.5.5-linux-amd64/kubectl /usr/bin/ # 单节点 etcd 集群 etcd --listen-client-urls=http://$PRIVATE_IP:2379 \ --advertise-client-urls=http://$PRIVATE_IP:2379 # 多节点 etcd 集群 etcd --listen-client-urls=\ http://$IP1:2379,\ http://$IP2:2379,\ http://$IP3:2379,\ http://$IP4:2379,\ http://$IP5:2379 \ --advertise-client-urls=\ http://$IP1:2379,\ http://$IP2:2379,\ http://$IP3:2379,\ http://$IP4:2379,\ http://$IP5:2379 # 查看版本 etcdctl version ☀ 常用插件 flannel 项目地址：https://github.com/flannel-io/flannel kubectl # 最新版 kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml # 特定版 kubectl apply -f https://github.com/flannel-io/flannel/releases/download/v0.24.0/kube-flannel.yml helm kubectl create ns kube-flannel kubectl label --overwrite ns kube-flannel pod-security.kubernetes.io/enforce=privileged helm repo add flannel https://flannel-io.github.io/flannel/ helm upgrade flannel flannel/flannel \ --install \ --create-namespace \ --namespace kube-flannel \ --set podCidr="10.244.0.0/16" Metrics Server 项目地址：https://github.com/kubernetes-sigs/metrics-server kubectl # 下载 metrics-server 插件配置文件 #curl -L https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.4/components.yaml \ # -o kubernetes-metrics-server.yaml #curl -L https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.4/high-availability.yaml \ # -o kubernetes-metrics-server.yaml curl -L https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.4/high-availability-1.21+.yaml \ -o kubernetes-metrics-server.yaml # 替换镜像源为国内 registry.cn-hangzhou.aliyuncs.com/google_containers # 不使用证书需添加参数 '- --kubelet-insecure-tls' sed -i \ -e 's|registry.k8s.io/metrics-server|registry.cn-hangzhou.aliyuncs.com/google_containers|g' \ -e 's|k8s.gcr.io/metrics-server|registry.cn-hangzhou.aliyuncs.com/google_containers|g' \ -e 's|- --metric-resolution=15s|- --metric-resolution=15s\n - --kubelet-insecure-tls|g' \ kubernetes-metrics-server.yaml kubectl apply -f kubernetes-metrics-server.yaml helm https://artifacthub.io/packages/helm/metrics-server/metrics-server helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/ helm upgrade metrics-server metrics-server/metrics-server \ --install \ --create-namespace \ --namespace metrics-server \ --set image.repository=registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server \ --set replicas=2 \ --set args="{--kubelet-insecure-tls}" Ingress-NGINX Controller https://github.com/kubernetes/ingress-nginx ingress的api版本历经过多次变化他们的配置项也不太一样分别是： extensions/v1beta1：1.16版本之前使用 networking.k8s.io/v1beta1：1.19版本之前使用 networking.k8s.io/v1：1.19版本之后使用 kubectl curl -L https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.5/deploy/static/provider/cloud/deploy.yaml \ -o kubernetes-ingress-nginx-controller.yaml # -e 's|- /nginx-ingress-controller|- /nginx-ingress-controller\n - --watch-ingress-without-class=true|g' \ sed -i \ -e 's|image: registry.k8s.io/ingress-nginx/controller:|image: registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:|g' \ -e 's|image: registry.k8s.io/ingress-nginx|image: registry.cn-hangzhou.aliyuncs.com/google_containers|g' \ -e 's|@sha256.*||g' \ kubernetes-ingress-nginx-controller.yaml #启用 ingress rewrite kubectl edit -n ingress-nginx configmaps ingress-nginx-controller data: allow-snippet-annotations: "true" helm helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm upgrade ingress-nginx ingress-nginx/ingress-nginx \ --install \ --create-namespace \ --namespace ingress-nginx \ --set controller.kind=DaemonSet \ --set controller.hostPort.enabled=true \ --set controller.service.enabled=false \ --set controller.image.registry=registry.cn-hangzhou.aliyuncs.com \ --set controller.image.image=google_containers/nginx-ingress-controller \ --set controller.image.digest= \ --set controller.opentelemetry.image.registry=registry.cn-hangzhou.aliyuncs.com \ --set controller.opentelemetry.image.image=google_containers/opentelemetry \ --set controller.opentelemetry.image.digest= \ --set controller.admissionWebhooks.patch.image.registry=registry.cn-hangzhou.aliyuncs.com \ --set controller.admissionWebhooks.patch.image.image=google_containers/kube-webhook-certgen \ --set controller.admissionWebhooks.patch.image.digest= \ --set controller.allowSnippetAnnotations=true `#允许重定向` Prometheus # https://artifacthub.io/packages/helm/prometheus-community/prometheus helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm upgrade prometheus prometheus-community/prometheus \ --install \ --create-namespace \ --namespace monitoring \ --set alertmanager.enabled=false \ --set server.persistentVolume.enabled=false \ --set server.image.repository=quay.dockerproxy.com/prometheus/prometheus \ --set configmapReload.prometheus.image.repository=quay.dockerproxy.com/prometheus-operator/prometheus-config-reloader \ --set server.ingress.enabled=true \ --set server.ingress.ingressClassName=nginx \ --set server.ingress.pathType=ImplementationSpecific \ --set server.ingress.path="/prometheus(/|$)(.*)" \ --set server.ingress.hosts\[0\]="iphn-008-155.xxz.moe" \ --set server.ingress.annotations."nginx\.ingress\.kubernetes\.io/rewrite-target"="/\$2" helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install cadvisor ckotzbauer/cadvisor --version 2.3.3 helm upgrade prometheus prometheus-community/prometheus \ --install \ --create-namespace \ --namespace monitoring \ --set alertmanager.enabled=false \ --set server.persistentVolume.enabled=false \ --set server.image.repository=quay.dockerproxy.com/prometheus/prometheus \ --set configmapReload.prometheus.image.repository=quay.dockerproxy.com/prometheus-operator/prometheus-config-reloader \ --set server.ingress.enabled=true \ --set server.ingress.ingressClassName=nginx \ --set server.ingress.pathType=ImplementationSpecific \ --set server.ingress.path="/prometheus(/|$)(.*)" \ --set server.ingress.hosts\[0\]="iphn-008-155.xxz.moe" \ --set server.ingress.annotations."nginx\.ingress\.kubernetes\.io/rewrite-target"="/\$2" KubeSphere helm upgrade \ --install \ -n kubesphere-system \ --create-namespace ks-core https://charts.kubesphere.io/main/ks-core-1.1.2.tgz \ --debug \ --wait \ --set global.imageRegistry=swr.cn-southwest-2.myhuaweicloud.com/ks \ --set extension.imageRegistry=swr.cn-southwest-2.myhuaweicloud.com/ks Grafana https://artifacthub.io/packages/helm/grafana/grafana helm repo add grafana https://grafana.github.io/helm-charts helm upgrade grafana grafana/grafana \ --install \ --create-namespace \ --namespace monitoring \ --set global.imageRegistry=harbor.xxz.moe \ --set ingress.enabled=true \ --set ingress.ingressClassName=nginx \ --set ingress.pathType=ImplementationSpecific \ --set ingress.path="/grafana(/|$)(.*)" \ --set ingress.hosts\[0\]="iphn-008-155.xxz.moe" \ --set ingress.annotations."nginx\.ingress\.kubernetes\.io/rewrite-target"="/\$2" \ --set 'grafana\.ini'.server\.root_url='%(protocol)s://%(domain)s:/grafana/' \ --set 'grafana\.ini'.server\.domain=null ElasticSearch https://artifacthub.io/packages/helm/elastic/elasticsearch helm repo add elastic https://helm.elastic.co helm upgrade elasticsearch elastic/elasticsearch \ --install \ --create-namespace \ --namespace elk \ --set image=harbor.xxz.moe/elastic/elasticsearch \ --set imageTag=8.12.2 \ --set replicas=2 \ --set ingress.enabled=true \ --set ingress.ingressClassName=nginx \ --set ingress.pathType=ImplementationSpecific \ --set ingress.path="/elasticsearch(/|$)(.*)" \ --set ingress.hosts\[0\]="iphn-008-155.xxz.moe" \ --set ingress.annotations."nginx\.ingress\.kubernetes\.io/rewrite-target"="/\$2" helm upgrade kibana elastic/kibana \ --install \ --create-namespace \ --namespace elk \ --set image=harbor.xxz.moe/elastic/kibana \ --set imageTag=8.12.2 \ --set elasticsearchHosts=https://elasticsearch-master-headless.elk.svc.cluster.local:9200 \ --set ingress.enabled=true \ --set ingress.ingressClassName=nginx \ --set ingress.pathType=ImplementationSpecific \ --set ingress.path="/kibana(/|$)(.*)" \ --set ingress.hosts\[0\]="iphn-008-155.xxz.moe" \ --set ingress.annotations."nginx\.ingress\.kubernetes\.io/rewrite-target"="/\$2" Dashboard https://github.com/kubernetes/dashboard DASHBOARD v2.5.1 支持 kubernetes v1.23 版本 kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.1/aio/deploy/recommended.yaml kubectl kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml #EXPOSE kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard # 修改 type: ClusterIP >> type: NodePort # 旧版生成key kubectl describe secrets dashboard-admin -n kubernetes-dashboard # 新版生成key kubectl create token dashboard-admin -n kubernetes-dashboard kubectl -n kubernetes-dashboard delete serviceaccount admin-user kubectl -n kubernetes-dashboard delete clusterrolebinding admin-user helm # Add kubernetes-dashboard repository helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/ # Deploy a Helm Release named "kubernetes-dashboard" using the kubernetes-dashboard chart helm upgrade kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard \ --install \ --create-namespace \ --namespace kubernetes-dashboard \ --set=nginx.enabled=false \ --set=cert-manager.enabled=false \ --set=app.ingress.enabled=false ingress apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: dashboard-ingress namespace: kubernetes-dashboard annotations: cert-manager.io/cluster-issuer: letsencrypt-dns01 # 配置自动生成 https 证书 nginx.ingress.kubernetes.io/backend-protocol: HTTPS nginx.ingress.kubernetes.io/ssl-redirect: 'true' # 强制跳转 https nginx.ingress.kubernetes.io/secure-backends: 'true' nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx tls: - hosts: - 'iphn-211.xxz.moe' secretName: dashboard-letsencrypt-tls rules: - host: iphn-211.xxz.moe http: paths: - path: /dashboard pathType: Prefix backend: service: name: kubernetes-dashboard port: number: 443 cert-manager 项目地址：https://cert-manager.io/ kubectl kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.crds.yaml kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml helm helm repo add jetstack https://charts.jetstack.io helm repo update helm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --version v1.14.3 \ --set installCRDs=true MetalLB https://metallb.universe.tf/ # https://github.com/metallb/metallb/blob/main/config/manifests/metallb-native.yaml https://github.com/metallb/metallb/blob/main/config/manifests/metallb-native.yaml kubectl apply -f https://github.com/metallb/metallb/blob/v0.13.12/config/manifests/metallb-native.yaml nfs-subdir-external-provisioner helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ helm upgrade nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \ --install \ --create-namespace \ --namespace nfs-provisioner \ --set nfs.server=nas.xxz.moe \ --set nfs.path=/mnt/ST8000WSD08P2B/IT \ --set image.repository=k8s.dockerproxy.com/sig-storage/nfs-subdir-external-provisioner ☀ 配置命令 基本概念 Pod 和 Deployment pod是最小部署单元 deployment是一组pod的集合 单独创建pod的时候就不会有deployment 创建deployment的时候一定会创建pod YAML 与 YML 无区别 .yaml .yml 一样 kubectl get - 列出资源 kubectl describe - 显示有关资源的详细信息 kubectl logs - 打印 pod 和其中容器的日志 kubectl exec - 在 pod 中的容器上执行命令 格式化 get 输出 kubectl get pods -o custom-columns="NAME:.metadata.name,RSRC:.metadata.resourceVersion" kubectl get pods -o custom-columns="NAME:.metadata.name,IMAGE:.spec.containers[*].image" kubectl get 排序 # 获取 deployments,pods,svc kubectl get # 获取排序信息 kubectl get pods -o yaml # 按 pod 名排序 kubectl get pods --sort-by=.metadata.name # 按 pod 创建时间排序 kubectl get pods --sort-by=.metadata.creationTimestamp # 按 pod IP 排序 kubectl get pods --sort-by=.status.hostIP 查看pod运行日志 kubectl logs pod/[pod_name] label # 添加标签 kubectl label pod podname [label_name]=[label_value] kubectl label node nodename [label_name]=[label_value] kubectl label svc svcname [label_name]=[label_value] # 修改标签 kubectl label pod podname [label_name]=[new_label_value] --overwrite # 删除标签 kubectl label pod podname [label_name]- # 查找对象标签 kubectl get pods --show-labels # 查找指定标签值对象 kubectl get pods --show-labels -l [label_name]=[label_value] kubectl get pods --show-labels -l [label_name] kubectl get pods --show-labels -l '[label_name] in ([label_value1],[label_value2])' yaml # 生成yaml文件 [--dry-run=client -o yaml] kubectl create deployment [deployment_name] --image=[image_name] --dry-run=client -o yaml > [file_name].yaml kubectl get deployment [deployment_name] -o yaml > [file_name].yaml Taint & Toleration Taint：污点 Toleration：容忍度 节点亲和性 是 Pod 的一种属性，它使 Pod 被吸引到一类特定的节点 （这可能出于一种偏好，也可能是硬性要求）。 污点（Taint） 则相反——它使节点能够排斥一类特定的 Pod。 容忍度（Toleration） 是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。 容忍度允许调度但并不保证调度：作为其功能的一部分， 调度器也会评估其他参数。 #查看master节点是否打污点 kubectl describe nodes | grep -A1 Taints # 增加污点 kubectl taint nodes [node_name] [key1]=[value1]:NoSchedule # 去除污点 kubectl taint nodes [node_name] [key1]=[value1]:NoSchedule- #NoSchedule：表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上 #PreferNoSchedule：表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上 #NoExecute：表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上，同时会将 Node 上已经存在的 Pod 驱逐出去 使用容忍度 tolerations: - key: "key1" operator: "Equal" value: "value1" effect: "NoSchedule" tolerations: - key: "key1" operator: "Exists" effect: "NoSchedule" 设置toleration tolerations: - key: "key1" operator: "Equal" value: "value1" effect: "NoSchedule" - key: "key1" operator: "Equal" value: "value1" effect: "NoExecute" - key: "node.alpha.kubernetes.io/unreachable" operator: "Exists" effect: "NoExecute" tolerationSeconds: 6000 value 的值可以为 NoSchedule、PreferNoSchedule 或 NoExecute。 tolerationSeconds 是当 pod 需要被驱逐时，可以继续在 node 上运行的时间。 亲和性 https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/ https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity 集群升级 ########## # master # ########## # 禁止调度到该节点 kubectl cordon [node_name] # 驱逐节点pod kubectl drain [node_name] --ignore-daemonsets # 安装新版 kubeadm yum install kubeadm-1.23.16 # 查看升级计划 kubeadm upgrade plan # kubeadm upgrade apply --etcd-upgrade=false v1.20.7 # 根据升级计划中提示的升级命令将Contron Plane节点相关静态pod组件升级为1.20.7，如果不升级etcd则添加--etcd-upgrade=false参数，默认是true kubeadm upgrade apply v1.23.16 # 升级 kubelet kubectl yum install kubelet-1.23.16 kubectl-1.23.16 systemctl daemon-reload systemctl restart kubelet kubectl uncordon [node_name] ######### # other # ######### # 在主节点上禁止该节点调度并驱逐pod kubectl cordon [node_name] kubectl drain [node_name] --ignore-daemonsets # 在其他节点 yum install kubeadm-1.23.16 kubeadm upgrade node yum install kubelet-1.23.16 kubectl-1.23.16 systemctl daemon-reload systemctl restart kubelet # 在主节点启用该节点 kubectl uncordon [node_name] ☀ 资源类型 Node | 节点 # 禁止Pod调度到该节点 kubectl cordon <node> # 驱逐节点所有Pod kubectl drain <node> Namespaces | 命名空间 # 创建 Namespace kubectl create namespace [namespace_name] # 删除 Namespace # 同时也会删除 Namespace 下所有资源 kubectl delete namespace [namespace_name] # 指定命名空间 kubectl ...... -n [namespace] kubectl ...... --namespace [namespace] kubectl config set-context cks --namespace=cks --cluster kubernetes --user=kubernetes-admin # 设置 Namespace 偏好 kubectl config set-context --current --namespace=[new_namespace_name] # 查看 Namespace 偏好 kubectl config view --minify | grep namespace: Pod # 创建 pod kubectl run [pod_name] --image=[image_name] kubectl run NAME --image=image [--env="key=value"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...] # 删除 pod kubectl delete pod [pod_name] Deployment # 创建 deployment kubectl create deployment [deployment_name] --image=[image_name] kubectl create deploy [deployment_name] --image=nginx --replicas=2 # 删除 deployment kubectl delete deployment [deployment_name] 升级回滚 # 查看对象历史版本 kubectl rollout history deployment [deployment_name] # 查看对象版本详情 kubectl rollout history deployment [deployment_name] --revision=[serial_number] # 设置对象历史 change-cause kubectl annotate deployment [deployment_name] kubernetes.io/change-cause="[notes]" # 设置对象版本 kubectl set image deployment [deployment_name] [image_name]=[image_name]:[image_version] kubectl set image deployment [deployment_name] [image_name]=[image_name]:latest # 回退对象到上一历史版本 kubectl rollout undo deployment [deployment_name] # 回退对象指定历史版本 kubectl rollout undo deployment [deployment_name] --to-revision=[serial_number] # 编辑对象 kubectl edit deployment [deployment_name] # 查看rollout状态 kubectl rollout status deployment [deployment_name] 弹性伸缩 # 弹性伸缩 [增加或减少容器为指定数量] kubectl scale deployment [deployment_name] --replicas=10 # 自动弹性伸缩 kubectl autoscale deployment [deployment_name] --min=10 --max=15 --cpu-percent=80 StatefulSet | 有状态服务 apiVersion: v1 kind: Service metadata: labels: xxz: statefulset name: xxz-statefulset-service spec: ports: - port: 80 name: nginx clusterIP: None selector: app: statefulset-nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: statefulset-nginx-deploy spec: serviceName: nginx-x4 replicas: 3 selector: matchLabels: xxz: statefulset-nginx-deploy-containers template: metadata: labels: xxz: statefulset-nginx-deploy-containers spec: containers: - image: nginx name: nginx ports: - containerPort: 80 static pod | 静态 Pod 静态 Pod 在指定的节点上由 kubelet 守护进程直接管理，不需要 API 服务器监管。 与由控制面管理的 Pod（例如，Deployment） 不同；kubelet 监视每个静态 Pod（在它失败之后重新启动）。 静态 Pod 始终都会绑定到特定节点的 Kubelet 上。 静态 Pod 配置文件存放在对应目录 [默认是 /etc/kubernetes/manifests] 会自动生成 kubelet 会尝试通过 Kubernetes API 服务器为每个静态 Pod 自动创建一个镜像 Pod。 这意味着节点上运行的静态 Pod 对 API 服务来说是可见的，但是不能通过 API 服务器来控制。 Pod 名称将把以连字符开头的节点主机名作为后缀。 如果你在运行一个 Kubernetes 集群，并且在每个节点上都运行一个静态 Pod， 就可能需要考虑使用 DaemonSet 替代这种方式。 静态 Pod 不支持临时容器。 将 pod yaml 文件放到对应节点 /etc/kubernetes/manifests 目录即可 # 查看配置文件地址 systemctl status kubelet | grep -e "--config" # 查看静态pod地址 cat /var/lib/kubelet/config.yaml | grep staticPodPath: DaemonSet 守护进程 DaemonSet 确保全部（或者某些）节点上运行一个 Pod 的副本。 当有节点加入集群时， 也会为他们新增一个 Pod 。 当有节点从集群移除时，这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。 DaemonSet 的一些典型用法： 在每个节点上运行集群守护进程 在每个节点上运行日志收集守护进程 在每个节点上运行监控守护进程 一种简单的用法是为每种类型的守护进程在所有的节点上都启动一个 DaemonSet。 一个稍微复杂的用法是为同一种守护进程部署多个 DaemonSet；每个具有不同的标志， 并且对不同硬件类型具有不同的内存、CPU 要求。 apiVersion: apps/v1 kind: DaemonSet metadata: name: xxz-daemonset-nginx labels: author: xxz spec: selector: matchLabels: app: daemonset-nginx template: metadata: labels: app: daemonset-nginx spec: containers: - name: logs image: nginx ports: - containerPort: 80 volumeMounts: - name: varlog mountPath: /tmp/log volumes: - name: varlog hostPath: path: /var/daemonset-nginx-log type: DirectoryOrCreate Service 服务 port 映射到外部的端口，如 http 的 80 端口，https 的 443 端口 targetport 容器内服务提供的端口 nodeport 是容器所在节点的端口，即外部机器可访问的端口 # --target-port 服务的端口 # --port 外部的端口 # 集群外部 kubectl expose deployment [deployment_name] --port=80 --target-port=80 --type=NodePort --name=web1 # 集群内部 kubectl expose pod [pod_name] --port=80 --target-port=80 --type=ClusterIP --name=web1 apiVersion: v1 kind: Service metadata: name: xxz-pv-use-deployment-svc spec: selector: app: xxz-pv-use-deployment ports: - name: http protocol: TCP port: 80 targetPort: 80 - name: https protocol: TCP port: 443 targetPort: 443 type: NodePort ClusterIP NodePort LoadBalancer ingress Deployment + IP 需要集群部署 loadblancer DaemonSet + hostpath 创建 Ingress 规则 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: t-ingress-nginx spec: rules: - host: k8sm1.xxz.moe http: paths: - path: / backend: serviceName: nginx servicePort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: t-ingress-nginx spec: rules: - host: k8sm1.xxz.moe http: paths: - backend: service: name: service port: name: port path: / pathType: Exact Job kubectl run job --image=nginx --restart=OnFailure 单工作队列 apiVersion: batch/v1 kind: Job metadata: name: job-1 spec: # 生命周期，存活时间 秒 activeDeadlineSeconds: 20 template: spec: containers: - name: job-task-1 image: busybox imagePullPolicy: IfNotPresent restartPolicy: OnFailure command: [ "/bin/sh", "-c", "sleep 120s" ] restartPolicy: Never 多任务队列 apiVersion: batch/v1 kind: Job metadata: name: job-2 spec: completions: 5 #任务数 parallelism: 3 #并行度 template: spec: containers: - name: job-task-2 image: busybox imagePullPolicy: IfNotPresent restartPolicy: OnFailure command: [ "/bin/sh", "-c", "sleep 120s" ] restartPolicy: Never 一次性任务 apiVersion: batch/v1 kind: Job metadata: name: xx spec: template: spec: containers: - name: pi image: perl command: ["perl", "-Mbignum=bpi", "print bpi(2000)"] restartPolicy: Never backoffLimit: 4 定时任务 k run cronjob --image=busybox # ┌───────────── 分钟 (0 - 59) # │ ┌───────────── 小时 (0 - 23) # │ │ ┌───────────── 月的某天 (1 - 31) # │ │ │ ┌───────────── 月份 (1 - 12) # │ │ │ │ ┌───────────── 周的某天 (0 - 6)（周日到周一；在某些系统上，7 也是星期日） # │ │ │ │ │ 或者是 sun，mon，tue，web，thu，fri，sat # │ │ │ │ │ # │ │ │ │ │ # * * * * * 例如 0 0 13 * 5 表示此任务必须在每个星期五的午夜以及每个月的 13 日的午夜开始。 apiVersion: batch/v1beta1 kind: CronJob metadata: name: yy spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: yy image: busybox command: - /bin/sh - -c - data; echo hello from the Kubernetes cluster restartPolicy: OnFailure PodSecurityPolicy ☀ Secret base64 加密 # 字符 Secret 默认采用base64 加密 # -n 不包含换行，默认自带换行符 # 输出加密文本 echo -n [words] | base64 # 解密文本 echo [words] | base64 --decode 创建 Secret # 从字符创建 Secret kubectl create secret \ generic `# 类型-常规密码`\ char-secret `# secret 名称`\ --from-literal=username=admin \ --from-literal=password='admin@12345' # 从文件创建 Secret kubectl create secret \ generic `# 类型-常规密码`\ file-secret `# secret 名称`\ --from-file=username.txt \ --from-file=password.txt #默认键名为文件名。 #通过 --from-file=[key=]source 设置键名，例如： kubectl create secret \ generic `# 类型-常规密码`\ file-secret `# secret 名称`\ --from-file=username=username.txt \ --from-file=password=password.txt # SSH Secret kubectl create secret \ generic `# 类型-常规密码`\ ssh-secret `# secret 名称`\ --from-file=ssh-privatekey=id_rsa \ --from-file=ssh-publickey=id_rsa.pub # TLS Secret kubectl create secret \ tls `# 类型-tls`\ tls-secret `# secret 名称`\ --cert=foobar.crt \ --key=foobar.prikey.pem 查看 Secret kubectl get secret t-secret -o jsonpath='{.data}' kubectl get secret t-secret -o jsonpath='{.data.password}' | base64 --decode secret 挂载 # secret 会被挂载到 /etc/foo 目录下，以文件形式存在 apiVersion: v1 kind: Pod metadata: labels: app: centos name: centos spec: containers: - image: centos name: centos volumeMounts: - name: char-secret mountPath: "/secret" readOnly: true volumes: - name: char-secret secret: secretName: char-secret optional: true # 默认如果 Secret 不能正常加载，pod将重试运行，可选将忽略 ☀ RBAC | 访问控制 Service Accounts # 创建 serviceaccount # kubectl create serviceaccount [serviceaccount_name] -n [namespace_name] apiVersion: v1 kind: ServiceAccount metadata: name: xxz namespace: neo Role 与 ClusterRole # 创建 role # kubectl create role [role_name] --verb=create,get,watch,list,update,delete --resource=Deployment,StatefulSet,DaemonSet -n [namespace_name] # 创建 clusterrole # kubectl create clusterrole [cluserrole_name] --verb=create,get,watch,list,update,delete --resource=Deployment,StatefulSet,DaemonSet -n [namespace_name] apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: observer namespace: neo rules: - apiGroups: - apps resources: - deployments - statefulsets - daemonsets verbs: - create - get - watch - list - update - delete Rolebinding 与 ClusterRoleBinding # 绑定 role 与 serviceaccount # kubectl create rolebinding [rolebinding_name] --clusterrole=[clusterrole_name] --serviceaccount=[namespace]:[serviceaccount_name] # 绑定 clusterrole 与 serviceaccount # kubectl create clusterolebinding [clusterrolebinding_name] --clusterrole=[clusterrole_name] --serviceaccount=[namespace]:[serviceaccount_name] apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: xxzobserver roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: observer subjects: - kind: ServiceAccount name: xxz namespace: neo ☀ 存储 | Storage Volumes | 卷 https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/ emptyDir 容器内部临时存储 你可以将 emptyDir.medium 字段设置为 "Memory"， 以告诉 Kubernetes 为你挂载 tmpfs（基于 RAM 的文件系统）。 apiVersion: v1 kind: Pod metadata: labels: app: centos name: centos spec: containers: - image: centos name: centos volumeMounts: - name: emptydir-1 mountPath: /emptydir-1 - name: emptydir-2 mountPath: /emptydir-2 - name: emptydir-ram mountPath: /emptydir-ram volumes: - name: emptydir-1 emptyDir: {} - name: emptydir-2 emptyDir: sizeLimit: 500Mi - name: emptydir-ram emptyDir: sizeLimit: 4Gi medium: Memory hostPath 将节点目录映射至容器存储 目录为容器运行所在节点的目录 hostPath type 取值 行为 空字符串（默认）用于向后兼容，这意味着在安装 hostPath 卷之前不会执行任何检查。 DirectoryOrCreate 如果在给定路径上什么都不存在，那么将根据需要创建空目录，权限设置为 0755，具有与 kubelet 相同的组和属主信息。 Directory 在给定路径上必须存在的目录。 FileOrCreate 如果在给定路径上什么都不存在，那么将在那里根据需要创建空文件，权限设置为 0644，具有与 kubelet 相同的组和所有权。 File 在给定路径上必须存在的文件。 Socket 在给定路径上必须存在的 UNIX 套接字。 CharDevice 在给定路径上必须存在的字符设备。 BlockDevice 在给定路径上必须存在的块设备。 apiVersion: v1 kind: Pod metadata: labels: app: centos name: centos spec: containers: - image: centos name: centos volumeMounts: - name: hostpath-directory mountPath: /path - name: hostpath-file mountPath: /etc/bashrc volumes: - name: hostpath-directory hostPath: # 宿主机目录位置 path: /var/log type: DirectoryOrCreate - name: hostpath-file hostPath: # 宿主机文件位置 path: /etc/bashrc type: FileOrCreate NFS bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount. helper program 挂载的pod所在节点需安装nfs相关软件：nfs-utils,rpcbind apiVersion: v1 kind: Pod metadata: labels: app: centos name: centos spec: containers: - image: centos name: centos volumeMounts: - name: nfs-directory mountPath: /path volumes: - name: nfs-directory nfs: server: nas.xxz.moe path: /mnt/nfs readOnly: true Storage Classes | 存储类 local StorageClass apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: standard provisioner: kubernetes.io/external-nfs parameters: path: /home/nfs server: m1 readOnly: "false" reclaimPolicy: Retain # 默认 Delete allowVolumeExpansion: true mountOptions: - debug volumeBindingMode: Immediate Persistent Volumes | 持久卷 https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/ 回收策略 保留 Retain -- 删除PV对象。与之相关的、位于外部基础设施中的存储资产仍然存在。 删除 Delete -- 删除PV对象。删除与之相关的、位于外部基础设施中的存储资产。需要插件 访问模式 ReadWriteOnce -- RWO -- 卷可以被一个节点以读写方式挂载 ReadOnlyMany -- ROX -- 卷可以被多个节点以只读方式挂载 ReadWriteMany -- RWX -- 卷可以被多个节点以读写方式挂载 ReadWriteOncePod -- RWOP -- 卷可以被单个Pod以读写方式挂载[v1.29] 阶段 Available -- 卷是一个空闲资源，尚未绑定到任何申领 Bound -- 该卷已经绑定到某申领 Released -- 所绑定的申领已被删除，但是关联存储资源尚未被集群回收 Failed -- 卷的自动回收操作失败 Local local 卷所代表的是某个被挂载的本地存储设备，例如磁盘、分区或者目录。 local 卷只能用作静态创建的持久卷。不支持动态配置。 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv-local spec: volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: local capacity: storage: 10Gi local: #仅存在于当前节点 #仅限处于当前节点的容器调用 #每个节点都可以存在 path: /home/xxz/ nodeAffinity: #节点亲和性 required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - iphn-008-155.xxz.moe --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pv-local spec: accessModes: - ReadWriteMany volumeMode: Filesystem storageClassName: local resources: requests: storage: 1Gi --- apiVersion: v1 kind: Pod metadata: labels: app: centos name: centos spec: containers: - image: centos name: centos command: ['sh', '-c', 'sleep 8888888888'] volumeMounts: - name: pv-local mountPath: /pv-local volumes: - name: pv-local persistentVolumeClaim: claimName: pv-local affinity: #亲和性，由于是本地存储，必须约束在相同节点 nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - iphn-008-155.xxz.moe NFS bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount. helper program 挂载的pod所在节点需安装nfs相关软件：nfs-utils,rpcbind apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs spec: capacity: storage: 10Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: nfs mountOptions: - hard - nfsvers=4.1 nfs: path: /mnt/pv-nfs server: nas.xxz.moe PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pv spec: accessModes: - ReadWriteMany volumeMode: Filesystem storageClassName: local resources: requests: storage: 1Gi --- # PVC 挂载 apiVersion: v1 kind: Pod metadata: labels: app: centos name: centos spec: containers: - image: centos name: centos command: ['sh', '-c', 'sleep 8888888888'] volumeMounts: - name: pv mountPath: /pv volumes: - name: pv persistentVolumeClaim: claimName: pv 杂项 关于 pod-network-cidr flannel calico k8s 中如何修改 pod-network-cidr 地址范围 创建集群时用的是 --pod-network-cidr=10.244.0.0/16 （当时准备使用Flannel），现在想改为 --pod-network-cidr=192.168.0.0/16 （现在准备使用Calico），请问在不重建集群的情况下如何修改？ 1）kubectl -n kube-system edit cm kubeadm-config 2）vim /etc/kubernetes/manifests/kube-scheduler.yaml 通过 kubectl cluster-info dump | grep -m 1 cluster-cidr 命令可以检查配置是否生效 value.yaml 变量 {{ .Values.xxxxx}} Kubernetes Resource 缩写 Short name Full name cm configmaps ds daemonsets deploy deployments ep endpoints ev events hpa horizontalpodautoscalers ing ingresses limits limitranges ns namespaces no nodes pvc persistentvolumeclaims pv persistentvolumes po pods rs replicasets rc replicationcontrollers quota resourcequotas sa serviceaccounts svc services 快速删除 pod export now="--force --grace-period 0" 定义 now 变量后，可以像下面这样快速删除一个 pod： # bash k delete pod test $now # zsh k delete pod test $=now 自动补全 # 前置 bash-completion dnf install bash-completion # 加载 bash-completion 插件 source /usr/share/bash-completion/bash_completion # 启用 kubectl 自动补全功能 # 当前用户 bash echo 'source <(kubectl completion bash)' >>~/.bashrc # 当前用户 zsh echo 'source <(kubectl completion zsh)' >>~/.zshrc # 全局 kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null 别名设置 alias k='kubectl' alias kga='kubectl get nodes,deployments,pods,svc' export kdr='--dry-run=client -o yaml' # zsh 中使用 export 变量 kubectl run pod --image=nginx $=kdr # 或在zsh配置中设置 setopt SH_WORD_SPLIT It seems like the kubelet isn't running or healthy. tail /var/log/message 首先修改或创建/etc/docker/daemon.json，添加如下配置 "exec-opts": ["native.cgroupdriver=systemd"], 问题1：sysctl是做什么的？ 在运行时配置内核参数。-p从指定的文件加载系统参数，如不指定即从/etc/sysctl.conf中加载 问题2：为什么要执行modprobe br_netfilter？ sysctl -p /etc/sysctl.d/k8s.conf出现报错： sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directory sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory 问题3：为什么开启net.bridge.bridge-nf-call-iptables内核参数？ 在centos下安装docker，执行docker info出现如下警告： WARNING: bridge-nf-call-iptables is disabled WARNING: bridge-nf-call-ip6tables is disabled 问题4：为什么要开启net.ipv4.ip_forward = 1参数？ kubeadm初始化k8s如果报错： [ERROR FileContent--proc-sys-net-ipv4-ip_forward]: /proc/sys/net/ipv4/ip_forward contents are not set to 1 net.ipv4.ip_forward是数据包转发： 出于安全考虑，Linux系统默认是禁止数据包转发的。所谓转发即当主机拥有多于一块的网卡时，其中一块收到数据包，根据数据包的目的ip地址将数据包发往本机另一块网卡，该网卡根据路由表继续发送数据包。这通常是路由器所要实现的功能。 要让Linux系统具有路由转发功能，需要配置一个Linux的内核参数net.ipv4.ip_forward。这个参数指定了Linux系统当前对路由转发功能的支持情况；其值为0时表示 Error from server (InternalError): error when creating "ingress-rules-demo1.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": x509: certificate has expired or is not yet valid: current time 2022-04-04T11:36:15Z is before 2022-04-04T11:38:06Z kubectl get validatingwebhookconfigurations.admissionregistration.k8s.io kubectl delete -A validatingwebhookconfigurations.admissionregistration.k8s.io ingress-nginx-admission kubectl apply -f ingress-rules-demo1.yaml ☀ Helm helm:命令行工具 Chart：yaml集合 Release：Chart版本管理工具 安装 # Helm HELMADDR="https://get.helm.sh/helm-v3.13.3-linux-amd64.tar.gz" curl -L $HELMADDR -o helm-linux-amd64.tar.gz tar -zxv -f helm-linux-amd64.tar.gz mv linux-amd64/helm /usr/local/bin/helm ln -s /usr/local/bin/helm /usr/bin/helm rm -rf linux-amd64 rm -f helm-linux-amd64.tar.gz helm repo # 搜索仓库 # 搜索在线仓库 helm search hub [name] # 搜索本地仓库 helm search repo [name] # 添加仓库 # helm repo add 仓库名称 仓库地址 helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx # 移除仓库 # helm repo remove 仓库名称 仓库地址 helm repo remove ingress-nginx https://kubernetes.github.io/ingress-nginx # 更新仓库 helm repo update helm status 名称 helm list Helm install helm install [NAME] [CHART] [flags] helm upgrade metrics-server metrics-server/metrics-server \ --install `#如果不存在则创建` \ --namespace kube-system \ --set image.repository=registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server \ --set replicas=2 自定义Chart # 创建Chart helm create mychart # 创建yaml放在templates文件夹下 # chart.yaml:当前chart属性配置信息 # templates：编写yaml文件放到这个目录中 # values.yaml: yaml文件可以使用的全局变量 ＃安装mychart helm install 名称 mychart/ helm upgrade 名称 mychart/ #remove-pending-namespace.sh [pending-namespace-name] #!/bin/bash if $# -ne 1 ; then echo "Please input only namespace name" exit 1 fi ns=$1 kubectl get ns ${ns} -o json > tmp.json cat ./tmp.json | jq 'del(.spec.finalizers[])' > ./modify.json kubectl replace --raw "/api/v1/namespaces/${ns}/finalize" -f ./modify.json rm -f tmp.json modify.json ELK ✅ Elasticsearch security features have been automatically configured! ✅ Authentication is enabled and cluster connections are encrypted. ℹ️ Password for the elastic user (reset with `bin/elasticsearch-reset-password -u elastic`): NKkL=*ku+EIxRCmswlCg ℹ️ HTTP CA certificate SHA-256 fingerprint: d1ee2e656eaa80ce014a4fb4d8333b023e80095a52f679fcf686b4c485f85700 ℹ️ Configure Kibana to use this cluster: • Run Kibana and click the configuration link in the terminal when Kibana starts. • Copy the following enrollment token and paste it into Kibana in your browser (valid for the next 30 minutes): [binary] ℹ️ Configure other nodes to join this cluster: • Copy the following enrollment token and start new Elasticsearch nodes with `bin/elasticsearch --enrollment-token <token>` (valid for the next 30 minutes): [binary] If you're running in Docker, copy the enrollment token and run: `docker run -e "ENROLLMENT_TOKEN=<token>" docker.elastic.co/elasticsearch/elasticsearch:8.12.2` /etc/sysctl.conf vm.max_map_count=262144 sysctl -w vm.max_map_count=262144 /home/xxz/elasticsearch-8.12.2/bin/elasticsearch /kibana/config/kibana.yml server.port: 5601 server.name: kibana server.host: "iphn-150.xxz.moe" elasticsearch.hosts: ["http://iphn-150.xxz.moe:9200"] xpack.monitoring.ui.container.elasticsearch.enabled: true /home/xxz/logstash-8.12.2/bin/logstash docker run \ --name filebeat \ --user=root \ --restart=always \ -v /var/log/:/var/log/ \ -v "/mnt/ST8000WSD08P2B/文档/linux/filebeat-8.12.2-linux-x86_64/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro" \ harbor.xxz.moe/elastic/filebeat:8.12.2 \ setup \ -E setup.kibana.host=kibana:5601 \ -E output.elasticsearch.hosts=["iphn-150.xxz.moe:9200"] --name=filebeat \ --user=root \ --volume="/var/lib/docker/containers:/var/lib/docker/containers:ro" \ --volume="/var/run/docker.sock:/var/run/docker.sock:ro" \ --volume="registry:/usr/share/filebeat/data:rw" \ docker.elastic.co/beats/filebeat:8.12.2 filebeat -e --strict.perms=false \ -E output.elasticsearch.hosts=["elasticsearch:9200"] docker run \ harbor.xxz.moe/elastic/kibana:8.12.2 docker run \ --name es01 \ -p 9200:9200 \ -it \ -m 1GB \ harbor.xxz.moe/elastic/elasticsearch:8.12.2 Defaulted container "elasticsearch" out of: elasticsearch, increase-vm-max-map (init), increase-fd-ulimit (init) Exception in thread "main" java.nio.file.FileSystemException: /usr/share/elasticsearch/config/elasticsearch.yml.TqtLvvf6TuqBDXb-awSm3A.tmp -> /usr/share/elasticsearch/config/elasticsearch.yml: Device or resource busy at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:100) at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106) at java.base/sun.nio.fs.UnixFileSystem.move(UnixFileSystem.java:873) at java.base/sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:309) at java.base/java.nio.file.Files.move(Files.java:1430) at org.elasticsearch.xpack.security.cli.AutoConfigureNode.fullyWriteFile(AutoConfigureNode.java:1134) at org.elasticsearch.xpack.security.cli.AutoConfigureNode.fullyWriteFile(AutoConfigureNode.java:1146) at org.elasticsearch.xpack.security.cli.AutoConfigureNode.execute(AutoConfigureNode.java:687) at org.elasticsearch.server.cli.ServerCli.autoConfigureSecurity(ServerCli.java:173) at org.elasticsearch.server.cli.ServerCli.execute(ServerCli.java:86) at org.elasticsearch.common.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:54) at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:85) at org.elasticsearch.cli.Command.main(Command.java:50) at org.elasticsearch.launcher.CliToolLauncher.main(CliToolLauncher.java:64) 重点在这: Device or resource busy 这是8以后才有的问题 由于这个视频是观看谷粒商城时出的问题,所以其他大佬未必能从我这收益 解决问题 是否elasticsearch.yml里有http.host: 0.0.0.0? 是否对elasticsearch给足了权限? chmod -R 777 /mydata/elasticsearch/ 8的特有问题解法:elasticsearch.yml里添加上:xpack.security.enabled: false echo "xpack.security.enabled: false" >> /mydata/elasticsearch/config/elasticsearch.yml elasticsearch 设置密码 bin/elasticsearch-setup-passwords interactive