LINE Corporation이 2023년 10월 1일부로 LY Corporation이 되었습니다. LY Corporation의 새로운 기술 블로그를 소개합니다. LY Corporation Tech Blog

Blog


OpenStack Summit Vancouver 2018 참관 후기(하)

안녕하세요? LINE Verda 2팀의 Nishiwaki입니다. 지난 포스팅에서는 청중 입장에서 흥미로웠던 컨테이너 관련 세션 몇 가지를 소개드렸는데, 이번 글에서는 'Excitingly simple multi-path OpenStack networking: LAG-less, L2-less, yet fully redundant' 라는 제목으로 저와 Samir가 발표한 내용을 소개해 드리겠습니다.

LINE에서는 새로운 리전(region)을 대상으로 East-West 네트워크 트래픽(데이터센터 내 트래픽)에 강한 데이터센터 네트워크를 설계하고 있습니다. 저희는 발표에서 이 아키텍처 및 설계와 관련된 Neutron 통합에 관해 얘기했습니다. 발표 영상과 자료는 아래에서 보실 수 있습니다.

수평 확장이 가능한 데이터센터 네트워크 설계

LINE 프라이빗 클라우드에서 사용하던 네트워크의 대역폭은 Hypervisor를 탑재한 ToR(Top of Rack) 스위치의 다운링크 대역폭보다 작습니다. 따라서 VM(Virtual Machine) 간의 통신량이 많아지면 ToR 간 통신에서 병목 현상이 발생하여 처리량이 현저히 저하되었습니다. 이 현상은 VM 간의 통신이 많지 않을 때는 큰 문제가 되지 않았습니다. 하지만 최근에 마이크로 서비스나 머신 러닝 등 구조 상 VM 간 통신이 많이 발생하는 시스템이 늘어나면서, 해결책을 찾아보게 되었습니다.

Overview

ToR 병목 현상을 막기 위해 처음 선택한 방법은 서로 간 통신이 필요한 VM들을 동일한 ToR의 Hypervisor에 스케줄링하는 구조로 만드는 것이었습니다. 그러나 이 구조에선 시스템 전체를 동일한 장애 도메인에 배치할 수 밖에 없고, VM을 해당 워크로드에 강제로 스케줄링하는 것도 OpenStack Nova만으로는 불가능했습니다. 따라서 OpenStack watcher 등의 VM 생성 후, VM의 워크로드에 따라 탑재될 Hypervisor를 변경하는 구조를 마련해야 하는 등의 추가 과제가 발생했습니다.

그래서 저희는 이 문제를 좀 더 근본적으로 해결하기 위해, BGP(Border Gateway Protocol, RFC7938 권장)를 사용하는 Clos Network 구조를 채택, 무중단(Non-blocking) 네트워크를 새로운 리전에 구축해보았습니다.

New approach

ToR 병목 현상은 Clos Network 도입으로 해결되었습니다. 하지만 ToR에서 L2 네트워크를 종단시키기 때문에 ToR별 서브넷 관리가 필요한 점, ToR 스위치 계획 점검 시 패킷이 손실될 수밖에 없는 점, 랙(rack)간 라이브 마이그레이션이 되지 않는 점 등이 아직 문제로 남아 있었습니다. 그래서 저희는 Clos Network 도입에서 더 나아가 Hypervisor와 ToR 간에도 EBGP(External Border Gateway Protocol)를 사용, L3 라우팅 도메인을 Hypervisor까지 확장시켜 보았습니다.

Use BGP

Hypervisor까지 L3 라우팅 도메인에 포함시키니, 기존처럼 VLAN(Virtual LAN)과 서브넷을 랙별로 생성하는 것이 어려워졌습니다. VLAN과 서브넷을 랙별로 생성하려면 Hypervisor 수만큼 네트워크와 서브넷을 생성해야 하고, 라이브 마이그레이션을 할 수 없게 되며, Hypervisor 수만큼 서브넷을 관리해야 하는 등 확장성이 현저히 떨어지게 됩니다. 따라서 VM 간 접속 방법, VM과 데이터센터 네트워크와의 접속 방법을 각각 별도로 검토해야 했습니다.

방법은 2가지가 있었습니다. 첫 번째는 VXLAN(Virtual Extensible LAN)이나 GRE(Generic Routing Encapsulation) 등의 오버레이(Overlay) 기술을 도입하는 방법입니다. 두 번째는 데이터센터 네트워크에 /32(서브넷 마스크 255.255.255.255)로 VM의 IP 주소를 동적으로 할당해서 직접 라우팅 접속성을 보장하는 방법입니다. 첫 번째 방법은 캡슐링에 오버헤드가 발생하고, 네트워크 아키텍처가 복잡해지는 단점이 있었습니다. 그래서 두 번째 방법, 오버레이 기술을 쓰지 않고 라우팅만으로 엔드 투 엔드(end-to-end) 접속성을 보장하는 방법을 선택했습니다.

Use routing

L2 Isolate 플러그인

지금까지 OpenStack에 대한 특별한 언급 없이 데이터센터 네트워크 설계에 대해 말씀드렸습니다. Hypervisor 내에 생성되는 클라우드 리소스(VM)를 데이터센터 네트워크에 접속시키는 것은 OpenStack Neutron이 담당합니다. 따라서, '/32 라우팅을 통해 VM 간 통신을 구현'하는 방법은 Neutron으로 해결해야 합니다. 하지만 기존 Linux Bridge AgentOVS(Open vSwitch) Agent는 그런 방법을 지원하지 않습니다.

저희는 이 문제를 해결하기 위해 Neutron 플러그인/Agent(L2 Isolate 플러그인)를 사내에서 자체 개발하였습니다. 이 플러그인은 ML2 플러그인의 드라이버 역할을 맡고 있으며, 다음 세 개의 파트로 구성됩니다.

  • L2 Isolate mechanism driver (ML2 플러그인)
  • Routed type driver (ML2 플러그인)
  • L2 Isolate Agent
Solution overview

L2 Isolate 플러그인은 단순히 Linuxbridge Agent 대신 L2 Isolate Agent를 사용한다고 동작하지 않습니다. OpenStack 클라우드를 배포하는 방법도 같이 변경해야 합니다.

아래 슬라이드에서 점선 위쪽이 Linux Bridge Agent를 사용한 OpenStack 배포 예시고, 점선 아래가 L2 Isolate Agent를 사용한 배포 예시입니다. 가장 눈에 띄는 변경 사항은 L2 Isolate 플러그인 Agent를 사용할 때 네트워크 노드가 필요하게 됐다는 점입니다. 이 환경에서는 컴퓨트 노드가 L2 네트워크를 종단하므로, VM에서 L2 도달성이 필요한 서비스는 각 컴퓨트 노드에서 제공되어야 합니다. 따라서 metadata agent를 네트워크 노드에서 컴퓨트 노드로 옮겼고, DHCP 서비스는 각 컴퓨트 노드에서 동작하는 L2 Isolate Agent가 설정한 dnsmasq에서 제공되도록 배포 방식을 변경하였습니다.

Deployment scope change

L2 Isolate Agent가 수행하는 설정

이제 L2 Isolate Agent가 실제로 어떤 설정을 컴퓨트 노드에서 수행하는지 5개 파트로 나누어 설명드리겠습니다.

Hypervisor 내부에서 다른 VM과 연결

다음 그림은 동일 Hypervisor 내 VM들의 L3 도달성을 보장하기 위한 설정을 보여줍니다. 그림 왼쪽 끝에 보이는 agent가 VIF_TYPE에 TAP을 넣어 Proxy ARP를 설정, VM들이 L2 도달성을 갖지 못하도록 합니다.

Connectivity

모든 VM에는 /32가 아닌 동일한 큰 prefix(슬라이드 예시에선 10.252.0.0/16)에서 IP 주소가 할당됩니다. 따라서 다른 VM으로 패킷을 보낼 때 VM에서 L2 도달성이 있다고 판단되어 ARP(Address Resolution Protocol)를 해결하지 못합니다. 근처 VM에 L3로 도달하기 위해선 먼저 VM이 IP 패킷을 Hypervisor로 보내게 해야 합니다. Proxy ARP를 설정하면 대상 VM의 ARP 요청에 대한 응답으로 Hypervisor의 MAC 주소가 반환되어 L3 도달성을 보장할 수 있게 됩니다.

이어서 no prefixroute 옵션을 설정하여 Gateway IP 주소를 tap 디바이스에 설정합니다. 'IP 주소가 설정되지 않은 인터페이스 상의 DHCP 서비스는 dnsmasq가 지원하지 않는다'는 제약 사항 때문입니다. no prefixroute 옵션은 앞서 언급했던 큰 prefix(10.252.0.0/16) 경로가 라우팅 테이블에 추가될 때, Hypervisor 사이를 오고가는 VM 간 통신 도달성이 사라진다는 점에서도 필요합니다.

마지막으로 /32의 static route를 라우팅 테이블에 추가하면, 동일한 Hypervisor 내 VM 간 L3 포워딩에 따라 도달성을 보장할 수 있습니다. /32의 static route는 목적지가 VM IP 주소인 패킷을 tap 인터페이스에 전송하는 역할을 합니다.

직접 이 방법대로 해보시려면, namespace나 veth로 VM을 모방하는 것을 추천합니다. 저희도 최초 검증 시 namespace와 veth로 같은 환경을 만들어 검증하였습니다.

Connectivity to VM from outside hypervisor

그렇다면 VM과 데이터센터 네트워크 내 디바이스 사이의 도달성은 어떻게 보장되는 것일까요? 저희가 개발한 환경에서는 각 컴퓨트 노드에서 L2 Isolate Agent와는 별개로 라우팅 소프트웨어가 실행됩니다. 라우팅 소프트웨어가 Hypervisor 내 라우팅 테이블을 모니터링하면서, 라우팅 테이블 변경 사항을 트리거로 삼아 VM의 IP 주소(/32 경로)를 ToR 스위치에 할당합니다. 이렇게 Hypervisor 외부에서 VM으로 접근하는 L3 도달성이 보장됩니다.

위 'VM IP 주소를 외부에 할당하는 로직'은 RFC 5549 등 특정 라우팅 소프트웨어에선 아직 구현되지 않은 기술을 이용했기 때문에, 라우팅 소프트웨어를 되도록 유연하게 변경할 수 있도록 플러거블(Pluggable)하면서 특정 라우팅 소프트웨어에 의존하지 않도록 설계 및 개발했습니다. 따라서 BIRD, FRR 등 여러 라우팅 소프트웨어와 조합할 수 있습니다.

Connectivity to VM from outside Hypervisor

DHCP

앞서 말씀드린 것처럼, 각 컴퓨트 노드에서 L2 네트워크를 종단하므로 해당 노드에서 DHCP 서비스를 제공해야 합니다. Neutron 기본 DHCP Agent인 neutron-dhcp-agent에서는 네트워크 당 하나의 dnsmasq를 생성하여 동일한 네트워크 내 VM에 대해 DHCP 서비스를 제공합니다. 반면, L2 Isolate Agent를 사용하면 네트워크 수에 상관 없이 하나의 dnsmasq가 동일한 Hypervisor 내 모든 VM에 대해 DHCP 서비스를 제공합니다. 또, L2 Isolate Agent를 사용하면 L2 장애 도메인을 고려할 필요가 없기 때문에, 큰 서브넷 아래에서 대량의 VM을 생성할 수 있습니다. 그래서 neutron-dhcp-agent가 생성되는 설정 파일 하나에 서브넷 아래 모든 VM 설정을 기술하는 방식을 사용하면, 설정 파일이 지나치게 커져버립니다. 이를 피하기 위해, L2 Isolate Agent는 설정 파일을 Neutron 상의 엔티티와 1:1로 매칭시켜 해당 컴퓨트 노드에서 필요한 엔티티 설정만을 dnsmasq에 읽어들이게 합니다. dnsmasq에는 디렉터리를 지정하면 추가 변경 사항을 모니터링해주는 옵션이 있어서 이를 활용했습니다.

DHCP

Metadata proxy

이제 Metadata 서비스 구현으로 넘어가려는데요. 이 부분에서는 트릭을 조금 사용하였습니다. 그래서 구현 설명 전에 일반적인 OpenStack 환경에서 Neutron이 Metadata API를 어떻게 연계하는지 잠깐 되짚어 보겠습니다.

Recap: Usual Neutron Medata access

Metadata API는 Nova API의 한 종류이며, nova-api 프로세스를 통해 제공됩니다. Nova Metadata API를 호출할 때는 헤더에 X-Instance-ID 등 VM에만 존재하는 정보를 넣어서 HTTP 요청을 보내야 합니다. 하지만 대부분의 경우, VM에서 Metadata API를 호출할 때(curl http://169.254.169.254) 위와 같은 설정을 하신 적이 별로 없으실 겁니다. X-Instance-ID 등의 부가 정보는 사실 API 요청 과정에서 Nova API에 도달하기 전, 별도 프로세스를 통해 주입된 것입니다. 이를 실행하는 것이 neutron-ns-metadata-proxyneutron-metadata-agent, 두 가지입니다.

  • neutron-ns-metadata-proxy
    • neutron-dhcp-agent가 네트워크별, 또는 neutron-l3-agent가 라우터별로 실행하는 프로세스
    • 오퍼레이터가 직접 실행하지 않음
    • 실행 시 network id 또는 router id 인수 지정 필요
  • neutron-metadata-agent
    • neutron-dhcp-agentneutron-dhcp-agent가 동작하는 노드에서 오퍼레이터가 함께 실행시키는 프로세스

Metadata API가 Nova API에 연계되기까지의 흐름은 아래와 같습니다(neutron-l3-agent를 예시로 설명하겠습니다).

  1. neutron-ns-metadata-proxy가 VM에서 직접 http://169.254.169.254의 API 요청을 수신하고, 요청 실행 시의 router id와 VM IP 주소를 헤더에 넣어 Unix 소켓을 경유해서 neutron-metadata-agent에 전송합니다. (코드)
    if self.network_id:
     cfg_info['res_type'] = 'Network'
     cfg_info['res_id'] = self.network_id
    else:
     cfg_info['res_type'] = 'Router'
     cfg_info['res_id'] = self.router_id
  2. neutron-metadata-agent는 해당 router id에 소속되어 있는 모든 네트워크의 리스트를 취득합니다. (코드)
    networks = self._get_router_networks(router_id)
  3. 2단계 결과와 VM IP 주소를 토대로 VM의 포트를 검색합니다(네트워크도 검색 조건에 포함시켜서 IP 주소가 중복되더라도 파악할 수 있게 함). (코드)
    return self._get_ports_for_remote_address(remote_address, networks)
  4. 3단계 검색 결과인 Port의 device owner에게서 VM의 Instance ID를 취득합니다. (코드)
    return ports[0]['device_id'], ports[0]['tenant_id']
  5. neutron-ns-metadata-proxy에서 전송된 유저 요청 헤더에 Instance ID 등을 설정하여 Nova Metadata API에 접속합니다. (코드)
    return self._proxy_request(instance_id, tenant_id, req)
Metadata Proxy

일반적인 Metadata API 연계 진행 과정을 살펴 보았습니다. 이제 neutron-dhcp-agentneutron-l3-agent를 모두 이용하지 않는 L2 Isolate Agent 환경에서는 어떻게 구현되는지 알아보겠습니다.

Metadata API와 같이 neutron-ns-metadata-proxy를 L2 Isolate Agent가 실행하고 있습니다. 그런데 neutron-ns-metadata-proxy 하나로 동일한 Hypervisor 내 모든 VM에 프록시 서비스를 제공합니다. 이는 네트워크와 서브넷이 서로 다른 VM이 동일한 Hypervisor 내에 존재하는 경우에도 마찬가지입니다. 보통 각 네트워크별로 생성되는 neutron-ns-metadata-proxy가 어떻게 프로세스 하나로 모든 VM을 지원하는 걸까요? L2 Isolate Agent는 가짜(fake) router id를 파라미터에 전달하고 neutron-ns-metadata-proxy를 실행합니다. 가짜 router id를 전달하면 neutron-metadata-agent에서 포트를 검색할 때 router id에서 네트워크 정보를 취득할 수 없기 때문에, VM의 IP 주소로만 포트 검색이 되어(참고 코드) Instance id를 취득할 수 있게 됩니다. IP 주소 중복이 허용되지 않는 방법이기는 하지만, neutron-ns-metadata-proxy 하나로 Metadata 서비스를 VM에 제공할 수 있게 됩니다. 코드 분량을 줄이기 위해 기존에 있던 것을 활용하고자 이런 방법을 사용했습니다.

시큐리티 그룹 지원

마지막으로 시큐리티 그룹입니다. 시큐리티 그룹 실제 설정은 각 Agent의 Firewall Driver가 정의합니다(참고 코드). 하지만, Neutron의 기본 IpTablesFirewallDriver는 tap device가 bridge에 접속되어 있는 것을 전제로 하기 때문에 bridge를 사용하지 않는 L2 Isolate Agent에서는 이용할 수 없었습니다. 그래서 트래픽이 VM과 관련이 있는지를 in/out 인터페이스만 보고 판별할 수 있게 변경한 tap 기반 Firewall Driver를 만들어 사용하였습니다.

Security group

프로젝트 성과

위와 같이 L2 Isolate Agent가 내장된 L2 Isolate 플러그인은 Neutron과 함께 동작하며, VM 간, VM과 데이터센터 네트워크 간 L3 도달성을 보장합니다.

이 커스텀 플러그인은 테스트까지 포함해서 약 두 달 동안 개발했으며, 프로덕션에서 이미 사용하고 있습니다.

두 달이라는 기간에 놀란 분들도 계실텐데요. Neutron에는 Linux Bridge Agent에서 사용되는 Agent 공통 처리를 정의한 프레임워크와 같은 것이 있어서 LINE에서도 이를 기반으로 커스텀 Agent를 만들었습니다. 그 외에도 RPC나 Neutron 내부 라이브러리 등 활용 가능한 자원이 많이 있기 때문에 Neutron을 이해하는 비용은 다소 클지 몰라도 실제 개발 비용은 크지 않았습니다. 또, Neutron의 동작을 코드 레벨에서 이해하는 것은 향후 발생할 수 있는 확장 이슈나 버그 트러블슈팅에도 도움이 됩니다. 이 또한 프로젝트에서 얻은 귀중한 소득이라 할 수 있습니다.

물론 지금까지 소개해드린 새로운 네트워크 아키텍처와 커스텀 플러그인이 만병통치약(Silver Bullet)은 아닙니다. 장점도 있지만 프로젝트를 시작하기 전부터 알고 있던 단점도 있습니다. LINE에서는 이런 단점을 최소화할 수 있는 운영 체제와 구조를 검토한 후 서비스 환경에 채택했습니다.

네트워크 관리자 시점

장점
  • ToR 스위치 사이를 오고 가는 VM 간 통신의 평균 처리량 향상
    • ToR uplink 대역폭 개선
    • 오버레이 네트워크 미사용(L2 Isolate 플러그인)
  • 업체에 의존하지 않는 심플한 데이터센터 네트워크 아키텍처
    • 데이터센터 내 L2 네트워크 이중화 기술 불필요
    • BGP로 모든 이중화 구현
  • 다운타임 없는 ToR 스위치 계획 점검
    • BGP로 유연한 트래픽 엔지니어링 달성
단점
  • 스케일이 큰 BGP 네트워크 운영 필요

클라우드 관리자 시점

장점
  • 랙 간 Live Migration 가능
  • VM 간 통신 요건에 따른 VM 스케줄링 불필요
단점
  • 지속적인 커스텀 플러그인 유지보수 필요

마치며

이번 글에서는 OpenStack Summit Vancouver에서 발표했던 'Excitingly simple multi-path OpenStack networking: LAG-less, L2-less, yet fully redundant'에 대해 소개드렸습니다.

LINE이 OpenStack Summit에서 발표한 것은 이번이 처음이었습니다. 현재 LINE에선 프라이빗 클라우드를 개선하기 위해 커스텀 플러그인 OSS(Open Source Software)화, 네트워크 분리를 위한 오버레이 네트워크 도입, 베어메탈(Bare Metal) 환경에 동일한 네트워크 아키텍처를 투명하게 적용하기 등 여러 가지 방안을 검토하고 있습니다. 앞으로 위와 같은 개선 활동에 대해서 컨퍼런스나 커뮤니티에 정기적으로 공개할 예정입니다.

계획했던 것보다 길어진 글을 끝까지 읽어 주셔서 감사합니다.