Deploy
You can easily and quickly deploy a WordPress with an integrated MariaDB using a Heat template that we provide:
openstack stack create --template https://docs.infomaniak.cloud/tutorials/03.wordpress-ha/wordpress-ha.yaml wordpress-ha \
--parameter key='<YOUR_KEY>' \
--parameter email='<YOUR_EMAIL>' \
--parameter website_url='<YOUR_URL>'
Warning
Remember to replace the variables <YOUR_KEY>
by your key pair , <YOUR_EMAIL>
by your email address and <YOUR_URL>
by your domain name !
Heat templates
Create a file called wordpress-ha.yaml
with the following content or download here :
See wordpress-ha.yml
---
heat_template_version: 2016-10-14
description: wordpress
parameters:
image:
type: string
description: Image used for servers
default: Debian 11 bullseye
key:
type: string
description: SSH key to connect to the servers
default: <your_keypair>
flavor:
type: string
description: flavor used by the web servers
default: a1-ram2-disk20-perf1
database_flavor:
type: string
description: flavor used by db servers
default: a2-ram4-disk20-perf1
stockage_flavor:
type: string
description: flavor used by the stockage server
default: a1-ram2-disk80-perf1
network:
type: string
description: Network used by the server
default: my-wordpress-network-2
subnet_id:
type: string
description: subnet on which the load balancer will be located
default: my-wordpress-subnet-2
database_name:
type: string
description: Name of the wordpress DB
default: wordpress
database_user:
type: string
description: Name of the wordpress user
default: wordpress
floating_network_id:
type: string
description: UUID of a Neutron external network
default: ext-floating1
sftp_user:
type: string
description: User for SFTP Server
default: www-data
website_url:
type: string
description: url for website
default: <your_domain>
email:
type: string
description: email for let's encrypt
default: <your_email>
resources:
# Security group
wordpress_security_group:
type: OS::Neutron::SecurityGroup
properties:
name: "wordpress_security_group"
description: >
Allows connection from remote IP (22, 80, 443, 2049, 3306)
rules:
- { direction: ingress, protocol: icmp}
- { direction: ingress, protocol: tcp, port_range_min: 22, port_range_max: 22}
- { direction: ingress, protocol: tcp, port_range_min: 80, port_range_max: 80}
- { direction: ingress, protocol: tcp, port_range_min: 443, port_range_max: 443}
- { direction: ingress, protocol: tcp, port_range_min: 2049, port_range_max: 2049}
- { direction: ingress, protocol: tcp, port_range_min: 3306, port_range_max: 3306}
# Private Network
wordpress_net:
type: OS::Neutron::Net
properties:
name: {get_param: network}
value_specs:
mtu: 1500
wordpress_subnet:
type: OS::Neutron::Subnet
properties:
name: 'wordpress-subnet'
network_id: {get_resource: wordpress_net}
cidr: "192.0.3.0/24"
dns_nameservers:
- "84.16.67.69"
- "84.16.67.70"
ip_version: 4
wordpress_router:
type: OS::Neutron::Router
properties:
name: 'wordpress-router'
external_gateway_info: { network: ext-floating1 }
wordpress_router_subnet_interface:
type: OS::Neutron::RouterInterface
properties:
router_id: {get_resource: wordpress_router}
subnet: {get_resource: wordpress_subnet}
my_wordpressdb_port:
type: OS::Neutron::Port
properties:
network: {get_resource: wordpress_net}
security_groups: [{get_resource: wordpress_security_group}]
fixed_ips:
- subnet_id: {get_resource: wordpress_subnet}
# IP address for databases and stockage servers
database_ip_1:
type: OS::Neutron::Port
properties:
network: {get_resource: wordpress_net}
security_groups: [{get_resource: wordpress_security_group}]
fixed_ips:
- ip_address: "192.0.3.10"
database_ip_2:
type: OS::Neutron::Port
properties:
network: {get_resource: wordpress_net}
security_groups: [{get_resource: wordpress_security_group}]
fixed_ips:
- ip_address: "192.0.3.11"
stockage_ip:
type: OS::Neutron::Port
properties:
network: {get_resource: wordpress_net}
security_groups: [{get_resource: wordpress_security_group}]
fixed_ips:
- ip_address: "192.0.3.20"
# Databases servers
database_password:
type: OS::Heat::RandomString
database_root_password:
type: OS::Heat::RandomString
db_1:
type: OS::Nova::Server
properties:
flavor: {get_param: database_flavor}
image: {get_param: image}
key_name: {get_param: key}
networks:
- port: { get_resource: database_ip_1 }
user_data_format: RAW
user_data:
str_replace:
template: |
#!/bin/bash -v
hostnamectl set-hostname db_1 --static
tee -a /etc/hosts<<EOF
192.0.3.10 db_1
192.0.3.11 db_2
EOF
apt update
apt -y install mariadb-server mariadb-client
systemctl enable mariadb.service
sed -i s/127.0.0.1/0.0.0.0/ /etc/mysql/mariadb.conf.d/50-server.cnf
cat <<EOF >>/etc/mysql/mariadb.conf.d/50-server.cnf
#replication
server_id = 1
report_host = master
log_bin = /var/lib/mysql/mariadb-bin
log_bin_index = /var/lib/mysql/mariadb-bin.index
relay_log = /var/lib/mysql/relay-bin
relay_log_index = /var/lib/mysql/relay-bin.index
EOF
systemctl restart mariadb.service
mysqladmin -u root password $db_rootpassword
cat << EOF | mysql -u root --password=$db_rootpassword
CREATE USER 'replication'@'%' IDENTIFIED BY 'replication';
GRANT REPLICATION SLAVE ON *.* TO 'replication'@'%';
CREATE DATABASE $db_name;
GRANT ALL PRIVILEGES ON $db_name.* TO "$db_user"@"%"
IDENTIFIED BY "$db_password";
FLUSH PRIVILEGES;
EXIT
EOF
systemctl restart mariadb
params:
$db_rootpassword: {get_attr: [database_root_password, value]}
$db_name: {get_param: database_name}
$db_user: {get_param: database_user}
$db_password: {get_attr: [database_password, value]}
db_2:
type: OS::Nova::Server
properties:
flavor: {get_param: database_flavor}
image: {get_param: image}
key_name: {get_param: key}
networks:
- port: { get_resource: database_ip_2 }
user_data_format: RAW
user_data:
str_replace:
template: |
#!/bin/bash -v
hostnamectl set-hostname db_2 --static
tee -a /etc/hosts<<EOF
192.0.3.10 db_1
192.0.3.11 db_2
EOF
apt update
apt -y install mariadb-server mariadb-client
systemctl enable mariadb.service
sed -i s/127.0.0.1/0.0.0.0/ /etc/mysql/mariadb.conf.d/50-server.cnf
cat <<EOF >>/etc/mysql/mariadb.conf.d/50-server.cnf
#replication
server_id = 2
report_host = slave
log_bin = /var/lib/mysql/mariadb-bin
log_bin_index = /var/lib/mysql/mariadb-bin.index
relay_log = /var/lib/mysql/relay-bin
relay_log_index = /var/lib/mysql/relay-bin.index
EOF
systemctl restart mariadb.service
mysqladmin -u root password $db_rootpassword
cat << EOF | mysql -u root --password=$db_rootpassword
STOP SLAVE;
CHANGE MASTER TO MASTER_HOST='192.0.3.10', MASTER_USER='replication', MASTER_PASSWORD='replication', MASTER_LOG_FILE='mariadb-bin.000001', MASTER_LOG_POS=664;
START SLAVE;
EXIT
EOF
systemctl restart mariadb
params:
$db_rootpassword: {get_attr: [database_root_password, value]}
$db_name: {get_param: database_name}
$db_user: {get_param: database_user}
$db_password: {get_attr: [database_password, value]}
# Stockage server
sftp_password:
type: OS::Heat::RandomString
stockage:
type: OS::Nova::Server
properties:
flavor: {get_param: stockage_flavor}
image: {get_param: image}
key_name: {get_param: key}
networks:
- port: { get_resource: stockage_ip }
user_data:
str_replace:
template: |
#!/bin/bash -v
hostnamectl set-hostname stockage --static
apt update
apt -y install nfs-kernel-server curl certbot
mkdir /ssl
usermod -d /ssl -s /bin/bash www-data
mkdir /wordpress
usermod -d /wordpress -s /bin/bash www-data
echo '$sftp_user:$sftp_password' | chpasswd
chown www-data:www-data /wordpress
echo "/wordpress 192.0.3.0/24(rw,async,no_subtree_check,anonuid=33,anongid=33)" >> /etc/exports
echo "/ssl 192.0.3.0/24(rw,async,no_subtree_check,anonuid=33,anongid=33)" >> /etc/exports
exportfs -r
exportfs -a
systemctl restart nfs-kernel-server
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
systemctl restart ssh
tee -a /ssl/certifssl.sh<<EOF
#!/bin/bash
if [ ! -f /ssl/cert.pem ]; then
certbot certonly -d $website_url --standalone -m $email --agree-tos -n
cp /etc/letsencrypt/live/$website_url/cert.pem /ssl/cert.pem
cp /etc/letsencrypt/live/$website_url/chain.pem /ssl/chain.pem
cp /etc/letsencrypt/live/$website_url/fullchain.pem /ssl/fullchain.pem
cp /etc/letsencrypt/live/$website_url/privkey.pem /ssl/privkey.pem
chown -R www-data:www-data /ssl
else
test=\$(find /etc/letsencrypt/live/$website_url -mtime -1 -type f -name "cert.pem")
if [ -n "\$test" ];then
cp /etc/letsencrypt/live/$website_url/cert.pem /ssl/cert.pem
cp /etc/letsencrypt/live/$website_url/chain.pem /ssl/chain.pem
cp /etc/letsencrypt/live/$website_url/fullchain.pem /ssl/fullchain.pem
cp /etc/letsencrypt/live/$website_url/privkey.pem /ssl/privkey.pem
fi
fi
if [[ \$1 = "reload" ]]; then
test=$(find /etc/letsencrypt/live/$website_url -mtime -1 -type f -name "cert.pem")
if [ -n "\$test" ];then
systemctl reload apache2
fi
fi
EOF
chmod +x /ssl/certifssl.sh
echo "0 0 4 1/1 * ? * root /ssl/certifssl.sh" >> /etc/cron.d/ssl
systemctl restart cron
params:
$sftp_user: {get_param: sftp_user}
$sftp_password: {get_attr: [sftp_password, value]}
$website_url: {get_param: website_url}
$email: {get_param: email}
member-22:
type: OS::Octavia::PoolMember
properties:
pool: {get_resource: pool-22}
address: {get_attr: [stockage, first_address]}
protocol_port: 22
subnet: {get_resource: wordpress_subnet}
member-letsencrypt:
type: OS::Octavia::PoolMember
properties:
pool: {get_resource: pool-letsencrypt}
address: {get_attr: [stockage, first_address]}
protocol_port: 80
subnet: {get_resource: wordpress_subnet}
# Wordpress servers
asg:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 1
resource:
type: lb_server.yaml
properties:
flavor: {get_param: flavor}
image: {get_param: image}
key_name: {get_param: key}
network: {get_resource: wordpress_net}
subnet: {get_resource: wordpress_subnet}
security_groups: {get_resource: wordpress_security_group}
pool_id_80: {get_resource: pool-80}
pool_id_443: {get_resource: pool-443}
metadata: {"metering.server_group": {get_param: "OS::stack_id"}}
user_data:
str_replace:
template: |
#!/bin/bash -v
tee -a /etc/hosts<<EOF
192.0.3.10 db
EOF
apt update
apt -y install wget curl apache2 rsync php-gd libapache2-mod-php libapache2-mod-php php-curl php-mysql libdbd-mysql-perl default-mysql-client fail2ban nfs-common
mkdir -p /var/www/html/
mkdir -p /etc/letsencrypt/live/$website_url
echo "$stockage_ip:/wordpress /var/www/html nfs defaults,user,auto" > /etc/fstab
echo "$stockage_ip:/ssl /etc/letsencrypt/live/$website_url nfs defaults,user,auto" >> /etc/fstab
mount -a
systemctl enable apache2.service
systemctl enable fail2ban
cd ~
wget http://wordpress.org/latest.tar.gz
tar xzvf latest.tar.gz
rsync -avP ~/wordpress/ /var/www/html/
chown -R www-data:www-data /var/www/html/*
cd /var/www/html
cp wp-config-sample.php wp-config.php
sed -i s/database_name_here/$db_name/ wp-config.php
sed -i s/username_here/$db_user/ wp-config.php
sed -i s/password_here/$db_password/ wp-config.php
sed -i s/localhost/192.0.3.10/ wp-config.php
SALTS=$(curl -s https://api.wordpress.org/secret-key/1.1/salt/)
while read -r SALT; do SEARCH="define( '$(echo "$SALT" | cut -d "'" -f 2)"; REPLACE=$(echo "$SALT" | cut -d "'" -f 4); sed -i "/^$SEARCH/s/put your unique phrase here/$(echo $REPLACE | sed -e 's/\\/\\\\/g' -e 's/\//\\\//g' -e 's/&/\\\&/g')/" wp-config.php
done <<< "$SALTS"
find /var/www/html/wordpress/ -type d -exec chmod 750 {} \;
find /var/www/html/wordpress/ -type f -exec chmod 640 {} \;
chown www-data:www-data /var/www/html/ -R
a2enmod rewrite
rm -f /var/www/html/index.html
systemctl restart apache2.service
systemctl restart fail2ban
while [ ! -f /etc/letsencrypt/live/$website_url/cert.pem ]
do
sleep 2
done
tee -a /etc/apache2/sites-available/$website_url.conf<<EOF
<VirtualHost *:80>
ServerAdmin $email
ServerName $website_url
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://$website_url/$1 [L,R=301]
</VirtualHost>
<VirtualHost *:443>
ServerAdmin $email
DocumentRoot /var/www/html
ServerName $website_url
<Directory /var/www/html/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/$website_url/cert.pem
SSLCertificateChainFile /etc/letsencrypt/live/$website_url/chain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/$website_url/privkey.pem
Header always set Strict-Transport-Security "max-age=15768000"
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
EOF
ln -s /etc/apache2/sites-available/$website_url.conf /etc/apache2/sites-enabled/$website_url.conf
a2enmod ssl
a2enmod headers
systemctl restart apache2.service
echo "0 0 4 1/1 * ? * root /etc/letsencrypt/live/$website_url/certifssl.sh reload" >> /etc/cron.d/ssl
systemctl restart cron
params:
$db_name: {get_param: database_name}
$db_user: {get_param: database_user}
$db_password: {get_attr: [database_password, value]}
$stockage_ip: {get_attr: [stockage, first_address]}
$website_url: {get_param: website_url}
$email: {get_param: email}
# Scale policy
web_server_scaleup_policy:
type: OS::Heat::ScalingPolicy
properties:
adjustment_type: change_in_capacity
auto_scaling_group_id: {get_resource: asg}
cooldown: 60
scaling_adjustment: 1
web_server_scaledown_policy:
type: OS::Heat::ScalingPolicy
properties:
adjustment_type: change_in_capacity
auto_scaling_group_id: {get_resource: asg}
cooldown: 60
scaling_adjustment: -1
cpu_alarm_high:
type: OS::Aodh::GnocchiAggregationByResourcesAlarm
properties:
description: Scale up if CPU > 80%
metric: cpu
aggregation_method: mean
granularity: 300
evaluation_periods: 1
threshold: 2400000000000.0
resource_type: instance
comparison_operator: gt
alarm_actions:
- str_replace:
template: trust+url
params:
url: {get_attr: [web_server_scaleup_policy, signal_url]}
query:
str_replace:
template: '{"=": {"server_group": "stack_id"}}'
params:
stack_id: {get_param: "OS::stack_id"}
cpu_alarm_low:
type: OS::Aodh::GnocchiAggregationByResourcesAlarm
properties:
description: Scale down if CPU < 20% for 5 minutes
metric: cpu
aggregation_method: rate:mean
granularity: 300
evaluation_periods: 1
threshold: 600000000000.0
resource_type: instance
comparison_operator: lt
alarm_actions:
- str_replace:
template: trust+url
params:
url: {get_attr: [web_server_scaledown_policy, signal_url]}
query:
str_replace:
template: '{"=": {"server_group": "stack_id"}}'
params:
stack_id: {get_param: "OS::stack_id"}
# Loadbalancer
lb-test:
type: OS::Octavia::LoadBalancer
properties:
vip_subnet: {get_resource: wordpress_subnet}
# Port 80
listener-80:
type: OS::Octavia::Listener
properties:
loadbalancer: {get_resource: lb-test}
protocol: HTTP
protocol_port: 80
pool-80:
type: OS::Octavia::Pool
properties:
listener: {get_resource: listener-80}
lb_algorithm: ROUND_ROBIN
protocol: HTTP
session_persistence:
type: SOURCE_IP
# Rule for let's encrypt challenge
pool-letsencrypt:
type: OS::Octavia::Pool
properties:
loadbalancer: {get_resource: lb-test}
lb_algorithm: ROUND_ROBIN
protocol: HTTP
session_persistence:
type: SOURCE_IP
# L7 Policy Let's Encrypt
# openstack loadbalancer l7policy create --action REDIRECT_TO_POOL --redirect-pool pool-letsencrypt --name letsencrypt-redirection listener-80 --position 1
l7policy-letsencrypt:
type: OS::Octavia::L7Policy
properties:
action: REDIRECT_TO_POOL
description: Redirect acme to storageserver
listener: {get_resource: listener-80}
name: letsencrypt-redirection
position: 1
redirect_pool: {get_resource: pool-letsencrypt}
# L7 Rule Let's Encrypt
# openstack loadbalancer l7rule create --compare-type STARTS_WITH --type PATH --value /.well-known/acme-challenge letsencrypt-redirection
l7rule-letsencrypt:
type: OS::Octavia::L7Rule
properties:
compare_type: STARTS_WITH
l7policy: {get_resource: l7policy-letsencrypt}
type: PATH
value: /.well-known/acme-challenge
# Port 22
listener-22:
type: OS::Octavia::Listener
properties:
loadbalancer: {get_resource: lb-test}
protocol: TCP
protocol_port: 22
pool-22:
type: OS::Octavia::Pool
properties:
listener: {get_resource: listener-22}
lb_algorithm: ROUND_ROBIN
protocol: TCP
session_persistence:
type: SOURCE_IP
# Port 443
listener-443:
type: OS::Octavia::Listener
properties:
loadbalancer: {get_resource: lb-test}
protocol: TCP
protocol_port: 443
pool-443:
type: OS::Octavia::Pool
properties:
listener: {get_resource: listener-443}
lb_algorithm: ROUND_ROBIN
protocol: TCP
session_persistence:
type: SOURCE_IP
# Monitor for port 80
lb_monitor:
type: OS::Octavia::HealthMonitor
properties:
pool: { get_resource: pool-80 }
type: HTTP
url_path: '/'
expected_codes: 200,302
delay: 5
max_retries: 5
timeout: 5
# IP
lb_floating:
type: OS::Neutron::FloatingIP
properties:
floating_network_id: {get_param: floating_network_id}
port_id: {get_attr: [lb-test, vip_port_id]}
outputs:
website_ip:
value:
str_replace:
template: host
params:
host: { get_attr: [lb_floating, floating_ip_address] }
description: >
Redirect you domain to this ip.
website_url:
value:
str_replace:
template: https://url
params:
url: {get_param: website_url}
description: >
This public URL and IP that can be used to access the wordpress site.
sftp_user:
value:
str_replace:
template: "SFTP user : wordpress"
params:
wordpress: {get_param: sftp_user}
description: >
This user can be used to access the stockage with SFTP.
sftp_password:
value:
str_replace:
template: "SFTP password : pwd"
params:
pwd: {get_attr: [sftp_password, value]}
description: >
This password can be used to access the stockage with SFTP.
database_password:
value:
str_replace:
template: "Database password : pwd"
params:
pwd: {get_attr: [database_password, value]}
description: >
This password can be used to access the database.
database_root_password:
value:
str_replace:
template: "Database root password : pwd"
params:
pwd: {get_attr: [database_root_password, value]}
description: >
This password can be used to access the database as root.
Create another called lb_server.yaml
with the following content or download here :
See lb_server.yaml
---
heat_template_version: 2016-10-14
description: A load-balancer server
parameters:
image:
type: string
description: Image used for servers
key_name:
type: string
description: SSH key to connect to the servers
flavor:
type: string
description: flavor used by the servers
pool_id_80:
type: string
description: Pool to contact
pool_id_443:
type: string
description: Pool to contact
user_data:
type: string
description: Server user_data
metadata:
type: json
network:
type: string
description: Network used by the server
subnet:
type: string
description: Subnet used by the server
security_groups:
type: string
description: Security_groups used by the server
resources:
server:
type: OS::Nova::Server
properties:
flavor: {get_param: flavor}
image: {get_param: image}
key_name: {get_param: key_name}
metadata: {get_param: metadata}
user_data: {get_param: user_data}
security_groups: [{get_param: security_groups}]
user_data_format: RAW
networks: [{network: {get_param: network} }]
member_80:
type: OS::Octavia::PoolMember
properties:
pool: {get_param: pool_id_80}
address: {get_attr: [server, first_address]}
protocol_port: 80
subnet: {get_param: subnet}
member_443:
type: OS::Octavia::PoolMember
properties:
pool: {get_param: pool_id_443}
address: {get_attr: [server, first_address]}
protocol_port: 443
subnet: {get_param: subnet}
outputs:
server_ip:
description: IP Address of the load-balanced server.
value: { get_attr: [server, first_address] }
lb_member:
description: LB member details.
value: { get_attr: [member_80, show] }
Create the stack
Change parameters on wordpress.yaml
:
image:
type: string
description: Image used for servers
default: Debian 11 bullseye
key:
type: string
description: SSH key to connect to the servers
default: <your_keypair>
flavor:
type: string
description: flavor used by the web servers
default: a1-ram2-disk20-perf1
database_flavor:
type: string
description: flavor used by db servers
default: a2-ram4-disk20-perf1
stockage_flavor:
type: string
description: flavor used by the stockage server
default: a1-ram2-disk80-perf1
network:
type: string
description: Network used by the server
default: my-wordpress-network-2
subnet_id:
type: string
description: subnet on which the load balancer will be located
default: my-wordpress-subnet-2
database_name:
type: string
description: Name of the wordpress DB
default: wordpress
database_user:
type: string
description: Name of the wordpress user
default: wordpress
floating_network_id:
type: string
description: UUID of a Neutron external network
default: ext-floating1
sftp_user:
type: string
description: User for SFTP Server
default: www-data
website_url:
type: string
description: url for website
default: <your_domain>
email:
type: string
description: email for let's encrypt
default: <your_email>
Create the stack with Openstack :
openstack stack create -t wordpress-ha.yaml wordpress-ha --wait
The stack takes several minutes to be created and operational.
Servers and IP
You can list freshly created servers to check their status, flavor and other informations.
Info
Database, stockage and web nodes are classic virtual machines
$ openstack server list
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------+-------+----------------------+
| ID | Name | Status | Networks | Image | Flavor |
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------+-------+----------------------+
| 1b7c799b-ae64-49aa-bba5-eb2eeb5acc81 | Wo-ycpt-42udzur6y242-nrgl6iphh46i-server-ewgo3dhkgblm | ACTIVE | my-wordpress-network-2=10.10.3.179 | | a1-ram2-disk20-perf1 |
| 486406b5-2289-4caf-95ba-92d46db721e0 | Wordpress-ha-stockage-vkzedswkl6i6 | ACTIVE | my-wordpress-network-2=10.10.3.20 | | a1-ram2-disk80-perf1 |
| 522c6319-7dd9-43ab-a9d4-5d56f5378092 | Wordpress-ha-db_1-lyay6o3v2pl3 | ACTIVE | my-wordpress-network-2=10.10.3.10 | | a2-ram4-disk20-perf1 |
| aa3bdb2c-163d-40fa-afe7-da138059427a | Wordpress-ha-db_2-nttqvyxhl4m5 | ACTIVE | my-wordpress-network-2=10.10.3.11 | | a2-ram4-disk20-perf1 |
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------+-------+----------------------+
Where are the loadbalancers ?
Well, loadbalancer nodes use the Octavia project, even though their are actual virtual machine running HAProxy you have to issue openstack loadbalancer
command family to interact with them.
More details here.
$ openstack loadbalancer list
+--------------------------------------+---------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| id | name | project_id | vip_address | provisioning_status | operating_status | provider |
+--------------------------------------+---------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| 421fa48b-e5b3-436f-b7c1-74b4b3724ad0 | Wordpress-ha-lb-pgirszcfyj| 0ee6dc5442204fb88c1137414ce32b91 | 10.10.3.232 | ACTIVE | ONLINE | amphora |
+--------------------------------------+---------------------------+----------------------------------+-------------+---------------------+------------------+----------+
Finally, the public IP.
The public IP is a floating IP attached to the port of the loadbalancer.
$ openstack floating ip list
+--------------------------------------+---------------------+------------------+--------------------------------------+--------------------------------------+----------------------------------+
| ID | Floating IP Address | Fixed IP Address | Port | Floating Network | Project |
+--------------------------------------+---------------------+------------------+--------------------------------------+--------------------------------------+----------------------------------+
| 27a2b35f-f334-4c22-a180-b7efd4ce701f | 195.15.246.87 | 10.10.3.232 | abbe8df1-e72d-4d48-bef8-a223a6fe945e | 0f9c3806-bd21-490f-918d-4a6d1c648489 | d1440aa24a65411fb9bac2b842c8defa |
+--------------------------------------+---------------------+------------------+--------------------------------------+--------------------------------------+----------------------------------+