[ English | English (United Kingdom) | Deutsch | русский ]

HAProxy и Keepalived внутри LXC контейнеров

Может быть вариант использования, когда вы захотите запустить HAProxy и Keepalived внутри контейнеров LXC. Например, запуск этих служб на физических серверах предполагает, что маршрут по умолчанию для хостов должен быть установлен в направлении публичной сети. Этот сценарий может быть непредпочтительным для некоторых развертываний, особенно в случаях, когда у вас нет отдельных балансировщиков нагрузки, но они размещены вместе с другими инфраструктурными службами.

Определение inventory

Чтобы указать dynamic_inventory сгенерировать набор контейнеров для HAProxy, необходимо создать файл /etc/openstack_deploy/env.d/haproxy.yml со следующим содержимым:

---
# This file contains an example to show how to set
# the cinder-volume service to run in a container.
#
# Important note:
# In most cases you need to ensure that default route inside of the
# container doesn't go through eth0, which is part of lxcbr0 and
# SRC nat-ed. You need to pass "public" VIP interface inside of the
# container and ensure "default" route presence on it.

container_skel:
  haproxy_container:
    properties:
      is_metal: false

Настройка сети хоста

Чтобы сделать публичную сеть доступной, вам необходимо обеспечить наличие соответствующего моста на ваших хостах, к которому будут подключены контейнеры HAProxy с одной стороны пары veth. Мост также должен содержать интерфейс VLAN, обеспечивающий «публичное» подключение.

Вы можете создать мост вручную или использовать нашу роль systemd_networkd, которая способна настраивать необходимые сетевые подключения на хостах.

Для примера ниже давайте назовем наш мост br-public-api и публичный vlan с идентификатором 40. В вашем user_variables.yml определите следующие переменные:

_systemd_networkd_generic_devices:
  - NetDev:
      Name: bond0
      Kind: bond
    Bond:
      Mode: 802.3ad
      TransmitHashPolicy: layer3+4
      LACPTransmitRate: fast
      MIIMonitorSec: 100
    filename: 05-generic-bond0

_systemd_networkd_public_api_devices:
  - NetDev:
      Name: vlan-public-api
      Kind: vlan
    VLAN:
      Id: 40
    filename: 10-openstack-vlan-public-api
  - NetDev:
      Name: br-public-api
      Kind: bridge
    Bridge:
      ForwardDelaySec: 0
      HelloTimeSec: 2
      MaxAgeSec: 12
      STP: off
    filename: 11-openstack-br-public-api

openstack_hosts_systemd_networkd_devices: |-
  {% set devices = [] %}
  {% if is_metal %}
  {%   set _ = devices.extend(_systemd_networkd_generic_devices) %}
  {%   if inventory_hostname in groups['haproxy_hosts'] %}
  {%     set _ = devices.extend(_systemd_networkd_public_api_devices) %}
  {%   endif %}
  {% endif %}
  {{ devices }}

_systemd_networkd_bonded_networks:
  - interface: ens3
    filename: 05-generic-ens3
    bond: bond0
    link_config_overrides:
      Match:
        MACAddress: df:25:83:e1:77:c8
  - interface: ens6
    filename: 05-generic-ens6
    bond: bond0
    link_config_overrides:
      Match:
        MACAddress: df:25:83:e1:77:c9
  - interface: bond0
    filename: 05-general-bond0
    vlan:
      - vlan-public-api

_systemd_networkd_public_api_networks:
  - interface: "vlan-public-api"
    bridge: "br-public-api"
    filename: 10-openstack-vlan-public-api
  - interface: "br-public-api"
    filename: "11-openstack-br-public-api"

openstack_hosts_systemd_networkd_networks: |-
  {% set networks = [] %}
  {% if is_metal %}
  {%   set _ = networks.extend(_systemd_networkd_bonded_networks) %}
  {%   if inventory_hostname in groups['haproxy_hosts'] %}
  {%     set _ = networks.extend(_systemd_networkd_public_api_networks) %}
  {%   endif %}
  {% endif %}
  {{ networks }}

Настройка сети контейнера

В случае развертывания HAProxy внутри LXC необходимо обеспечить подключение к публичной сети и то, что haproxy_bind_external_lb_vip_address будет присутствовать внутри контейнера, а external_lb_vip_address будет доступен.

Для этого нам необходимо выполнить следующие изменения в файле openstack_user_config.yml.

  1. В cidr_networks добавьте сеть, которая должна использоваться как «публичная» сеть для доступа к API. Например, мы будем использовать 203.0.113.128/28:

cidr_networks:
  ...
  public_api: 203.0.113.128/28
  1. В used_ips вам необходимо зарезервировать IP-адрес для вашего шлюза и haproxy_keepalived_external_vip_cidr/external_lb_vip_address

    used_ips:
      ...
      - "203.0.113.129"
      - "203.0.113.140-203.0.113.142"
    
  2. В provider_networks необходимо определить новую сеть контейнеров и назначить ее группе HAproxy.

    global_overrides:
      ...
      provider_networks:
        ...
        - network:
          group_binds:
            - haproxy
          type: "raw"
          container_bridge: "br-public-api"
          container_interface: "eth20"
          container_type: "veth"
          ip_from_q: public_api
          static_routes:
            - cidr: 0.0.0.0/0
              gateway: 203.0.113.129
    

Хотя все эти изменения необходимо внести в openstack_user_config.yml, необходимо применить еще одно переопределение.

Как вы могли заметить, мы определяем маршрут по умолчанию для контейнера через eth20. Однако по умолчанию все контейнеры имеют свой маршрут по умолчанию через eth0, который является локальным мостом LXC, где адрес получается через DHCP. Чтобы избежать конфликта, вам нужно убедиться, что маршрут по умолчанию не будет установлен для eth0 внутри контейнера. Для этого создайте файл /etc/openstack_deploy/group_vars/haproxy со следующим содержимым:

---
lxc_container_networks:
  lxcbr0_address:
    bridge: "{{ lxc_net_bridge | default('lxcbr0') }}"
    bridge_type: "{{ lxc_net_bridge_type | default('linuxbridge') }}"
    interface: eth0
    type: veth
    dhcp_use_routes: False

Настройка привязки HAProxy внутри контейнеров

Поскольку предоставление IP-адресов внутри контейнеров довольно случайно, не всегда может быть удобно привязывать HAProxy к определенному IP-адресу. Если это так, вы можете привязать HAProxy к интерфейсу, поскольку мы всегда знаем имена интерфейсов внутри контейнеров. При этом предполагается, что публичные/внутренние VIP-адреса keepalived будут добавлены в used_ips, поэтому вы по-прежнему можете свободно их определять.

В примере ниже показано возможное содержимое user_variables.yml:

haproxy_bind_external_lb_vip_interface: eth20
haproxy_bind_internal_lb_vip_interface: eth1
haproxy_bind_external_lb_vip_address: "*"
haproxy_bind_internal_lb_vip_address: "*"
haproxy_keepalived_external_vip_cidr: 203.0.113.140/32
haproxy_keepalived_internal_vip_cidr: 172.29.236.9/32
haproxy_keepalived_external_interface: "{{ haproxy_bind_external_lb_vip_interface }}"
haproxy_keepalived_internal_interface: "{{ haproxy_bind_internal_lb_vip_interface }}"

В качестве альтернативы вы можете обнаружить IP-адреса, используемые внутри ваших контейнеров для настройки привязок HAProxy. Это можно сделать, обратившись к сопоставлению container_networks:

haproxy_bind_external_lb_vip_address: "{{ container_networks['public_api_address']['address'] }}"
haproxy_bind_internal_lb_vip_address: "{{ container_networks['management_address']['address'] }}"

Создание контейнеров

После того, как все шаги выше выполнены, пришло время создать наши новые контейнеры HAProxy. Для этого выполните следующую команду:

# openstack-ansible playbooks/lxc-containers-create.yml --limit haproxy,lxc_hosts