본문 바로가기

Ryu's Tech

오픈스택 히트 [Openstack HEAT] Provisioning테스트

 

해당 포스트는 오픈스택 히트 템플릿을 이용한 프로비저닝 테스트입니다. 구성도는 아래와 같습니다.

 

 

 

 

 

 

이런 형태의 토폴로지라고 해야할까요

1. 외부 인터넷에서 특정 공인 IP로 들어오면 그 IP를 172.17.17.11 (로드밸런서 IP)로 NAT 시켜서 오픈스택 안으로 던져주고

2. 로드밸런서1은 vFW (suricata) 로 LB 처리를 해 준 뒤

3. vFW는 받은 트래픽을 HEAT 100 네트워크로 전해주면서 SNAT (다시 트래픽을 돌려받기 위한 vFW IP)와 DNAT( 로드밸런서2의 IP)를 처리한 뒤 내보내면

4. 로드밸런2는 이를 서버의 IP로 LB 처리.

 

구성은 간단한듯 복잡하나.. 이를 HEAT로 구성하면 provider 네트워크만 기존에 존재한다고 가정하고 진행해 보면

 

A. 인스턴스 이미지 qcow2 사전 준비

 

각각의 인스턴스 이미지는 세가지로

 

FW는 suricata 설치

LB는 HAproxy 설치

WEB은 httpd 설치

 

위와 같이 각각의 역활별로 Centos에 cloud-init 패키지가 설치된 이미지로 각각을 qcow2로 준비

 

B. 프로비저닝 시 배포 순서 고민

 

여기서 고민해 보아야 할 것이 로드밸런서는 실제 서비스 IP를 지정해 주어야 하는데 이를 위해서는 프로비저닝 시 대상 서버 배포 이후 로드밸런서가 배포되어야 한다.

 

그래서 depends_on을 통해 배포 순서를 고민하여 적용하여야 하고.

 

C. 네트워크 경계를 넘어갈 시 NAT 처리 고민

 

NAT 처리는 리눅스의 iptables를 통해 진행. SNAT와 DNAT 사용 위치에 대한 고민이 필요

현재는 vFW에서만 SNAT/DNAT를 처리하도록 한다.

 

web server에서 들어오는 연결은 가능하나 외부 접근은 불가능한 상태

이를 가능하게 하려면 아래와 같은 형태로 제공하면 되나 오히려 외부 트래픽의 경우 현재 모델이 서비스 제공을 위한 모델이지 서버의 편의를 제공하는 것이 아닌 만큼 서비스 경로는 필요하다면 다른 경로를 제공하는 것이 나을 것 같다.

 

a. webserver에 default route 추가

ip route add default via [LB2_IP-10.10.200.X]

 

b. Loadbalancer2에 default route 추가

ip route add default via [vFW_IP-10.10.100.X]

 

c. firewall에 webserver IP 대역 추가 및 PAT enable

ip route add 10.10.200.0/24 via [LB2_IP-10.10.100.X]

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

 

 

 

D. parameter 값에 대한 고민

HEAT를 통해 provisioning 배포 시 parameter 값을 넘겨 받을 수 있는데 현재는 정해놓지 않았으나 차후 이를 이용하면 하나의 서비스 구성이 아닌 동일한 다수의 서비스를 한 번에 배포 시킬 수 있다.

 

E. vWAF 추가

현재 구성에 vWAF를 배포하여야 하나 vWAF의 경우 proxy 형태로 heatservice 200 네트워크에 들어가므로 특별한 문제는 없을것으로 보인다.

 

 

아래는 실제 HEAT 템플릿으로 사용 및 배포 성공한 스크립트로 작성시 가장 주의할 점은

 

- \t 탭을 제공하지 않으며 사용시 아예 스크립트를 읽어들이지 못한다. 또한 이를 대신하여 구분문자로 스페이스 두번을 통해 절대적으로 맞춰 주어야 한다.

탭이 아닌 스페이스를 통해 indent/outdent 를 제공하는 편집툴을 사용하면 편하다(visual studio code) 처음에는 notepad++를 사용했으나 자동으로 탭을 넣어주는 바람에 오히려 에러가 잦았다.

 

- 연관관계/리소스를 불러오는 방법에 대한 고민이 많이 필요하다. 생성한 리소스와 기존에 있는 리소스를 불러오는 방법은 확실히 다르다. 기존 리소스인 provider 네트워크와 heat에서 생성한 heatnetwork_100 이라는 네트워크가 있다면

기존 리소스는 network: provider 를 사용해 불러와도 되지만 생성한 네트워크는 network: {get_resource: heatnetwork_100} 의 형태로 분명히 다른것으로 보인다.

 

- 문제 발생시 heat 내 이벤트 로그/실제 서버의 로그 등 다양하게 확인해보고 삽질이 필요하다.

 

heat_template_version: '2016-04-08'

resources:
  heatnetwork_100:
    type: OS::Neutron::Net
    properties:
      name: heatnetwork_100
      tenant_id: da0df6b3ca594c748700a7f13fffe6de

  heatnetwork_200:
    type: OS::Neutron::Net
    properties:
      name: heatnetwork_200
      tenant_id: da0df6b3ca594c748700a7f13fffe6de

  heatnet_100:
    type: OS::Neutron::Subnet
    depends_on: heatnetwork_100
    properties:
      name: heatnet_100
      network_id: { get_resource: heatnetwork_100 }
      cidr: 10.10.100.0/24
      gateway_ip:
      enable_dhcp: true
      allocation_pools: [{"start": 10.10.100.100, "end": 10.10.100.250}]

  heatnet_200:
    type: OS::Neutron::Subnet
    depends_on: heatnetwork_200
    properties:
      name: heatnet_200
      network_id: { get_resource: heatnetwork_200 }
      cidr: 10.10.200.0/24
      gateway_ip:
      enable_dhcp: true
      allocation_pools: [{"start": 10.10.200.100, "end": 10.10.200.250}]

  provider_port:
    type: OS::Neutron::Port
    properties:
      name: "provider_port"
      network: provider

  heatnet_100_port:
    type: OS::Neutron::Port
    depends_on: heatnet_100
    properties:
      name: "heatnet_100_port"
      network: { get_resource: heatnetwork_100 }
      fixed_ips:
        - subnet: { get_resource: heatnet_100 }

  heatnet_200_port:
    depends_on: heatnet_200
    type: OS::Neutron::Port
    properties:
      name: "heatnet_200_port"
      network: { get_resource: heatnetwork_200 }
      fixed_ips:
        - subnet: { get_resource: heatnet_200 }

  lb1_port_vip:
    depends_on: provider_port
    type: OS::Neutron::Port
    properties:
      name: "lb1_port_vip"
      network: "provider"
      fixed_ips: [{"subnet": "provider", "ip_address": "172.17.17.11"}]

  lb2_port_vip:
    depends_on: heatnet_100
    type: OS::Neutron::Port
    properties:
      name: "lb2_port_vip"
      network: {get_resource: "heatnetwork_100"}
      fixed_ips:
        - ip_address: "10.10.100.11"

  lb3_port_vip:
    depends_on: heatnet_200
    type: OS::Neutron::Port
    properties:
      name: "lb3_port_vip"
      network: {get_resource: "heatnetwork_200"}
      fixed_ips:
        - ip_address: "10.10.200.11"

  suricata_port_gw1:
    depends_on: heatnet_100
    type: OS::Neutron::Port
    properties:
      name: "suricata_port_gw1"
      network: {get_resource: "heatnetwork_100"}
      fixed_ips:
        - ip_address: "10.10.100.21"

  suricata_port_gw2:
    depends_on: heatnet_100
    type: OS::Neutron::Port
    properties:
      name: "suricata_port_gw2"
      network: {get_resource: "heatnetwork_100"}
      fixed_ips:
        - ip_address: "10.10.100.22"

  haproxy1:
    depends_on: [ suricata1, suricata2 ]
    properties:
      name: "haproxy1"
      flavor: m1.small
      image: heat-vlb_03
      networks:
      - port: { get_resource: lb1_port_vip }
      user_data:
        str_replace:
          template: |
            #!/bin/bash
            echo "--- Sciprts_START ---"
            echo -e "\tserver server1 $suricata1:80 maxconn 2048" >> /etc/haproxy/haproxy.conf
            echo -e "\tserver server1 $suricata2:80 maxconn 2048" >> /etc/haproxy/haproxy.conf
            haproxy -f /etc/haproxy/haproxy.conf
            echo "--- Sciprts_END ---"
          params:
            $suricata1:
              get_attr: [suricata1, networks, provider, 0]
            $suricata2:
              get_attr: [suricata2, networks, provider, 0]
      user_data_format: RAW
    type: OS::Nova::Server

  haproxy2:
    depends_on: [ webserver1, webserver2 ]
    properties:
      name: "haproxy2"
      flavor: m1.small
      image: heat-vlb_03
      networks:
      - port: { get_resource: lb2_port_vip }
      - subnet: { get_resource: heatnet_200}
      user_data:
        str_replace:
          template: |
            #!/bin/bash
            echo "--- Sciprts_START ---"
            echo -e "\tserver server1 $svr1:80 maxconn 2048" >> /etc/haproxy/haproxy.conf
            echo -e "\tserver server2 $svr2:80 maxconn 2048" >> /etc/haproxy/haproxy.conf
            haproxy -f /etc/haproxy/haproxy.conf
            echo "--- Sciprts_END ---"
          params:
            $svr1:
              get_attr: [webserver1, networks, {get_resource: heatnetwork_200}, 0]
            $svr2:
              get_attr: [webserver2, networks, {get_resource: heatnetwork_200}, 0]
      user_data_format: RAW
    type: OS::Nova::Server

  suricata1:
    depends_on: haproxy2
    properties:
      name: "suricata1"
      flavor: m1.small
      image: heat-vfw_03
      networks:
      - network: provider
      - port: { get_resource: suricata_port_gw1 }
      user_data:
        str_replace:
          template: |
            #!/bin/bash
            echo "--- Sciprts_START ---"
            iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 10.10.100.11:80
            iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to $(ip addr show eth1 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
            iptables -I FORWARD -i eth0 -o eth1 -j NFQUEUE
            iptables -I FORWARD -i eth1 -o eth0 -j NFQUEUE
            suricata -c /etc/suricata/suricata.yaml -q 0 -D
            echo "--- Sciprts_END ---"
          params:
            $lb2:
              get_attr: [haproxy2, networks, {get_resource: heatnetwork_100}, 0]
      user_data_format: RAW
    type: OS::Nova::Server

  suricata2:
    depends_on: haproxy2
    properties:
      name: "suricata2"
      flavor: m1.small
      image: heat-vfw_03
      networks:
      - network: provider
      - port: { get_resource: suricata_port_gw2 }
      user_data:
        str_replace:
          template: |
            #!/bin/bash
            echo "--- Sciprts_START ---"
            iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 10.10.100.11:80
            iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to $(ip addr show eth1 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
            iptables -I FORWARD -i eth0 -o eth1 -j NFQUEUE
            iptables -I FORWARD -i eth1 -o eth0 -j NFQUEUE
            suricata -c /etc/suricata/suricata.yaml -q 0 -D
            echo "--- Sciprts_END ---"
          params:
            $lb2:
              get_attr: [haproxy2, networks, {get_resource: heatnetwork_100}, 0]
      user_data_format: RAW
    type: OS::Nova::Server

  webserver1:
    depends_on: heatnet_200
    properties:
      name: "webserver1"
      flavor: m1.small
      image: heat-vweb_03
      key_name: spark
      networks:
      - subnet: { get_resource: heatnet_200}
      user_data: |
        #!/bin/bash
        echo "--- Sciprts_START ---"
        echo "SVR1" > /var/www/html/index.html
        systemctl restart httpd
        echo "--- Sciprts_END ---"
      user_data_format: RAW
    type: OS::Nova::Server

  webserver2:
    depends_on: heatnet_200
    properties:
      name: "webserver2"
      flavor: m1.small
      image: heat-vweb_03
      key_name: spark
      networks:
      - subnet: { get_resource: heatnet_200}
      user_data: |
        #!/bin/bash
        echo "--- Sciprts_START ---"
        echo "SVR2" > /var/www/html/index.html
        systemctl restart httpd
        echo "--- Sciprts_END ---"
      user_data_format: RAW
    type: OS::Nova::Server
 

 

 

 

 

위와 같이 준비하여 배포하게 되면

아래와 같은 토폴로지와 결과 그리고 방화벽(suricata)에서 80 포트에 대한 alert 메세지를 확인 할 수 있다.