黑掉你的系统，了解为什么配置 SELinux 作为你的第一道容器防线是很重要的。当有些事情在你的 Linux 环境中不能正常工作时，最简单的方法就是禁用 安全增强型 Linux Security-Enhanced Linux （SELinux）。而当它突然可以工作了，你就会忘记了禁用这件事 —— 这是一个常见的陷阱，意味着你已经失去了一个非常强大的安全工具。随着容器、微服务和分布式架构的兴起，威胁也在上升。这是由于一个老的、众所周知的问题：速度。容器的优势在于它们能让你快速行动，做更多的事情，并迅速改变。这意味着容器的采用已经飞速发展，但它所提供的速度也意味着你会遇到更多的问题和漏洞。当你越来越快地做更多的事情时，这自然会发生。如何减轻威胁正如孙子所说，“不战而屈人之兵”。当涉及到容器的基本防御时，这句话真的很有共鸣。为了避免问题（战斗），确保你的容器主机是安全的，你可以使用 SELinux 作为你的第一道防线。SELinux 是一个开源项目，于 2000 年发布，2003 年集成到 Linux 内核中。根据 红帽公司的解释，“SELinux 是 Linux 系统 的一个安全架构，允许管理员对谁可以访问系统有更多的控制。它最初是由美国国家安全局（NSA）开发的，是使用 Linux 安全模块（LSM）对 Linux 内核 的一系列补丁。”开始吧当你想到容器时，首先想到的可能是 Docker。Docker 在 2013 年出现后掀起了一场容器采用革命。它是容器爆炸性流行的主要原因之一，但如上所述，大量采用增加了用户对安全风险的脆弱性。在你用 SELinux 保护你的 Docker 容器之前，你需要设置一些东西。前置条件安装并配置了 CentOS 8/RHEL 8。安装并配置好 Docker CE创建两个账户：root 和 非 root 用户（下面的例子中是 mcalizo）。如果你需要在你的 RHEL 8/CentOS 8 服务器上设置 Docker，你可以按照这些 说明。如果你运行的是 RHEL 8，你需要在开始之前删除预装的 Podman 和 runc 包。首先，确保 SELinux 被启用：[mcalizo@Rhel82 ~]$ sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Memory protection checking: actual (secure) Max kernel policy version: 31 [mcalizo@Rhel82 ~]$ 然后，验证你的操作系统版本和 Docker 正在运行。以 root 身份登录并运行：[root@rhel82 ~]# cat /etc/redhat-release Red Hat Enterprise Linux release 8.2 (Ootpa) [root@rhel82 ~]# [root@rhel82 ~]# systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled) Active: active (running) since Wed 2020-10-28 19:10:14 EDT; 15s ago Docs: https://docs.docker.com Main PID: 30768 (dockerd) Tasks: 8 Memory: 39.0M CGroup: /system.slice/docker.service └─30768 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock Oct 28 19:10:13 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:13.889602941-04:00" level=error msg="> Oct 28 19:10:13 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:13.903413613-04:00" level=warning msg> Oct 28 19:10:13 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:13.903427451-04:00" level=warning msg> Oct 28 19:10:13 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:13.903538271-04:00" level=info msg="L> Oct 28 19:10:14 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:14.132060506-04:00" level=info msg="D> Oct 28 19:10:14 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:14.308943088-04:00" level=info msg="L> Oct 28 19:10:14 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:14.319438549-04:00" level=info msg="D> Oct 28 19:10:14 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:14.319570298-04:00" level=info msg="D> Oct 28 19:10:14 rhel82.home.labs.com dockerd[30768]: time="2020-10-28T19:10:14.333419209-04:00" level=info msg="A> Oct 28 19:10:14 rhel82.home.labs.com systemd[1]: Started Docker Application Container Engine 检查你的 Docker 版本：[root@rhel82 ~]# docker --version Docker version 19.03.13, build 4484c46d9d 黑掉主机了解一个问题的最好方法之一就是去体验它。因此，我将告诉你，如果你的安全设置不当，向 Docker 主机注入恶意代码是多么容易。为了能够在 Docker 主机上做坏事，“恶意”的非 root 用户（本教程中为 mcalizo）必须是可以实例化 Docker 容器的组的成员。首先，确认 mcalizo 用户属于哪个组：[root@Rhel82 ~]# groups mcalizo mcalizo : mcalizo 输出显示，mcalizo 只属于它自己的组。这意味着 mcalizo 不能实例化 Docker 容器，如果它试图这样做，将会得到这个错误：[mcalizo@Rhel82 ~]$ docker run -it --rm centos:latest /bin/sh docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/create: dial unix /var/run/docker.sock: connect: permission denied. See 'docker run --help'. 要允许 mcalizo 实例化容器，将用户加入 docker 组：[root@Rhel82 ~]# usermod -G docker -a mcalizo [root@Rhel82 ~]# groups mcalizo mcalizo : mcalizo docker 接下来，部署一个 fedora:latest 的容器，并登录到实例化的容器中去探索它：[mcalizo@Rhel82 ~]$ docker run -it --rm fedora:latest /bin/sh Unable to find image 'fedora:latest' locally latest: Pulling from library/fedora ee7e89337106: Pull complete Digest: sha256:b9ec86d36fca7b1d3de39cd7c258e8d90c377d312c21a7748071ce49069b8db4 Status: Downloaded newer image for fedora:latest sh-5.0# cat /etc/redhat-release Fedora release 33 (Thirty Three) 当你登录到新创建的容器时，你可以看到你是以 root 身份自动登录的：sh-5.0# whoami root sh-5.0# 作为 root 用户，你可以在这个容器中做任何事情，这意味着你可以利用容器主机，做很多破坏。因为你可以实例化一个容器，即使你不属于主机的 sudoers 账户，你也可以对主机做一些事情。退出你刚刚创建的容器，并创建一个新的容器来演示这个漏洞：[mcalizo@Rhel82 ~]$ docker run -it --rm -v /:/exploit fedora:latest /bin/bash [root@131043f2e306 /]# -v 选项 将 Docker 主机的 / 目录挂载到 /exploit 目录下的容器：[root@131043f2e306 /]#ls exploit/ bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var 因为它已被挂载，你可以在 Docker 主机上做任何事情。例如，你可以删除文件、编辑特定的配置来破害系统，甚至安装木马程序或其他恶意软件来窃取重要信息。为什么会发生这种情况？你可能想知道，既然 SELinux 处于强制模式，为什么会出现这种情况？深入挖掘 SELinux，看看哪里出了问题。验证 SELinux 是否有一个 Docker 上下文：[mcalizo@Rhel82 ~]$ ps -eZ | grep docker system_u:system_r:container_runtime_t:s0 30768 ? 00:00:04 dockerd [mcalizo@Rhel82 ~]$ 正如预期的那样，它确实有。这意味着 SELinux 管理着 Docker 守护进程。检查 Docker 守护进程，看看 SELinux 是否默认启用：[mcalizo@Rhel82 ~]$ docker info | grep Security -A3 Security Options: seccomp Profile: default Kernel Version: 4.18.0-193.el8.x86_64 Docker 守护进程中的 SELinux 在默认情况下是 不启用 的。 这就是问题所在！要解决这个问题，按 文档 说明，通过更新或创建文件 /etc/docker/daemon.json 来启用 SELinux 来控制和管理 Docker（你必须有 root 权限才能这样做）：[root@Rhel82 ~]# cat /etc/docker/daemon.json { "selinux-enabled": true } [root@Rhel82 ~]# [root@Rhel82 ~]# systemctl restart docker 在创建或更新该文件并重启 Docker 后，你应该看到 Docker 守护进程中启用了 SELinux 支持：[root@Rhel82 ~]# systemctl restart docker [mcalizo@Rhel82 root]$ docker info | grep Security -A3 Security Options: seccomp Profile: default selinux [mcalizo@Rhel82 root]$ 虽然仍然可以在你的 Docker 容器上挂载 Docker 主机中的特定文件系统，但不再允许更新或访问该文件：[mcalizo@Rhel82 root]$ docker run -it --rm -v /:/exploit fedora:latest /bin/bash [root@ecb5836da1f6 /]# touch /exploit/etc/shadow.sh touch: cannot touch '/exploit/etc/shadow.sh': Permission denied [root@ecb5836da1f6 /]# 了解更多你在容器世界中的第一道防线取决于你对容器主机的操作系统的设置有多强。有许多方法可以实现 Linux 的安全性，包括市场上可供选择的方案，以增强你的安全态势。SELinux 是一个额外的安全层，默认情况下内置于 Linux 发行版 中。为了借助它保护你的系统不被破坏，请确保 SELinux 保持开启状态。如果你想了解更多，请参阅：如何在 CentOS 8 / RH 上安装 Docker CEDocker 安全速查表dockerd 文档卷的使用文档什么是 SELinux？via: https://opensource.com/article/20/11/selinux-containers作者：Mike Calizo 选题：lujun9972 译者：wxy 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
探索 Kubernetes 中不同容器日志记录模式的工作原理。服务器和应用程序日志记录是开发人员、运维人员和安全团队了解应用程序在其生产环境中运行状态的重要工具。日志记录使运维人员能够确定应用程序和所需组件是否运行平稳，并检测是否发生了异常情况，以便他们能够对这种情况做出反应。对于开发人员，日志记录提供了在开发期间和之后对代码进行故障排除的可见性。在生产环境中，开发人员通常依赖于没有调试工具的日志记录工具。在加上系统的日志记录，开发人员可以与运维人员携手合作，有效地解决问题。日志记录工具最重要的受益者是安全团队，尤其是在云原生的环境中。能够从应用程序和系统日志中收集信息使得安全团队能够分析来自身份验证、应用程序访问恶意软件活动的数据，并在需要时进行响应。Kubernetes 是领先的容器平台，越来越多的应用程序通过 Kubernetes 部署到生产环境。我相信了解 Kubernetes 的日志架构是一项非常重要的工作，每个开发、运维和安全团队都需要认真对待。在本文中，我将讨论 Kubernetes 中不同容器日志记录模式的工作原理。系统日志记录和应用日志记录在深入研究 Kubernetes 日志记录架构之前，我想探索不同的日志记录方法以及这两种功能如何成为 Kubernetes 日志记录的关键特性。有两种类型的系统组件：在容器中运行的组件和不在容器中运行的组件。例如：Kubernetes 调度者和 kube-proxy 运行在容器中。kubelet 和容器运行时不在容器中运行。与容器日志类似，系统容器日志存储在 /var/log 目录中，你应该定期轮换它们。在这里，我研究的是容器日志记录。首先，我看一下集群级别的日志记录以及为什么它对集群运维人员很重要。集群日志提供有关集群如何执行的信息。诸如为什么 吊舱 Pod 被下线或节点死亡之类的信息。集群日志记录还可以捕获诸如集群和应用程序访问以及应用程序如何利用计算资源等信息。总体而言，集群日志记录工具为集群运维人员提供操作集群和安全有用的信息。捕获容器日志的另一种方法是通过应用程序的本机日志记录工具。现代应用程序设计很可能具有日志记录机制，可帮助开发人员通过标准输出 （stdout） 和错误流 （stderr） 解决应用程序性能问题。为了拥有有效的日志记录工具，Kubernetes 实现需要应用程序和系统日志记录组件。Kubernetes 容器日志的 3 种类型如今，在大多数的 Kubernetes 实现中，你可以看到三种主要的集群级日志记录方法。节点级日志代理用于日志记录的 挎斗 Sidecar 容器应用程序将应用程序日志直接暴露给日志后端节点级日志代理我想考虑节点级日志代理。你通常使用 DaemonSet 作为部署策略来实现这些，以便在所有 Kubernetes 节点中部署一个吊舱（充当日志代理）。然后，该日志代理被配置为从所有 Kubernetes 节点读取日志。你通常将代理配置为读取节点 /var/logs 目录捕获 stdout/stderr 流并将其发送到日志记录后端存储。下图显示了在所有节点中作为代理运行的节点级日志记录。以使用 fluentd 方法为例设置节点级日志记录，你需要执行以下操作：1、首先，你需要创建一个名为 fluentdd 的服务账户。Fluentd 吊舱使用此服务账户来访问 Kubernetes API，你需要在日志命名空间中使用标签 app: fluentd 创建它们：#fluentd-SA.yaml apiVersion: v1 kind: ServiceAccount metadata: name: fluentd namespace: logging labels: app: fluentd 你可以在此 仓库 中查看完整示例。2、接着，你需要创建一个名称为 fluentd-configmap 的 ConfigMap。这为 fluentd daemonset 提供了一个配置文件，其中包含所有必需的属性。#fluentd-daemonset.yaml apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: fluentd namespace: logging labels: app: fluentd kubernetes.io/cluster-service: "true" spec: selector: matchLabels: app: fluentd kubernetes.io/cluster-service: "true" template: metadata: labels: app: fluentd kubernetes.io/cluster-service: "true" spec: serviceAccount: fluentd containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:v1.7.3-debian-elasticsearch7-1.0 env: - name: FLUENT_ELASTICSEARCH_HOST value: "elasticsearch.logging.svc.cluster.local" - name: FLUENT_ELASTICSEARCH_PORT value: "9200" - name: FLUENT_ELASTICSEARCH_SCHEME value: "http" - name: FLUENT_ELASTICSEARCH_USER value: "elastic" - name: FLUENT_ELASTICSEARCH_PASSWORD valueFrom: secretKeyRef: name: efk-pw-elastic key: password - name: FLUENT_ELASTICSEARCH_SED_DISABLE value: "true" resources: limits: memory: 512Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: fluentconfig mountPath: /fluentd/etc/fluent.conf subPath: fluent.conf terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: fluentconfig configMap: name: fluentdconf 你可以在此 仓库 中查看完整示例。现在，我们来看看如何将 fluentd daemonset 部署为日志代理的代码。#fluentd-daemonset.yaml apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: fluentd namespace: logging labels: app: fluentd kubernetes.io/cluster-service: "true" spec: selector: matchLabels: app: fluentd kubernetes.io/cluster-service: "true" template: metadata: labels: app: fluentd kubernetes.io/cluster-service: "true" spec: serviceAccount: fluentd containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:v1.7.3-debian-elasticsearch7-1.0 env: - name: FLUENT_ELASTICSEARCH_HOST value: "elasticsearch.logging.svc.cluster.local" - name: FLUENT_ELASTICSEARCH_PORT value: "9200" - name: FLUENT_ELASTICSEARCH_SCHEME value: "http" - name: FLUENT_ELASTICSEARCH_USER value: "elastic" - name: FLUENT_ELASTICSEARCH_PASSWORD valueFrom: secretKeyRef: name: efk-pw-elastic key: password - name: FLUENT_ELASTICSEARCH_SED_DISABLE value: "true" resources: limits: memory: 512Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: fluentconfig mountPath: /fluentd/etc/fluent.conf subPath: fluent.conf terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: fluentconfig configMap: name: fluentdconf 将这些放在一起执行：kubectl apply -f fluentd-SA.yaml \ -f fluentd-configmap.yaml \ -f fluentd-daemonset.yaml 用于日志记录的挎斗容器应用程序另一种方法是使用带有日志代理的专用挎斗容器。容器最常见的实现是使用 Fluentd 作为日志收集器。在企业部署中（你无需担心一点计算资源开销），使用 fluentd（或类似）实现的挎斗容器提供了集群级日志记录的灵活性。这是因为你可以根据需要捕获的日志类型、频率和其它可能的调整来调整和配置收集器代理。下图展示了作为日志代理的挎斗容器。例如，一个吊舱运行单个容器，容器使用两种不同的格式写入两个不同的日志文件。吊舱的配置文件如下：#log-sidecar.yaml apiVersion: v1 kind: Pod metadata: name: counter spec: containers: - name: count image: busybox 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 image: busybox args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log'] volumeMounts: - name: varlog mountPath: /var/log volumes: - name: varlog emptyDir: {} 把它们放在一起，你可以运行这个吊舱：$ kubectl apply -f log-sidecar.yaml 要验证挎斗容器是否用作日志代理，你可以执行以下操作：$ kubectl logs counter count-log 预期的输出如下所示：$ kubectl logs counter count-log-1 Thu 04 Nov 2021 09:23:21 NZDT Thu 04 Nov 2021 09:23:22 NZDT Thu 04 Nov 2021 09:23:23 NZDT Thu 04 Nov 2021 09:23:24 NZDT 将应用程序日志直接暴露给日志后端第三种方法（在我看来）是 Kubernetes 容器和应用程序日志最灵活的日志记录解决方案，是将日志直接推送到日志记录后端解决方案。尽管此模式不依赖于原生 Kubernetes 功能，但它提供了大多数企业需要的灵活性，例如：扩展对网络协议和输出格式的更广泛支持。提供负载均衡能力并提高性能。可配置为通过上游聚合接受复杂的日志记录要求。因为这第三种方法通过直接从每个应用程序推送日志来依赖非 Kubernetes 功能，所以它超出了 Kubernetes 的范围。结论Kubernetes 日志记录工具是企业部署 Kubernetes 集群的一个非常重要的组件。我讨论了三种可能的可用模式。你需要找到适合你需求的模式。如上所述，使用 daemonset 的节点级日志记录是最容易使用的部署模式，但它也有一些限制，可能不适合你的组织的需要。另一方面，挎斗 模式提供了灵活性和自定义，允许你自定义要捕获的日志类型，但是会提高计算机的资源开销。最后，将应用程序日志直接暴露给后端日志工具是另一种允许进一步定制的诱人方法。选择在你，你只需要找到适合你组织要求的方法。via: https://opensource.com/article/21/11/cluster-logging-kubernetes作者：Mike Calizo 选题：lujun9972 译者：perfiffer 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
复制控制器负责管理吊舱的生命周期并确保在任何时候运行着所需的指定数量的吊舱。你有没有想过，谁负责监督和管理 Kubernetes 集群内运行的“ 吊舱 pod ”的确切数量？Kubernetes 可以通过多种方式做到这一点，但一个常见的方法是使用 “ 复制控制器 ReplicationController （RC）”。RC 负责管理吊舱的生命周期，并确保在任何时候运行着所需的指定数量的吊舱。但另一方面，它不负责高级的集群能力，如执行自动扩展、准备度和活跃探测以及其他高级的复制能力。Kubernetes 集群中的其他组件可以更好地执行这些功能。简而言之，RC 的职责有限，通常用于不需要复杂逻辑就能达到某些要求的具体实现（例如，确保所需的吊舱数量总是与指定的数量相符）。如果超过了所需的数量，RC 会删除多余的，并确保即使在节点故障或吊舱终止的情况下，也有相同数量的存在。简单的事情不需要复杂的解决方案，对我来说，这就是 RC 如何被使用的一个完美的比喻。如何创建一个 RC像大多数 Kubernetes 资源一样，你可以使用 YAML 或 JSON 格式创建一个 RC，然后将其发布到 Kubernetes API 端点。$ kubectl create -f rcexample.yaml replicationcontroller/rcexample created 现在，我将深入一下 rcexample.yaml 的样子。apiVersion: v1 kind: ReplicationController → RC 描述符 metadata: name: rcexample → 复制控制器名字 spec: replicas: 3 → 预期的吊舱数量 selector: → 这个 RC 的吊舱选择器 app: nginx template: → 用于创建新吊舱的模板 metadata: labels: app: nginx spec: containers: - name: nginx image: nginx 进一步解释，这个文件在执行时创建了一个名为 rcexample 的 RC，确保三个名为 nginx 的吊舱实例一直在运行。如果一个或所有的 app=nginx 吊舱没有运行，新的吊舱将根据定义的吊舱模板创建。一个 RC 有三个部分：复制品：3吊舱模板：app=nginx吊舱选择器：app=nginx注意，吊舱模板要与吊舱选择器相匹配，以防止 RC 一直创建吊舱。如果你创建的 RC 的吊舱选择器与模板不匹配，Kubernetes API 服务器会给你一个错误。为了验证 RC rcexample 是否被创建：$ kubectl get po NAME READY STATUS RESTARTS AGE rcexample-53thy 0/1 Running 0 10s rcexample-k0xz6 0/1 Running 0 10s rcexample-q3vkg 0/1 Running 0 10s 要删除 RC：$ kubectl delete rc rcexample replicationcontroller "rcexample" deleted 注意，你可以对 RC 中的服务使用 滚动更新 策略，逐个替换吊舱。其他复制容器的方法在 Kubernetes 部署中，有多种方法可以实现容器的复制。Kubernetes 成为容器平台的主要选择的主要原因之一是复制容器以获得可靠性、负载平衡和扩展的原生能力。我在上面展示了你如何轻松地创建一个 RC，以确保在任何时候都有一定数量的吊舱可用。你可以通过更新副本的数量来手动扩展吊舱。另一种可能的方法是通过使用 “ 复制集 ReplicaSet （RS）”来达到复制的目的。(kind: ReplicaSet) RS 的功能几乎与 RC 相同。主要区别在于，RS 不允许滚动更新策略。另一种实现复制的方法是通过使用 “ 部署 Deployments ”。(kind: Deployment) 部署是一种更高级的容器复制方法。从功能上讲，部署提供了相同的功能，但在需要时可以推出和回滚变化。这种功能之所以能够实现，是因为部署有 “ 策略类型 StrategyType ” 规范来用新的吊舱替换旧的吊舱。你可以定义两种类型的部署策略：“ 重新创建 Recreate ” 和 “ 滚动更新 RollingUpdate ”。你可以如下指定部署策略：StrategyType: RollingUpdate 总结容器的复制功能是大多数企业考虑采用 Kubernetes 的主要原因之一。复制可以让你达到大多数关键应用程序需要的可靠性和可扩展性，作为生产的最低要求。了解在 Kubernetes 集群中使用哪些方法来实现复制，对于决定哪种方法最适合你的应用架构考虑非常重要。via: https://opensource.com/article/21/11/kubernetes-replicationcontroller作者：Mike Calizo 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
在你通过 Kubernetes 部署一个应用之前，了解 Kubernetes 的网络策略是一个基本的要求。随着越来越多的云原生应用程序通过 Kubernetes 部署到生产环境，安全性是你必须在早期就需要考虑的一个重要检查项。在设计云原生应用程序时，预先嵌入安全策略非常重要。不这样做会导致后续的安全问题，从而导致项目延迟，并最终给你带来不必要的压力和金钱投入。这么多年来，人们总是把安全留到最后，直到他们的部署即将发布到生产环境时才考虑安全。这种做法会导致项目交付的延迟，因为每个组织都有要遵守的安全标准，这些规定被绕过或不遵守，并承受大量的风险才得以实现可交付成果。对于刚开始学习 Kubernetes 实施的人来说，理解 Kubernetes 网络策略 NetworkPolicy 可能会令人生畏。但这是在将应用程序部署到 Kubernetes 集群之前必须了解的基本要求之一。在学习 Kubernetes 和云原生应用程序时，请把“不要把安全抛在脑后！”定为你的口号。NetworkPolicy 概念 网络策略 NetworkPolicy 取代了你所知道的数据中心环境中的防火墙设备 —— 如 吊舱 Pod 之于计算实例、网络插件之于路由器和交换机，以及卷之于存储区域网络（SAN）。默认情况下，Kubernetes 网络策略允许 吊舱 Pod 从任何地方接收流量。如果你不担心吊舱的安全性，那么这可能没问题。但是，如果你正在运行关键工作负载，则需要保护吊舱。控制集群内的流量（包括入口和出口流量），可以通过网络策略来实现。要启用网络策略，你需要一个支持网络策略的网络插件。否则，你应用的任何规则都将变得毫无用处。Kubernetes.io 上列出了不同的网络插件：CNI 插件：遵循 容器网络接口 Container Network Interface （CNI）规范，旨在实现互操作性。Kubernetes 遵循 CNI 规范的 v0.4.0 版本。Kubernetes 插件：使用桥接器和主机本地 CNI 插件实现基本的 cbr0。应用网络策略要应用网络策略，你需要一个工作中的 Kubernetes 集群，并有支持网络策略的网络插件。但首先，你需要了解如何在 Kubernetes 的环境使用网络策略。Kubernetes 网络策略允许 吊舱 从任何地方接收流量。这并不是理想情况。为了吊舱安全，你必须了解吊舱是可以在 Kubernetes 架构内进行通信的端点。1、使用 podSelector 进行吊舱间的通信： - namespaceSelector: matchLabels: project: myproject 2、使用 namespaceSelector 和/或 podSelector 和 namespaceSelector 的组合进行命名空间之间的通信和命名空间到吊舱的通信。： - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend 3、对于吊舱的 IP 块通信，使用 ipBlock 定义哪些 IP CIDR 块决定源和目的。 - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 注意吊舱、命名空间和基于 IP 的策略之间的区别。对于基于吊舱和命名空间的网络策略，使用选择器来控制流量，而对基于 IP 的网络策略，使用 IP 块（CIDR 范围）来定义控制。把它们放在一起，一个网络策略应如下所示：apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: app: backend policyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 192.168.1.0/24 - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978 参考上面的网络策略，请注意 spec 部分。在此部分下，带有标签 app=backend 的 podSelector 是我们的网络策略的目标。简而言之，网络策略保护给定命名空间内称为 backend 的应用程序。此部分也有 policyTypes 定义。此字段指示给定策略是否适用于选定吊舱的入口流量、选定吊舱的出口流量，或两者皆有。spec: podSelector: matchLabels: app: backend policyTypes: - Ingress - Egress 现在，请看 Ingress（入口）和 Egress（出口）部分。该定义规定了网络策略的控制。首先，检查 ingress from 部分。此实例中，网络策略允许从以下位置进行吊舱连接：ipBlock允许 172.17.0.0/16拒绝 192.168.1.0/24namespaceSelectormyproject: 允许来自此命名空间并具有相同标签 project=myproject 的所有吊舱。podSelectorfrontend: 允许与标签 role=frontend 匹配的吊舱。ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 192.168.1.0/24 - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend 现在，检查 egress to 部分。这决定了从吊舱出去的连接：ipBlock10.0.0.0/24: 允许连接到此 CIDRPorts: 允许使用 TCP 和端口 5978 进行连接egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978 网络策略的限制仅靠网络策略无法完全保护你的 Kubernetes 集群。你可以使用操作系统组件或 7 层网络技术来克服已知限制。你需要记住，网络策略只能解决 IP 地址和端口级别的安全问题 —— 即开放系统互联（OSI）中的第 3 层或第 4 层。为了解决网络策略无法处理的安全要求，你需要使用其它安全解决方案。以下是你需要知道的一些 用例，在这些用例中，网络策略需要其他技术的增强。总结了解 Kubernetes 的网络策略很重要，因为它是实现（但不是替代）你通常在数据中心设置中使用的防火墙角色的一种方式，但适用于 Kubernetes。将此视为容器安全的第一层，仅仅依靠网络策略并不是一个完整的安全解决方案。网络策略使用选择器和标签在吊舱和命名空间上实现安全性。此外，网络策略还可以通过 IP 范围实施安全性。充分理解网络策略是在 Kubernetes 环境中安全采用容器化的一项重要技能。via: https://opensource.com/article/21/10/kubernetes-networkpolicy作者：Mike Calizo 选题：lujun9972 译者：perfiffer 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
用 Ansible 自动化系统管理员的 5 个日常任务
通过使用 Ansible 自动执行可重复的日常任务，提高工作效率并避免错误。如果你讨厌执行重复性的任务，那么我有一个提议给你，去学习 Ansible!Ansible 是一个工具，它可以帮助你更轻松、更快速地完成日常任务，这样你就可以更有效地利用时间，比如学习重要的新技术。对于系统管理员来说，它是一个很好的工具，因为它可以帮助你实现标准化，并在日常活动中进行协作，包括：安装、配置和调配服务器和应用程序；定期更新和升级系统；监测、减轻和排除问题。通常，许多这些基本的日常任务都需要手动步骤，而根据个人的技能的不同，可能会造成不一致并导致配置发生漂移。这在小规模的实施中可能是可以接受的，因为你管理一台服务器，并且知道自己在做什么。但当你管理数百或数千台服务器时会发生什么？如果不小心，这些手动的、可重复的任务可能会因为人为的错误而造成延误和问题，而这些错误可能会影响你及你的组织的声誉。这就是自动化的价值所在。而 Ansible 是自动化这些可重复的日常任务的完美工具。自动化的一些原因是：你想要一个一致和稳定的环境。你想要促进标准化。你希望减少停机时间，减少严重事故案例，以便可以享受生活。你想喝杯啤酒，而不是排除故障问题!本文提供了一些系统管理员可以使用 Ansible 自动化的日常任务的例子。我把本文中的剧本和角色放到了 GitHub 上的 系统管理员任务仓库 中，以方便你使用它们。这些剧本的结构是这样的（我的注释前面有 ==>）。[root@homebase 6_sysadmin_tasks]# tree -L 2 . ├── ansible.cfg ==> 负责控制 Ansible 行为的配置文件 ├── ansible.log ├── inventory │ ├── group_vars │ ├── hosts ==> 包含我的目标服务器列表的清单文件 │ └── host_vars ├── LICENSE ├── playbooks ==> 包含我们将在本文中使用的剧本的目录 │ ├── c_logs.yml │ ├── c_stats.yml │ ├── c_uptime.yml │ ├── inventory │ ├── r_cron.yml │ ├── r_install.yml │ └── r_script.yml ├── README.md ├── roles ==> 包含我们将在本文中使用的角色的目录 │ ├── check_logs │ ├── check_stats │ ├── check_uptime │ ├── install_cron │ ├── install_tool │ └── run_scr └── templates ==> 包含 jinja 模板的目录 ├── cron_output.txt.j2 ├── sar.txt.j2 └── scr_output.txt.j2 清单类似这样的：[root@homebase 6_sysadmin_tasks]# cat inventory/hosts [rhel8] master ansible_ssh_host=192.168.1.12 workernode1 ansible_ssh_host=192.168.1.15 [rhel8:vars] ansible_user=ansible ==> 请用你的 ansible 用户名更新它 这里有五个你可以用 Ansible 自动完成的日常系统管理任务。1、检查服务器的正常运行时间你需要确保你的服务器一直处于正常运行状态。机构会拥有企业监控工具来监控服务器和应用程序的正常运行时间，但自动监控工具时常会出现故障，你需要登录进去验证一台服务器的状态。手动验证每台服务器的正常运行时间需要花费大量的时间。你的服务器越多，你需要花费的时间就越长。但如果有了自动化，这种验证可以在几分钟内完成。使用 check\_uptime 角色和 c_uptime.yml 剧本：[root@homebase 6_sysadmin_tasks]# ansible-playbook -i inventory/hosts playbooks/c_uptime.yml -k SSH password: PLAY [Check Uptime for Servers] **************************************************************************************************************************************** TASK [check_uptime : Capture timestamp] ************************************************************************************************* . 截断... . PLAY RECAP ************************************************************************************************************************************************************* master : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 workernode1 : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@homebase 6_sysadmin_tasks]# 剧本的输出是这样的：[root@homebase 6_sysadmin_tasks]# cat /var/tmp/uptime-master-20210221004417.txt ----------------------------------------------------- Uptime for master ----------------------------------------------------- 00:44:17 up 44 min, 2 users, load average: 0.01, 0.09, 0.09 ----------------------------------------------------- [root@homebase 6_sysadmin_tasks]# cat /var/tmp/uptime-workernode1-20210221184525.txt ----------------------------------------------------- Uptime for workernode1 ----------------------------------------------------- 18:45:26 up 44 min, 2 users, load average: 0.01, 0.01, 0.00 ----------------------------------------------------- 使用 Ansible，你可以用较少的努力以人类可读的格式获得多个服务器的状态，Jinja 模板 允许你根据自己的需要调整输出。通过更多的自动化，你可以按计划运行，并通过电子邮件发送输出，以达到报告的目的。2、配置额外的 cron 作业你需要根据基础设施和应用需求定期更新服务器的计划作业。这似乎是一项微不足道的工作，但必须正确且持续地完成。想象一下，如果你对数百台生产服务器进行手动操作，这需要花费多少时间。如果做错了，就会影响生产应用程序，如果计划的作业重叠，就会导致应用程序停机或影响服务器性能。使用 install\_cron 角色和 r_cron.yml 剧本：[root@homebase 6_sysadmin_tasks]# ansible-playbook -i inventory/hosts playbooks/r_cron.yml -k SSH password: PLAY [Install additional cron jobs for root] *************************************************************************************************************************** . 截断... . PLAY RECAP ************************************************************************************************************************************************************* master : ok=10 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 workernode1 : ok=10 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 验证剧本的结果：[root@homebase 6_sysadmin_tasks]# ansible -i inventory/hosts all -m shell -a "crontab -l" -k SSH password: master | CHANGED | rc=0 >> 1 2 3 4 5 /usr/bin/ls /tmp #Ansible: Iotop Monitoring 0 5,2 * * * /usr/sbin/iotop -b -n 1 >> /var/tmp/iotop.log 2>> /var/tmp/iotop.err workernode1 | CHANGED | rc=0 >> 1 2 3 4 5 /usr/bin/ls /tmp #Ansible: Iotop Monitoring 0 5,2 * * * /usr/sbin/iotop -b -n 1 >> /var/tmp/iotop.log 2>> /var/tmp/iotop.err 使用 Ansible，你可以以快速和一致的方式更新所有服务器上的 crontab 条目。你还可以使用一个简单的点对点 Ansible 命令来报告更新后的 crontab 的状态，以验证最近应用的变化。3、收集服务器统计和 sars在常规的故障排除过程中，为了诊断服务器性能或应用程序问题，你需要收集 系统活动报告 system activity reports （sars）和服务器统计。在大多数情况下，服务器日志包含非常重要的信息，开发人员或运维团队需要这些信息来帮助解决影响整个环境的具体问题。安全团队在进行调查时非常特别，大多数时候，他们希望查看多个服务器的日志。你需要找到一种简单的方法来收集这些文档。如果你能把收集任务委托给他们就更好了。通过 check\_stats 角色和 c_stats.yml 剧本来完成这个任务：$ ansible-playbook -i inventory/hosts playbooks/c_stats.yml PLAY [Check Stats/sar for Servers] *********************************************************************************************************************************** TASK [check_stats : Get current date time] *************************************************************************************************************************** changed: [master] changed: [workernode1] . 截断... . PLAY RECAP *********************************************************************************************************************************************************** master : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 workernode1 : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 输出看起来像这样：$ cat /tmp/sar-workernode1-20210221214056.txt ----------------------------------------------------- sar output for workernode1 ----------------------------------------------------- Linux 4.18.0-193.el8.x86_64 (node1) 21/02/21 _x86_64_ (2 CPU) 21:39:30 LINUX RESTART (2 CPU) ----------------------------------------------------- 4、收集服务器日志除了收集服务器统计和 sars 信息，你还需要不时地收集日志，尤其是当你需要帮助调查问题时。通过 check\_logs 角色和 r_cron.yml 剧本来实现：$ ansible-playbook -i inventory/hosts playbooks/c_logs.yml -k SSH password: PLAY [Check Logs for Servers] **************************************************************************************************************************************** . 截断... . TASK [check_logs : Capture Timestamp] ******************************************************************************************************************************** changed: [master] changed: [workernode1] PLAY RECAP *********************************************************************************************************************************************************** master : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 workernode1 : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 为了确认输出，打开转储位置生成的文件。日志应该是这样的：$ cat /tmp/logs-workernode1-20210221214758.txt | more ----------------------------------------------------- Logs gathered: /var/log/messages for workernode1 ----------------------------------------------------- Feb 21 18:00:27 node1 kernel: Command line: BOOT_IMAGE=(hd0,gpt2)/vmlinuz-4.18.0-193.el8.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel -swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet Feb 21 18:00:27 node1 kernel: Disabled fast string operations Feb 21 18:00:27 node1 kernel: x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers' Feb 21 18:00:27 node1 kernel: x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers' Feb 21 18:00:27 node1 kernel: x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers' Feb 21 18:00:27 node1 kernel: x86/fpu: xstate_offset[2]: 576, xstate_sizes[2]: 256 Feb 21 18:00:27 node1 kernel: x86/fpu: Enabled xstate features 0x7, context size is 832 bytes, using 'compacted' format. 5、安装或删除软件包和软件你需要能够持续快速地在系统上安装和更新软件和软件包。缩短安装或更新软件包和软件所需的时间，可以避免服务器和应用程序不必要的停机时间。通过 install\_tool 角色和 r_install.yml 剧本来实现这一点：$ ansible-playbook -i inventory/hosts playbooks/r_install.yml -k SSH password: PLAY [Install additional tools/packages] *********************************************************************************** TASK [install_tool : Install specified tools in the role vars] ************************************************************* ok: [master] => (item=iotop) ok: [workernode1] => (item=iotop) ok: [workernode1] => (item=traceroute) ok: [master] => (item=traceroute) PLAY RECAP ***************************************************************************************************************** master : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 workernode1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 这个例子安装了在 vars 文件中定义的两个特定包和版本。使用 Ansible 自动化，你可以比手动安装更快地安装多个软件包或软件。你也可以使用 vars 文件来定义你要安装的软件包的版本。$ cat roles/install_tool/vars/main.yml --- # vars file for install_tool ins_action: absent package_list: - iotop-0.6-16.el8.noarch - traceroute 拥抱自动化要成为一名有效率的系统管理员，你需要接受自动化来鼓励团队内部的标准化和协作。Ansible 使你能够在更少的时间内做更多的事情，这样你就可以将时间花在更令人兴奋的项目上，而不是做重复的任务，如管理你的事件和问题管理流程。有了更多的空闲时间，你可以学习更多的知识，让自己可以迎接下一个职业机会的到来。via: https://opensource.com/article/21/3/ansible-sysadmin作者：Mike Calizo 选题：lujun9972 译者：wxy 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
容器现在是无所不在，它们已经快速的改变了 IT 格局。关于容器你需要知道一些什么呢？因为容器给企业所带来的巨大的价值和大量的好处，它快速的改变了 IT 格局。几乎所有最新的业务创新，都有容器化贡献的一部分因素，甚至是主要因素。在现代化应用架构中，能够快速的把变更交付到生产环境的能力，让你比你的竞争对手更胜一筹。容器通过使用微服务架构，帮助开发团队开发功能、更小的失败、更快的恢复，从而加快交付速度。容器化还让应用软件能够快速启动、按需自动扩展云资源。还有，DevOps 通过灵活性、移动性、和有效性让产品可以尽快进入市场，从而将容器化的所能带来的好处最大化。在 DevOps 中，虽然速度、敏捷、灵活是容器化的主要保障，但安全则是一个重要的因素。这就导致了 DevSecOps 的出现。它从一开始，到贯穿容器化应用的整个生命周期，都始终将安全融合到应用的开发中。默认情况下，容器化大大地增强了安全性，因为它将应用和宿主机以及其他的容器化应用相互隔离开来。什么是容器？容器是单体式应用程序所遗留的问题的解决方案。虽然单体式有它的优点，但是它阻碍了组织以敏捷的方式快速前进。而容器则让你能够将单体式分解成 微服务。本质上来说，容器只是一些轻量化组件的应用集，比如软件依赖、库、配置文件等等，然后运行在一个隔离的环境之中，这个隔离的环境又是运行在传统操作系统之上的，或者为了可移植性和灵活性而运行在虚拟化环境之上。总而言之，容器通过利用像 cgroup、 内核命名空间 和 SELinux 这样的内核技术来实现隔离。容器跟宿主机共用一个内核，因此比虚拟机占用更少的资源。容器的优势这种架构所带来的敏捷性是虚拟机所不可能做到的。此外，在计算和内存资源方面，容器支持一种更灵活的模型，而且它支持突发资源模式，因此应用程序可以在需要的时候，在限定的范围内，使用更多的资源。用另一句话来说，容器提供的扩展性和灵活性，是你在虚拟机上运行的应用程序中所无法实现的。容器让在公有云或者私有云上部署和分享应用变得非常容易。更重要的是，它所提供的连贯性，帮助运维和开发团队降低了在跨平台部署的过程中的复杂度。容器还可以实现一套通用的构建组件，可以在开发的任何阶段拿来复用，从而可以重建出一样的环境供开发、测试、预备、生产使用，将“一次编写、到处执行”的概念加以扩展。和虚拟化相比，容器使实现灵活性、连贯性和快速部署应用的能力变得更加简单 —— 这是 DevOps 的主要原则。Docker 因素Docker 已经变成了容器的代名词。Docker 让容器技术发生彻底变革并得以推广普及，虽然早在 Docker 之前容器技术就已经存在。这些容器技术包括 AIX 工作负载分区、 Solaris 容器、以及 Linux 容器（LXC），后者被用来 在一台 Linux 宿主机上运行多个 Linux 环境。Kubernetes 效应Kubernetes 如今已被广泛认为是 编排引擎 中的领导者。在过去的几年里，Kubernetes 的普及 加上容器技术的应用日趋成熟，为运维、开发、以及安全团队可以拥抱日益变革的行业，创造了一个理想的环境。Kubernetes 为容器的管理提供了完整全面的解决方案。它可以在一个集群中运行容器，从而实现类似自动扩展云资源这样的功能，这些云资源包括：自动的、分布式的事件驱动的应用需求。这就保证了“免费的”高可用性。（比如，开发和运维都不需要花太大的劲就可以实现）此外，在 OpenShift 和 类似 Kubernetes 这样的企业的帮助下，容器的应用变得更加的容易。容器会替代虚拟机吗？KubeVirt 和类似的 开源 项目很大程度上表明，容器将会取代虚拟机。KubeVirt 通过将虚拟机转化成容器，把虚拟机带入到容器化的工作流中，因此它们就可以利用容器化应用的优势。现在，容器和虚拟机更多的是互补的关系，而不是相互竞争的。容器在虚拟机上面运行，因此增加可用性，特别是对于那些要求有持久性的应用。同时容器可以利用虚拟化技术的优势，让硬件的基础设施（如：内存和网络）的管理更加便捷。那么 Windows 容器呢？微软和开源社区方面都对 Windows 容器的成功实现做了大量的推动。Kubernetes 操作器 Operator 加速了 Windows 容器的应用进程。还有像 OpenShift 这样的产品现在可以启用 Windows 工作节点 来运行 Windows 容器。Windows 的容器化创造出巨大的诱人的可能性。特别是对于使用混合环境的企业。在 Kubernetes 集群上运行你最关键的应用程序，是你成功实现混合云/多种云环境的目标迈出的一大步。容器的未来容器在 IT 行业日新月异的变革中扮演着重要的角色，因为企业在向着快速、敏捷的交付软件及解决方案的方向前进，以此来 超越竞争对手。容器会继续存在下去。在不久的将来，其他的使用场景，比如边缘计算中的无服务器，将会浮现出来，并且更深地影响我们对从数字设备来回传输数据的速度的认知。唯一在这种变化中存活下来的方式，就是去应用它们。via: https://opensource.com/article/20/12/containers-101作者：Mike Calizo 选题：lujun9972 译者：AmorSu 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
了解 Kubernetes 调度器是如何发现新的吊舱并将其分配到节点。Kubernetes 已经成为容器和容器化工作负载的标准编排引擎。它提供一个跨公有云和私有云环境的通用和开源的抽象层。对于那些已经熟悉 Kuberbetes 及其组件的人，他们的讨论通常围绕着如何尽量发挥 Kuberbetes 的功能。但当你刚刚开始学习 Kubernetes 时，尝试在生产环境中使用前，明智的做法是从一些关于 Kubernetes 相关组件（包括 Kubernetes 调度器） 开始学习，如下抽象视图中所示：Kubernetes 也分为控制平面和工作节点：控制平面： 也称为主控，负责对集群做出全局决策，以及检测和响应集群事件。控制平面组件包括：etcdkube-apiserverkube-controller-manager调度器工作节点： 也称节点，这些节点是工作负载所在的位置。它始终和主控联系，以获取工作负载运行所需的信息，并与集群外部进行通讯和连接。工作节点组件包括：kubeletkube-proxyCRI我希望这个背景信息可以帮助你理解 Kubernetes 组件是如何关联在一起的。Kubernetes 调度器是如何工作的Kubernetes 吊舱 pod 由一个或多个容器组成组成，共享存储和网络资源。Kubernetes 调度器的任务是确保每个吊舱分配到一个节点上运行。（LCTT 译注：容器技术领域大量使用了航海比喻，pod 一词，意为“豆荚”，在航海领域指“吊舱” —— 均指盛装多个物品的容器。常不翻译，考虑前后文，可译做“吊舱”。）在更高层面下，Kubernetes 调度器的工作方式是这样的：每个需要被调度的吊舱都需要加入到队列新的吊舱被创建后，它们也会加入到队列调度器持续地从队列中取出吊舱并对其进行调度调度器源码（scheduler.go）很大，约 9000 行，且相当复杂，但解决了重要问题：等待/监视吊舱创建的代码监视吊舱创建的代码始于 scheduler.go 的 8970 行，它持续等待新的吊舱：// Run begins watching and scheduling. It waits for cache to be synced, then starts a goroutine and returns immediately. func (sched *Scheduler) Run() { if !sched.config.WaitForCacheSync() { return } go wait.Until(sched.scheduleOne, 0, sched.config.StopEverything) 负责对吊舱进行排队的代码负责对吊舱进行排队的功能是：// queue for pods that need scheduling podQueue *cache.FIFO 负责对吊舱进行排队的代码始于 scheduler.go 的 7360 行。当事件处理程序触发，表明新的吊舱显示可用时，这段代码将新的吊舱加入队列中：func (f *ConfigFactory) getNextPod() *v1.Pod { for { pod := cache.Pop(f.podQueue).(*v1.Pod) if f.ResponsibleForPod(pod) { glog.V(4).Infof("About to try and schedule pod %v", pod.Name) return pod } } } 处理错误代码在吊舱调度中不可避免会遇到调度错误。以下代码是处理调度程序错误的方法。它监听 podInformer 然后抛出一个错误，提示此吊舱尚未调度并被终止：// scheduled pod cache podInformer.Informer().AddEventHandler( cache.FilteringResourceEventHandler{ FilterFunc: func(obj interface{}) bool { switch t := obj.(type) { case *v1.Pod: return assignedNonTerminatedPod(t) default: runtime.HandleError(fmt.Errorf("unable to handle object in %T: %T", c, obj)) return false } }, 换句话说，Kubernetes 调度器负责如下：将新创建的吊舱调度至具有足够空间的节点上，以满足吊舱的资源需求。监听 kube-apiserver 和控制器是否创建新的吊舱，然后调度它至集群内一个可用的节点。监听未调度的吊舱，并使用 /binding 子资源 API 将吊舱绑定至节点。例如，假设正在部署一个需要 1 GB 内存和双核 CPU 的应用。因此创建应用吊舱的节点上需有足够资源可用，然后调度器会持续运行监听是否有吊舱需要调度。了解更多要使 Kubernetes 集群工作，你需要使以上所有组件一起同步运行。调度器有一段复杂的的代码，但 Kubernetes 是一个很棒的软件，目前它仍是我们在讨论或采用云原生应用程序时的首选。学习 Kubernetes 需要精力和时间，但是将其作为你的专业技能之一能为你的职业生涯带来优势和回报。有很多很好的学习资源可供使用，而且 官方文档 也很棒。如果你有兴趣了解更多，建议从以下内容开始：Kubernetes the hard wayKubernetes the hard way on bare metalKubernetes the hard way on AWS你喜欢的 Kubernetes 学习方法是什么？请在评论中分享吧。via: https://opensource.com/article/20/11/kubernetes-scheduler作者：Mike Calizo 选题：lujun9972 译者：MZqk 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
资源配额控制应用的 CPU 或内存使用情况，防止资源被过量使用或被抢占。当 Kubernetes 集群运行过一段时间或者在被开发者大量使用后，Kubernetes 资源（例如 CPU 和内存）的控制的问题就会显现出来。而在大多情况下只有集群出问题后，我们才会意识到资源控制的重要性。Kubernetes 部署过程如果没有能充分考虑到将来的扩展性，资源类问题将会非常常见，此类问题与集群的管理和部署团队的经验有关。如果不加以合理控制，一个暴力的应用或者开发者可能影响到共享该集群的所有业务，大家因此会相互埋怨、指责并保护性地抢占资源。这对于集群管理和开发人员都是非常难以处理的场景。在 Kubernetes 环境中控制应用的计算资源使用有多种方式。大部分情况下，我们可以使用“资源控制”和“限制范围”。注意存储管理不在我们讨论范围之内，存储管理可以通过 持久卷 Persistent Volume 件，以实现针对不同的存储控制需求。资源配额是一种控制 Kubernetes 计算资源的方法。本文告诉你如何使用该功能来管理开发人员行为并控制应用的资源使用。什么是资源配额简而言之，资源配额 提供了限制每个命名空间资源消耗的约束条件，它们只能在命名空间级别上应用，这意味着它们可以应用于计算资源，并限制命名空间内的对象数量。Kubernetes资源配额通过 ResourceQuota 对象来为每个命名空间设置资源配额，对以下对象类型的 CPU 和内存进行限制： 吊舱 Pod 服务 Service 机密信息 Secret 持久卷断言 Persistent Volume Claim （PVC） 配置映射 ConfigMap Kubernetes 通过 request 和 limit 两个参数对 CPU 和内存进行限制（参考 LimitRange 文档）。前者表示容器最小被保证资源，后者表示容器最大可用资源。实际上最大可用资源还受限于其它容器的实际使用情况。下一张图片解释了配额中 request 和 limit 的区别：下面我们就通过一个例子来说明如何设置资源配额来创建约束，将应用程序限制在某些资源上，它还展示了实现资源配额以获得对 Kubernetes 的控制的有用性。准备环境首先你需要一个 Kubernetes 环境。以下是我使用 Kubernetes 环境：Minikube v1.14.2Fedora 33 操作系统互联网接入如果你想在 Linux 机器上通过 Minikube 搭建 Kubernetes 测试环境，可以参考 Bryant Son 的《Minikube 入门》 一文。Window 或者 macOS 用户可以参考这篇文章。设置资源配额这里我们仅展示 CPU 配额设置步骤，配置内存配额或两者的组合与之类似。在生产环境中，CPU 是最需要被控制的资源，尤其是在多应用的场景下特别需要注意防止某些应用消耗太多 CPU 而影响到其它应用。首先我们创建一个命名空间，在其中设置 CPU 配额：$ kubectl create namespace quota-test namespace/quota-test created准备 cpu-quota.yaml 文件，内容如下：apiVersion: v1 kind: ResourceQuota metadata: name: test-cpu-quota spec: hard: requests.cpu: "100m" limits.cpu: "200m"应用 CPU 配额到 Kubernetes 集群：$ kubectl apply -f cpu-qouta.yaml resourcequota/test-cpu-quota created使用 kubectl describe 检查配额配置情况：$ kubectl describe resourcequota/test-cpu-quota --namespace quota-test Name: test-cpu-quota Namespace: quota-test Resource Used Hard -------- ---- ---- limits.cpu 0 200m requests.cpu 0 100m在 Used resources 列中显示了当前情况，该列值会随着 吊舱 Pod 的部署而变化。下面是我们来验证限额管理的场景。我们将在同一命名空间下部署三个不同的吊舱，为它们配置以不同的资源限制如下：PodA：第一个被实例化，使用 50% 可用 CPU 资源PodB：第二个被实例化，使用其余 50% 可用 CPU 资源PodC：没有可用 CPU 资源，因此不会被部署部署吊舱PodA：$ kubectl create -n quota-test -f - << EOF apiVersion: v1 kind: Pod metadata: name: poda spec: containers: - name: quota-test image: busybox imagePullPolicy: IfNotPresent command: ['sh', '-c', 'echo Pod is Running ; sleep 5000'] resources: requests: cpu: "50m" limits: cpu: "100m" restartPolicy: Never EOF部署 PodA 后，再次查看配额描述信息中的 Used CPU 信息：$ kubectl describe resourcequota/test-cpu-quota --namespace quota-test Name: test-cpu-quota Namespace: quota-test Resource Used Hard -------- ---- ---- limits.cpu 100m 200m requests.cpu 50m 100mPodB：$ kubectl create -n quota-test -f - << EOF apiVersion: v1 kind: Pod metadata: name: podb spec: containers: - name: quota-test image: busybox imagePullPolicy: IfNotPresent command: ['sh', '-c', 'echo Pod is Running ; sleep 5000'] resources: requests: cpu: "50m" limits: cpu: "100m" restartPolicy: Never EOF再次查看 CPU 资源使用，此时 PodB 启动后 CPU 限制已经达到上限：$ kubectl describe resourcequota/test-cpu-quota --namespace quota-test Name: test-cpu-quota Namespace: quota-test Resource Used Hard -------- ---- ---- limits.cpu 200m 200m requests.cpu 100m 100mPodC：试着创建 PodC，此时 CPU 配额已经被 PodA 和 PodB 用尽：$ kubectl create -n quota-test -f - << EOF apiVersion: v1 kind: Pod metadata: name: podc spec: containers: - name: quota-test image: busybox imagePullPolicy: IfNotPresent command: ['sh', '-c', 'echo Pod is Running ; sleep 5000'] resources: requests: cpu: "5m" limits: cpu: "10m" restartPolicy: Never EOF正我们期望，第三个 Pod 无法被启动，配额限制了吊舱的创建：Error from server (Forbidden): error when creating "STDIN": pods "podc" is forbidden: exceeded quota: test-cpu-quota, requested: limits.cpu=10m,requests.cpu=5m, used: limits.cpu=200m,requests.cpu=100m, limited: limits.cpu=200m,requests.cpu=100m如我们的例子所示，定义合理的资源配额限制开发者行为对 Kubernetes 管理十分重要。清理删除刚才创建的命名空间 quota-test:$ kubectl delete -n quota-test规划资源配额Kubernetes 中提供多种方式来控制资源的抢占和使用，合理的规划和配置配额、限制范围和其它原生参数对保持集群的稳定性十分必要。你应该十分谨慎地控制计算资源的资源配额，特别是关键业务的生产应用环境。在规划资源配额时，开发人员的参与很重要，需要他们预估并给出最合理的资源使用值。via: https://opensource.com/article/20/12/kubernetes-resource-quotas作者：Mike Calizo 选题：lujun9972 译者：larryzju 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
建立一个支持学习和实验新软件的环境。能够构建和拆解公有云环境是非常有用的，但我们大多数人都不能轻松访问公有云。退而求其次的最好办法就是在本地机器上建立一个实验室，但即使在本地机器上运行也会带来性能、灵活性和其他挑战。大多数时候，本地机器上额外的工作负载会干扰我们日常的工作，它们当然也会影响你提供一个现成的环境来玩耍和实验新软件。几年前，当我和我的团队开始学习 Ansible 时，我们就遇到了这个挑战。我们找不到一个可以单独使用的环境，我们对这种情况的失望导致我们中的一些人停止了实验。我们知道需要找到一个解决方案。我们花了很多时间研究各种方案，得出了一套工具，使我们的好奇心能够在我们完全控制的环境中学习。我们可以在本地机器上轮换和拆解实验室环境，而不需要访问内部实验室或公共云。本文将解释如何在 20 分钟内以完全自动化的方式在本地机器上部署自己的实验室环境。你可以在我的 GitHub 仓库中找到这个练习的所有代码。工具和软件本方案使用以下工具和软件：Ansible 是我们选择的自动化工具，因为它易于使用，而且足够灵活，可以满足实验室的要求。Vagrant 易于使用，用于构建和维护虚拟机。VirtualBox 是一个托管管理程序，可以在 Windows 和 Linux 环境中使用。Fedora v30+ 是我本地机器上的操作系统。你必须进行以下设置才能建立环境：一个互联网连接在 BIOS 中启用虚拟化技术支持（以下是在我的联想笔记本上的过程）Vagrant v2.2.9最新版本的 Ansible最新版本的 VirtualBoxFedora v30+ 宿主机操作系统这个实验室环境有什么？这个项目旨在部署一个带有 Ansible 引擎和多个 Linux 节点的 Ansible 主机，以及一些预加载和预配置的应用程序（httpd 和 MySQL）。它还启用了 Cockpit，这样你就可以在测试过程中监控虚拟机（VM）的状态。使用预部署的应用程序的原因是为了提高效率（所以你不必花时间安装这些组件）。这样你就可以专注于创建角色和剧本，并针对上述工具部署的环境进行测试。我们确定，对于我们的用例来说，最好的方案是多机 Vagrant 环境。Vagrant 文件创建了三个 CentOS 虚拟机，以模拟两个目标主机和一个 Ansible 控制机。Host1: 没有图形用户界面（GUI），安装 httpd 和 MySQLHost2: 没有 GUI，安装了 httpd 和 MySQLAnsible-host：没有 GUI，安装了 Ansible 引擎启用多个管理程序如果使用了多个管理程序，一些管理程序可能不允许你拉起虚拟机。要解决这个问题，请遵循以下步骤（基于 Vagrant 的安装说明）。首先，找出管理程序的名称：$ lsmod | grep kvm kvm_intel 204800 6 kvm 593920 1 kvm_intel irqbypass 16384 1 kvm 我感兴趣的是 kvm_intel，但你可能需要另一个（比如 kvm_amd）。以 root 身份运行以下内容，将该管理程序列入黑名单：$ echo 'blacklist kvm-intel' >> /etc/modprobe.d/blacklist.conf 重新启动你的机器并尝试再次运行 Vagrant。Vagrant 文件cat Vagrantfile # -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| # Define VMs with static private IP addresses, vcpu, memory and vagrant-box. boxes = [ { :name => "web1.demo.com", ⇒ Host1 this is one of the target nodes :box => "centos/8", ⇒ OS version :ram => 1024, ⇒ Allocated memory :vcpu => 1, ⇒ Allocated CPU :ip => "192.168.29.2" ⇒ Allocated IP address of the node }, { :name => "web2.demo.com", ⇒ Host2 this is one of the target nodes :box => "centos/8", :ram => 1024, :vcpu => 1, :ip => "192.168.29.3" }, { :name => "ansible-host", ⇒ Ansible Host with Ansible Engine :box => "centos/8", :ram => 8048, :vcpu => 1, :ip => "192.168.29.4" } ] # Provision each of the VMs. boxes.each do |opts| config.vm.define opts[:name] do |config| # Only Enable this if you are connecting to Proxy server # config.proxy.http = "http://usernam:password@x.y:80"⇒ Needed if you have a proxy # config.proxy.https = "http://usernam:password@x.y:80" # config.proxy.no_proxy = "localhost,127.0.0.1" config.vm.synced_folder ".", "/vagrant", id: "vagrant-root", disabled: true config.ssh.insert_key = false config.vm.box = opts[:box] config.vm.hostname = opts[:name] config.vm.provider :virtualbox do |v| ⇒ Defines the vagrant provider v.memory = opts[:ram] v.cpus = opts[:vcpu] end config.vm.network :private_network, ip: opts[:ip] config.vm.provision :file do |file| file.source = './keys/vagrant' ⇒ vagrant keys to allow access to the nodes file.destination = '/tmp/vagrant' ⇒ the location to copy the vagrant key end config.vm.provision :shell, path: "bootstrap-node.sh" ⇒ script that copy hosts entry config.vm.provision :ansible do |ansible| ⇒ declaration to run ansible playbook ansible.verbose = "v" ansible.playbook = "playbook.yml" ⇒ the playbook used to configure the hosts end end end end 这些是你需要注意的重要文件。inventory-test.yaml：连接到节点的清单文件playbook.yaml：Vagrant 供应者调用的用于配置节点的剧本文件`Vagrantfile'：Vagrant 用来部署环境的文件Vagrant 密钥文件：连接实验室环境中各节点的 Vagrant 密钥你可以根据你的需要调整这些文件。Ansible 的灵活性使你有能力根据你的需要声明性地改变你的环境。部署你的实验室环境首先，克隆这个 GitHub 仓库 中的代码：$ git clone https://github.com/mikecali/ansible-labs-101.git Cloning into 'ansible-labs-101'... remote: Enumerating objects: 15, done. remote: Counting objects: 100% (15/15), done. remote: Compressing objects: 100% (13/13), done. remote: Total 15 (delta 2), reused 10 (delta 0), pack-reused 0 Unpacking objects: 100% (15/15), 6.82 KiB | 634.00 KiB/s, done. 接下来，将你的目录改为 vagrant-session-2，并查看其内容：$ ls Bootstrap-node.sh inventory keys playbook.yml README.md Vagrantfile 现在你已经拥有了实验室环境所需的所有工件和配置文件。要部署环境，请运行：$ vagrant up 只要有一个像样的网络连接，只需要 20 分钟左右就可以得到一个运行环境：$ vagrant up Bringing machine 'web1.demo.com' up with 'virtualbox' provider... Bringing machine 'web2.demo.com' up with 'virtualbox' provider... Bringing machine 'ansible-host' up with 'virtualbox' provider... ==> web1.demo.com: Importing base box 'centos/8'... ==> web1.demo.com: Matching MAC address for NAT networking... ==> web1.demo.com: Checking if box 'centos/8' version '1905.1' is up to date... ==> web1.demo.com: Setting the name of the VM: ansible-labs_web1democom_1606434176593_70913 ==> web1.demo.com: Clearing any previously set network interfaces... ==> web1.demo.com: Preparing network interfaces based on configuration... web1.demo.com: Adapter 1: nat web1.demo.com: Adapter 2: hostonly ==> web1.demo.com: Forwarding ports... web1.demo.com: 22 (guest) => 2222 (host) (adapter 1) ==> web1.demo.com: Running 'pre-boot' VM customizations... ==> web1.demo.com: Booting VM... ==> web1.demo.com: Waiting for machine to boot. This may take a few minutes... web1.demo.com: SSH address: 127.0.0.1:2222 web1.demo.com: SSH username: vagrant web1.demo.com: SSH auth method: private key [...] 一旦该剧本执行完成，你会看到这样的输出：PLAY RECAP ********************************* Ansible-host : ok=20 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=3 Real 18m14.288s User 2m26.978s Sys 0m26.849s 确认所有虚拟机都在运行：$ vagrant status Current machine states: Web1.demo.com running (virtualbox) Web2.demo.com running (virtualbox) ansible-host running (virtualbox) [...] 你可以通过登录其中一个虚拟机进一步调查。访问 ansible-host：> vagrant ssh ansible-host Activate the web console with: systemctl enable --now cockpit.socket Last login: Thu Nov 26 12:21:23 2020 from 10.0.2.2 [vagrant@ansible-host ~] uptime 16:46:42 up 1:24, 1 user, load average: 0.00, 0.01, 0.04 最后，你可以使用 Ansible 模块来 ping 你创建的其他节点：[vagrant@ansible-host]$ ansible -i inventory-test.yaml \ webservers -m ping -u vagrant 192.168.29.2 | SUCCESS => { "Ansible-facts": { "Discovered_interpreter_python": "/usr/libexec/platform-python" }, "Changed": false; "Ping": "pong" } [...] 清理运行如下命令来清理环境：$ vagrant destroy [vagrant machine name] 你的输出会像这样：有创意的学习在自己的实验室里利用自己的时间学习 Ansible 这样的软件是一个好习惯，但由于受到无法控制的限制，可能会很困难。有时候，你需要发挥创意，找到另一种方法。在开源社区中，你可以选择很多方案；我们选择这些工具的主要原因之一是，它们是许多人常用和熟悉的。另外，请注意，这些剧本并没有按照我的要求进行优化。请随时改进它们，并在评论中分享你的工作。via: https://opensource.com/article/20/12/ansible-lab作者：Mike Calizo 选题：lujun9972 译者：wxy 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出