當前位置:
首頁 > 知識 > HAProxy、Keepalived 在 Ocatvia 的應用實現與分析

HAProxy、Keepalived 在 Ocatvia 的應用實現與分析

目錄

文章目錄

目錄

Amphora

啟動 keepalived

啟動 haproxy

最後

Amphora

創建一個 loadbalancer 需要佔用一到兩台 Amphora Instance 作為 「負載均衡器「 的運行載體,實際提供高可用負載均衡底層支撐是 HAProxy & Keepalived。

HAProxy:L4-L7 負載均衡器

Keepalived:Linux 體系的高可用解決方案

不過 Amphora 並非是一開始就運行著 haproxy 和 keepalived 服務進程的,而是在需要運行它們的時候才會被 amphora-agent 啟動。

啟動 keepalived

HAProxy、Keepalived 在 Ocatvia 的應用實現與分析

打開今日頭條,查看更多圖片

keepalived 服務進程在 Amphora 被 loadbalancer 納管後啟動,TASK:AmphoraVRRPStart 就是啟動服務的邏輯實現,而且從 UML 圖可見,只有當 loadbalancer_topology = ACTIVE_STANDBY 時才會載入 keepalived,提供高可用服務。

# file: /opt/rocky/octavia/octavia/controller/worker/tasks/amphora_driver_tasks.py

class AmphoraVRRPStart(BaseAmphoraTask):

"""Task to start keepalived of all amphorae of a LB."""

def execute(self, loadbalancer):

self.amphora_driver.start_vrrp_service(loadbalancer)

LOG.debug("Started VRRP of loadbalancer %s amphorae",

loadbalancer.id)

1

2

3

4

5

6

7

8

9

進過一系列調用後最終由 AmphoraAPIClient 發出 PUT vrrp/start 請求到 amphora-agent 的 view_func:manage_service_vrrp 接收處理。

# file: /opt/rocky/octavia/octavia/amphorae/backends/agent/api_server/keepalived.py

def manager_keepalived_service(self, action):

action = action.lower()

if action not in [consts.AMP_ACTION_START,

consts.AMP_ACTION_STOP,

consts.AMP_ACTION_RELOAD]:

return webob.Response(json=dict(

message="Invalid Request",

details="Unknown action: {0}".format(action)), status=400)

if action == consts.AMP_ACTION_START:

keepalived_pid_path = util.keepalived_pid_path()

try:

# Is there a pid file for keepalived?

with open(keepalived_pid_path, "r") as pid_file:

pid = int(pid_file.readline())

os.kill(pid, 0)

# If we got here, it means the keepalived process is running.

# We should reload it instead of trying to start it again.

action = consts.AMP_ACTION_RELOAD

except (IOError, OSError):

pass

cmd = ("/usr/sbin/service octavia-keepalived {action}".format(

action=action))

try:

subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)

except subprocess.CalledProcessError as e:

LOG.debug("Failed to %s octavia-keepalived service: %s %s",

action, e, e.output)

return webob.Response(json=dict(

message="Failed to {0} octavia-keepalived service".format(

action), details=e.output), status=500)

return webob.Response(

json=dict(message="OK",

details="keepalived {action}ed".format(action=action)),

status=202)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

上述代碼可知,Amphora 中的 amphora-agent 是通過執行 CLI /usr/sbin/service octavia-keepalived start 來啟動 keepalived 的。

# file: /usr/lib/systemd/system/octavia-keepalived.service

[Unit]

Description=Keepalive Daemon (LVS and VRRP)

After=network-online.target .service

Wants=network-online.target

Requires=.service

[Service]

# Force context as we start keepalived under "ip netns exec"

SELinuxContext=system_u:system_r:keepalived_t:s0

Type=forking

KillMode=process

ExecStart=/sbin/ip netns exec amphora-haproxy /usr/sbin/keepalived -D -d -f /var/lib/octavia/vrrp/octavia-keepalived.conf -p /var/lib/octavia/vrrp/octavia-keepalived.pid

ExecReload=/bin/kill -HUP $MAINPID

PIDFile=/var/lib/octavia/vrrp/octavia-keepalived.pid

[Install]

WantedBy=multi-user.target

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

octavia-keepalived.service 定義了 keepalived 的啟動腳本、配置文件以 PID 文件的路徑。配置文件的內容如下:

# file: /var/lib/octavia/vrrp/octavia-keepalived.conf

vrrp_script check_script {

script /var/lib/octavia/vrrp/check_script.sh

interval 5

fall 2

rise 2

}

vrrp_instance 01197be798d5440da846cd70f52dc503 { # VRRP instance name is loadbalancer UUID

state MASTER # Master router

interface eth1 # VRRP IP device

virtual_router_id 1 # VRID

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 # VRRP IP

unicast_peer {

172.16.1.7 # Backup router VRRP IP

}

virtual_ipaddress {

172.16.1.10 # VIP address

}

track_script {

check_script

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

從配置文件可知 keepalived 使用 NIC eth1 作為 VRRP IP 和 VIP 的 interface,但是直接在 Amphora 執行 ifconfig 是看不見 eth1 的。因為 Amphora 將 VIP 設置到了 namespace amphora-haproxy 中:

root@amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77:~# ip netns

amphora-haproxy

root@amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77:~# ip netns exec amphora-haproxy bash

root@amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77:~# ifconfig

eth1 Link encap:Ethernet HWaddr fa:16:3e:f4:69:4b

inet addr:172.16.1.3 Bcast:172.16.1.255 Mask:255.255.255.0

inet6 addr: fe80::f816:3eff:fef4:694b/64 Scope:Link

UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1

RX packets:0 errors:0 dropped:0 overruns:0 frame:0

TX packets:8 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:1000

RX bytes:0 (0.0 B) TX bytes:648 (648.0 B)

eth1:0 Link encap:Ethernet HWaddr fa:16:3e:f4:69:4b

inet addr:172.16.1.10 Bcast:172.16.1.255 Mask:255.255.255.0

UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

除了 start 之外,view_func:manage_service_vrrp 還支持 stop 和 reload 操作。而 keepalived 配置文件的更新則交由 view_func:upload_keepalived_config 來完成。

而在 octavia-worker 端,更新 keepalived 配置文件的邏輯實現在 Task:AmphoraVRRPUpdate,AmphoraVRRPUpdate 在 AmphoraVRRPStart 之前執行,配置文件通渲染 Jinja 模板的方式生成。

啟動 haproxy

我們知道 HAProxy 負責監聽 frontend 的請求,然後根據不同的條件和 ACL 規則將請求分發到 backend,這一特性正是 Octavia Listener 對象的定義。所以,當為 loadbalancer 創建 listener 時才會啟動 haproxy 服務進程。

HAProxy、Keepalived 在 Ocatvia 的應用實現與分析

從 UML 可知,執行指令 openstack loadbalancer listener create --protocol HTTP --protocol-port 8080 lb-1 創建 Listener 時會執行到 Task:ListenersUpdate,由 ListenersUpdate 完成了 haproxy 配置文件的 Upload 和 haproxy 服務進程的 Reload。

配置文件 /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/haproxy.cfg,其中 1385d3c4-615e-4a92-aea1-c4fa51a75557 為 Listener UUID:

# Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503

global

daemon

user nobody

log /dev/log local0

log /dev/log local1 notice

stats socket /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557.sock mode 0666 level user

maxconn 1000000

defaults

log global

retries 3

option redispatch

peers 1385d3c4615e4a92aea1c4fa51a75557_peers

peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7:1025

peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3:1025

frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557

option httplog

maxconn 1000000

bind 172.16.1.10:8080

mode http

timeout client 50000

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

因為此時的 Listener 只指定了監聽的協議和埠,所以 frontend section 也設置了相應的 bind 172.16.1.10:8080 和 mode http。

服務進程:systemctl status haproxy-1385d3c4-615e-4a92-aea1-c4fa51a75557.service 其中 1385d3c4-615e-4a92-aea1-c4fa51a75557 為 Listener UUID:

# file: /usr/lib/systemd/system/haproxy-1385d3c4-615e-4a92-aea1-c4fa51a75557.service

[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:system_r:haproxy_t:s0

Environment="CONFIG=/var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/haproxy.cfg" "USERCONFIG=/var/lib/octavia/haproxy-default-user-group.conf" "PIDFILE=/var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/1385d3c4-615e-4a92-aea1-c4fa51a75557.pid"

ExecStartPre=/usr/sbin/haproxy -f $CONFIG -f $USERCONFIG -c -q -L O08zAgUhIv9TEXhyYZf2iHdxOkA

ExecReload=/usr/sbin/haproxy -c -f $CONFIG -f $USERCONFIG -L O08zAgUhIv9TEXhyYZf2iHdxOkA

ExecReload=/bin/kill -USR2 $MAINPID

ExecStart=/sbin/ip netns exec amphora-haproxy /usr/sbin/haproxy-systemd-wrapper -f $CONFIG -f $USERCONFIG -p $PIDFILE -L O08zAgUhIv9TEXhyYZf2iHdxOkA

KillMode=mixed

Restart=always

LimitNOFILE=2097152

[Install]

WantedBy=multi-user.target

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

從服務進程配置可以看出實際啟動的服務為 /usr/sbin/haproxy-systemd-wrapper,它是運行在 namespace amphora-haproxy 中的,該腳本做的事情可以從日誌了解到:

Nov 15 10:12:01 amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77 ip[13206]: haproxy-systemd-wrapper: executing /usr/sbin/haproxy -f /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/haproxy.cfg -f /var/lib/octavia/haproxy-default-user-group.conf -p /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/1385d3c4-615e-4a92-aea1-c4fa51a75557.pid -L O08zAgUhIv9TEXhyYZf2iHdxOkA -Ds

1

就是調用了 /usr/sbin/haproxy 指令而已。

最後

本篇介紹了 Octavia 是如何將 HAProxy、Keepalived 等常用的負載均衡解決方案封裝到 Amphora Instance 的,同時也介紹了 Create Listener 所需要處理的事情。需要注意的是 HAProxy 的配置文件會隨著 Listener、Pool、Member、L7policy、L7rule、health-monitor 等對象的變更而變更,這些我們以後再作討論。還有一點補充的就是創建 Listener 會執行 Task:UpdateVIP,這是因為 Lisenter 含有的協議及埠信息都需要被更新到 VIP 的安全組規則中,否則 Listener 要如何監聽得到傳輸層的數據包呢?

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

關於jqGrid+mybatisplus遇到的坑
撲克牌的面向對象建模

TAG:程序員小新人學習 |