host-aggregation을 availability-zone으로 사용하기

Host aggregation은 Availability zone 처럼 사용할 수 있습니다.

기본 설치 상태에서 보면 아래처럼 nova zone만 보입니다.

# nova availability-zone-list
+-----------------------+----------------------------------------+
| Name                  | Status                                 |
+-----------------------+----------------------------------------+
| internal              | available                              |
| |- control0           |                                        |
| | |- nova-conductor   | enabled :-) 2013-12-11T08:36:33.000000 |
| | |- nova-consoleauth | enabled :-) 2013-12-11T08:36:36.000000 |
| | |- nova-cert        | enabled :-) 2013-12-11T08:36:36.000000 |
| | |- nova-scheduler   | enabled :-) 2013-12-11T08:36:31.000000 |
| nova                  | available                              |
| |- compute003         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T08:36:31.000000 |
| |- compute002         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T08:36:31.000000 |
| |- compute001         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T08:36:31.000000 |
| |- compute000         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T08:36:31.000000 |
+-----------------------+----------------------------------------+

여기서 host-aggregate를 2개 만들고 각각에 2개씩 compute node를 넣어봅니다.

# nova aggregate-create agg1
# nova aggregate-create agg2
# nova aggregate-list
+----+------+-------------------+
| Id | Name | Availability Zone |
+----+------+-------------------+
| 1  | agg1 | None              |
| 2  | agg2 | None              |
+----+------+-------------------+
# nova aggregate-add-host 1 compute000
Aggregate 1 has been successfully updated.
+----+------+-------------------+-----------------+----------+
| Id | Name | Availability Zone | Hosts           | Metadata |
+----+------+-------------------+-----------------+----------+
| 1  | agg1 | None              | [u'compute000'] | {}       |
+----+------+-------------------+-----------------+----------+
# nova aggregate-add-host 1 compute001
Aggregate 1 has been successfully updated.
+----+------+-------------------+--------------------------------+----------+
| Id | Name | Availability Zone | Hosts                          | Metadata |
+----+------+-------------------+--------------------------------+----------+
| 1  | agg1 | None              | [u'compute000', u'compute001'] | {}       |
+----+------+-------------------+--------------------------------+----------+
# nova aggregate-add-host 2 compute002
Aggregate 2 has been successfully updated.
+----+------+-------------------+-----------------+----------+
| Id | Name | Availability Zone | Hosts           | Metadata |
+----+------+-------------------+-----------------+----------+
| 2  | agg2 | None              | [u'compute002'] | {}       |
+----+------+-------------------+-----------------+----------+
# nova aggregate-add-host 2 compute003
Aggregate 2 has been successfully updated.
+----+------+-------------------+--------------------------------+----------+
| Id | Name | Availability Zone | Hosts                          | Metadata |
+----+------+-------------------+--------------------------------+----------+
| 2  | agg2 | None              | [u'compute002', u'compute003'] | {}       |
+----+------+-------------------+--------------------------------+----------+

여기서 다시 nova availability-zone-list 하면 아무런 변화기 없습니다. nova aggregate-list 결과에 보면 "Availability Zone"이라는 항목이 있습니다. 녜.. host aggregate에 zone 설정이 없어서 그런 것이죠..

다시 host aggregate에 availability zone을 설정해보죠.

# nova aggregate-update 1 agg1 zone1
Aggregate 1 has been successfully updated.
+----+------+-------------------+--------------------------------+----------------------------------+
| Id | Name | Availability Zone | Hosts                          | Metadata                         |
+----+------+-------------------+--------------------------------+----------------------------------+
| 1  | agg1 | zone1             | [u'compute000', u'compute001'] | {u'availability_zone': u'zone1'} |
+----+------+-------------------+--------------------------------+----------------------------------+
# nova aggregate-update 2 agg1 zone2
Aggregate 2 has been successfully updated.
+----+------+-------------------+--------------------------------+----------------------------------+
| Id | Name | Availability Zone | Hosts                          | Metadata                         |
+----+------+-------------------+--------------------------------+----------------------------------+
| 2  | agg1 | zone2             | [u'compute002', u'compute003'] | {u'availability_zone': u'zone2'} |
+----+------+-------------------+--------------------------------+----------------------------------+

이제 다시 availability zone을 보면

# nova availability-zone-list
+-----------------------+----------------------------------------+
| Name                  | Status                                 |
+-----------------------+----------------------------------------+
| internal              | available                              |
| |- control0           |                                        |
| | |- nova-conductor   | enabled :-) 2013-12-11T09:01:15.000000 |
| | |- nova-consoleauth | enabled :-) 2013-12-11T09:01:09.000000 |
| | |- nova-cert        | enabled :-) 2013-12-11T09:01:09.000000 |
| | |- nova-scheduler   | enabled :-) 2013-12-11T09:01:14.000000 |
| zone1                 | available                              |
| |- compute001         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T09:01:15.000000 |
| |- compute000         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T09:01:15.000000 |
| zone2                 | available                              |
| |- compute003         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T09:01:15.000000 |
| |- compute002         |                                        |
| | |- nova-compute     | enabled :-) 2013-12-11T09:01:15.000000 |
+-----------------------+----------------------------------------+

녜.. 그렇습니다. host aggregate를 availability zone으로 사용할 수 있습니다.

그리고 이렇게 만들어진 존을 지정하여 인스턴스를 만드는 것은 아래처럼 할 수 있습니다.

# nova boot --flavor=<flavor> --nic net-id=<net-id> \
    --image=<image-id> --key_name=<keyname> \
    --availability-zone zone1:compute000 <instance-name>

admin 권한으로는 compute node까지 지정이 가능하고, admin이 아닌 경우는 zone 까지만 지정 가능합니다.

참고링크: http://russellbryantnet.wordpress.com/2013/05/21/availability-zones-and-host-aggregates-in-openstack-compute-nova/

quantum lbaas

create pool

$ NET_ID=e5768970-4e96-467d-9be1-e0e0b789f025
$ quantum lb-pool-create --name test --lb-method \
  ROUND_ROBIN --protocol TCP --subnet-id=$NET_ID

quantum lb-pool-list
+--------------------------------------+------+-------------+----------+----------------+----------------+
| id                                   | name | lb_method   | protocol | admin_state_up | status         |
+--------------------------------------+------+-------------+----------+----------------+----------------+
| 044208a8-9e29-4fee-b41b-9b58567f3874 | test | ROUND_ROBIN | TCP      | True           | PENDING_CREATE |
+--------------------------------------+------+-------------+----------+----------------+----------------+

create vip

$ quantum lb-vip-create --name test-vip1 --protocol-port 80 --protocol TCP --subnet-id=$NET_ID test
$ quantum lb-vip-list
+--------------------------------------+-----------+-------------+----------+----------------+--------+
| id                                   | name      | address     | protocol | admin_state_up | status |
+--------------------------------------+-----------+-------------+----------+----------------+--------+
| 515fa2c5-a135-4b59-b247-803e844383f1 | test-vip1 | 10.10.100.5 | TCP      | True           | ACTIVE |
+--------------------------------------+-----------+-------------+----------+----------------+--------+

여기서 pool도 status가 ACTIVE로 바뀌고.. lbaas가 동작하는 노드에 qlbaas- 로 ip namespace가 생긴다.

생성된 vip에... 다른 port로 만들면 오류가 발생

$ quantum lb-vip-create --name test-vip-https --protocol-port 443 --protocol TCP --subnet-id=$NET_ID test --address=10.10.100.5
Another Vip already exists for pool 044208a8-9e29-4fee-b41b-9b58567f3874

haproxy: https redirect, ip block 등등..

haproxy는 loadbalancing 기능 이외에도 다양한 기능이 많습니다.

/admin을 내부 아이피만 허용하는 경우는 아래처럼 할 수 있습니다.

  acl is-internal-net    src  172.16.0.0/12 192.168.0.0/16 10.0.0.0/8
  acl is-admin           url_beg  /admin
  block if !is-internal-net is-admin

http를 https로 redirect하는 것도 haproxy에서 사용할 수 있죠.

  redirect scheme https if !{ ssl_fc }

haproxy가 ssl offload 기능을 제공하기에 backend에는 http만 사용합니다. 그리고 밑의 application에서 https에서 온 request라는 것을 확인하기 위해서 x-forwarded-protocol을 붙여줍니다.

  bind 0.0.0.0:443 ssl crt <your-certificate-file>
  acl is-ssl             dst_port 443
  reqadd x-forwarded-protocol:\ https if is-ssl

종합하면 아래처럼 되겠습니다.

listen webapp
  bind 0.0.0.0:443 ssl crt <your-certificate-file>
  server server1 10.10.10.10:80 check
  server server1 10.10.10.11:80 check
  acl is-internal-net    src  172.16.0.0/12 192.168.0.0/16 10.0.0.0/8
  acl is-admin           url_beg  /admin
  acl is-ssl             dst_port 443
  block if !is-internal-net is-admin
  redirect scheme https if !{ ssl_fc }
  reqadd x-forwarded-protocol:\ https if is-ssl

veth가 어떤 디바이스에 연결되어있는지 찾아보기..

Quantum으로 네트워크를 구성하다보면(또는 XLC...) veth를 통해서 다른 network namespace에 interface가 peer로 역이는 것을 볼 수 있습니다.

Network Namespace는 Introducing Linux Network Namespaces 글을 참조하시면 좋습니다. 간단히 말에서 device, routing, iptables, socket 등을 포함하여 linux network stack이 완전히 격리되는 시스템입니다. namespace를 만들어 network stack을 격리시키면 외부랑 완전히 차단이 되기 때문에 root namespace의 interface와 veth로 pair를 맺어 이 interface로 통신합니다.

veth는 참고할 만한 문서를 딱히 못찾았는데요.. interface link의 클론이라고 생각하시면 됩니다. 한쪽에 들어간 패킷은 다른 한쪽으로 흐릅니다. 그리고 당연히 각각이 interface이기 때문에 mac, ipaddress가 다를 수 있습니다. 어떻게보면 bridge와도 비슷합니다.

근데 문제는 veth로 만드는 것은 쉽게 알 수 있는데, 해당 인터페이스가 어떤 인터페이스랑 peer인지 알아내는 방법이 쉽지 않습니다. iproute로는 말이지요.

아래는 quantum으로 구성한 dhcp namespace의 interface 입니다.

# ip netns exec qdhcp-fabc7ead-6132-4920-af57-2ad5e199e1c1 ip a
59: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
68: ns-39d60faa-aa: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether fa:16:3e:98:e6:f3 brd ff:ff:ff:ff:ff:ff
    inet 10.10.100.2/24 brd 10.10.100.255 scope global ns-39d60faa-aa
    inet 169.254.169.254/16 brd 169.254.255.255 scope global ns-39d60faa-aa
    inet6 fe80::f816:3eff:fe98:e6f3/64 scope link
       valid_lft forever preferred_lft forever
# ip netns exec qdhcp-fabc7ead-6132-4920-af57-2ad5e199e1c1 route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.10.100.1     0.0.0.0         UG    0      0        0 ns-39d60faa-aa
10.10.100.0     0.0.0.0         255.255.255.0   U     0      0        0 ns-39d60faa-aa
169.254.0.0     0.0.0.0         255.255.0.0     U     0      0        0 ns-39d60faa-aa

여기서 routing table을 보면 ns-39d60faa-aa 인터페이스를 통해서 외부로 나가는 것으로 보이는데, namespace 이므로 외부와 직접 연결할 수 없습니다. 당연히 ns-39d60faa-aa는 root namespace의 interface와 veth peer를 이루고 있으며, peer interface를 통해서 외부와 통신합니다.(그 peer는 bridge를 통해서 eth#으로 나가겠지요..)

그럼 여기서 본론인 어떤 interface와 peer를 이루고 있는지 알아보려고 열심히 ifconfig 또는 ip link 명령을 뒤져봐야 안나됩니다. mac도 다르고, peer 정보도 안나오고...

찾는 방법은 조금 복잡하기도 한데... ethtool을 통해서 찾을 수 있습니다.

# ip netns exec qdhcp-fabc7ead-6132-4920-af57-2ad5e199e1c1 \
  ethtool -S ns-39d60faa-aa
NIC statistics:
     peer_ifindex: 69

결과에서 예상하다시피 interface index가 69번인 interface와 peer를 이루고 있습니다. 그럼 아래처럼 찾을 수 있겠습니다.

# ip link ls | grep ^69
69: tap39d60faa-aa: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
# ip link show tap39d60faa-aa
69: tap39d60faa-aa: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 62:41:1f:e2:b2:9d brd ff:ff:ff:ff:ff:ff
# ethtool -S tap39d60faa-aa
NIC statistics:
peer_ifindex: 68

마찬가지로 tap39d60faa-aa interface는 namespace에 있는 68번 interface와 veth peer로 연결되어 있습니다.

지금은 필요없을 지 몰라도 OpenStack 하다보면 언젠가는 마주칠지 모르는 녀석이니 참고하세용..

ps. iproute 사용법은 너무 불친절합니다.

security group: 인스턴스 그룹에 접근 허용하기

SecurityGroup은 인스턴스에 연결된 포트에 허용되는 트래픽을 제어하는 기능입니다.

처음 오픈스택 인스턴스 프로젝트를 할당받고 가장 먼저 하는 것이 22번 포트에 대한 접근을 허용하는 것이니.. 대부분 잘 알고 있을 겁니다.

그런데 기본으로 제공되는 default security group만 운영하다 보면(아마 대부분 그럴지도) 하나의 의문점이 생깁니다.

아래처럼 인스턴스가 있다고 가정했을 때

  • web
  • mysql

web 인스턴스는 80번 포트, mysql 포트는 3306 포트가 필요합니다. 그래서 별 생각없이 default security group에 80, 3306 포트를 열어줍니다. 하지만 여기서 생각해볼 것이..

  • web에서는 3306 포트를 열 필요가 없습니다.
  • mysql에서는 80번 포트를 열 필요가 없습니다. 그리고 db 접근은 web에서만 하니 web에서 접근하는 트래픽만 허용합니다.

첫번째 경우는 큰 문제가 없습니다.  단순히 80번 포트만 여는 security group을 만들고 web 인스턴스에 적용하면 되니깐요.

$ nova secgroup-create web 'allow web access'
+--------------------------------------+------+------------------+
| Id                                   | Name | Description      |
+--------------------------------------+------+------------------+
| d60f5be1-8a60-4523-ba4e-3230edf5b444 | web  | allow web access |
+--------------------------------------+------+------------------+
$ nova secgroup-add-rule web tcp 80 80 0.0.0/0
+-------------+-----------+---------+----------+--------------+
| IP Protocol | From Port | To Port | IP Range | Source Group |
+-------------+-----------+---------+----------+--------------+
| tcp         | 80        | 80      | 0.0.0/0  |              |
+-------------+-----------+---------+----------+--------------+
$ nova add-secgroup web web
$ nova show web
$ nova show charlie-web | grep security
| security_groups             | [{u'name': u'default'}, {u'name': u'web'}]                     |

자.. 이제 MySQL 인스턴스에 Security Group을 적용해야하는데... web에만 적용하면 되니깐.. security group을 만들려고 보는데..

$ nova secgroup-create mysql 'allow mysql access'
+--------------------------------------+-------+--------------------+
| Id                                   | Name  | Description        |
+--------------------------------------+-------+--------------------+
| 7d197716-501f-46fa-a6fb-bd4e0c9dd5bb | mysql | allow mysql access |
+--------------------------------------+-------+--------------------+

이제 rule을 추가하려는데.. web에서만 접근을 하용하는 것이니깐.. web 인스턴스 ip만 cidr로 허용합니다.

$ nova secgroup-add-rule mysql tcp 3306 3306 172.16.202.214/32
+-------------+-----------+---------+-------------------+--------------+
| IP Protocol | From Port | To Port | IP Range          | Source Group |
+-------------+-----------+---------+-------------------+--------------+
| tcp         | 3306      | 3306    | 172.16.202.214/32 |              |
+-------------+-----------+---------+-------------------+--------------+

문제없나요? 근데.. 뭔가 이상하지 않나요? 만일 web 인스턴스가 늘어난다면? 새로운 인스턴스에 대한 rule을 계속 추가해야될까요?.. 만일 그래야된다면... 이거 Security Group이 참 써 먹을 것이 못되는 것이 됩니다.

역시나 예상하다시피 tenant 인스턴스 group에 대해서 traffic을 제어하는 기능이 있습니다. rule을 추가된 후에 나오는 source group이라는 것이 키워드입니다. nova에도 해당 명령이 있고.. 이에 대한 설명은 아래처럼 굉장히 불친절하게 되어있습니다.

secgroup-add-group-rule Add a source group rule to a security group.

설명을 봐도 무슨 말인지 쉽게 알 수 없습니다. 하지만 내용은 간단한 것으로 특정 cidr의 rule을 주는 것이 아니라 주어진 security group이 적용된 인스턴스에 대해서 secutiry group을 적용한다는 이이기 입니다.

즉 mysql 접근을 web에서만 접근하려고 하고, web 인스턴스는 모두 web security group이 적용되어 있다고 한다면

$ nova secgroup-add-group-rule mysql web tcp 3306 3306
+-------------+-----------+---------+----------+--------------+
| IP Protocol | From Port | To Port | IP Range | Source Group |
+-------------+-----------+---------+----------+--------------+
| tcp         | 3306      | 3306    |          | web          |
+-------------+-----------+---------+----------+--------------+

source group을 web으로 지정하여 web 인스턴스에 대해서 mysql port에 대한 접근을 허용할 수 있습니다. 여기서 다른 인스턴스에 web security group을 적용하면 그 인스턴스도 바로 mysql에 접근이 가능합니다(물리 노드에서 보면 해당 인스턴스의 ip address가 iptables에 바로 추가됩니다). 물론 다른 녀석들은 mysql 포트에 접근이 제한되구요.

그러므로 만일 source group을 default로 한다면 해당 tenant의 모든 인스턴스가 mysql에 접근할 수 있습니다

어떤 vm을 누가 만들었는지 찾기 귀찮아서…

$ cat get_user_from_ip.sh
#!/bin/bash
ip=$1

port_id=$(quantum port-list | grep $ip | awk '{print $2}')
device_id=$(quantum port-show $port_id | awk '/device_id/{print $4}')
user_id=$(nova show $device_id | awk '/user_id/{print $4}')
keystone user-get $user_id

$ ./get_user_from_ip.sh x.x.x.x