Mike Calizo 发布的文章

了解 Kubernetes 调度器是如何发现新的吊舱并将其分配到节点。

 title=

Kubernetes 已经成为容器和容器化工作负载的标准编排引擎。它提供一个跨公有云和私有云环境的通用和开源的抽象层。

对于那些已经熟悉 Kuberbetes 及其组件的人,他们的讨论通常围绕着如何尽量发挥 Kuberbetes 的功能。但当你刚刚开始学习 Kubernetes 时,尝试在生产环境中使用前,明智的做法是从一些关于 Kubernetes 相关组件(包括 Kubernetes 调度器) 开始学习,如下抽象视图中所示:

Kubernetes 也分为控制平面和工作节点:

  1. 控制平面: 也称为主控,负责对集群做出全局决策,以及检测和响应集群事件。控制平面组件包括:
  • etcd
  • kube-apiserver
  • kube-controller-manager
  • 调度器
  1. 工作节点: 也称节点,这些节点是工作负载所在的位置。它始终和主控联系,以获取工作负载运行所需的信息,并与集群外部进行通讯和连接。工作节点组件包括:
  • kubelet
  • kube-proxy
  • CRI

我希望这个背景信息可以帮助你理解 Kubernetes 组件是如何关联在一起的。

Kubernetes 调度器是如何工作的

Kubernetes 吊舱 pod 由一个或多个容器组成组成,共享存储和网络资源。Kubernetes 调度器的任务是确保每个吊舱分配到一个节点上运行。

(LCTT 译注:容器技术领域大量使用了航海比喻,pod 一词,意为“豆荚”,在航海领域指“吊舱” —— 均指盛装多个物品的容器。常不翻译,考虑前后文,可译做“吊舱”。)

在更高层面下,Kubernetes 调度器的工作方式是这样的:

  1. 每个需要被调度的吊舱都需要加入到队列
  2. 新的吊舱被创建后,它们也会加入到队列
  3. 调度器持续地从队列中取出吊舱并对其进行调度

调度器源码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 学习方法是什么?请在评论中分享吧。


via: https://opensource.com/article/20/11/kubernetes-scheduler

作者:Mike Calizo 选题:lujun9972 译者:MZqk 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

资源配额控制应用的 CPU 或内存使用情况,防止资源被过量使用或被抢占。

 title=

当 Kubernetes 集群运行过一段时间或者在被开发者大量使用后,Kubernetes 资源(例如 CPU 和内存)的控制的问题就会显现出来。而在大多情况下只有集群出问题后,我们才会意识到资源控制的重要性。

Kubernetes 部署过程如果没有能充分考虑到将来的扩展性,资源类问题将会非常常见,此类问题与集群的管理和部署团队的经验有关。

如果不加以合理控制,一个暴力的应用或者开发者可能影响到共享该集群的所有业务,大家因此会相互埋怨、指责并保护性地抢占资源。这对于集群管理和开发人员都是非常难以处理的场景。

在 Kubernetes 环境中控制应用的计算资源使用有多种方式。大部分情况下,我们可以使用“资源控制”和“限制范围”。注意存储管理不在我们讨论范围之内,存储管理可以通过 持久卷 Persistent Volume 件,以实现针对不同的存储控制需求。

资源配额是一种控制 Kubernetes 计算资源的方法。本文告诉你如何使用该功能来管理开发人员行为并控制应用的资源使用。

什么是资源配额

简而言之,资源配额 提供了限制每个命名空间资源消耗的约束条件,它们只能在命名空间级别上应用,这意味着它们可以应用于计算资源,并限制命名空间内的对象数量。

Kubernetes资源配额通过 ResourceQuota 对象来为每个命名空间设置资源配额,对以下对象类型的 CPU 和内存进行限制:

  • 吊舱 Pod
  • 服务 Service
  • 机密信息 Secret
  • 持久卷断言 Persistent Volume Claim (PVC)
  • 配置映射 ConfigMap

Kubernetes 通过 requestlimit 两个参数对 CPU 和内存进行限制(参考 LimitRange 文档)。前者表示容器最小被保证资源,后者表示容器最大可用资源。实际上最大可用资源还受限于其它容器的实际使用情况。

下一张图片解释了配额中 requestlimit 的区别:

 title=

下面我们就通过一个例子来说明如何设置资源配额来创建约束,将应用程序限制在某些资源上,它还展示了实现资源配额以获得对 Kubernetes 的控制的有用性。

准备环境

首先你需要一个 Kubernetes 环境。以下是我使用 Kubernetes 环境:

  • Minikube v1.14.2
  • Fedora 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   100m

PodB:

$ 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  100m

PodC:

试着创建 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
  • 最新版本的 VirtualBox
  • Fedora v30+ 宿主机操作系统

这个实验室环境有什么?

这个项目旨在部署一个带有 Ansible 引擎和多个 Linux 节点的 Ansible 主机,以及一些预加载和预配置的应用程序(httpd 和 MySQL)。它还启用了 Cockpit,这样你就可以在测试过程中监控虚拟机(VM)的状态。使用预部署的应用程序的原因是为了提高效率(所以你不必花时间安装这些组件)。这样你就可以专注于创建角色和剧本,并针对上述工具部署的环境进行测试。

我们确定,对于我们的用例来说,最好的方案是多机 Vagrant 环境。Vagrant 文件创建了三个 CentOS 虚拟机,以模拟两个目标主机和一个 Ansible 控制机。

  • Host1: 没有图形用户界面(GUI),安装 httpd 和 MySQL
  • Host2: 没有 GUI,安装了 httpd 和 MySQL
  • Ansible-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:[email protected]:80"⇒ Needed if you have a proxy
#      config.proxy.https   = "http://usernam:[email protected]: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 =&gt; {
  "Ansible-facts": {
      "Discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "Changed": false;
    "Ping": "pong"
}
[...]

清理

运行如下命令来清理环境:

$ vagrant destroy [vagrant machine name]

你的输出会像这样:

 title=

有创意的学习

在自己的实验室里利用自己的时间学习 Ansible 这样的软件是一个好习惯,但由于受到无法控制的限制,可能会很困难。

有时候,你需要发挥创意,找到另一种方法。在开源社区中,你可以选择很多方案;我们选择这些工具的主要原因之一是,它们是许多人常用和熟悉的。

另外,请注意,这些剧本并没有按照我的要求进行优化。请随时改进它们,并在评论中分享你的工作。


via: https://opensource.com/article/20/12/ansible-lab

作者:Mike Calizo 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出