NMState:一个声明式网络配置工具,


这篇文章描述并演示了 NMState,这是一个使用声明式方法配置主机的网络管理器。这意味着你可以通过 API 定义所需的配置状态,而工具则通过提供者provider来应用配置。

配置方法:命令式与声明式

网络管理有时候是一项非常复杂的任务,这取决于环境的规模和多样性。在 IT 的早期,网络管理依赖于网络管理员在网络设备上手动执行命令。如今,基础设施即代码Infrastructure as Code(IaC)允许以不同的方式将这些任务自动化。z这基本上有两种方法:命令式或声明式。

在命令式方法中,你会定义“如何”达到所需的配置状态。而在声明式范式里则定义了“什么”是所需的配置状态,所以它不确定哪些步骤是必需的,也不确定它们必须以何种顺序执行。这种方法目前正在聚集更多的人员参与,你可以在目前使用的大多数管理和编排工具上找到它。

NMState:一个声明式的工具

NMState 是一个网络管理器,允许你按照声明式方法配置主机。这意味着你通过一个北向的声明式 API 定义所需的配置状态,这个工具通过南向的提供者provider应用配置。

目前 NMState 支持的唯一的提供者是 NetworkManager,它是为 Fedora Linux 提供网络功能的主要服务。不过,NMState 的开发计划中将逐渐增加其他提供者。

关于 NMState 的进一步信息,请访问其项目 站点 或 GitHub 仓库。

安装

NMState 在 Fedora Linux 29+ 上可用,需要在系统上安装并运行 NetworkManager 1.26 或更高版本。下面是在 Fedora Linux 34 上的安装情况:

  1. $ sudo dnf -y install nmstate
  2. ...
  3. 输出节略
  4. ...
  5. Installed:
  6. NetworkManager-config-server-1:1.30.4-1.fc34.noarch gobject-introspection-1.68.0-3.fc34.x86_64 nispor-1.0.1-2.fc34.x86_64 nmstate-1.0.3-2.fc34.noarch
  7. python3-gobject-base-3.40.1-1.fc34.x86_64 python3-libnmstate-1.0.3-2.fc34.noarch python3-nispor-1.0.1-2.fc34.noarch python3-varlink-30.3.1-2.fc34.noarch
  8.  
  9. Complete!

这样,你可以使用 nmstatectl 作为 NMState 的命令行工具。请参考 nmstatectl -help 或 man nmstatectl 以了解关于这个工具的进一步信息。

使用 NMstate

首先要检查系统中安装的 NMState 版本:

  1. $ nmstatectl version
  2. 1.0.3

检查一个网络接口的当前配置,例如 eth0 的配置:

  1. $ nmstatectl show eth0
  2. 2021-06-29 10:28:21,530 root DEBUG NetworkManager version 1.30.4
  3. 2021-06-29 10:28:21,531 root DEBUG Async action: Retrieve applied config: ethernet eth0 started
  4. 2021-06-29 10:28:21,531 root DEBUG Async action: Retrieve applied config: ethernet eth1 started
  5. 2021-06-29 10:28:21,532 root DEBUG Async action: Retrieve applied config: ethernet eth0 finished
  6. 2021-06-29 10:28:21,533 root DEBUG Async action: Retrieve applied config: ethernet eth1 finished
  7. ---
  8. dns-resolver:
  9. config: {}
  10. running:
  11. search: []
  12. server:
  13. - 192.168.122.1
  14. route-rules:
  15. config: []
  16. routes:
  17. config: []
  18. running:
  19. - destination: fe80::/64
  20. metric: 100
  21. next-hop-address: ''
  22. next-hop-interface: eth0
  23. table-id: 254
  24. - destination: 0.0.0.0/0
  25. metric: 100
  26. next-hop-address: 192.168.122.1
  27. next-hop-interface: eth0
  28. table-id: 254
  29. - destination: 192.168.122.0/24
  30. metric: 100
  31. next-hop-address: ''
  32. next-hop-interface: eth0
  33. table-id: 254
  34. interfaces:
  35. - name: eth0
  36. type: ethernet
  37. state: up
  38. ipv4:
  39. enabled: true
  40. address:
  41. - ip: 192.168.122.238
  42. prefix-length: 24
  43. auto-dns: true
  44. auto-gateway: true
  45. auto-route-table-id: 0
  46. auto-routes: true
  47. dhcp: true
  48. ipv6:
  49. enabled: true
  50. address:
  51. - ip: fe80::c3c9:c4f9:75b1:a570
  52. prefix-length: 64
  53. auto-dns: true
  54. auto-gateway: true
  55. auto-route-table-id: 0
  56. auto-routes: true
  57. autoconf: true
  58. dhcp: true
  59. lldp:
  60. enabled: false
  61. mac-address: 52:54:00:91:E4:4E
  62. mtu: 1500

正如你在上面看到的,这个网络配置显示了四个主要部分:

  • dns-resolver:这部分是这个接口的名字服务器配置。
  • route-rules:它说明了路由规则。
  • routes:它包括动态和静态路由。
  • interfaces:这部分描述了 ipv4 和 ipv6 设置。

修改配置

你可以在两种模式下修改所需的配置状态:

  • 交互式:通过 nmstatectl edit 编辑接口配置。这个命令调用环境变量 EDITOR 定义的文本编辑器,因此可以用 yaml 格式编辑网络状态。完成编辑后,NMState 将应用新的网络配置,除非有语法错误。
  • 基于文件的:使用 nmstatectl apply 应用接口配置,它从先前创建的 yaml 或 json 文件中导入一个所需的配置状态。

下面几节告诉你如何使用 NMState 来改变网络配置。这些改变可能会对系统造成破坏,所以建议在测试系统或客户虚拟机上执行这些任务,直到你对 NMState 有更好的理解。

这里使用的测试系统有两个以太网接口,eth0 和 eth1

  1. $ ip -br -4 a
  2. lo UNKNOWN 127.0.0.1/8
  3. eth0 UP 192.168.122.238/24
  4. eth1 UP 192.168.122.108/24

互动配置模式的例子

使用 nmstatectl edit 命令将 eth0 接口的 MTU 改为 9000 字节,如下所示:

  1. $ sudo nmstatectl edit eth0
  2.  
  3. ---
  4. dns-resolver:
  5. config: {}
  6. running:
  7. search: []
  8. server:
  9. - 192.168.122.1
  10. route-rules:
  11. config: []
  12. routes:
  13. config: []
  14. running:
  15. - destination: fe80::/64
  16. metric: 100
  17. next-hop-address: ''
  18. next-hop-interface: eth0
  19. table-id: 254
  20. - destination: 0.0.0.0/0
  21. metric: 100
  22. next-hop-address: 192.168.122.1
  23. next-hop-interface: eth0
  24. table-id: 254
  25. - destination: 192.168.122.0/24
  26. metric: 100
  27. next-hop-address: ''
  28. next-hop-interface: eth0
  29. table-id: 254
  30. interfaces:
  31. - name: eth0
  32. type: ethernet
  33. state: up
  34. ipv4:
  35. enabled: true
  36. address:
  37. - ip: 192.168.122.123
  38. prefix-length: 24
  39. auto-dns: true
  40. auto-gateway: true
  41. auto-route-table-id: 0
  42. auto-routes: true
  43. dhcp: true
  44. ipv6:
  45. enabled: true
  46. address:
  47. - ip: fe80::c3c9:c4f9:75b1:a570
  48. prefix-length: 64
  49. auto-dns: true
  50. auto-gateway: true
  51. auto-route-table-id: 0
  52. auto-routes: true
  53. autoconf: true
  54. dhcp: true
  55. lldp:
  56. enabled: false
  57. mac-address: 52:54:00:91:E4:4E
  58. mtu: 9000

在保存并退出编辑器后,NMState 应用新的网络期望状态:

  1. 2021-06-29 11:29:05,726 root DEBUG Nmstate version: 1.0.3
  2. 2021-06-29 11:29:05,726 root DEBUG Applying desire state: {'dns-resolver': {'config': {}, 'running': {'search': [], 'server': ['192.168.122.1']}}, 'route-rules': {'config': []}, 'routes': {'config': [], 'running': [{'destination': 'fe80::/64', 'metric': 102, 'next-hop-address': '', 'next-hop-interface': 'eth0', 'table-id': 254}, {'destination': '0.0.0.0/0', 'metric': 102, 'next-hop-address': '192.168.122.1', 'next-hop-interface': 'eth0', 'table-id': 254}, {'destination': '192.168.122.0/24', 'metric': 102, 'next-hop-address': '', 'next-hop-interface': 'eth0', 'table-id': 254}]}, 'interfaces': [{'name': 'eth0', 'type': 'ethernet', 'state': 'up', 'ipv4': {'enabled': True, 'address': [{'ip': '192.168.122.238', 'prefix-length': 24}], 'auto-dns': True, 'auto-gateway': True, 'auto-route-table-id': 0, 'auto-routes': True, 'dhcp': True}, 'ipv6': {'enabled': True, 'address': [{'ip': 'fe80::5054:ff:fe91:e44e', 'prefix-length': 64}], 'auto-dns': True, 'auto-gateway': True, 'auto-route-table-id': 0, 'auto-routes': True, 'autoconf': True, 'dhcp': True}, 'lldp': {'enabled': False}, 'mac-address': '52:54:00:91:E4:4E', 'mtu': 9000}]}
  3. --- output omitted ---
  4. 2021-06-29 11:29:05,760 root DEBUG Async action: Update profile uuid:2bdee700-f62b-365a-bd1d-69d9c31a9f0c iface:eth0 type:ethernet started
  5. 2021-06-29 11:29:05,792 root DEBUG Async action: Update profile uuid:2bdee700-f62b-365a-bd1d-69d9c31a9f0c iface:eth0 type:ethernet finished

现在,使用 ip 命令和 eth0 的配置文件来检查 eth0 的 MTU 是不是 9000 字节。

  1. $ ip link show eth0
  2. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
  3. link/ether 52:54:00:91:e4:4e brd ff:ff:ff:ff:ff:ff
  4. altname enp1s0
  5.  
  6. $ sudo cat /etc/NetworkManager/system-connections/eth0.nmconnection
  7. [sudo] password for admin:
  8. [connection]
  9. id=eth0
  10. uuid=2bdee700-f62b-365a-bd1d-69d9c31a9f0c
  11. type=ethernet
  12. interface-name=eth0
  13. lldp=0
  14. permissions=
  15.  
  16. [ethernet]
  17. cloned-mac-address=52:54:00:91:E4:4E
  18. mac-address-blacklist=
  19. mtu=9000
  20.  
  21. [ipv4]
  22. dhcp-client-id=mac
  23. dhcp-timeout=2147483647
  24. dns-search=
  25. method=auto
  26.  
  27. [ipv6]
  28. addr-gen-mode=eui64
  29. dhcp-duid=ll
  30. dhcp-iaid=mac
  31. dhcp-timeout=2147483647
  32. dns-search=
  33. method=auto
  34. ra-timeout=2147483647
  35.  
  36. [proxy]

基于文件的配置模式的例子

让我们使用基于文件的方法来设置一个新的配置状态。这里我们禁用 eth1 接口的 IPv6 配置。

首先,创建一个 yaml 文件来定义 eth1 接口的期望状态。使用 nmstatectl show 来保存当前设置,然后使用 nmstatectl edit 来禁用 IPv6。

  1. $ nmstatectl show eth1 > eth1.yaml
  2.  
  3. $ vi eth1.yaml
  4. ---
  5. dns-resolver:
  6. config: {}
  7. running:
  8. search: []
  9. server:
  10. - 192.168.122.1
  11. route-rules:
  12. config: []
  13. routes:
  14. config: []
  15. running:
  16. - destination: fe80::/64
  17. metric: 101
  18. next-hop-address: ''
  19. next-hop-interface: eth1
  20. table-id: 254
  21. - destination: 0.0.0.0/0
  22. metric: 101
  23. next-hop-address: 192.168.122.1
  24. next-hop-interface: eth1
  25. table-id: 254
  26. - destination: 192.168.122.0/24
  27. metric: 101
  28. next-hop-address: ''
  29. next-hop-interface: eth1
  30. table-id: 254
  31. interfaces:
  32. - name: eth1
  33. type: ethernet
  34. state: up
  35. ipv4:
  36. enabled: true
  37. address:
  38. - ip: 192.168.122.108
  39. prefix-length: 24
  40. auto-dns: true
  41. auto-gateway: true
  42. auto-route-table-id: 0
  43. auto-routes: true
  44. dhcp: true
  45. ipv6:
  46. enabled: false
  47. address:
  48. - ip: fe80::5054:ff:fe3c:9b04
  49. prefix-length: 64
  50. auto-dns: true
  51. auto-gateway: true
  52. auto-route-table-id: 0
  53. auto-routes: true
  54. autoconf: true
  55. dhcp: true
  56. lldp:
  57. enabled: false
  58. mac-address: 52:54:00:3C:9B:04
  59. mtu: 1500

保存新的配置后,用它来应用新的状态:

  1. $ sudo nmstatectl apply eth1.yaml
  2.  
  3. 2021-06-29 12:17:21,531 root DEBUG Nmstate version: 1.0.3
  4. 2021-06-29 12:17:21,531 root DEBUG Applying desire state: {'dns-resolver': {'config': {}, 'running': {'search': [], 'server': ['192.168.122.1']}}, 'route-rules': {'config': []}, 'routes': {'config': [], 'running': [{'destination': 'fe80::/64', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '0.0.0.0/0', 'metric': 101, 'next-hop-address': '192.168.122.1', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '192.168.122.0/24', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}]}, 'interfaces': [{'name': 'eth1', 'type': 'ethernet', 'state': 'up', 'ipv4': {'enabled': True, 'address': [{'ip': '192.168.122.108', 'prefix-length': 24}], 'auto-dns': True, 'auto-gateway': True, 'auto-route-table-id': 0, 'auto-routes': True, 'dhcp': True}, 'ipv6': {'enabled': False}, 'lldp': {'enabled': False}, 'mac-address': '52:54:00:3C:9B:04', 'mtu': 1500}]}
  5. --- output omitted ---
  6. 2021-06-29 12:17:21,582 root DEBUG Async action: Update profile uuid:5d7244cb-673d-3b88-a675-32e31fad4347 iface:eth1 type:ethernet started
  7. 2021-06-29 12:17:21,587 root DEBUG Async action: Update profile uuid:5d7244cb-673d-3b88-a675-32e31fad4347 iface:eth1 type:ethernet finished
  8. --- output omitted ---
  9. Desired state applied:
  10. ---
  11. dns-resolver:
  12. config: {}
  13. running:
  14. search: []
  15. server:
  16. - 192.168.122.1
  17. route-rules:
  18. config: []
  19. routes:
  20. config: []
  21. running:
  22. - destination: fe80::/64
  23. metric: 101
  24. next-hop-address: ''
  25. next-hop-interface: eth1
  26. table-id: 254
  27. - destination: 0.0.0.0/0
  28. metric: 101
  29. next-hop-address: 192.168.122.1
  30. next-hop-interface: eth1
  31. table-id: 254
  32. - destination: 192.168.122.0/24
  33. metric: 101
  34. next-hop-address: ''
  35. next-hop-interface: eth1
  36. table-id: 254
  37. interfaces:
  38. - name: eth1
  39. type: ethernet
  40. state: up
  41. ipv4:
  42. enabled: true
  43. address:
  44. - ip: 192.168.122.108
  45. prefix-length: 24
  46. auto-dns: true
  47. auto-gateway: true
  48. auto-route-table-id: 0
  49. auto-routes: true
  50. dhcp: true
  51. ipv6:
  52. enabled: false
  53. lldp:
  54. enabled: false
  55. mac-address: 52:54:00:3C:9B:04
  56. mtu: 1500

你可以检查看到 eth1 接口没有配置任何 IPv6:

  1. $ ip -br a
  2. lo UNKNOWN 127.0.0.1/8 ::1/128
  3. eth0 UP 192.168.122.238/24 fe80::5054:ff:fe91:e44e/64
  4. eth1 UP 192.168.122.108/24
  5.  
  6. $ sudo cat /etc/NetworkManager/system-connections/eth1.nmconnection
  7. [connection]
  8. id=eth1
  9. uuid=5d7244cb-673d-3b88-a675-32e31fad4347
  10. type=ethernet
  11. interface-name=eth1
  12. lldp=0
  13. permissions=
  14.  
  15. [ethernet]
  16. cloned-mac-address=52:54:00:3C:9B:04
  17. mac-address-blacklist=
  18. mtu=1500
  19.  
  20. [ipv4]
  21. dhcp-client-id=mac
  22. dhcp-timeout=2147483647
  23. dns-search=
  24. method=auto
  25.  
  26. [ipv6]
  27. addr-gen-mode=eui64
  28. dhcp-duid=ll
  29. dhcp-iaid=mac
  30. dns-search=
  31. method=disabled
  32.  
  33. [proxy]

临时应用改变

NMState 的一个有趣的功能允许你临时配置一个期望的网络状态。如果你对这个配置感到满意,你可以事后提交。否则,当超时(默认为 60 秒)过后,它将回滚。

修改前面例子中的 eth1 配置,使它有一个 IPv4 静态地址,而不是通过 DHCP 动态获得。

  1. $ vi eth1.yaml
  2.  
  3. ---
  4. dns-resolver:
  5. config: {}
  6. running:
  7. search: []
  8. server:
  9. - 192.168.122.1
  10. route-rules:
  11. config: []
  12. routes:
  13. config: []
  14. running:
  15. - destination: fe80::/64
  16. metric: 101
  17. next-hop-address: ''
  18. next-hop-interface: eth1
  19. table-id: 254
  20. - destination: 0.0.0.0/0
  21. metric: 101
  22. next-hop-address: 192.168.122.1
  23. next-hop-interface: eth1
  24. table-id: 254
  25. - destination: 192.168.122.0/24
  26. metric: 101
  27. next-hop-address: ''
  28. next-hop-interface: eth1
  29. table-id: 254
  30. interfaces:
  31. - name: eth1
  32. type: ethernet
  33. state: up
  34. ipv4:
  35. enabled: true
  36. address:
  37. - ip: 192.168.122.110
  38. prefix-length: 24
  39. auto-dns: true
  40. auto-gateway: true
  41. auto-route-table-id: 0
  42. auto-routes: true
  43. dhcp: false
  44. ipv6:
  45. enabled: false
  46. lldp:
  47. enabled: false
  48. mac-address: 52:54:00:3C:9B:04
  49. mtu: 1500

现在,使用选项 no-commit 临时应用这个配置,让它只在 30 秒内有效。这可以通过添加选项 timeout 来完成。同时,我们将运行 ip -br a 命令三次,看看配置在 eth1 接口的 IPv4 地址是如何变化的,然后配置就会回滚。

  1. $ ip -br a && sudo nmstatectl apply --no-commit --timeout 30 eth1.yaml && sleep 10 && ip -br a && sleep 25 && ip -br a
  2. lo UNKNOWN 127.0.0.1/8 ::1/128
  3. eth0 UP 192.168.122.238/24 fe80::5054:ff:fe91:e44e/64
  4. eth1 UP 192.168.122.108/24
  5. 2021-06-29 17:29:18,266 root DEBUG Nmstate version: 1.0.3
  6. 2021-06-29 17:29:18,267 root DEBUG Applying desire state: {'dns-resolver': {'config': {}, 'running': {'search': [], 'server': ['192.168.122.1']}}, 'route-rules': {'config': []}, 'routes': {'config': [], 'running': [{'destination': 'fe80::/64', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '0.0.0.0/0', 'metric': 101, 'next-hop-address': '192.168.122.1', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '192.168.122.0/24', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}]}, 'interfaces': [{'name': 'eth1', 'type': 'ethernet', 'state': 'up', 'ipv4': {'enabled': True, 'address': [{'ip': '192.168.122.110', 'prefix-length': 24}], 'dhcp': False}, 'ipv6': {'enabled': False}, 'lldp': {'enabled': False}, 'mac-address': '52:54:00:3C:9B:04', 'mtu': 1500}]}
  7. --- output omitted ---
  8. Desired state applied:
  9. ---
  10. dns-resolver:
  11. config: {}
  12. running:
  13. search: []
  14. server:
  15. - 192.168.122.1
  16. route-rules:
  17. config: []
  18. routes:
  19. config: []
  20. running:
  21. - destination: fe80::/64
  22. metric: 101
  23. next-hop-address: ''
  24. next-hop-interface: eth1
  25. table-id: 254
  26. - destination: 0.0.0.0/0
  27. metric: 101
  28. next-hop-address: 192.168.122.1
  29. next-hop-interface: eth1
  30. table-id: 254
  31. - destination: 192.168.122.0/24
  32. metric: 101
  33. next-hop-address: ''
  34. next-hop-interface: eth1
  35. table-id: 254
  36. interfaces:
  37. - name: eth1
  38. type: ethernet
  39. state: up
  40. ipv4:
  41. enabled: true
  42. address:
  43. - ip: 192.168.122.110
  44. prefix-length: 24
  45. dhcp: false
  46. ipv6:
  47. enabled: false
  48. lldp:
  49. enabled: false
  50. mac-address: 52:54:00:3C:9B:04
  51. mtu: 1500
  52. Checkpoint: NetworkManager|/org/freedesktop/NetworkManager/Checkpoint/7
  53. lo UNKNOWN 127.0.0.1/8 ::1/128
  54. eth0 UP 192.168.122.238/24 fe80::5054:ff:fe91:e44e/64
  55. eth1 UP 192.168.122.110/24
  56. lo UNKNOWN 127.0.0.1/8 ::1/128
  57. eth0 UP 192.168.122.238/24 fe80::5054:ff:fe91:e44e/64
  58. eth1 UP 192.168.122.108/24

从上面可以看到,eth1 的 IP 地址从 192.168.122.108 暂时变成了 192.168.122.110,然后在超时结束后又回到了 192.168.122.108

总结

NMState 是一个声明式的网络配置工具,目前可以通过 NetworkManager API 在主机中应用所需的网络配置状态。这种状态既可以用文本编辑器交互式地定义,也可以用基于文件的方法创建一个 yaml 或 json 文件。

这种工具提供了“基础设施即代码”,它可以自动化网络任务,也减少了使用传统配置方法可能出现的潜在错误配置或不稳定的网络情况。

相关内容