Skip to content

WordPress : High Available and auto scalable

Create a High Available and scalabale WordPress blog in a single command.

$ openstack stack create -t wordpress.yaml wordpress

WordPress

Architecture

WordPress

There are three kinds of nodes:

  • Database node : In this setup there's only one node hosting the database.

  • Web nodes : The web nodes run Apache and serve HTTP requests. They connect to the database node. The number of web nodes depends on the CPU usage and may vary from 1 to 3 nodes.

The nodes increase or decrease when CPU usage is above 60% or below 30%.

  • Load Balancer nodes : These nodes run haproxy and redirect the traffic to the web node(s). They are highly available, act as Active/Passive and manage the public IP.

Prerequisites

Heat templates

Create a file called wordpress.yaml with the following content:

---
heat_template_version: 2016-10-14
description: AutoScaling Wordpress
parameters:
  image:
    type: string
    description: Image used for servers
    default: debian-buster-10.9.0-amd64.raw
  key:
    type: string
    description: SSH key to connect to the servers
    default: mykeypair
  flavor:
    type: string
    description: flavor used by the web servers
    default: a1-ram2-disk20-perf1
  database_flavor:
    type: string
    description: flavor used by the db server
    default: a2-ram4-disk20-perf1
  network:
    type: string
    description: Network used by the server
    default: my-wordpress-network
  subnet_id:
    type: string
    description: subnet on which the load balancer will be located
    default: my-wordpress-subnet
  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

resources:
  # Security group
  wordpress_security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "wordpress_security_group"
      description: >
        Allows connection from remote IP (22, 80, 443, 3306)
      rules:
         - { 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: 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: {get_param: subnet_id}
      network_id: {get_resource: wordpress_net}
      cidr: "10.11.12.0/24"
      dns_nameservers:
        - "84.16.67.69"
        - "84.16.67.70"
      ip_version: 4

  wordpress_router:
    type: OS::Neutron::Router
    properties:
      name:  'worpress-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}

  database_password:
    type: OS::Heat::RandomString
  database_root_password:
    type: OS::Heat::RandomString
  db:
    type: OS::Nova::Server
    properties:
      flavor: {get_param: database_flavor}
      image: {get_param: image}
      key_name: {get_param: key}
      networks:
          - port: { get_resource: my_wordpressdb_port }
      user_data_format: RAW
      user_data:
        str_replace:
          template: |
            #!/bin/bash -v
            apt -y install mariadb-server
            systemctl enable mariadb.service
            sed -i s/127.0.0.1/0.0.0.0/ /etc/mysql/mariadb.conf.d/50-server.cnf
            systemctl restart mariadb.service
            mysqladmin -u root password $db_rootpassword
            cat << EOF | mysql -u root --password=$db_rootpassword
            CREATE DATABASE $db_name;
            GRANT ALL PRIVILEGES ON $db_name.* TO "$db_user"@"%"
            IDENTIFIED BY "$db_password";
            FLUSH PRIVILEGES;
            EXIT
            EOF
          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]}
  asg:
    type: OS::Heat::AutoScalingGroup
    properties:
      min_size: 1
      max_size: 3
      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: {get_resource: pool}
          metadata: {"metering.server_group": {get_param: "OS::stack_id"}}
          user_data:
            str_replace:
              template: |
                #!/bin/bash -v
                apt update
                apt -y install wget curl apache2 rsync php-gd libapache2-mod-php libapache2-mod-php7.3 php-mysql libdbd-mysql-perl default-mysql-client fail2ban
                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/
                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/$db_host/ 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
              params:
                $db_name: {get_param: database_name}
                $db_user: {get_param: database_user}
                $db_password: {get_attr: [database_password, value]}
                $db_host: {get_attr: [db, first_address]}
  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: 240000000000.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: 60000000000.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"}
  lb:
    type: OS::Octavia::LoadBalancer
    properties:
      vip_subnet: {get_resource: wordpress_subnet}
  listener:
    type: OS::Octavia::Listener
    properties:
      loadbalancer: {get_resource: lb}
      protocol: HTTP
      protocol_port: 80
  pool:
    type: OS::Octavia::Pool
    properties:
      listener: {get_resource: listener}
      lb_algorithm: ROUND_ROBIN
      protocol: HTTP
      session_persistence:
        type: SOURCE_IP
  lb_monitor:
    type: OS::Octavia::HealthMonitor
    properties:
      pool: { get_resource: pool }
      type: HTTP
      url_path: '/'
      expected_codes: 200,302
      delay: 5
      max_retries: 5
      timeout: 5
  # assign a floating ip address to the load balancer
  # pool.
  lb_floating:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network_id: {get_param: floating_network_id}
      port_id: {get_attr: [lb, vip_port_id]}

outputs:
  website_url:
    value:
      str_replace:
        template: http://host
        params:
          host: { get_attr: [lb_floating, floating_ip_address] }
    description: >
      This public URL and IP that can be used to access the Wordpress site.

Create another called lb_server.yaml with the following content :

---
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:
    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:
    type: OS::Octavia::PoolMember
    properties:
      pool: {get_param: pool_id}
      address: {get_attr: [server, first_address]}
      protocol_port: 80
      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, show] }

Create the stack

$ openstack stack create -t wordpress.yaml wordpress

The stack takes several minutes to be created and operational.

Once created check the url and the public IP to finish the wordpress setup :

In the example below it is: http://195.15.244.24

$ openstack stack show wordpress --fit-width
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field                 | Value                                                                                                                                                                            |
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id                    | 6ac35326-b5a9-4124-aace-39430ab0c06c                                                                                                                                             |
| stack_name            | wordpress                                                                                                                                                                        |
| description           | AutoScaling Wordpress                                                                                                                                                            |
| creation_time         | 2021-05-03T13:25:25Z                                                                                                                                                             |
| updated_time          | None                                                                                                                                                                             |
| stack_status          | CREATE_COMPLETE                                                                                                                                                                  |
| stack_status_reason   | Stack CREATE completed successfully                                                                                                                                              |
| parameters            | OS::project_id: d1440aa24a65411fb9bac2b842c8defa                                                                                                                                 |
|                       | OS::stack_id: 6ac35326-b5a9-4124-aace-39430ab0c06c                                                                                                                               |
|                       | OS::stack_name: wordpress                                                                                                                                                        |
|                       | database_flavor: a2-ram4-disk20-perf1                                                                                                                                            |
|                       | database_name: wordpress                                                                                                                                                         |
|                       | database_user: wordpress                                                                                                                                                         |
|                       | flavor: a2-ram4-disk20-perf1                                                                                                                                                     |
|                       | floating_network_id: ext-floating1                                                                                                                                               |
|                       | image: debian-10-openstack-amd64.raw                                                                                                                                             |
|                       | key: yubikey-taylor                                                                                                                                                              |
|                       | network: my-wordpress-network                                                                                                                                                    |
|                       | subnet_id: my-wordpress-subnet                                                                                                                                                   |
|                       |                                                                                                                                                                                  |
| outputs               | - description: This public URL and IP that can be used to access the Wordpress site.                                                                                             |
|                       |   output_key: website_url                                                                                                                                                        |
|                       |   output_value: http://195.15.244.24                                                                                                                                             |
|                       |                                                                                                                                                                                  |
| links                 | - href: https://api.pub1.infomaniak.cloud/v1/d1440aa24a65411fb9bac2b842c8defa/stacks/wordpress/6ac35326-b5a9-4124-aace-39430ab0c06c                                              |
|                       |   rel: self                                                                                                                                                                      |
|                       |                                                                                                                                                                                  |
| deletion_time         | None                                                                                                                                                                             |
| notification_topics   | []                                                                                                                                                                               |
| capabilities          | []                                                                                                                                                                               |
| disable_rollback      | True                                                                                                                                                                             |
| timeout_mins          | None                                                                                                                                                                             |
| stack_owner           | taylor                                                                                                                                                                           |
| parent                | None                                                                                                                                                                             |
| stack_user_project_id | 4b936b75dd934fd8bb0644709b531a59                                                                                                                                                 |
| tags                  | []                                                                                                                                                                               |
|                       |                                                                                                                                                                                  |
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Servers and IP

You can list freshly created servers to check their status, flavor and other informations.

Info

​ Database and web nodes are classic virtual machines

$ openstack server list
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------------------+-------------------------------+----------------------+
| ID                                   | Name                                                  | Status | Networks                                       | Image                         | Flavor               |
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------------------+-------------------------------+----------------------+
| b5cbd7f6-cf22-4d2b-9968-47e7e92003d9 | wo-pbso-lwlc5mvjfq27-pl3fnem36r5k-server-tmddooqgsz5q | ACTIVE | my-wordpress-network=10.11.12.211              | debian-10-openstack-amd64.raw | a2-ram4-disk20-perf1 |
| 8a82e7ad-0c42-4cd1-a7f0-4ea7a6755f20 | wordpress-db-s7inj4sfzgq2                             | ACTIVE | my-wordpress-network=10.11.12.140              | debian-10-openstack-amd64.raw | 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 |
+--------------------------------------+---------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| 7acc9d54-6b33-4148-a2ef-cc3c0097e1b5 | wordpress-lb-6z4twath627y | d1440aa24a65411fb9bac2b842c8defa | 10.11.12.26 | 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.244.24       | 10.11.12.26      | abbe8df1-e72d-4d48-bef8-a223a6fe945e | 0f9c3806-bd21-490f-918d-4a6d1c648489 | d1440aa24a65411fb9bac2b842c8defa |
+--------------------------------------+---------------------+------------------+--------------------------------------+--------------------------------------+----------------------------------+

Auto scale up and down

The auto scale uses Gnocchi project for reading metrics and Aodh project to trigger actions based on these metrics. More details about metrics here and here regarding the alerting CPU usage based.

To see the alarm status:

$ openstack alarm list
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+
| alarm_id                             | type                                       | name                                        | state | severity | enabled |
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+
| f29c6b48-501f-4b44-b608-c0d8991cd617 | gnocchi_aggregation_by_resources_threshold | wordpress-cpu_alarm_low-lqffbb77z67s        | alarm | low      | True    |
| 8c8beea0-fb77-46b1-8574-46009dad6b14 | gnocchi_aggregation_by_resources_threshold | wordpress-cpu_alarm_high-nfitaw5w2kkj       | ok    | low      | True    |
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+

wordpress-cpu_alarm_low-lqffbb77z67s state alarm means take action which is keep the number of VMs low with a minimum of 1. wordpress-cpu_alarm_high-nfitaw5w2kkj state ok means does nothing.

Testing the autoscale

Assign temporay a public IP to access the web node and to stress the cpu, after a few minutes additional web nodes should be created.

  • Reserve a floating IP
$ openstack floating ip create ext-floating1
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| created_at          | 2021-05-03T14:07:16Z                 |
| description         |                                      |
| dns_domain          | None                                 |
| dns_name            | None                                 |
| fixed_ip_address    | None                                 |
| floating_ip_address | 195.15.244.171                       |
| floating_network_id | 0f9c3806-bd21-490f-918d-4a6d1c648489 |
| id                  | 847b323f-9386-4f8b-99a1-79b1ff71e61f |
| name                | 195.15.244.171                       |
| port_details        | None                                 |
| port_id             | None                                 |
| project_id          | d1440aa24a65411fb9bac2b842c8defa     |
| qos_policy_id       | None                                 |
| revision_number     | 0                                    |
| router_id           | None                                 |
| status              | DOWN                                 |
| subnet_id           | None                                 |
| tags                | []                                   |
| updated_at          | 2021-05-03T14:07:16Z                 |
+---------------------+--------------------------------------+
  • Attach the floating IP to the web node port
$ openstack port list --server wo-pbso-lwlc5mvjfq27-pl3fnem36r5k-server-tmddooqgsz5q
+--------------------------------------+------+-------------------+-----------------------------------------------------------------------------+--------+
| ID                                   | Name | MAC Address       | Fixed IP Addresses                                                          | Status |
+--------------------------------------+------+-------------------+-----------------------------------------------------------------------------+--------+
| 570260e2-4659-447b-957b-2483817394dd |      | fa:16:3e:c7:92:22 | ip_address='10.11.12.211', subnet_id='31fdc6c2-cb37-4ca1-b7df-32ab7a60eb42' | ACTIVE |
+--------------------------------------+------+-------------------+-----------------------------------------------------------------------------+--------+
$ openstack floating ip set --port 570260e2-4659-447b-957b-2483817394dd 195.15.244.171
  • ssh the web node and stress the cpu
$ ssh debian@195.15.244.171
debian@wo-pbso-lwlc5mvjfq27-pl3fnem36r5k-server-tmddooqgsz5q:~$ sudo apt install stress
debian@wo-pbso-lwlc5mvjfq27-pl3fnem36r5k-server-tmddooqgsz5q:~$ stress -c 2
  • Check the alarms and the number of VMs

CPU metrics are aggregated every 5 minutes so it can take up to 5 minutes to start new VMs.

$ openstack alarm list
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+
| alarm_id                             | type                                       | name                                        | state | severity | enabled |
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+
| f29c6b48-501f-4b44-b608-c0d8991cd617 | gnocchi_aggregation_by_resources_threshold | wordpress-cpu_alarm_low-lqffbb77z67s        | ok    | low      | True    |
| 8c8beea0-fb77-46b1-8574-46009dad6b14 | gnocchi_aggregation_by_resources_threshold | wordpress-cpu_alarm_high-nfitaw5w2kkj       | ok    | low      | True    |
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+
OK for both alarms means that the CPU usage is between the thresholds:

wordpress-cpu_alarm_low-lqffbb77z67s < cpu_usage < wordpress-cpu_alarm_high-nfitaw5w2kkj

  • Check the alarms and the number of VMs after a few minutes
$ openstack alarm list
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+
| alarm_id                             | type                                       | name                                        | state | severity | enabled |
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+
| f29c6b48-501f-4b44-b608-c0d8991cd617 | gnocchi_aggregation_by_resources_threshold | wordpress-cpu_alarm_low-lqffbb77z67s        | ok    | low      | True    |
| 8c8beea0-fb77-46b1-8574-46009dad6b14 | gnocchi_aggregation_by_resources_threshold | wordpress-cpu_alarm_high-nfitaw5w2kkj       | alarm | low      | True    |
+--------------------------------------+--------------------------------------------+---------------------------------------------+-------+----------+---------+

wordpress-cpu_alarm_low-lqffbb77z67s < wordpress-cpu_alarm_high-nfitaw5w2kkj < cpu_usage

New VMs shoud be created:

$ openstack server list
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------------------+--------------------------------+----------------------+
| ID                                   | Name                                                  | Status | Networks                                       | Image                          | Flavor               |
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------------------+--------------------------------+----------------------+
| a3cb4b3f-43b9-413c-b391-59ee82bb4367 | wo-pbso-sn72lqvcjhq7-eqhn3s4jsorb-server-vzye4rbbwsah | BUILD  |                                                | debian-buster-10.9.0-amd64.raw | a2-ram4-disk20-perf1 |
| b5cbd7f6-cf22-4d2b-9968-47e7e92003d9 | wo-pbso-lwlc5mvjfq27-pl3fnem36r5k-server-tmddooqgsz5q | ACTIVE | my-wordpress-network=10.11.12.211              | debian-10-openstack-amd64.raw | a2-ram4-disk20-perf1 |
| 8a82e7ad-0c42-4cd1-a7f0-4ea7a6755f20 | wordpress-db-s7inj4sfzgq2                             | ACTIVE | my-wordpress-network=10.11.12.140              | debian-10-openstack-amd64.raw | a2-ram4-disk20-perf1 |
+--------------------------------------+-------------------------------------------------------+--------+------------------------------------------------+--------------------------------+----------------------+

The autoscale works as expected, a new VM is being provisionned and will automatically receive requests fron the loadbalancer.

Back to top