閱讀804 返回首頁    go 阿裏雲 go 技術社區[雲棲]


LXD 2.0 係列(四):資源控製

因為 LXD 容器管理有很多命令,因此這篇文章會很長。 如果你想要快速地瀏覽這些相同的命令,你可以嚐試下我們的在線演示

可用資源限製

LXD 提供了各種資源限製。其中一些與容器本身相關,如內存配額、CPU 限製和 I/O 優先級。而另外一些則與特定設備相關,如 I/O 帶寬或磁盤用量限製。

與所有 LXD 配置一樣,資源限製可以在容器運行時動態更改。某些可能無法啟用,例如,如果設置的內存值小於當前內存用量,但 LXD 將會試著設置並且報告失敗。

所有的限製也可以通過配置文件繼承,在這種情況下每個受影響的容器將受到該限製的約束。也就是說,如果在默認配置文件中設置 limits.memory=256MB,則使用默認配置文件(通常是全都使用)的每個容器的內存限製為 256MB。

我們不支持資源限製池,將其中的限製由一組容器共享,因為我們沒有什麼好的方法通過現有的內核 API 實現這些功能。

磁盤

這或許是最需要和最明顯的需求。隻需設置容器文件係統的大小限製,並對容器強製執行。

LXD 確實可以讓你這樣做!

不幸的是,這比它聽起來複雜得多。 Linux 沒有基於路徑的配額,而大多數文件係統隻有基於用戶和組的配額,這對容器沒有什麼用處。

如果你正在使用 ZFS 或 btrfs 存儲後端,這意味著現在 LXD 隻能支持磁盤限製。也有可能為 LVM 實現此功能,但這取決於與它一起使用的文件係統,並且如果結合實時更新那會變得棘手起來,因為並不是所有的文件係統都允許在線增長,而幾乎沒有一個允許在線收縮。

CPU

當涉及到 CPU 的限製,我們支持 4 種不同的東西:

  • 隻給我 X 個 CPU 核心

    在這種模式下,你讓 LXD 為你選擇一組核心,然後為更多的容器和 CPU 的上線/下線提供負載均衡。

    容器隻看到這個數量的 CPU 核心。

  • 給我一組特定的 CPU 核心(例如,核心1、3 和 5)

    類似於第一種模式,但是不會做負載均衡,你會被限製在那些核心上,無論它們有多忙。

  • 給我你擁有的 20% 處理能力

    在這種模式下,你可以看到所有的 CPU,但調度程序將限製你使用 20% 的 CPU 時間,但這隻有在負載狀態才會這樣!所以如果係統不忙,你的容器可以跑得很歡。而當其他的容器也開始使用 CPU 時,它會被限製用量。

  • 每測量 200ms,給我 50ms(並且不超過)

    此模式與上一個模式類似,你可以看到所有的 CPU,但這一次,無論係統可能是多麼空閑,你隻能使用你設置的極限時間下的盡可能多的 CPU 時間。在沒有過量使用的係統上,這可使你可以非常整齊地分割 CPU,並確保這些容器的持續性能。

另外還可以將前兩個中的一個與最後兩個之一相結合,即請求一組 CPU,然後進一步限製這些 CPU 的 CPU 時間。

除此之外,我們還有一個通用的優先級調節方式,可以告訴調度器當你處於負載狀態時,兩個爭奪資源的容器誰會取得勝利。

內存

內存聽起來很簡單,就是給我多少 MB 的內存!

它絕對可以那麼簡單。 我們支持這種限製以及基於百分比的請求,比如給我 10% 的主機內存!

另外我們在上層支持一些額外的東西。 例如,你可以選擇在每個容器上打開或者關閉 swap,如果打開,還可以設置優先級,以便你可以選擇哪些容器先將內存交換到磁盤!

內存限製默認是“hard”。 也就是說,當內存耗盡時,內核將會開始殺掉你的那些進程。

或者,你可以將強製策略設置為“soft”,在這種情況下,隻要沒有別的進程的情況下,你將被允許使用盡可能多的內存。一旦別的進程想要這塊內存,你將無法分配任何內存,直到你低於你的限製或者主機內存再次有空餘。

網絡 I/O

網絡 I/O 可能是我們看起來最簡單的限製,但是相信我,實現真的不簡單!

我們支持兩種限製。 第一個是對網絡接口的速率限製。你可以設置入口和出口的限製,或者隻是設置“最大”限製然後應用到出口和入口。這個隻支持“橋接”和“p2p”類型接口。

第二種是全局網絡 I/O 優先級,僅當你的網絡接口趨於飽和的時候再使用。

塊 I/O

我把最古怪的放在最後。對於用戶看起來它可能簡單,但有一些情況下,它的結果並不會和你的預期一樣。

我們在這裏支持的基本上與我在網絡 I/O 中描述的相同。

你可以直接設置磁盤的讀寫 IO 的頻率和速率,並且有一個全局的塊 I/O 優先級,它會通知 I/O 調度程序更傾向哪個。

古怪的是如何設置以及在哪裏應用這些限製。不幸的是,我們用於實現這些功能的底層使用的是完整的塊設備。這意味著我們不能為每個路徑設置每個分區的 I/O 限製。

這也意味著當使用可以支持多個塊設備映射到指定的路徑(帶或者不帶 RAID)的 ZFS 或 btrfs 時,我們並不知道這個路徑是哪個塊設備提供的。

這意味著,完全有可能,實際上確實有可能,容器使用的多個磁盤掛載點(綁定掛載或直接掛載)可能來自於同一個物理磁盤。

這就使限製變得很奇怪。為了使限製生效,LXD 具有猜測給定路徑所對應塊設備的邏輯,這其中包括詢問 ZFS 和 btrfs 工具,甚至可以在發現一個文件係統中循環掛載的文件時遞歸地找出它們。

這個邏輯雖然不完美,但通常會找到一組應該應用限製的塊設備。LXD 接著記錄並移動到下一個路徑。當遍曆完所有的路徑,然後到了非常奇怪的部分。它會平均你為相應塊設備設置的限製,然後應用這些。

這意味著你將在容器中“平均”地獲得正確的速度,但這也意味著你不能對來自同一個物理磁盤的“/fast”和一個“/slow”目錄應用不同的速度限製。 LXD 允許你設置它,但最後,它會給你這兩個值的平均值。

它怎麼工作?

除了網絡限製是通過較舊但是良好的“tc”實現的,上述大多數限製是通過 Linux 內核的 cgroup API 來實現的。

LXD 在啟動時會檢測你在內核中啟用了哪些 cgroup,並且將隻應用你的內核支持的限製。如果你缺少一些 cgroup,守護進程會輸出警告,接著你的 init 係統將會記錄這些。

在 Ubuntu 16.04 上,默認情況下除了內存交換審計外將會啟用所有限製,內存交換審計需要你通過swapaccount = 1這個內核引導參數來啟用。

應用這些限製

上述所有限製都能夠直接或者用某個配置文件應用於容器。容器範圍的限製可以使用:


  1. lxc config set CONTAINER KEY VALUE

或對於配置文件設置:


  1. lxc profile set PROFILE KEY VALUE

當指定特定設備時:


  1. lxc config device set CONTAINER DEVICE KEY VALUE

或對於配置文件設置:


  1. lxc profile device set PROFILE DEVICE KEY VALUE

有效配置鍵、設備類型和設備鍵的完整列表可以看這裏

CPU

要限製使用任意兩個 CPU 核心可以這麼做:


  1. lxc config set my-container limits.cpu 2

要指定特定的 CPU 核心,比如說第二和第四個:


  1. lxc config set my-container limits.cpu 1,3

更加複雜的情況還可以設置範圍:


  1. lxc config set my-container limits.cpu 0-3,7-11

限製實時生效,你可以看下麵的例子:


  1. stgraber@dakara:~$ lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
  2. processor : 0
  3. processor : 1
  4. processor : 2
  5. processor : 3
  6. stgraber@dakara:~$ lxc config set zerotier limits.cpu 2
  7. stgraber@dakara:~$ lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
  8. processor : 0
  9. processor : 1

注意,為了避免完全混淆用戶空間,lxcfs 會重排 /proc/cpuinfo 中的條目,以便沒有錯誤。

就像 LXD 中的一切,這些設置也可以應用在配置文件中:


  1. stgraber@dakara:~$ lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
  2. processor : 0
  3. processor : 1
  4. processor : 2
  5. processor : 3
  6. stgraber@dakara:~$ lxc profile set default limits.cpu 3
  7. stgraber@dakara:~$ lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
  8. processor : 0
  9. processor : 1
  10. processor : 2

要限製容器使用 10% 的 CPU 時間,要設置下 CPU allowance:


  1. lxc config set my-container limits.cpu.allowance 10%

或者給它一個固定的 CPU 時間切片:


  1. lxc config set my-container limits.cpu.allowance 25ms/200ms

最後,要將容器的 CPU 優先級調到最低:


  1. lxc config set my-container limits.cpu.priority 0

內存

要直接應用內存限製運行下麵的命令:


  1. lxc config set my-container limits.memory 256MB

(支持的後綴是 KB、MB、GB、TB、PB、EB)

要關閉容器的內存交換(默認啟用):


  1. lxc config set my-container limits.memory.swap false

告訴內核首先交換指定容器的內存:


  1. lxc config set my-container limits.memory.swap.priority 0

如果你不想要強製的內存限製:


  1. lxc config set my-container limits.memory.enforce soft

磁盤和塊 I/O

不像 CPU 和內存,磁盤和 I/O 限製是直接作用在實際的設備上的,因此你需要編輯原始設備或者屏蔽某個具體的設備。

要設置磁盤限製(需要 btrfs 或者 ZFS):


  1. lxc config device set my-container root size 20GB

比如:


  1. stgraber@dakara:~$ lxc exec zerotier -- df -h /
  2. Filesystem Size Used Avail Use% Mounted on
  3. encrypted/lxd/containers/zerotier 179G 542M 178G 1% /
  4. stgraber@dakara:~$ lxc config device set zerotier root size 20GB
  5. stgraber@dakara:~$ lxc exec zerotier -- df -h /
  6. Filesystem Size Used Avail Use% Mounted on
  7. encrypted/lxd/containers/zerotier 20G 542M 20G 3% /

要限製速度,你可以:


  1. lxc config device set my-container root limits.read 30MB
  2. lxc config device set my-container root.limits.write 10MB

或者限製 IO 頻率:


  1. lxc config device set my-container root limits.read 20Iops
  2. lxc config device set my-container root limits.write 10Iops

最後你在一個過量使用的繁忙係統上,你或許想要:


  1. lxc config set my-container limits.disk.priority 10

將那個容器的 I/O 優先級調到最高。

網絡 I/O

隻要機製可用,網絡 I/O 基本等同於塊 I/O。

比如:


  1. stgraber@dakara:~$ lxc exec zerotier -- wget http://speedtest.newark.linode.com/100MB-newark.bin -O /dev/null
  2. --2016-03-26 22:17:34-- http://speedtest.newark.linode.com/100MB-newark.bin
  3. Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
  4. Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
  5. HTTP request sent, awaiting response... 200 OK
  6. Length: 104857600 (100M) [application/octet-stream]
  7. Saving to: '/dev/null'
  8. /dev/null 100%[===================>] 100.00M 58.7MB/s in 1.7s
  9. 2016-03-26 22:17:36 (58.7 MB/s) - '/dev/null' saved [104857600/104857600]
  10. stgraber@dakara:~$ lxc profile device set default eth0 limits.ingress 100Mbit
  11. stgraber@dakara:~$ lxc profile device set default eth0 limits.egress 100Mbit
  12. stgraber@dakara:~$ lxc exec zerotier -- wget http://speedtest.newark.linode.com/100MB-newark.bin -O /dev/null
  13. --2016-03-26 22:17:47-- http://speedtest.newark.linode.com/100MB-newark.bin
  14. Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
  15. Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
  16. HTTP request sent, awaiting response... 200 OK
  17. Length: 104857600 (100M) [application/octet-stream]
  18. Saving to: '/dev/null'
  19. /dev/null 100%[===================>] 100.00M 11.4MB/s in 8.8s
  20. 2016-03-26 22:17:56 (11.4 MB/s) - '/dev/null' saved [104857600/104857600]

這就是如何將一個千兆網的連接速度限製到僅僅 100Mbit/s 的!

和塊 I/O 一樣,你可以設置一個總體的網絡優先級:


  1. lxc config set my-container limits.network.priority 5

獲取當前資源使用率

LXD API 可以導出目前容器資源使用情況的一點信息,你可以得到:

  • 內存:當前、峰值、目前內存交換和峰值內存交換
  • 磁盤:當前磁盤使用率
  • 網絡:每個接口傳輸的字節和包數。

另外如果你使用的是非常新的 LXD(在寫這篇文章時的 git 版本),你還可以在lxc info中得到這些信息:


  1. stgraber@dakara:~$ lxc info zerotier
  2. Name: zerotier
  3. Architecture: x86_64
  4. Created: 2016/02/20 20:01 UTC
  5. Status: Running
  6. Type: persistent
  7. Profiles: default
  8. Pid: 29258
  9. Ips:
  10. eth0: inet 172.17.0.101
  11. eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
  12. eth0: inet6 fe80::216:3eff:feec:65a8
  13. lo: inet 127.0.0.1
  14. lo: inet6 ::1
  15. lxcbr0: inet 10.0.3.1
  16. lxcbr0: inet6 fe80::f0bd:55ff:feee:97a2
  17. zt0: inet 29.17.181.59
  18. zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
  19. zt0: inet6 fe80::79:e7ff:fe0d:5123
  20. Resources:
  21. Processes: 33
  22. Disk usage:
  23. root: 808.07MB
  24. Memory usage:
  25. Memory (current): 106.79MB
  26. Memory (peak): 195.51MB
  27. Swap (current): 124.00kB
  28. Swap (peak): 124.00kB
  29. Network usage:
  30. lxcbr0:
  31. Bytes received: 0 bytes
  32. Bytes sent: 570 bytes
  33. Packets received: 0
  34. Packets sent: 0
  35. zt0:
  36. Bytes received: 1.10MB
  37. Bytes sent: 806 bytes
  38. Packets received: 10957
  39. Packets sent: 10957
  40. eth0:
  41. Bytes received: 99.35MB
  42. Bytes sent: 5.88MB
  43. Packets received: 64481
  44. Packets sent: 64481
  45. lo:
  46. Bytes received: 9.57kB
  47. Bytes sent: 9.57kB
  48. Packets received: 81
  49. Packets sent: 81
  50. Snapshots:
  51. zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)

總結

LXD 團隊花費了幾個月的時間來迭代我們使用的這些限製的語言。 它是為了在保持強大和功能明確的基礎上同時保持簡單。

實時地應用這些限製和通過配置文件繼承,使其成為一種非常強大的工具,可以在不影響正在運行的服務的情況下實時管理服務器上的負載。

原文發布時間為:2017-12-29

本文來自雲棲社區合作夥伴“Linux中國”

最後更新:2017-05-31 11:04:40

  上一篇:go  如何在 Ubuntu 環境下搭建郵件服務器(一)
  下一篇:go  《Spring實戰(第4版)》——2.4 通過XML裝配bean