2016-06-14

RHEL 7 网络初探

Redhat Linux 7已经出来好久了,但是我一直没有仔细研究。
现在因为项目需要,我需要弄明白Oracle Linux 7上面的网络配置。
Oracle Linux其实和Redhat Linux 7是几乎完全一样的。
所以我下面要写的东西对于两者应该是通用的。CentOS 7应该也可以。

NetworkManager vs network

在RHEL 6里面,其实已经有NetworkManager这个东西了。
但是我们platform team总是给我们在ifcfg-*里面加上了NM_CONTROLLED=no,
所以很长时间以来我们用的都是古老的network scripts。

为了方便地做各种实验,我用vagrant创建一台两个网口的虚拟机:

Vagrant.configure(2) do |config|
  config.vm.box = "OL7DockerBtrfs"
  config.vm.box_check_update = false
  config.vm.network "private_network", ip: "192.168.56.101"
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "2048"
  end
end

这个Vagrantfile里面的box OL7DockerBtrfs是我自己用packer做出来的。
换用标准的rhel 7或者centos 7应该行为没有差别。

在我刚刚启动起来的OL7 vagrant虚拟机里面:

[root@BasicOL7-Docker ~]# systemctl status network
● network.service - LSB: Bring up/down networking
   Loaded: loaded (/etc/rc.d/init.d/network)
   Active: active (exited) since Tue 2016-06-14 07:18:52 EDT; 1min 25s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 652 ExecStart=/etc/rc.d/init.d/network start (code=exited, status=0/SUCCESS)

Jun 14 07:18:52 BasicOL7-Docker systemd[1]: Starting LSB: Bring up/down network....
Jun 14 07:18:52 BasicOL7-Docker network[652]: Bringing up loopback interface:  ...'
Jun 14 07:18:52 BasicOL7-Docker network[652]: Could not load file '/etc/sysconf...'
Jun 14 07:18:52 BasicOL7-Docker network[652]: Could not load file '/etc/sysconf...'
Jun 14 07:18:52 BasicOL7-Docker network[652]: Could not load file '/etc/sysconf...'
Jun 14 07:18:52 BasicOL7-Docker network[652]: [  OK  ]
Jun 14 07:18:52 BasicOL7-Docker network[652]: Bringing up interface enp0s3:  [ ...]
Jun 14 07:18:52 BasicOL7-Docker systemd[1]: Started LSB: Bring up/down networking.
Hint: Some lines were ellipsized, use -l to show in full.
[root@BasicOL7-Docker ~]# systemctl status NetworkManager
● NetworkManager.service - Network Manager
   Loaded: loaded (/usr/lib/systemd/system/NetworkManager.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2016-06-14 11:18:46 EDT; 3h 58min left
 Main PID: 530 (NetworkManager)
   CGroup: /system.slice/NetworkManager.service
           ├─530 /usr/sbin/NetworkManager --no-daemon
           └─590 /sbin/dhclient -d -q -sf /usr/libexec/nm-dhcp-helper -pf /var/r...

Jun 14 11:18:46 BasicOL7-Docker NetworkManager[530]:   NetworkManager stat...
Jun 14 11:18:46 BasicOL7-Docker NetworkManager[530]:   Policy set 'enp0s3'...
Jun 14 11:18:46 BasicOL7-Docker NetworkManager[530]:   (enp0s3): Activatio...
Jun 14 11:18:46 BasicOL7-Docker dhclient[590]: bound to 10.0.2.15 -- renewal in....
Jun 14 07:18:52 BasicOL7-Docker NetworkManager[530]:   startup complete
Jun 14 07:18:58 BasicOL7-Docker NetworkManager[530]:   ifcfg-rh: new conne...
Jun 14 07:18:58 BasicOL7-Docker NetworkManager[530]:   ifcfg-rh: Ignoring ...
Jun 14 07:18:58 BasicOL7-Docker NetworkManager[530]:   (enp0s8): device st...
Jun 14 07:18:58 BasicOL7-Docker NetworkManager[530]:   (enp0s8): link disc...
Jun 14 07:18:58 BasicOL7-Docker NetworkManager[530]:   (enp0s8): link conn...
Hint: Some lines were ellipsized, use -l to show in full.

这里可以看到network和NetworkManager都起来了。
这里的network.service是由systemd-sysv-generator在启动时创建的,为了兼容
古老的网络启动脚本。

启动网口

Vagrant虚拟机运行起来之后,我可以用ip -4 addr看到,我要的两个网口都顺利
拿到了IP地址:

1: lo:  mtu 65536 qdisc noqueue state UNKNOWN
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp0s3:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 86111sec preferred_lft 86111sec
3: enp0s8:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 192.168.56.101/24 brd 192.168.56.255 scope global enp0s8
       valid_lft forever preferred_lft forever

然后去看看这两个网卡的ifcfg文件:

[root@BasicOL7-Docker ~]# cd /etc/sysconfig/network-scripts/
[root@BasicOL7-Docker network-scripts]# ls ifcfg-*
ifcfg-enp0s3  ifcfg-enp0s8  ifcfg-lo
[root@BasicOL7-Docker network-scripts]# cat ifcfg-enp0s3
# Generated by dracut initrd
NAME="enp0s3"
DEVICE="enp0s3"
ONBOOT=yes
NETBOOT=yes
UUID="909dae31-7e36-4c00-a74f-d33399600fe0"
IPV6INIT=yes
BOOTPROTO=dhcp
TYPE=Ethernet
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
PEERDNS=yes
PEERROUTES=yes
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
[root@BasicOL7-Docker network-scripts]# cat ifcfg-enp0s8
#VAGRANT-BEGIN
# The contents below are automatically generated by Vagrant. Do not modify.
NM_CONTROLLED=no
BOOTPROTO=none
ONBOOT=yes
IPADDR=192.168.56.101
NETMASK=255.255.255.0
DEVICE=enp0s8
PEERDNS=no
#VAGRANT-END

看上去不错哦,一个网卡enp0s3是用network manager管理的,另一个enp0s8则是
用传统方法管理的。
这一点也可以在nmcli device的输出里面看出来:

[root@BasicOL7-Docker network-scripts]# nmcli device
DEVICE  TYPE      STATE      CONNECTION
enp0s3  ethernet  connected  enp0s3     
enp0s8  ethernet  unmanaged  --         
lo      loopback  unmanaged  --   

现在问题来了,如果我想让enp0s8也给network manager管理,怎么办?
我试试看去掉NM_CONTROLLED=NO这一行,然后再次运行nmcli device,发现
它依然是unmanaged。直到我运行过systemctl restart NetworkManager之后,
设备enp0s8现在才是connected状态:

[root@BasicOL7-Docker network-scripts]# nmcli device
DEVICE  TYPE      STATE      CONNECTION
enp0s3  ethernet  connected  enp0s3     
enp0s8  ethernet  unmanaged  --         
lo      loopback  unmanaged  --         
[root@BasicOL7-Docker network-scripts]# systemctl restart NetworkManager
[root@BasicOL7-Docker network-scripts]# nmcli device
DEVICE  TYPE      STATE      CONNECTION
enp0s3  ethernet  connected  enp0s3     
enp0s8  ethernet  connected  enp0s8     
lo      loopback  unmanaged  --        

不重启NetworkManager可以吗?

是可以的。
只需要nmcli connection load /etc/sysconfig/network-scripts/ifcfg-enp0s8
或者nmcli connection reload

检查网口状态

传统的命令当然还是能继续用的,比如说:

  • ip link show
  • ip -4 addr
  • ifconfig enp0s8

现在,用nmcli,可以:

  • nmcli device show enp0s8
  • nmcli device status

要改变网口状态,比方说要把一个网口断线,以前我们有这些命令:

  • ip link set enp0s8 down
  • ifconfig enp0s8 down
  • ifdown enp0s8

现在,用nmcli,可以:

  • nmcli device disconnect enp0s8

我测试里面发现,ifup enp0s8的效果和nmcli device connect enp0s8相同。
应该ifup就是简单封装了一下nmcli device connect
同样地,ifdownnmcli device disconnect效果相同。

这两组命令执行完了之后,状态都可以通过nmcli device status看出来。

但是,要小心了,底层的ip link命令和ifconfig就不一样了。仔细看下面的例子:

[root@BasicOL7-Docker ~]# nmcli device show enp0s8
GENERAL.DEVICE:                         enp0s8
GENERAL.TYPE:                           ethernet
GENERAL.HWADDR:                         08:00:27:D1:3F:20
GENERAL.MTU:                            1500
GENERAL.STATE:                          100 (connected)
GENERAL.CONNECTION:                     System enp0s8
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/3
WIRED-PROPERTIES.CARRIER:               on
IP4.ADDRESS[1]:                         192.168.56.101/24
IP4.GATEWAY:                            
IP6.GATEWAY:                            
[root@BasicOL7-Docker ~]# ip link set enp0s8 down
[root@BasicOL7-Docker ~]# nmcli device show enp0s8
GENERAL.DEVICE:                         enp0s8
GENERAL.TYPE:                           ethernet
GENERAL.HWADDR:                         08:00:27:D1:3F:20
GENERAL.MTU:                            1500
GENERAL.STATE:                          100 (connected)
GENERAL.CONNECTION:                     System enp0s8
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/3
WIRED-PROPERTIES.CARRIER:               off
IP4.ADDRESS[1]:                         192.168.56.101/24
IP4.GATEWAY:                            
IP6.GATEWAY:          

这里我做过了ip link set enp0s8 down之后,nmcli device show enp0s8看到的
状态依旧是connected。不过有一个property发生了变化:WIRED-PROPERTIES.CARRIER变成了off。

__ 也就是说,我们要看一个网卡是不是能收发数据,除了看state,还需要看carrier属性。 __

监控网卡状态事件

用传统办法监控网卡状态,我们可以用netlink socket,可是那需要编程啊,
还挺费事的。

NetworkManager有一个好处,它是挂到dbus上面的一个服务。借助于dbus,
我们可以方便地查询状态、监控事件变化,还可以修改配置。

比方说,我们用gdbus命令监视NetworkManager上面的事件:

# gdbus monitor --system --dest org.freedesktop.NetworkManager

当我在另一个窗口做ip link set enp0s8 up的时候,这个gdbus就会瞬间输出:

/org/freedesktop/NetworkManager/Devices/1: org.freedesktop.NetworkManager.Device.Wired.PropertiesChanged ({'Carrier': , 'AvailableConnections': <[objectpath '/org/freedesktop/NetworkManager/Settings/1']>},)

这里看到Carrier属性变成了true。org.freedesktop.NetworkManager.Device.Wired.PropertiesChanged是一个signal,所以我们很容易写程序向dbus注册一个listener。

__ TODO: 写一个python程序监控网口状态 __

创建bond口

现在我想把enp0s8变成bond0。这个bond口里面只有这一个slave。
使用nmcli,这事情不难。

# nmcli connection add con-name bond0 ifname bond0 type bond
# nmcli connection modify bond0 bond.options "mode=active-backup,primary_reselect=failure"
# nmcli connection modify enp0s8 connection.master bond0 connection.slave-type bond

这就好了吗?
做完了这些之后,我去看ifcfg-bond0,没问题。看ifcfg-enp0s8,也没问题。

可是,下面的输出就奇怪了:

[root@BasicOL7-Docker network-scripts]# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)

Bonding Mode: load balancing (round-robin)
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

什么鬼?我不是已经把mode改成active-backup了吗?另外,为什么enp0s8还没有到slave里面?

做一次nmcli connection reload试试看,不起作用。
那么做一次nmcli connection down bond0nmcli connection up bond0
效果怎样?

Bonding Mode: fault-tolerance (active-backup)
Primary Slave: None
Currently Active Slave: None
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

好了,mode变了,可喜可贺。可是为什么slave还不出来?

再来做一次nmcli connection down enp0s8nmcli connection up enp0s8
这一次终于生效了。

__ 注意,这里发现了一个坑。修改bond options之后,必须connection down/up。 __
__ 注意,修改了eth口的master之后,必须connection down/up。 __

删除bond0口

这个操作和上面的操作刚好相反。我已经有bond口了,怎么把它删掉呢?

乍一看,简单嘛:nmcli connection connection delete bond0
这一步做完了之后,的确ifcfg-bond0消失了。
可是别忘了把slave网卡上面的master和slave属性去掉:

# nmcli connection modify enp0s8 connection.master "" connection.slave-type ""

这个命令执行完了之后,nmcli connection show enp0s8显示的确master属性没了。

可是,可是……为什么检查ifcfg-enp0s8,这可恶的MASTER和SLAVE还在?

而且我做一次nmcli connection reload之后,master和slave又回来了。

__ 这肯定是一个bug。明天上班之后向Oracle Linux team报bug去。__

今天就玩这么多,洗洗睡了。

你可能感兴趣的:(2016-06-14)