Skip to content

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 |
+--------------------------------------+---------------------+------------------+--------------------------------------+--------------------------------------+----------------------------------+