蚊章汇 - Mike Calizo
type: channel
link: https://linuxcn.bwsl.wang/author/1492/
description: Mike Calizo
items: 用 SELinux 保护你的容器
items: Kubernetes 集群日志基础
items: Kubernetes 复制控制器如何工作
items: Kubernetes 网络策略基础
items: 用 Ansible 自动化系统管理员的 5 个日常任务
items: 6 个必知必会的关于容器的概念
items: Kubernetes 调度器是如何工作的
items: Kubernetes 资源配额使用指南
items: 20 分钟建立一个 Ansible 实验室

用 SELinux 保护你的容器
type: item
date: 2022-03-02T17:36:24+08:00
description: 黑掉你的系统，了解为什么配置 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:…

Kubernetes 集群日志基础
type: item
date: 2022-01-02T18:58:21+08:00
description: 探索 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: "t…

Kubernetes 复制控制器如何工作
type: item
date: 2021-11-25T21:11:08+08:00
description: 复制控制器负责管理吊舱的生命周期并确保在任何时候运行着所需的指定数量的吊舱。你有没有想过，谁负责监督和管理 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 集群中使用哪些方法来实…

Kubernetes 网络策略基础
type: item
date: 2021-11-21T21:02:25+08:00
description: 在你通过 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 部…

用 Ansible 自动化系统管理员的 5 个日常任务
type: item
date: 2021-04-01T07:39:12+08:00
description: 通过使用 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] ***************…

6 个必知必会的关于容器的概念
type: item
date: 2021-03-03T04:47:59+08:00
description: 容器现在是无所不在，它们已经快速的改变了 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 行业日新月异的变革中扮演着重要的角色，因为企业在向着快速、敏捷的交付软件及解决方案的方向前进，以此来 …

Kubernetes 调度器是如何工作的
type: item
date: 2021-02-26T20:35:01+08:00
description: 了解 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 和控制器是否创建新的吊舱，然后调度它至集群内一个可用的节点。监听未调…

Kubernetes 资源配额使用指南
type: item
date: 2021-01-12T20:53:00+08:00
description: 资源配额控制应用的 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-t…

20 分钟建立一个 Ansible 实验室
type: item
date: 2020-12-31T19:27:21+08:00
description: 建立一个支持学习和实验新软件的环境。能够构建和拆解公有云环境是非常有用的，但我们大多数人都不能轻松访问公有云。退而求其次的最好办法就是在本地机器上建立一个实验室，但即使在本地机器上运行也会带来性能、灵活性和其他挑战。大多数时候，本地机器上额外的工作负载会干扰我们日常的工作，它们当然也会影响你提供一个现成的环境来玩耍和实验新软件。几年前，当我和我的团队开始学习 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 …