TL;DR

  • 리눅스는 기본적으로 새로운 장치가 붙었을 때 드라이버를 알아서 붙인다
    • 물론, 미리 커널에 모듈이 인식되어야 한다 (모듈이 아예 없다면 modprobe, insmod 등을 찾아보자)
  • 가상 네트워크 카드를 붙였다면, lspci를 통해 그 상태를 확인하자
  • 새로 붙인 NIC에 “kernel driver in use: ~~~” 가 없다면 드라이버 로딩에 실패한 것이다.
    • 드라이버까지 제대로 붙은 경우에는 인터페이스를 잡아줘야 한다 (요약 맨 아래 참고)
  • 이 경우, /var/log/dmesg 또는 dmesg -H 명령어를 통해서 드라이버 관련 로그를 볼 수 있다.
  • kworker/u8:2: page allocation failure: order:6, mode:0xc0d0 와 유사한 로그가 남아있다면 메모리 부족 또는 메모리 자체의 문제이다.
  • sync; echo 3 > /proc/sys/vm/drop_caches; echo 1 > /proc/sys/vm/compact_memory 를 실행한 후 NIC를 떼고 다시 붙여보자
  • lspci 에서 “kernel driver in use:”가 나오면 성공한 것이다.
  • 부팅후에는 새로 NIC가 잡혀도 커널이 알아서 설정해주지 않는다. 수동으로 설정해야 한다.
  • 못하겠으면… 재부팅이 편할 수도 있다. 부팅시에는 메모리가 널널하기 때문에 메모리 할당 실패가 발생하기 어렵다. 또한, 네트워크 구성도 알아서 해 주는 경우가 많다.

디도스와의 전쟁

내가 도와주는 서비스 중에 한 곳이 요즘 디도스로 골머리를 앓고 있다. 이 서비스는 분 단위로 디도스를 받고 있다. 한번 공격이 오면 5분간 지속된다. 그러고는 몇 시간 동안 잠잠하다. 잠잠한 기간이 한시간일 때도, 3시간일 때도, 그 다음날이 될 때도 있다. 서버가 다운됐다는 알림을 받고 조치를 취하려고 보면 이미 물러간 뒤다. 이 문제가 몇일 정도 반복되고 있다.

일단 뭐가 어쨋든, 사용자들이 불편을 겪으면 안된다. 그래서 IP를 새로 발급받고 StackPath나 Cloudflare의 뒤에 숨는것을 하려고 했다. 공격자가 기존 IP를 알고 있을것이기에, IP를 바꾸지 않고 CDN 방화벽에 숨는것은 의미가 없다. 새 IP를 붙이는데 VNIC를 새로 만들고, 해당 서버에 붙이는 방식으로 진행했다. 네트워크 카드를 하나 더 붙인 후 과거 인터페이스는 정지하고 새 인터페이스로만 접속을 받으려고 했다. 공격자가 예측하지 못하도록 다른 서브넷의 IP를 사용함과 동시에 기존 접속자들도 DNS TTL(업데이트 기간) 동안 문제없이 접속하게 하기 위해서다.

우리 서버는 오라클 클라우드 안에 있다. 오라클 클라우드 내에서는 가상 게이트웨이를 만들수 있고, 각 서버 인스턴스에 가상 NIC를 붙였다가 땟다가 할 수도 있다. 원래 있던 Primary VNIC는 냅두고 Secondary VNIC를 재부팅 없이 붙일수 있다. 우리는 이 방식을 이용해서 서버에 새 IP를 부여하고자 했다.

오류 발생

오라클에서는 VNIC를 붙일 수 있는 스크립트를 제공한다. (https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingVNICs.htm#Linux) 해당 스크립트를 이용하면 VNIC를 손쉽게 붙일 수 있다. 미리 관리 페이지에서 VNIC를 만든 후에 attach를 하고, 해당 스크립트를 돌리면 알아서 IP등을 잡아준다. 그러나 이게 순탄치 못했다. 아래와 같이 네트워크가 구성됐기 때문이다:

[root@linux_centos7 ~]# ifconfig
ens3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9000
        inet 10.0.0.~~  netmask 255.255.255.0  broadcast 10.0.0.255
        inet6 fe80::17ff:fe00:89b8  prefixlen 64  scopeid 0x20<link>
        ether ~~:~~:~~:~~:~~:~~  txqueuelen 1000  (Ethernet)
        RX packets 45852203794  bytes 4269063429639 (3.8 TiB)
        RX errors 0  dropped 68558182  overruns 0  frame 0
        TX packets 43595189024  bytes 10049848053900 (9.1 TiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens3.3263: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9000
        inet6 fe80::17ff:fe02:8305  prefixlen 64  scopeid 0x20<link>
        ether ~~:~~:~~:~~:~~:~~  txqueuelen 1000  (Ethernet)
        RX packets 2  bytes 112 (112.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 16  bytes 1312 (1.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens3v3263: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9000
        inet 10.0.0.33  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::17ff:fe02:8305  prefixlen 64  scopeid 0x20<link>
        ether ~~:~~:~~:~~:~~:~~  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 656 (656.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 20256730095  bytes 4740998121177 (4.3 TiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 20256730095  bytes 4740998121177 (4.3 TiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

여기서 ens3가 메인 NIC이다. 그리고 위의 스크립트의 실행 결과로 ens3.3263과 ens3v3263이 생겨났다. 새로운 NIC를 붙였는데 ens3 하위에 네트워크 인터페이스가 생겼다. 만약 NIC가 정상적으로 붙었다면 ens3, ens5 처럼 독립된 네트워크 인터페이스가 생겨야 한다. 무언가 문제가 있음을 알 수 있다. ens3 하위에 네트워크 인터페이스가 생긴것은 ip link show 명령어를 통해서도 확인 가능하다:

[root@linux_centos7 ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff
7: ens3.3263@ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff
8: ens3v3263@ens3.3263: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff

위의 결과를 보면 명확히 알 수 있다. ens3 아래에 ens3.3263가 만들어졌다. ens3.3263 아래에 ens3v3263이 만들어다. 절대 정상적인 모습이 아니다. 우리가 바라는 모습은 아래의 사진과 같아야 한다.

ens3 하위에 인터페이스가 생긴것은 VLAN tagging이라고 하여 하나의 NIC에서 여러개의 가상 인터페이스를 붙일때 사용하는 기법이다. 물리선 하나로 여러개의 IP를 할당받을때나 쓰이는 것이다. 그렇기 때문에 우리와 같이 가상 네트워크를 새로 만든 경우에 서로의 네트워크가 달라서 아예 작동이 안된다. 쉽게 말해서, 하나의 컴퓨터에 KT와 LG 인터넷을 붙였는데 KT측을 통해서 LG에 접속하려는것과 같다.

NIC 살펴보기

우선 NIC가 제대로 붙었는지 확인하기로 했다. lspci를 실행했더니 다음과 같은 결과가 나왔다:

[root@linux_centos7 ~]# lspci -vv
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
        ~~~

00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
        ~~~

00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II] (prog-if 80 [ISA Compatibility mode-only controller, supports bus mastering])
        ~~~

00:01.2 USB controller: Intel Corporation 82371SB PIIX3 USB [Natoma/Triton II] (rev 01) (prog-if 00 [UHCI])
        ~~~

00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
        ~~~

00:02.0 VGA compatible controller: Device 1234:1111 (rev 02) (prog-if 00 [VGA controller])
        ~~~

00:03.0 Ethernet controller: Mellanox Technologies MT28800 Family [ConnectX-5 Ex Virtual Function]
        Subsystem: Mellanox Technologies Device 0150
        Physical Slot: 3
        Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Latency: 0
        Region 0: Memory at 2000000000 (64-bit, prefetchable) [size=2M]
        Capabilities: [60] Express (v2) Endpoint, MSI 00
                DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s unlimited, L1 unlimited
                        ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset+ SlotPowerLimit 0.000W
                DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
                        RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop- FLReset-
                        MaxPayload 128 bytes, MaxReadReq 128 bytes
                DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
                LnkCap: Port #0, Speed 16GT/s, Width x16, ASPM not supported, Exit Latency L0s unlimited, L1 unlimited
                        ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp+
                LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
                        ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
                LnkSta: Speed unknown, Width x0, TrErr- Train- SlotClk- DLActive- BWMgmt- ABWMgmt-
                DevCap2: Completion Timeout: Range ABC, TimeoutDis+, LTR-, OBFF Not Supported
                DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled
                LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
                         EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
        Capabilities: [9c] MSI-X: Enable+ Count=6 Masked-
                Vector table: BAR=0 offset=00002000
                PBA: BAR=0 offset=00003000
        Capabilities: [c0] Vendor Specific Information: Len=18 <?>
        Kernel driver in use: mlx5_core
        Kernel modules: mlx5_core

00:04.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
        ~~~

00:05.0 Ethernet controller: Mellanox Technologies MT28800 Family [ConnectX-5 Ex Virtual Function]
        Subsystem: Mellanox Technologies Device 0150
        Physical Slot: 5
        Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Region 0: Memory at c1200000 (64-bit, prefetchable) [size=2M]
        Capabilities: [60] Express (v2) Endpoint, MSI 00
                DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s unlimited, L1 unlimited
                        ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset+ SlotPowerLimit 0.000W
                DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
                        RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop- FLReset-
                        MaxPayload 128 bytes, MaxReadReq 128 bytes
                DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
                LnkCap: Port #0, Speed 16GT/s, Width x16, ASPM not supported, Exit Latency L0s unlimited, L1 unlimited
                        ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp+
                LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
                        ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
                LnkSta: Speed unknown, Width x0, TrErr- Train- SlotClk- DLActive- BWMgmt- ABWMgmt-
                DevCap2: Completion Timeout: Range ABC, TimeoutDis+, LTR-, OBFF Not Supported
                DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled
                LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
                         EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
        Capabilities: [9c] MSI-X: Enable- Count=6 Masked-
                Vector table: BAR=0 offset=00002000
                PBA: BAR=0 offset=00003000
        Capabilities: [c0] Vendor Specific Information: Len=18 <?>
        Kernel modules: mlx5_core

여기서 00:03.0의 NIC(Mellanox MT28800)는 드라이버가 로딩 됐는데, 00:05.0의 NIC는 드라이버가 로딩이 안됐다는 부분이 중요하다. pci 03번에서는 kernel driver in use가 정확히 잡혀있다. 그러나 pci 05번에서는 kernel module만 있을 뿐이지, 로딩된 드라이버 정보는 없다. kernel modules는 [커널에서 로딩한·사용중인 드라이버] 가 아니다.

혹시나 초기화 중에 오류가 발생했나 싶어서 웹콘솔에서 수차례 뗏다가 붙였다를 반복했다. 해당 장치는 PCI Hot-Plugged 방식으로 붙었기 때문에 커널이 인식하자 마자 (최소한 lspci에서 인식한 시점에서는) 드라이버 로딩을 시도했을 것이다. 그러나 계속해서 드라이버가 붙지 않았다. 03번에서 mlx5_core를 로딩한 것으로 보아, 모듈 자체는 따로 건들 필요 없어 보였다. (insmod 등이 필요없어 보였다) /sys/bus/pci/devices/ 쪽을 통해서 reset 걸어도 반응은 동일했다.

로그 까보기

문제가 명확해 졌다. VNIC를 붙였으나 드라이버가 로딩되지 않아서 OS가 사용하지 못하고 있는게 근본 문제였다. 그래서 oracle에서 제공한 쉘 스크립트가 어거지로 네트워크 인터페이스를 붙이려다 보니까 ens3 하위에 vlan 을 생성해 버린 것이다. 그러면 왜 드라이버가 로딩되지 않았을까? 로그를 봐야만 풀 수 있는 부분이다.

리눅스에서는 dmesg 라는것이 있다. 장치, 모듈 또는 드라이버 관련 로그가 이곳에 모인다. /var/log/dmesg 를 열거나 명령어 dmesg -H 를 실행하면 된다. 해당 로그의 맨 아래에서 이 문제와 관련된 정보를 찾을 수 있었다

[Mar29 16:33] pci 0000:00:05.0: [15b3:101a] type 00 class 0x020000
[  +0.000336] pci 0000:00:05.0: reg 0x10: [mem 0x00000000-0x001fffff 64bit pref]
[  +0.000872] pci 0000:00:05.0: 0.000 Gb/s available PCIe bandwidth, limited by Unknown speed x0 link at 0000:00:05.0 (capable of 252.048 Gb/s with 16 GT/s x16 lin
[  +0.080488] pci 0000:00:05.0: BAR 0: assigned [mem 0xc1200000-0xc13fffff 64bit pref]
[  +0.041238] kworker/u8:0: page allocation failure: order:6, mode:0xc0d0
[  +0.036203] CPU: 0 PID: 13968 Comm: kworker/u8:0 Kdump: loaded Tainted: G        W      ------------   3.10.0-1160.2.2.el7.x86_64 #1
[  +0.071484] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 0.0.0 02/06/2015
[  +0.042077] Workqueue: kacpi_hotplug acpi_hotplug_work_fn
[  +0.040801] Call Trace:
[  +0.037513]  [<ffffffffad5813c0>] dump_stack+0x19/0x1b
[  +0.035209]  [<ffffffffacfc4780>] warn_alloc_failed+0x110/0x180
[  +0.034694]  [<ffffffffad57c8dd>] __alloc_pages_slowpath+0x6bb/0x729
[  +0.035825]  [<ffffffffacfc8d76>] __alloc_pages_nodemask+0x436/0x450
[  +0.035678]  [<ffffffffad0189d8>] alloc_pages_current+0x98/0x110
[  +0.034155]  [<ffffffffacfe5678>] kmalloc_order+0x18/0x40
[  +0.035127]  [<ffffffffad0243a6>] kmalloc_order_trace+0x26/0xa0
[  +0.034037]  [<ffffffffad028331>] __kmalloc+0x211/0x230
[  +0.034081]  [<ffffffffc02bbb50>] devlink_alloc+0x20/0xb0 [devlink]
[  +0.033118]  [<ffffffffc0465b2e>] init_one+0x2e/0x5c0 [mlx5_core]
[  +0.032068]  [<ffffffffad1d660a>] local_pci_probe+0x4a/0xb0
[  +0.032179]  [<ffffffffad1d7d59>] pci_device_probe+0x109/0x160
[  +0.032375]  [<ffffffffad2bb1b5>] driver_probe_device+0xc5/0x3e0
[  +0.032835]  [<ffffffffad2bb4d0>] ? driver_probe_device+0x3e0/0x3e0
[  +0.033014]  [<ffffffffad2bb513>] __device_attach+0x43/0x50
[  +0.031254]  [<ffffffffad2b8e35>] bus_for_each_drv+0x75/0xc0
[  +0.030177]  [<ffffffffad2baff0>] device_attach+0x90/0xb0
[  +0.029844]  [<ffffffffad1cb16f>] pci_bus_add_device+0x4f/0xa0
[  +0.031344]  [<ffffffffad1cb1f9>] pci_bus_add_devices+0x39/0x80
[  +0.031200]  [<ffffffffad1f4279>] enable_slot+0x239/0x4a0
[  +0.030512]  [<ffffffffad1f3498>] ? get_slot_status+0xa8/0x110
[  +0.030236]  [<ffffffffad1f45e7>] acpiphp_check_bridge.part.9+0x107/0x140
[  +0.031690]  [<ffffffffad1f4867>] acpiphp_hotplug_notify+0x177/0x210
[  +0.029808]  [<ffffffffad1f46f0>] ? acpiphp_post_dock_fixup+0xd0/0xd0
[  +0.029773]  [<ffffffffad21abae>] acpi_device_hotplug+0x3b7/0x41a
[  +0.030758]  [<ffffffffad213c77>] acpi_hotplug_work_fn+0x1e/0x29
[  +0.031040]  [<ffffffffacebdc4f>] process_one_work+0x17f/0x440
[  +0.029995]  [<ffffffffacebed66>] worker_thread+0x126/0x3c0
[  +0.031572]  [<ffffffffacebec40>] ? manage_workers.isra.26+0x2a0/0x2a0
[  +0.032243]  [<ffffffffacec5c21>] kthread+0xd1/0xe0
[  +0.032169]  [<ffffffffacec5b50>] ? insert_kthread_work+0x40/0x40
[  +0.033033]  [<ffffffffad593de4>] ret_from_fork_nospec_begin+0xe/0x21
[  +0.034892]  [<ffffffffacec5b50>] ? insert_kthread_work+0x40/0x40
[  +0.036174] Mem-Info:
[  +0.032307] active_anon:725168 inactive_anon:629875 isolated_anon:0
 active_file:186572 inactive_file:158577 isolated_file:0
 unevictable:0 dirty:202 writeback:0 unstable:0
 slab_reclaimable:35702 slab_unreclaimable:28494
 mapped:37104 shmem:41889 pagetables:11779 bounce:0
 free:38727 free_pcp:216 free_cma:0
[  +0.216437] Node 0 DMA free:14916kB min:20kB low:24kB high:28kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anonresent:15004kB managed:14916kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kBree_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
[  +0.158587] lowmem_reserve[]: 0 2813 7551 7551
[  +0.037504] Node 0 DMA32 free:98616kB min:4132kB low:5164kB high:6196kB active_anon:794480kB inactive_anon:1169108kB active_file:301244kB inactive_file:273208kB non):0kB isolated(file):0kB present:3127020kB managed:2880540kB mlocked:0kB dirty:268kB writeback:0kB mapped:70444kB shmem:87352kB slab_reclaimable:85296kB slab_un_stack:5280kB pagetables:16944kB unstable:0kB bounce:0kB free_pcp:576kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
[  +0.195141] lowmem_reserve[]: 0 0 4738 4738
[  +0.041189] Node 0 Normal free:39232kB min:6968kB low:8708kB high:10452kB active_anon:2107520kB inactive_anon:1350392kB active_file:445044kB inactive_file:361100d(anon):0kB isolated(file):0kB present:4980736kB managed:4851916kB mlocked:0kB dirty:540kB writeback:0kB mapped:77972kB shmem:80204kB slab_reclaimable:57512kB slabnel_stack:2640kB pagetables:30172kB unstable:0kB bounce:0kB free_pcp:1044kB local_pcp:4kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
[  +0.219098] lowmem_reserve[]: 0 0 0 0
[  +0.046731] Node 0 DMA: 1*4kB (U) 0*8kB 0*16kB 2*32kB (UM) 2*64kB (U) 1*128kB (U) 1*256kB (U) 0*512kB 2*1024kB (UM) 2*2048kB (M) 2*4096kB (M) = 14916kB
[  +0.098095] Node 0 DMA32: 5100*4kB (UEM) 5877*8kB (UEM) 1734*16kB (UEM) 78*32kB (UEM) 3*64kB (M) 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 97848kB
[  +0.093204] Node 0 Normal: 9858*4kB (UEM) 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 39432kB
[  +0.101772] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
[  +0.049001] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
[  +0.050096] 397052 total pagecache pages
[  +0.052915] 10011 pages in swap cache
[  +0.053253] Swap cache stats: add 5283596, delete 5273500, find 585359396/586771231
[  +0.051613] Free swap  = 7858912kB
[  +0.048800] Total swap = 8388604kB
[  +0.052412] 2030690 pages RAM
[  +0.049651] 0 pages HighMem/MovableOnly
[  +0.046786] 93847 pages reserved
[  +0.050504] mlx5_core 0000:00:05.0: kzalloc failed
[  +0.054219] mlx5_core: probe of 0000:00:05.0 failed with error -12

로그를 통해 PCI 장치가 붙었다는것을 인식 한 후 kworker가 관련된 작업을 하려 시도했으나, 메모리 할당 문제로 실패했음을 (kworker/u8:0: page allocation failure: order:6, mode:0xc0d0) 알 수 있다.

시스템의 메모리는 어느정도 충분했다. 8GB의 메모리 중에서 1.5GB의 메모리가 사용 가능했다. 남은 1.5GB가 캐시메모리로 잡혀있긴 했지만, 캐시 메모리는 언제든 반환이 가능하다. 라는 생각이 들어서 처음엔 왜그런지 이해하지 못했다. 이 문제로 검색해 보다가 https://www.2cpu.co.kr/QnA/746191 글을 발견했다. 해당 댓글에 보면 전문가들이 쓴 내용이 많다. 일단 메모리 자체의 불량일 가능성은 냅뒀고, 메모리 부족과 단편화 문제가 원인일 것으로 판단했다. 그래서 다음과 같이 명령을 실행했다:

sync; echo 3 > /proc/sys/vm/drop_caches
echo 1 > /proc/sys/vm/compact_memory

사실 첫번째 줄은 필요한지 잘 모르겠다. 첫번째 줄(sync와 drop_caches)은 캐시 메모리를 강제로 반환하고 inodes와 dentries를 내리는 기능을 한다. 혹시나 여유공간에 도움이 될까… 하고 실행은 했지만, 이게 결과에 도움을 줬는지는 확신이 없다. 특히 메모리 공간이 부족하면 캐시메모리를 알아서 반환 할 것으로 생각하기 때문에 더더욱 잘 모르겠다.

중요한것은 두번째 줄이다. 이것은 메모리에 흩어저있는 페이지를 모아주는 역할을 한다. 옛날 디스크 조각모음을 생각하면 편하다. 그 덕분에 연속된 공간의 메모리가 필요할때 문제없이 할당받을 수 있다.

윈도우XP 시절의 디스크 조각모음 프로그램 (사진 출처: http://blog.shane-smith.com/blog:116)

아무튼, 위의 명령어를 실행하고 나서 웹페이지에서 VNIC를 뗏다가 다시 붙였더니 정상적으로 드라이버를 로딩했다. dmesg -H를 실행했을때 아래와 같이 irq 할당까지 하고 드라이버 로딩이 완료되면 성공한 것이다. 비로소 커널에서 VNIC를 사용할 준비가 됐기에 오라클에서 제공해 준 스크립트를 실행하면 된다. 해당 스크립트를 실행하면 인터페이스가 정상적으로 잡힌다.

dmesg -H 로 본 로그. 정상적으로 드라이버가 잡혔다.

만약, 메모리가 이미 꽉 차서 메모리 정리와 compact를 해도 여유공간이 나오지 않는다면 어쩔 수 없다. 필요없는 서비스를 최대한 내리고 다시 시도 해야 할 것이다. 실 서비스중이 아니라면, 서버를 재부팅 하는것이 가장 편한 방법일 것이다.

한편, 리눅스는 네트워크 장치가 준비됐다고 해서 네트워크 인터페이스 까지 알아서 잡아주진 않는다. 부팅시라면 알아서 잡아주겠지만, 지금과 같이 시스템을 리부팅 할 생각이 없으면 수동으로 잡아줘야 한다. 찾아보진 않았지만 ip link를 통해서 dev를 특정하고 up 시켜주면 되지 않을까 싶다. (아니면 오라클의 스크립트를 분석해 보자)

해당 과정을 끝내면 아래와 같이 네트워크가 잘 잡힌다:

[root@linux_centos7 ~]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff
11: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff

이제 ens3 하위의 인터페이스가 아닌, 독립된 ens5 인터페이스가 생긴것을 볼 수 있다. 그리고 통신 또한 정상적으로 이루어짐을 확인 할 수 있다: curl -v --interface "ens5" esukmean.com

curl로 특정 인터페이스(ens5)를 통해 HTTP 통신을 한 결과

모든게 끝나고 echo 1 > /proc/sys/vm/compact_memory 기능이 무엇인지 궁금해서 찾아보았다. https://www.kernel.org/doc/Documentation/sysctl/vm.txt 에 따르면, 이 기능을 사용할 경우 메모리 공간을 연속되게 모은다고 한다. 그래서 메모리의 빈 공간이 최대한 길어지게 한다고 설명되어 있다. 깊게 확인은 안해봤지만, 최소한 커널버전 2.6.29 부터는 있었던 기능으로 보인다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

최신 글