차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
| 다음 판 | 이전 판 | ||
| octavia_lb_구현_및_분석 [2024/10/10 04:23] – 만듦 koov | octavia_lb_구현_및_분석 [2024/10/10 06:29] (현재) – koov | ||
|---|---|---|---|
| 줄 47: | 줄 47: | ||
| {{: | {{: | ||
| - | 2단계. 리스너가 수신할 프로토콜과 포트를 설정합니다. 외부 액세스를 모니터링합니다 http://< | + | 2단계. 리스너가 수신할 프로토콜과 포트를 설정합니다. 외부 액세스를 모니터링합니다 |
| {{: | {{: | ||
| 줄 298: | 줄 298: | ||
| </ | </ | ||
| - | 두 번째는 '' | + | 두 번째는 '' |
| 또한 '' | 또한 '' | ||
| 줄 620: | 줄 620: | ||
| 따라서 '' | 따라서 '' | ||
| + | |||
| ===== haproxy 서비스 프로세스 시작 ===== | ===== haproxy 서비스 프로세스 시작 ===== | ||
| 줄 656: | 줄 657: | ||
| </ | </ | ||
| </ | </ | ||
| + | 리스너는 HTTP 프로토콜 및 포트 8080에서 수신하도록 지정하므로 '' | ||
| + | |||
| + | '' | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # file: / | ||
| + | |||
| + | [Unit] | ||
| + | Description=HAProxy Load Balancer | ||
| + | After=network.target syslog.service amphora-netns.service | ||
| + | Before=octavia-keepalived.service | ||
| + | Wants=syslog.service | ||
| + | Requires=amphora-netns.service | ||
| + | |||
| + | [Service] | ||
| + | # Force context as we start haproxy under "ip netns exec" | ||
| + | SELinuxContext=system_u: | ||
| + | |||
| + | Environment=" | ||
| + | |||
| + | ExecStartPre=/ | ||
| + | |||
| + | ExecReload=/ | ||
| + | ExecReload=/ | ||
| + | |||
| + | ExecStart=/ | ||
| + | |||
| + | KillMode=mixed | ||
| + | Restart=always | ||
| + | LimitNOFILE=2097152 | ||
| + | |||
| + | [Install] | ||
| + | WantedBy=multi-user.target | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 구성에서 실제 서비스가 시작된 것은 ''/ | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | Nov 15 10:12:01 amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77 ip[13206]: haproxy-systemd-wrapper: | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 리스너 외에도 풀, 멤버, L7정책, L7규칙, '' | ||
| + | |||
| + | ===== 풀 생성 프로세스 분석 ===== | ||
| + | 풀 플로우 생성 UML 도표 | ||
| + | {{:: | ||
| + | |||
| + | '' | ||
| + | |||
| + | '' | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503 | ||
| + | global | ||
| + | daemon | ||
| + | user nobody | ||
| + | log /dev/log local0 | ||
| + | log /dev/log local1 notice | ||
| + | stats socket / | ||
| + | maxconn 1000000 | ||
| + | |||
| + | defaults | ||
| + | log global | ||
| + | retries 3 | ||
| + | option redispatch | ||
| + | |||
| + | peers 1385d3c4615e4a92aea1c4fa51a75557_peers | ||
| + | peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7: | ||
| + | peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3: | ||
| + | |||
| + | |||
| + | frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557 | ||
| + | option httplog | ||
| + | maxconn 1000000 | ||
| + | bind 172.16.1.10: | ||
| + | mode http | ||
| + | default_backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | timeout client 50000 | ||
| + | |||
| + | backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | mode http | ||
| + | balance roundrobin | ||
| + | fullconn 1000000 | ||
| + | option allbackups | ||
| + | timeout connect 5000 | ||
| + | timeout server 50000 | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 풀을 생성할 때 '' | ||
| + | 다음 명령을 실행하여 공유 풀을 생성합니다. | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | $ openstack loadbalancer pool create --protocol HTTP --lb-algorithm ROUND_ROBIN --loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503 | ||
| + | +---------------------+--------------------------------------+ | ||
| + | | Field | Value | | ||
| + | +---------------------+--------------------------------------+ | ||
| + | | admin_state_up | ||
| + | | created_at | ||
| + | | description | ||
| + | | healthmonitor_id | ||
| + | | id | 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 | | ||
| + | | lb_algorithm | ||
| + | | listeners | ||
| + | | loadbalancers | ||
| + | | members | ||
| + | | name | | | ||
| + | | operating_status | ||
| + | | project_id | ||
| + | | protocol | ||
| + | | provisioning_status | PENDING_CREATE | ||
| + | | session_persistence | None | | ||
| + | | updated_at | ||
| + | +---------------------+--------------------------------------+ | ||
| + | |||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 리스너에 바인딩하지 않고 단순히 공유 풀을 만들면 '' | ||
| + | |||
| + | ===== 멤버 프로세스 분석 만들기 ===== | ||
| + | 다음 명령을 사용하여 클라우드 호스트가 위치한 서브넷, IP 주소 및 수신된 데이터가 전달되는 '' | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | [root@control01 ~]# openstack loadbalancer member create --subnet-id 2137f3fb-00ee-41a9-b66e-06705c724a36 --address 192.168.1.14 --protocol-port 80 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | +---------------------+--------------------------------------+ | ||
| + | | Field | Value | | ||
| + | +---------------------+--------------------------------------+ | ||
| + | | address | ||
| + | | admin_state_up | ||
| + | | created_at | ||
| + | | id | b6e464fd-dd1e-4775-90f2-4231444a0bbe | | ||
| + | | name | | | ||
| + | | operating_status | ||
| + | | project_id | ||
| + | | protocol_port | ||
| + | | provisioning_status | PENDING_CREATE | ||
| + | | subnet_id | ||
| + | | updated_at | ||
| + | | weight | ||
| + | | monitor_port | ||
| + | | monitor_address | ||
| + | | backup | ||
| + | +---------------------+--------------------------------------+ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | {{: | ||
| + | |||
| + | 몇 가지 주요 작업은 다음과 같이 확장됩니다. | ||
| + | |||
| + | ==== CalculateDelta ==== | ||
| + | '' | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | |||
| + | class CalculateAmphoraDelta(BaseNetworkTask): | ||
| + | |||
| + | default_provides = constants.DELTA | ||
| + | |||
| + | def execute(self, | ||
| + | LOG.debug(" | ||
| + | |||
| + | # Figure out what networks we want | ||
| + | # seed with lb network(s) | ||
| + | vrrp_port = self.network_driver.get_port(amphora.vrrp_port_id) | ||
| + | desired_network_ids = {vrrp_port.network_id}.union( | ||
| + | CONF.controller_worker.amp_boot_network_list) | ||
| + | |||
| + | for pool in loadbalancer.pools: | ||
| + | member_networks = [ | ||
| + | self.network_driver.get_subnet(member.subnet_id).network_id | ||
| + | for member in pool.members | ||
| + | if member.subnet_id | ||
| + | ] | ||
| + | desired_network_ids.update(member_networks) | ||
| + | |||
| + | nics = self.network_driver.get_plugged_networks(amphora.compute_id) | ||
| + | # assume we don't have two nics in the same network | ||
| + | actual_network_nics = dict((nic.network_id, | ||
| + | |||
| + | del_ids = set(actual_network_nics) - desired_network_ids | ||
| + | delete_nics = list( | ||
| + | actual_network_nics[net_id] for net_id in del_ids) | ||
| + | |||
| + | add_ids = desired_network_ids - set(actual_network_nics) | ||
| + | add_nics = list(n_data_models.Interface( | ||
| + | network_id=net_id) for net_id in add_ids) | ||
| + | delta = n_data_models.Delta( | ||
| + | amphora_id=amphora.id, | ||
| + | add_nics=add_nics, | ||
| + | return delta | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 간단히 말해, 먼저 필요할 것으로 예상되는 '' | ||
| + | |||
| + | ==== HandleNetworkDeltas ==== | ||
| + | '' | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | |||
| + | class HandleNetworkDelta(BaseNetworkTask): | ||
| + | """ | ||
| + | |||
| + | Plug or unplug networks based on delta | ||
| + | """ | ||
| + | |||
| + | def execute(self, | ||
| + | """ | ||
| + | added_ports = {} | ||
| + | added_ports[amphora.id] = [] | ||
| + | for nic in delta.add_nics: | ||
| + | interface = self.network_driver.plug_network(delta.compute_id, | ||
| + | | ||
| + | port = self.network_driver.get_port(interface.port_id) | ||
| + | port.network = self.network_driver.get_network(port.network_id) | ||
| + | for fixed_ip in port.fixed_ips: | ||
| + | fixed_ip.subnet = self.network_driver.get_subnet( | ||
| + | fixed_ip.subnet_id) | ||
| + | added_ports[amphora.id].append(port) | ||
| + | for nic in delta.delete_nics: | ||
| + | try: | ||
| + | self.network_driver.unplug_network(delta.compute_id, | ||
| + | | ||
| + | except base.NetworkNotFound: | ||
| + | LOG.debug(" | ||
| + | except Exception: | ||
| + | LOG.exception(" | ||
| + | return added_ports | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 마지막으로, | ||
| + | |||
| + | ==== AmphoraePostNetworkPlug ==== | ||
| + | '' | ||
| + | {{: | ||
| + | |||
| + | Member를 추가한 후 Amphora의 네트워크 상태를 다시 확인합니다 | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | root@amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77: | ||
| + | eth1 Link encap: | ||
| + | inet addr: | ||
| + | inet6 addr: fe80:: | ||
| + | UP BROADCAST RUNNING MULTICAST | ||
| + | RX packets: | ||
| + | TX packets: | ||
| + | collisions: | ||
| + | RX bytes: | ||
| + | |||
| + | eth1: | ||
| + | inet addr: | ||
| + | UP BROADCAST RUNNING MULTICAST | ||
| + | |||
| + | eth2 Link encap: | ||
| + | inet addr: | ||
| + | inet6 addr: fe80:: | ||
| + | UP BROADCAST RUNNING MULTICAST | ||
| + | RX packets:8 errors:2 dropped:0 overruns:0 frame:2 | ||
| + | TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 | ||
| + | collisions: | ||
| + | RX bytes:2156 (2.1 KB) TX bytes:808 (808.0 B) | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 구성 파일은 다음과 같습니다 | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | # Generated by Octavia agent | ||
| + | auto eth2 | ||
| + | iface eth2 inet static | ||
| + | address 192.168.1.3 | ||
| + | broadcast 192.168.1.255 | ||
| + | netmask 255.255.255.0 | ||
| + | mtu 1450 | ||
| + | post-up / | ||
| + | post-down / | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | ==== ListenersUpdate ==== | ||
| + | 최종적으로 haproxy 구성 변경은 '' | ||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503 | ||
| + | global | ||
| + | daemon | ||
| + | user nobody | ||
| + | log /dev/log local0 | ||
| + | log /dev/log local1 notice | ||
| + | stats socket / | ||
| + | maxconn 1000000 | ||
| + | |||
| + | defaults | ||
| + | log global | ||
| + | retries 3 | ||
| + | option redispatch | ||
| + | |||
| + | peers 1385d3c4615e4a92aea1c4fa51a75557_peers | ||
| + | peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7: | ||
| + | peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3: | ||
| + | |||
| + | |||
| + | frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557 | ||
| + | option httplog | ||
| + | maxconn 1000000 | ||
| + | bind 172.16.1.10: | ||
| + | mode http | ||
| + | default_backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | timeout client 50000 | ||
| + | |||
| + | backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | mode http | ||
| + | balance roundrobin | ||
| + | fullconn 1000000 | ||
| + | option allbackups | ||
| + | timeout connect 5000 | ||
| + | timeout server 50000 | ||
| + | server b6e464fd-dd1e-4775-90f2-4231444a0bbe 192.168.1.14: | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 실제로, member를 추가하는 것은 '' | ||
| + | |||
| + | ===== L7policy, L7rule 및 Health Monitor의 생성 프로세스 분석 ===== | ||
| + | L7policy 객체의 의미는 전송 동작 유형(예: pool로 전송, URL로 전송 또는 전송 거부)을 설명하는 데 사용되며, | ||
| + | {{: | ||
| + | |||
| + | L7Rule 객체의 의미는 데이터 전송의 매칭 필드를 나타내며, | ||
| + | {{: | ||
| + | |||
| + | Health Monitor 객체는 Pool 내 Member의 상태를 모니터링하는 데 사용되며, | ||
| + | {{: | ||
| + | |||
| + | 왜 이 세 가지(L7policy, | ||
| + | |||
| + | ==== 예시 1. 기본 풀로 전송 ==== | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | $ openstack loadbalancer healthmonitor create --name healthmonitor1 --type PING --delay 5 --timeout 10 --max-retries 3 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | |||
| + | $ openstack loadbalancer l7policy create --name l7p1 --action REDIRECT_TO_POOL --redirect-pool 8196f752-a367-4fb4-9194-37c7eab95714 1385d3c4-615e-4a92-aea1-c4fa51a75557 | ||
| + | |||
| + | $ openstack loadbalancer l7rule create --type HOST_NAME --compare-type STARTS_WITH --value " | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503 | ||
| + | global | ||
| + | daemon | ||
| + | user nobody | ||
| + | log /dev/log local0 | ||
| + | log /dev/log local1 notice | ||
| + | stats socket / | ||
| + | maxconn 1000000 | ||
| + | external-check | ||
| + | |||
| + | defaults | ||
| + | log global | ||
| + | retries 3 | ||
| + | option redispatch | ||
| + | |||
| + | peers 1385d3c4615e4a92aea1c4fa51a75557_peers | ||
| + | peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7: | ||
| + | peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3: | ||
| + | |||
| + | |||
| + | frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557 | ||
| + | option httplog | ||
| + | maxconn 1000000 | ||
| + | # frontend http:// | ||
| + | bind 172.16.1.10: | ||
| + | mode http | ||
| + | # ACL 전송규칙 | ||
| + | acl 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 req.hdr(host) -i -m beg server | ||
| + | # if ACL 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 충족되면 backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | use_backend 8196f752-a367-4fb4-9194-37c7eab95714 if 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 | ||
| + | # 어떤 ACL 규칙도 일치하지 않으면 backend 8196f752-a367-4fb4-9194-37c7eab95714 로 전송 | ||
| + | default_backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | timeout client 50000 | ||
| + | |||
| + | backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | # http 프로토콜 사용 | ||
| + | mode http | ||
| + | # RR 알고리즘 사용 | ||
| + | balance roundrobin | ||
| + | timeout check 10s | ||
| + | option external-check | ||
| + | # ping-wrapper.sh 스크립트를 사용하여 server 상태 모니터링 | ||
| + | external-check command / | ||
| + | fullconn 1000000 | ||
| + | option allbackups | ||
| + | timeout connect 5000 | ||
| + | timeout server 50000 | ||
| + | # 뒷단의 실제 서버(real server),서비스포트 80,모니터링규칙 inter 5s fall 3 rise 3 | ||
| + | server b6e464fd-dd1e-4775-90f2-4231444a0bbe 192.168.1.14: | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Health Check Script ('' | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | #!/bin/bash | ||
| + | if [[ $HAPROXY_SERVER_ADDR =~ ":" | ||
| + | /bin/ping6 -q -n -w 1 -c 1 $HAPROXY_SERVER_ADDR > /dev/null 2>&1 | ||
| + | else | ||
| + | /bin/ping -q -n -w 1 -c 1 $HAPROXY_SERVER_ADDR > /dev/null 2>&1 | ||
| + | fi | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | ==== 예시 2. 공유 풀로 전송 ==== | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | $ openstack loadbalancer healthmonitor create --name healthmonitor1 --type PING --delay 5 --timeout 10 --max-retries 3 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 | ||
| + | |||
| + | $ openstack loadbalancer l7policy create --name l7p1 --action REDIRECT_TO_POOL --redirect-pool 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 1385d3c4-615e-4a92-aea1-c4fa51a75557 | ||
| + | |||
| + | $ openstack loadbalancer l7rule create --type HOST_NAME --compare-type STARTS_WITH --value " | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503 | ||
| + | global | ||
| + | daemon | ||
| + | user nobody | ||
| + | log /dev/log local0 | ||
| + | log /dev/log local1 notice | ||
| + | stats socket / | ||
| + | maxconn 1000000 | ||
| + | external-check | ||
| + | |||
| + | defaults | ||
| + | log global | ||
| + | retries 3 | ||
| + | option redispatch | ||
| + | |||
| + | peers 1385d3c4615e4a92aea1c4fa51a75557_peers | ||
| + | peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7: | ||
| + | peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3: | ||
| + | |||
| + | |||
| + | frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557 | ||
| + | option httplog | ||
| + | maxconn 1000000 | ||
| + | bind 172.16.1.10: | ||
| + | mode http | ||
| + | acl 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 req.hdr(host) -i -m beg server | ||
| + | use_backend 8196f752-a367-4fb4-9194-37c7eab95714 if 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 | ||
| + | acl c76f36bc-92c0-4f48-8d57-a13e3b1f09e1 req.hdr(host) -i -m beg server | ||
| + | use_backend 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 if c76f36bc-92c0-4f48-8d57-a13e3b1f09e1 | ||
| + | default_backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | timeout client 50000 | ||
| + | |||
| + | backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | mode http | ||
| + | balance roundrobin | ||
| + | timeout check 10s | ||
| + | option external-check | ||
| + | external-check command / | ||
| + | fullconn 1000000 | ||
| + | option allbackups | ||
| + | timeout connect 5000 | ||
| + | timeout server 50000 | ||
| + | server b6e464fd-dd1e-4775-90f2-4231444a0bbe 192.168.1.14: | ||
| + | |||
| + | backend 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 | ||
| + | mode http | ||
| + | balance roundrobin | ||
| + | timeout check 10s | ||
| + | option external-check | ||
| + | external-check command / | ||
| + | fullconn 1000000 | ||
| + | option allbackups | ||
| + | timeout connect 5000 | ||
| + | timeout server 50000 | ||
| + | server 7da6f176-36c6-479a-9d86-c892ecca6ae5 192.168.1.6: | ||
| + | </ | ||
| + | </ | ||
| + | 볼 수 있듯이, listener에 공유 풀을 추가한 후 '' | ||
| + | |||
| + | ===== Amphora의 보안 통신 구현 ===== | ||
| + | ==== 자체 CA를 통해 구현된 SSL 통신 ==== | ||
| + | 계속해서 '' | ||
| + | |||
| + | <WRAP center round info 80%> | ||
| + | **참고**: 실제 운영 환경에서는 클라이언트 인증서를 발급하는 CA와 서버 인증서를 발급하는 CA가 달라야 합니다. 그래야 해킹된 amphora의 서버 인증서를 사용해 다른 인스턴스를 제어하지 못하게 할 수 있습니다. | ||
| + | </ | ||
| + | |||
| + | Octavia와 Dashboard가 동일한 인증서를 사용한다면 OpenStack 관리/API 네트워크를 공개하는 것과 다름없습니다. 간단히 말해서, Octavia가 자체 CA 인증서를 사용하는 데에는 두 가지 중요한 이유가 있습니다: | ||
| + | |||
| + | - '' | ||
| + | - 악의적인 사용자가 amphora를 ' | ||
| + | |||
| + | Octavia는 또한 OpenSSL을 사용하여 CA 센터를 생성하는 자동화 스크립트를 제공합니다. 다음 명령어를 실행하면 완료됩니다. | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | $ source / | ||
| + | </ | ||
| + | </ | ||
| + | CA 센터에 대해 좀 더 설명하자면, | ||
| + | {{: | ||
| + | |||
| + | Octavia가 자체적으로 구축한 CA 센터 | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | $ ll / | ||
| + | total 44 | ||
| + | -rw-r--r-- 1 stack stack 1294 Oct 26 12:51 ca_01.pem | ||
| + | -rw-r--r-- 1 stack stack 989 Oct 26 12:51 client.csr | ||
| + | -rw-r--r-- 1 stack stack 1708 Oct 26 12:51 client.key | ||
| + | -rw-r--r-- 1 stack stack 4405 Oct 26 12:51 client-.pem | ||
| + | -rw-r--r-- 1 stack stack 6113 Oct 26 12:51 client.pem | ||
| + | -rw-r--r-- 1 stack stack 71 Oct 26 12:51 index.txt | ||
| + | -rw-r--r-- 1 stack stack 21 Oct 26 12:51 index.txt.attr | ||
| + | -rw-r--r-- 1 stack stack 0 Oct 26 12:51 index.txt.old | ||
| + | drwxr-xr-x 2 stack stack 20 Oct 26 12:51 newcerts | ||
| + | drwx------ 2 stack stack 23 Oct 26 12:51 private | ||
| + | -rw-r--r-- 1 stack stack 3 Oct 26 12:51 serial | ||
| + | -rw-r--r-- 1 stack stack 3 Oct 26 12:51 serial.old | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | 아래는 CA 인증과 관련된 설정 항목들을 나열한 것입니다. | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # create new amphora flow에 적용되는 **TASK: | ||
| + | [certificates] | ||
| + | ca_private_key_passphrase = foobar | ||
| + | ca_private_key = / | ||
| + | ca_certificate = / | ||
| + | |||
| + | # AmphoraAPIClient에 적용되며, | ||
| + | [haproxy_amphora] | ||
| + | server_ca = / | ||
| + | client_cert = / | ||
| + | |||
| + | # Task: | ||
| + | [controller_worker] | ||
| + | client_ca = / | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 먼저 SSL 통신을 설정하는 과정을 간단히 정리한 후, 구체적인 구현을 자세히 살펴보겠습니다: | ||
| + | |||
| + | - Amphora를 생성할 때 CA 센터에 서버 인증서 서명을 요청하며, | ||
| + | - '' | ||
| + | |||
| + | ==== Amphora Agent 인증서 로딩 ==== | ||
| + | 먼저 amphora에 대한 인증서 생성 구현을 살펴봅니다. | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | class GenerateServerPEMTask(BaseCertTask): | ||
| + | """ | ||
| + | |||
| + | Use the amphora_id for the CN | ||
| + | """ | ||
| + | |||
| + | def execute(self, | ||
| + | cert = self.cert_generator.generate_cert_key_pair( | ||
| + | cn=amphora_id, | ||
| + | validity=CERT_VALIDITY) | ||
| + | |||
| + | return cert.certificate + cert.private_key | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Octavia Certificates는 '' | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | @classmethod | ||
| + | def generate_cert_key_pair(cls, | ||
| + | | ||
| + | pk = cls._generate_private_key(bit_length, | ||
| + | csr = cls._generate_csr(cn, | ||
| + | cert = cls.sign_cert(csr, | ||
| + | cert_object = local_common.LocalCert( | ||
| + | certificate=cert, | ||
| + | private_key=pk, | ||
| + | private_key_passphrase=passphrase | ||
| + | ) | ||
| + | return cert_object | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 위의 **LocalCertGenerator.generate_cert_key_pair**의 의미는 다음과 같습니다: | ||
| + | |||
| + | - Amphora 개인 키 생성 | ||
| + | - Amphora 인증서 서명 요청(CSR) 생성 | ||
| + | - CA 센터에 Amphora 서버 인증서 서명을 요청 | ||
| + | |||
| + | 이는 일반적인 인증서 생성 절차에 속하며, **create_certificates.sh** 스크립트와의 차이점은 Octavia Certificates가 **cryptography** 라이브러리를 사용하여 구현되었다는 점입니다. | ||
| + | |||
| + | **TASK: | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # file: / | ||
| + | |||
| + | [amphora_agent] | ||
| + | agent_server_ca = / | ||
| + | agent_server_cert = / | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Gunicorn HTTP 서버가 시작될 때 인증서 파일을 로드하며, | ||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | options = { | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | } | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | ==== AmphoraAPIClient가 인증서 요청을 전송 ==== | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | class AmphoraAPIClient(object): | ||
| + | def __init__(self): | ||
| + | super(AmphoraAPIClient, | ||
| + | ... | ||
| + | self.session = requests.Session() | ||
| + | self.session.cert = CONF.haproxy_amphora.client_cert | ||
| + | self.ssl_adapter = CustomHostNameCheckingAdapter() | ||
| + | self.session.mount(' | ||
| + | ... | ||
| + | |||
| + | def request(self, | ||
| + | ... | ||
| + | LOG.debug(" | ||
| + | _request = getattr(self.session, | ||
| + | _url = self._base_url(amp.lb_network_ip) + path | ||
| + | LOG.debug(" | ||
| + | reqargs = { | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | reqargs.update(kwargs) | ||
| + | headers = reqargs.setdefault(' | ||
| + | ... | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 위의 코드는 **requests** 라이브러리를 사용하여 HTTPS 요청을 실행하는 일반적인 구현입니다: | ||
| + | |||
| + | - '' | ||
| + | - '' | ||
| + | |||
| + | 마지막으로, | ||
| + | {{: | ||
| + | |||
| + | ===== Amphora의 장애 조치(페일오버) 구현 ===== | ||
| + | |||
| + | ==== Health Manager ==== | ||
| + | <WRAP center round info 80%> | ||
| + | Health Manager - 이 하위 구성 요소는 개별 amphora를 모니터링하여 정상적으로 작동하고 건강한 상태인지 확인합니다. 또한 amphora가 예상치 못하게 실패할 경우 장애 조치(페일오버) 이벤트를 처리합니다. | ||
| + | </ | ||
| + | |||
| + | 간단히 말해, Health Manager는 각 amphora의 상태를 모니터링하고, | ||
| + | |||
| + | 따라서 Health Manager Service를 이해하려면 먼저 이 서비스가 amphora의 상태를 어떻게 모니터링하는지 파악한 후, 장애 조치 프로세스의 세부 사항을 알아야 합니다. | ||
| + | |||
| + | ==== Amphora의 상태 모니터링 ==== | ||
| + | 먼저 프로그램의 진입점( **octavia/ | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | |||
| + | class UDPStatusGetter(object): | ||
| + | """ | ||
| + | |||
| + | The heartbeats are transmitted via UDP and this class will bind to a port | ||
| + | and absorb them | ||
| + | """ | ||
| + | def __init__(self): | ||
| + | self.key = cfg.CONF.health_manager.heartbeat_key | ||
| + | self.ip = cfg.CONF.health_manager.bind_ip | ||
| + | self.port = cfg.CONF.health_manager.bind_port | ||
| + | self.sockaddr = None | ||
| + | LOG.info(' | ||
| + | | ||
| + | self.sock = None | ||
| + | self.update(self.key, | ||
| + | |||
| + | self.executor = futures.ProcessPoolExecutor( | ||
| + | max_workers=cfg.CONF.health_manager.status_update_threads) | ||
| + | self.repo = repositories.Repositories().amphorahealth | ||
| + | |||
| + | def update(self, | ||
| + | """ | ||
| + | |||
| + | :param key: The hmac key used to verify the UDP packets. String | ||
| + | :param ip: The ip address the UDP server will read from | ||
| + | :param port: The port the UDP server will read from | ||
| + | :return: None | ||
| + | """ | ||
| + | self.key = key | ||
| + | for addrinfo in socket.getaddrinfo(ip, | ||
| + | ai_family = addrinfo[0] | ||
| + | self.sockaddr = addrinfo[4] | ||
| + | if self.sock is not None: | ||
| + | self.sock.close() | ||
| + | self.sock = socket.socket(ai_family, | ||
| + | self.sock.settimeout(1) | ||
| + | self.sock.bind(self.sockaddr) | ||
| + | if cfg.CONF.health_manager.sock_rlimit > 0: | ||
| + | rlimit = cfg.CONF.health_manager.sock_rlimit | ||
| + | LOG.info(" | ||
| + | self.sock.setsockopt(socket.SOL_SOCKET, | ||
| + | | ||
| + | break # just used the first addr getaddrinfo finds | ||
| + | if self.sock is None: | ||
| + | raise exceptions.NetworkConfig(" | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | **Class: | ||
| + | |||
| + | **참고**: 여기서 **amphora**와 **octavia-health-manager** 서비스 간의 네트워크 토폴로지 세부 사항을 강조해야 합니다. | ||
| + | |||
| + | * Octavia를 배포할 때, **ext-net**을 직접 **octavia**의 " | ||
| + | * Octavia 배포 시, 별도로 생성된 **tenant network**를 **lb-mgmt-net**으로 사용하는 경우, **CONF.health_manager.bind_ip**는 **lb-mgmt-net** IP 풀 내의 주소여야 합니다. 이 경우 **lb-mgmt-net**과 **OpenStack Management Network** 간의 통신 문제를 해결해야 합니다. **devstack**에서는 **lb-mgmt-net**의 포트 하나를 **ex-int**에 연결하여, | ||
| + | |||
| + | Devstack에서 로컬 네트워크를 연결하는 명령어 | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | $ neutron port-create --name octavia-health-manager-standalone-listen-port \ | ||
| + | --security-group < | ||
| + | --device-owner Octavia: | ||
| + | --binding: | ||
| + | --tenant-id <octavia service> | ||
| + | |||
| + | $ ovs-vsctl --may-exist add-port br-int o-hm0 \ | ||
| + | -- set Interface o-hm0 type=internal \ | ||
| + | -- set Interface o-hm0 external-ids: | ||
| + | -- set Interface o-hm0 external-ids: | ||
| + | -- set Interface o-hm0 external-ids: | ||
| + | | ||
| + | # / | ||
| + | request subnet-mask, | ||
| + | do-forward-updates false; | ||
| + | |||
| + | $ ip link set dev o-hm0 address <Health Manager Listen Port MAC> | ||
| + | $ dhclient -v o-hm0 -cf / | ||
| + | |||
| + | |||
| + | o-hm0: flags=4163< | ||
| + | inet 192.168.0.4 | ||
| + | inet6 fe80:: | ||
| + | ether fa: | ||
| + | RX packets 1240893 | ||
| + | RX errors 0 dropped 45 overruns 0 frame 0 | ||
| + | TX packets 417078 | ||
| + | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | 다시 주제로 돌아가서, | ||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | def check(self): | ||
| + | try: | ||
| + | obj, srcaddr = self.dorecv() | ||
| + | except socket.timeout: | ||
| + | # Pass here as this is an expected cycling of the listen socket | ||
| + | pass | ||
| + | except exceptions.InvalidHMACException: | ||
| + | # Pass here as the packet was dropped and logged already | ||
| + | pass | ||
| + | except Exception as e: | ||
| + | LOG.warning(' | ||
| + | ' | ||
| + | ' | ||
| + | else: | ||
| + | self.executor.submit(update_health, | ||
| + | self.executor.submit(update_stats, | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | 계속해서 **amphora**가 어떻게 heartbeats를 전송하는지 살펴보겠습니다. | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | def main(): | ||
| + | # comment out to improve logging | ||
| + | service.prepare_service(sys.argv) | ||
| + | |||
| + | gmr.TextGuruMeditation.setup_autorun(version) | ||
| + | |||
| + | health_sender_proc = multiproc.Process(name=' | ||
| + | | ||
| + | | ||
| + | health_sender_proc.daemon = True | ||
| + | health_sender_proc.start() | ||
| + | |||
| + | # Initiate server class | ||
| + | server_instance = server.Server() | ||
| + | |||
| + | bind_ip_port = utils.ip_port_str(CONF.haproxy_amphora.bind_host, | ||
| + | | ||
| + | options = { | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | } | ||
| + | AmphoraAgent(server_instance.app, | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | **amphora-agent** 서비스 프로세스가 시작될 때, **health_daemon.run_sender**가 로드되며, | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | def run_sender(cmd_queue): | ||
| + | LOG.info(' | ||
| + | sender = health_sender.UDPStatusSender() | ||
| + | |||
| + | keepalived_cfg_path = util.keepalived_cfg_path() | ||
| + | keepalived_pid_path = util.keepalived_pid_path() | ||
| + | |||
| + | while True: | ||
| + | |||
| + | try: | ||
| + | # If the keepalived config file is present check | ||
| + | # that it is running, otherwise don't send the health | ||
| + | # heartbeat | ||
| + | if os.path.isfile(keepalived_cfg_path): | ||
| + | # Is there a pid file for keepalived? | ||
| + | with open(keepalived_pid_path, | ||
| + | pid = int(pid_file.readline()) | ||
| + | os.kill(pid, | ||
| + | |||
| + | message = build_stats_message() | ||
| + | sender.dosend(message) | ||
| + | |||
| + | except IOError as e: | ||
| + | # Missing PID file, skip health heartbeat | ||
| + | if e.errno == errno.ENOENT: | ||
| + | LOG.error(' | ||
| + | ' | ||
| + | else: | ||
| + | LOG.error(' | ||
| + | 'to exception %s, skipping health heartbeat.', | ||
| + | except OSError as e: | ||
| + | # Keepalived is not running, skip health heartbeat | ||
| + | if e.errno == errno.ESRCH: | ||
| + | LOG.error(' | ||
| + | ' | ||
| + | else: | ||
| + | LOG.error(' | ||
| + | 'to exception %s, skipping health heartbeat.', | ||
| + | except Exception as e: | ||
| + | LOG.error(' | ||
| + | ' | ||
| + | |||
| + | try: | ||
| + | cmd = cmd_queue.get_nowait() | ||
| + | if cmd == ' | ||
| + | LOG.info(' | ||
| + | CONF.reload_config_files() | ||
| + | elif cmd == ' | ||
| + | LOG.info(' | ||
| + | break | ||
| + | except queue.Empty: | ||
| + | pass | ||
| + | time.sleep(CONF.health_manager.heartbeat_interval) | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | **run_sender** 함수는 **build_stats_message()**를 호출하여 heartbeats를 구성한 다음, **UDPStatusSender.dosend()**를 호출하여 데이터를 전송합니다. 중요한 점은, **keepalived** 서비스 프로세스가 정상적으로 실행되지 않으면 heartbeats를 전송하지 않는다는 것입니다. 즉, **keepalived**가 비정상인 **amphora**는 장애가 발생한 **amphora**로 처리됩니다. 데이터 전송은 여전히 UDP 소켓을 사용하며, | ||
| + | |||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # file: / | ||
| + | |||
| + | [health_manager] | ||
| + | bind_port = 5555 | ||
| + | bind_ip = 192.168.0.4 | ||
| + | controller_ip_port_list = 192.168.0.4: | ||
| + | </ | ||
| + | </ | ||
| + | 간단히 말하면, **octavia-health-manager**와 **amphora-agent**는 주기적인 heartbeats 프로토콜을 구현하여 **amphora**의 건강 상태를 모니터링합니다. | ||
| + | |||
| + | ==== failover(장애 조치) ==== | ||
| + | 장애 조치 메커니즘은 **health_manager.HealthManager.health_check()**에 의해 주기적으로 모니터링되고 트리거됩니다. | ||
| + | |||
| + | **health_check** 메서드는 주기적으로 **amphora_health** 테이블에서 "stale amphora" | ||
| + | <WRAP prewrap> | ||
| + | <code py> | ||
| + | # file: / | ||
| + | |||
| + | def get_stale_amphora(self, | ||
| + | """ | ||
| + | |||
| + | :param session: A Sql Alchemy database session. | ||
| + | :returns: [octavia.common.data_model] | ||
| + | """ | ||
| + | |||
| + | timeout = CONF.health_manager.heartbeat_timeout | ||
| + | expired_time = datetime.datetime.utcnow() - datetime.timedelta( | ||
| + | seconds=timeout) | ||
| + | |||
| + | amp = session.query(self.model_class).with_for_update().filter_by( | ||
| + | busy=False).filter( | ||
| + | self.model_class.last_update < expired_time).first() | ||
| + | |||
| + | if amp is None: | ||
| + | return None | ||
| + | |||
| + | amp.busy = True | ||
| + | |||
| + | return amp.to_data_model() | ||
| + | </ | ||
| + | </ | ||
| + | 만약 **stale amphora**가 존재하고 loadbalancer의 상태가 **PENDING_UPDATE**가 아니면, **failover amphora** 프로세스에 진입하게 됩니다. **failover amphora**의 taskflow는 **self._amphora_flows.get_failover_flow**입니다. | ||
| + | |||
| + | **failover**의 UML 다이어그램 | ||
| + | {{: | ||
| + | |||
| + | 분명히, 전체 **failover_flow**는 " | ||
| + | |||
| + | * delete old amphora | ||
| + | * MarkAmphoraPendingDeleteInDB | ||
| + | * MarkAmphoraHealthBusy | ||
| + | * ComputeDelete: | ||
| + | * WaitForPortDetach: | ||
| + | * MarkAmphoraDeletedInDB | ||
| + | |||
| + | **참고**: 만약 장애가 발생한 amphora가 **free amphora**라면, | ||
| + | |||
| + | * get a new amphora | ||
| + | * **get_amphora_for_lb_subflow**: | ||
| + | * **UpdateAmpFailoverDetails**: | ||
| + | * **ReloadLoadBalancer & ReloadAmphora**: | ||
| + | * **GetAmphoraeNetworkConfigs & GetListenersFromLoadbalancer & GetAmphoraeFromLoadbalancer**: | ||
| + | * **PlugVIPPort**: | ||
| + | * **AmphoraPostVIPPlug**: | ||
| + | * **update_amps_subflow\AmpListenersUpdate**: | ||
| + | * **CalculateAmphoraDelta**: | ||
| + | * **HandleNetworkDelta**: | ||
| + | * **AmphoraePostNetworkPlug**: | ||
| + | * **ReloadLoadBalancer** | ||
| + | * **MarkAmphoraMasterInDB** | ||
| + | * **AmphoraUpdateVRRPInterface**: | ||
| + | * **CreateVRRPGroupForLB**: | ||
| + | * **AmphoraVRRPUpdate**: | ||
| + | * **AmphoraVRRPStart**: | ||
| + | * **ListenersStart**: | ||
| + | * **DisableAmphoraHealthMonitoring**: | ||
| + | |||
| + | 간단히 정리하면, | ||
| + | |||
| + | 주의해야 할 점: | ||
| + | <WRAP center round info 80%> | ||
| + | 직관적으로는 **old amphora**를 삭제하기 전에 **new amphora**를 부팅하는 것이 더 나아 보일 수 있지만, 이는 복잡한 문제입니다. 만약 타겟 호스트가 자원 제한(anti-affinity 규칙 때문에)을 겪고 있다면, **old amphora** 삭제 후 부팅은 성공할 수 있지만, 먼저 부팅하려고 하면 실패할 수 있습니다. 이로 인해 비동기 API와의 통신에서 **LB**가 **ERROR** 상태로 끝날 수 있지만, **amphora**는 여전히 살아 있게 됩니다. 향후에는 이 문제를 처리하기 위해 실패 시 재시도 흐름을 고려하거나, | ||
| + | </ | ||
| + | |||
| + | 비록 장애 조치는 **old amphora**를 삭제한 후 **new amphora**를 얻는 것이지만, | ||
| + | |||
| + | ==== 장애 조치 테스트 ==== | ||
| + | MASTER amphora의 전원을 끄면 **octavia-health-manager** 서비스가 **amphora failover**를 트리거합니다. | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | Nov 22 11:22:31 control01 octavia-health-manager[29147]: | ||
| + | Nov 22 11:22:31 control01 octavia-health-manager[29147]: | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | old amphorae | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | | ||
| + | | b237b2b8-afe4-407b-83f2-e2e60361fa07 | amphora-bcff6f9e-4114-4d43-a403-573f1d97d27e | ACTIVE | lb-mgmt-net=192.168.0.11 | ||
| + | | 46eccf47-be10-47ec-89b2-0de44ea3caec | amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77 | ACTIVE | lb-mgmt-net=192.168.0.9; | ||
| + | | bc043b23-d481-45c4-9410-f7b349987c98 | amphora-a1c1ba86-6f99-4f60-b469-a4a29d7384c5 | ACTIVE | lb-mgmt-net=192.168.0.3; | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | new amphoras | ||
| + | <WRAP prewrap> | ||
| + | <code bash> | ||
| + | | 712ff785-c082-4b53-994c-591d1ec0bf7b | amphora-caa6ba0f-1a68-4f22-9be9-8521695ac4f4 | ACTIVE | lb-mgmt-net=192.168.0.13 | ||
| + | | 2ddc4ba5-b829-4962-93d8-562de91f1dab | amphora-4ff5d6fe-854c-4022-8194-0c6801a7478b | ACTIVE | lb-mgmt-net=192.168.0.23; | ||
| + | | b237b2b8-afe4-407b-83f2-e2e60361fa07 | amphora-bcff6f9e-4114-4d43-a403-573f1d97d27e | ACTIVE | lb-mgmt-net=192.168.0.11 | ||
| + | | bc043b23-d481-45c4-9410-f7b349987c98 | amphora-a1c1ba86-6f99-4f60-b469-a4a29d7384c5 | ACTIVE | lb-mgmt-net=192.168.0.3; | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | new amphora haproxy config | ||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503 | ||
| + | global | ||
| + | daemon | ||
| + | user nobody | ||
| + | log /dev/log local0 | ||
| + | log /dev/log local1 notice | ||
| + | stats socket / | ||
| + | maxconn 1000000 | ||
| + | external-check | ||
| + | |||
| + | defaults | ||
| + | log global | ||
| + | retries 3 | ||
| + | option redispatch | ||
| + | |||
| + | peers 1385d3c4615e4a92aea1c4fa51a75557_peers | ||
| + | peer 3dVescsRZ-RdRBfYVLW6snVI9gI 172.16.1.3: | ||
| + | peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7: | ||
| + | |||
| + | |||
| + | frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557 | ||
| + | option httplog | ||
| + | maxconn 1000000 | ||
| + | bind 172.16.1.10: | ||
| + | mode http | ||
| + | acl 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 req.hdr(host) -i -m beg server | ||
| + | use_backend 8196f752-a367-4fb4-9194-37c7eab95714 if 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 | ||
| + | acl c76f36bc-92c0-4f48-8d57-a13e3b1f09e1 req.hdr(host) -i -m beg server | ||
| + | use_backend 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 if c76f36bc-92c0-4f48-8d57-a13e3b1f09e1 | ||
| + | default_backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | timeout client 50000 | ||
| + | |||
| + | backend 8196f752-a367-4fb4-9194-37c7eab95714 | ||
| + | mode http | ||
| + | balance roundrobin | ||
| + | timeout check 10s | ||
| + | option external-check | ||
| + | external-check command / | ||
| + | fullconn 1000000 | ||
| + | option allbackups | ||
| + | timeout connect 5000 | ||
| + | timeout server 50000 | ||
| + | server b6e464fd-dd1e-4775-90f2-4231444a0bbe 192.168.1.14: | ||
| + | |||
| + | backend 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 | ||
| + | mode http | ||
| + | balance roundrobin | ||
| + | timeout check 10s | ||
| + | option external-check | ||
| + | external-check command / | ||
| + | fullconn 1000000 | ||
| + | option allbackups | ||
| + | timeout connect 5000 | ||
| + | timeout server 50000 | ||
| + | server 7da6f176-36c6-479a-9d86-c892ecca6ae5 192.168.1.6: | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | new amphora keepalived config | ||
| + | <WRAP prewrap> | ||
| + | <code vim> | ||
| + | vrrp_script check_script { | ||
| + | script / | ||
| + | interval 5 | ||
| + | fall 2 | ||
| + | rise 2 | ||
| + | } | ||
| + | |||
| + | vrrp_instance 01197be798d5440da846cd70f52dc503 { | ||
| + | state MASTER | ||
| + | interface eth1 | ||
| + | virtual_router_id 1 | ||
| + | priority 100 | ||
| + | nopreempt | ||
| + | garp_master_refresh 5 | ||
| + | garp_master_refresh_repeat 2 | ||
| + | advert_int 1 | ||
| + | authentication { | ||
| + | auth_type PASS | ||
| + | auth_pass b76d77e | ||
| + | } | ||
| + | |||
| + | unicast_src_ip 172.16.1.3 | ||
| + | unicast_peer { | ||
| + | 172.16.1.7 | ||
| + | } | ||
| + | |||
| + | virtual_ipaddress { | ||
| + | 172.16.1.10 | ||
| + | } | ||
| + | track_script { | ||
| + | check_script | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | **new amphora**의 구성 파일과 네트워크 설정이 **old amphora**와 동일하며, | ||
| + | |||
| + | |||
| + | ===== Neutron-lbaas vs. LBaaS v2 API vs. Octavia vs. Octavia v2 API ===== | ||
| + | 사용자들이 가장 많이 묻는 질문 중 LBaaS v2 API와 Octavia v2 API를 혼동하는 것이 단연 1위입니다. 여기서 이 개념들을 간단히 구분해 보겠습니다. | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| ===== 참조링크 ===== | ===== 참조링크 ===== | ||
| * https:// | * https:// | ||
| + | * https:// | ||
| * https:// | * https:// | ||