Creating a three-tier highly available architecture on an IBM Cloud VPC
This tutorial describes the three-tiered architecture of an application that is rehosted on IBM cloud. The tutorial outlines the steps to implement a Highly Available solution in the IBM Cloud VPC infrastructure that spans multiple availability zones in one region. The strategies can differ and vary depending upon your application (web, app, and DB) and the redundancy necessary at each level. The tutorial provides a few guidelines for setting up a three tier OLTP application. Wherever possible, guidelines are provided on infrastructure, security, and operations. You might need to refer to your vendors’ supported methods of replication for the tiers, depending on the software components in your architecture.
Architecture diagram
This diagram shows the 3-tier architecture that you create with the tutorial.
You must have full administrative privileges for managing the VPC resources. For more information about user permissions in a VPC, see [Getting user permissions for VPC resources.
Create VPC
Create a VPC named vpcname-region1. For more information, see Deploying critical applications with IBM Cloud MZR - Step 1: Create a VPC
Create Subnets
Create the following subnets:
- vpcname-zone1web-subnet
- vpcname-zone2web-subnet
- vpcname-zone1mt-subnet
- vpcname-zone2mt-subnet
- vpcname-zone1db-subnet
- vpcname-zone2db-dubnet
Make note of the CIDR blocks for the subnets that you created.
For detailed steps, see Deploying critical applications with IBM Cloud MZR - Step 2 Create Subnets
Create SSH Keys
Create SSH keys for the bastion host. You need to be able to log in to the bastion host with the private and public keys (asymmetric pair) from your client computer. For more information, see SSH keys for details on how to generate your ssh keys on your Mac or Linux® clients. If you are using Windows®, generate the keys with PuTTYgen. Use the private and public keys for generating the ssh keys for your region.
- Log in to the IBM Cloud® console.
- Select Menu icon > VPC Infrastructure > SSH keys.
- Select Create a new key.
- Select your region and paste the public key that you generated earlier on your client.
- Make a note of your SSH key name. You use the SSH key when you create the bastion host.
Create Security Groups
Security Groups for Bastion Host
Create a security group (bastion-sg) for the bastion host. You attach the bastion-sg to the bastion Virtual Server Instance that you create later.
Inbound rules for bastion-sg
Protocol | Source type | Value |
---|---|---|
TCP | Any | Ports 22-22 |
ICMP | Any | Type: 8 Code: empty |
Outbound rules for bastion-sg
Protocol | Source type | Value |
---|---|---|
All | Any | n/a |
These rules allow ssh access to the bastion host from the outside world. You can also set up a VPN to the bastion host.
For more information, see Deploying critical applications with IBM Cloud MZR - Step 3 Create two security groups.
Maintenance security groups for virtual server instances
Because you need to log in to the web, app, and database tiers from the bastion host you must create a maintenance security group. The maintenance security groups allow SSH access from the Bastion host to all of the Virtual Server Instances. The maintenance security group is used for administrative tasks only. It can be enabled on the Virtual Server Instances for routine maintenance work such as patching, viewing logs, and troubleshooting, and can be disabled when these tasks are complete.
For detailed steps, see Deploying critical applications with IBM Cloud MZR - Step 3 Create two security groups.
Create the inbound and outbound rules for vpc-secure-bastion-sg with these attributes:
Inbound rules for vpc-secure-bastion-sg
Protocol | Source type | Source | Value |
---|---|---|---|
TCP | Security group | bastion-sg | Ports 22-22 |
ICMP | Security group | bastion-sg | Type: 8 Code: empty |
Outbound rules for vpc-secure-bastion-sg
Protocol | Source type | Value |
---|---|---|
All | Any | n/a |
When this security group is attached to a virtual server instance, all outbound connections that originate from the virtual server instance are allowed. However, the incoming connections (SSH and ICMP) are restricted only from the bastion host.
Security Groups for Virtual Server Instances
Create a third Security Group that is specific for each of the Virtual Server Instances, such as on the web, app, and database tiers. These Security Groups are always attached to the Virtual Server Instances and allow the HTTP and TCP traffic, to and from the Virtual Server Instances.
Create three Security Groups, one for each tier with inbound and outbound rules as follows:
Security Group | Direction | Protocol | Source type | Source | Value |
---|---|---|---|---|---|
nginx-web-sg | Inbound | TCP | any | -- | ports 80-80 |
TCP | any | -- | ports 443-443 | ||
Outbound | any | any | -- | -- | |
tomcat-mt-sg | Inbound | TCP | CIDR Block | vpcname-zone1web-subnet CIDR Block | ports 8080-8080 |
TCP | CIDR Block | vpcname-zone2web-subnet | ports 8080-8080 | ||
outgoing | any | any | -- | -- | |
mysql-db-sg | Inbound | TCP | CIDR Block | vpcname-zone1mt-subnet CIDR Block | ports 3306-3306 |
TCP | CIDR Block | vpcname-zone2mt-subnet CIDR Block | ports 3306-3306 | ||
TCP | CIDR Block | vpcname-zone2db-subnet CIDR Block | ports 3306-3306 | ||
TCP | CIDR Block | vpcname-zone2db-subnet CIDR Block | ports 3306-3306 | ||
outgoing | any | any | -- | -- |
Note the following information:
- Each security group accepts incoming requests from its preceding subnet and on the appropriate port. Security groups are stateful.
- No restrictions are imposed on outgoing traffic.
- The database security group is opened not only to the middle-tier incoming traffic but also from the database subnets. This arrangement takes care of database replication between the MySQL nodes.
- Opening the subnets instead of IP addresses, allows incoming traffic from any newly created Virtual Server Instances (either manually or through Auto Scaling groups) that might be added later.
- When you create Virtual Server Instances, you attach the Security Groups to the Virtual Server Instances
- Only the maintenance Security Group, vpc-secure-bastion-sg, provide SSH access to the web, Middle Tier, and DB servers. The other Security Groups for the web, mt, and db are needed in the production system and are always enabled.
Create Bastion Virtual Server Instance
Create Bastion Host
- Log in to the IBM Cloud® console.
- Select Menu icon > VPC infrastructure > Virtual server instances.
- Click Create.
- Enter a name for the virtual server instance and:
- Select your resource group,
- Select your region and zone
- Select the Virtual Server Instance as your public virtual server.
- Choose the OS, for example Ubuntu Linux®.
- Select a balanced profile like bx2-2x8.
- Select the SSH keys that you created earlier for the bastion host.
- Edit the boot volume and update the resource group if necessary.
- Choose the VPC that you created.
- Select the interface name and edit the interface details. You can select either the default subnet or any one of the subnets you created earlier.
- Attach the Security group bastion-sg. Save the details.
- Click Create.
Post Install steps on Bastion host
-
Log in to the bastion host as ssh -i ~/.ssh/id_rsa root@ip-address from your Mac.
-
Create user and sudo access to the new user if you want to avoid root user for operations.
-
Run apt upgrade –y to bring the most recent patches to your kernel. Make sure that the public gateway is enabled on the subnet-hosting bastion server that is acting as NAT GW to download the necessary packages. Restart the host as soobnn as the upgrade is complete.
-
Create private and public keys on the bastion host. Refer to SSH keys for details on how to generate your ssh keys on your Mac or Linux® clients. If you are using Windows®, generate the keys with PuTTYgen. Use the private and public keys for generating the ssh keys for your region.
-
Create a set of SSH keys for access to Virtual Server Instances from the bastion host. Follow the same steps that you used earlier to Create SSH Keys. You can name the new keys as infrakeys. The public key that was generated on the bastion host in bullet(4) is used in creating these keys. You use this SSH key (infrakeys) while you are creating the Virtual Server Instances for web, app, and database tiers. Creating the ssh key allows logging in to these Virtual Server Instances from the bastion host.
You can create two different bastion hosts one in each Availability zone to have an HA against zone failures and can even shut down one of these hosts. Follow the notes on the Securely access remote instances with a bastion host page for more details on bastion hosts.
Creating Virtual Server Instances for the application
Create 6 Virtual Server Instances, for the web tier, middle tier and database tiers, one in each zone, and in the respective subnet.
Virtual Server Instance | Subnet name | Zone | Attach Security Group |
---|---|---|---|
nginx-web1-zone1 | vpcname-zone1web-subnet | Zone 1 | nginx-web-sg |
vpc-secure-bastion-sg | |||
nginx-web2-zone2 | vpcname-zone2web-subnet | Zone 2 | nginx-web-sg |
• vpc-secure-bastion-sg | |||
tomcat-mt1-zone1 | vpcname-zone1mt-subnet | Zone 1 | tomcat-mt-sg |
• vpc-secure-bastion-sg | |||
tomcat-mt2-zone2 | vpcname-zone2mt-subnet | Zone 2 | tomcat-mt-sg |
• vpc-secure-bastion-sg | |||
mysql-master1-zone1 | vpcname-zone1db-subnet | Zone 1 | mysql-db-sg |
• vpc-secure-bastion-sg | |||
mysql-master2-zone2 | vpcname-zone2db-subnet | Zone 2 | mysql-db-sg |
• vpc-secure-bastion-sg |
Create each Virtual Server Instance.
-
Log in to the IBM Cloud® console.
-
Select Menu icon > VPC infrastructure > Virtual server instances.
-
Click Create.
-
Enter the name of the Virtual Server Instance
-
Select the:
- Resource groups and tags, if any.
- Region and the availability zone.
- CPU and RAM profiles for your web, middle tier, and database servers.
- Infrakeys as the SSH key that you created earlier.
- Your operating system. For this example, choose Ubuntu 20.04.
- Your VPC and Network interface.
- A boot volume for the Virtual Server Instances.
- For MySql nodes, create and attach a secondary data volume of your wanted size.
-
Repeat steps 1-5 for each of the remaining virtual server instances.
After the Virtual Server Instances are created, follow these steps:
-
Make sure that the subnet is attached with public gateway to upgrade the servers.
-
Make sure that vpc-secure-bastion-sg security group is attached to all the Virtual Server Instance’s.
-
Log in to the bastion host and from there to each of these servers.
-
Run apt-upgrade -y on each server to update the kernel to the most recent patch levels and restart.
-
Open up firewall ports that use this procedure:
- For web servers: ufw allow from any to any port 80 proto tcp.
- For Middle tier servers: ufw allow from any to any port 8080 proto tcp
- For Database servers: ufw allow from any to any port 3306 proto tcp
- For Database servers: ufw port 22 for SSH access on all these servers.
Change the port numbers for web, Middle tier, and Database servers if you plan to run the services on different ports. While firewall ports are opened as any, they are still bound by the inbound and outbound rules created earlier with security groups.
-
Validate that the ports are open from one tier to the other and also between availability zones.
From web1 and web2: nc -w5 -z -v < ipaddress of mt1/mt2 > 8080.
From mt1 and mt2: nc -w5 -z -v -w5 -z -v < ipaddress of mysql1/mysql2 > 3306.
From mysql1 to mysql2: nc -w5 -z -v < ipaddress of mysql1/mysql2 > 3306.
Install Software on Virtual Server Instances
Install MySQL software on DB nodes:
-
Log in to DB nodes through Bastion server.
-
Make sure that the name resolution works from each of the nodes as update
/etc/hosts
on each mysql node with each other's address. -
Install mysql server:
apt-get install mysql-server mysql-client
This command installs the sql server in the default location.
-
Move the sql files to the data volume that you created earlier.
-
Stop the mysql service.
service mysql stop
-
Check the new volume on the mysql servers with lsblk. For instance, if it is named as vdb:
vdb 254:16 0 100G 0 disk
-
Partition this volume with parted or fdisk. For example, with fdisk, you can choose n to create a new partition, set first sector as 2048, and size as +100G, then save the changes with w. Verify that the partitions are created with lsblk or fdisk.
-
Create a Linux® file system on the vdb1 partition.
mkfs.xfs /dev/vdb1
-
Identify the UUID for secondary volume (vdb1) partition.
blkid | grep /dev/vdb1 /dev/vdb1: UUID="a65690bf-0c5e-467b-bb27-7c1558f1ed65" TYPE="xfs" PARTUUID="c4022fcb-01"
-
Enter this UUID command in fstab to make the mounts persistent after it restarts.
UUID= a65690bf-0c5e-467b-bb27-7c1558f1ed65 /data xfs defaults 1 2
-
Create a mount point and mount the created file system.
mkdir /data mount /data
-
Make sure that mysql is not running and copy the contents from default location to /data.
service mysql stop rsync -av /var/lib/mysql /data
This command creates directory mysql under /data and copies the contents.
-
Update the configuration files.
-
Update /etc/mysql/mysql.conf.d/mysqld.cn:
-
Set datadir=/data/mysql
-
Set server-id=1
-
Set log_bin=/data/mysql/mysql-bin.log
-
Comment out all bind-addresses.
-
-
Update /etc/apparmor.d/tunables/alias:
- Set /var/lib/mysql -> /data/mysql/
-
-
Restart services.
systemctl restart apparmor systemctl restart mysql.service systemctl enable mysql.service
-
-
Repeat all the steps from 3 and 4 in the second mysql node in the second zone. Set the server-id in the cnf file to 2 instead of 1.
Set up replication between the two nodes
-
Log in to mysql on node1 as mysql -u root
-
Create user 'repluser'@'%' identified by < passwd >
-
Grant replication secondary on '*.*' to 'repluser'@'%'
-
Run the sql command SHOW MASTER STATUS and note the file name and position on both nodes and use the values that are returned from the previous steps, into the replication command.
-
Before you issue replication commands, make sure that the port 3306 is open between the two nodes. Run on both the nodes for 2-way replication:
nc -w5 -z -v <ip address> 3306
-
To avoid errors with sha authentication, run this command on both nodes:
Alter user 'repluser'@'% identified with mysql_native_password by '<password>';
-
On node2 enter the details of node1 in mysql:
stop slave; change master to master_host='<node1 ip>', master_user='repluser', master_password=’<passwd>' master_log_file=' mysql-bin.000001', master_log_pos= 697; start slave;
-
On node1 enter the details of node2 as in mysql:
stop slave change master to master_host='<node2 ip>', master_user='repluser', master_password=’<password>', master_log_file=' mysql-bin.000002', master_log_pos= 156; start slave
-
Check status of both the nodes.
show slave status\G
-
Verify that the replication is working fine.
a. Log in to node1 mysql
b. Create database testdb1;
Use testdb1
c. Create a test table and insert a couple of rows.
create table testtable1 (owner varchar(20), department varchar(20)); insert into testtable1 values(‘abc’,’hr’); insert into testtable1 values(‘xyz’,’eng’); commit;
b. Log in to Node 2 mysql
Use testdb1 Select * from testtable1.
You can now see the table of contents that you created on node1.
-
Repeat step 10 and create a table on node2 and check on node1 to make sure that 2-way replication is working.
Install Tomcat on Middle tier nodes
The following steps are a snippet for Tomcat install on the two middle tier servers. You can also install any other middle tier of your choice and make the changes.
-
Log in to one of the middle tiers Virtual Server Instances you created earlier.
-
Install Tomcat.
apt install -y tomcat9 tomcat9-admin
-
Update /etc/hosts with both mysql and middle tier node addresses.
-
Install jdbc drivers for your operating system from https://dev.mysql.com/downloads/connector/j/
Download and install the deb pkg as dpkg -i <.deb>
Depending on your application, you can download other JAR files like jstl-1.2 JAR files.
Alternatively, you can extract and copy the mysql-connector-java-8.0.23.JAR and jstl-1.2 JAR files to $CATALINA_HOME/lib directory.
-
Add or update these parameters to the context.xml file /var/lib/tomcat9/webapps/ROOT/META-INF.
< Context > < Resource name="jdbc/[YourDatabaseName]" auth="Container" type="javax.sql.DataSource" username="[DatabaseUsername]" password="[DatabasePassword]" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://<mysql Load Balancer>:3306/<DatabaseName>"/> < /Context >
-
Update web.xml with appropriate res-name and types. You might have to update this context file with the name of the database Load balancer after all the three load balancers are set up.
-
Restart and enable the Tomcat service.
systemctl restart tomcat9.service systemctl enable tomcat9.service
-
Repeat steps 1-7 on the second middle tier node.
Install Nginx on web nodes
-
Log in to one of the web Virtual Server Instances you created earlier.
-
Install nginx.
apt install nginx
-
Update the /etc/hosts with both nginx and middle-tier node details.
-
Restart and enable the nginx service.
systemctl restart nginx.service systemctl enable nginx.service
-
Repeat steps 1-4 on the second web node.
Install Load Balancers
Install three Application load balancers.
The web load balancer receives traffic from internet and forwards the requests to the web servers. The web load balancer is your public load balancer. The other two load balancers are private load balancers. The middle tier load balancer receives traffic from web tier and forwards to Apache Tomcat middle tier servers while the database load balancer forwards to mysql databases from middle tier.
This table lists the subnet connectivity and security groups:
Type | Load Balancer | Subnets | Security Groups | Comments |
---|---|---|---|---|
Public | Web Load Balancer | vpcname-zone1web-subnet, vpcname-zone2web-subnet | nginx-web-sg | This being public ALB then receives http traffic from the internet on port 80/443. |
Private | Middle Tier Load Balancer | vpcname-zone1web-subnet, vpcname-zone2web-subnet, vpcname-zone1mt-subnet, vpcname-zone2mt-subnet | nginx-web-sg, tomcat-mt-sg | Receives HTTP traffic on port 8080 from web server and forwards the traffic to Middle tier servers |
Private | Database Load Balancer | vpcname-zone1mt-subnet, vpcname-zone2mt-subnet, vpcname-zone1db-subnet, vpcname-zone2db-subnet | tomcat-mt-sg, mysql-db-sg | Receives TCP traffic on port 3306 and forwards the traffic to port 3306 |
Install all the three load balancers with the prior configuration.
For each load balancer, use these steps:
-
Log in to the IBM Cloud® console.
-
Click Menu icon > VPC infrastructure > Load Balancers.
-
Click Create.
-
Enter the information for the load balancer.
-
Select the Type for the Load Balancers.
- Set the Web Load Balancer to Public
- Set the Middle Tier and Database load balancers to Private
-
Select your resource group, region, and your VPC.
-
Select the checkboxes for the subnets.
- Assign these subnets to the Web Load Balancer:
- vpcname-zone1web-subnet
- vpcname-zone2web-subnet
- Assign these subnets to the Middle Tier Load Balancer:
- vpcname-zone1web-subnet
- vpcname-zone2web-subnet
- vpcname-zone1mt-subnet
- vpcname-zone2mt-subnet
- Assign these subnets to the Database Load Balancer:
- vpcname-zone1mt-subnet
- vpcname-zone2mt-subnet
- vpcname-zone1db-subnet
- vpcname-zone2db-subnet
- Assign these subnets to the Web Load Balancer:
-
Select the security groups.
- Assign the nginx-web-sg security group to the Web Load Balancer
- Assign the nginx-web-sg and tomcat-mt-sg security groups to the Middle Tier Load Balancer
- Assign the tomcat-mt-sg and mysql-db-sg security groups to the Database Load Balancer
-
Configure each of the load balancer back-end pools and front-end listeners:
- Public Web Tier LB (nginx-alb) Back-End Pool:
- Protocol – HTTP, Method – Round Robin
- Health Check – Path /
- HTTP 80
- Attach Virtual Servers Webserver1 and Webserver2
- Public Web Tier LB (nginx-alb) Front-End Listener:
- HTTP on Port 80 and attach to the back-end pool created. |
- Private Middle Tier LB (tomcat-alb) Back-End Pool:
- Protocol – HTTP,
- Method – Round Robin,
- Health Check – Path /
- HTTP 8080
- Attach Virtual Servers, MT1 and MT2 servers
- Private Middle Tier LB (tomcat-alb) Front-End Listener:
- HTTP on Port 8080 and attach to the back-end pool created.
- Private Database Tier LB (mysql-alb) Back-End Pool:
- Protocol – TCP
- Method – Round Robin
- Health Check – TCP 3306
- Attach Virtual Servers, mysql1 and mysql2 database servers
- Private Database Tier LB (mysql-alb) Front-End Listener:
- TCP on Port 3036 and attach to the back-end pool created
- Public Web Tier LB (nginx-alb) Back-End Pool:
-
Click Create Load Balancer.
-
Make a note of the hostname of each load balancer.
-
With the services up and running on the VM, verify that the ports are open from the VMs to the load balancers.
- From external network: nc -w5 -z -v < hostname of Web Load Balancer > 80
- From web1 and web2: nc -w5 -z -v < hostname of Web Load Balancer > 80
- From MT1 and MT2: nc -w5 -z -v < hostname of MT Load Balancer > 8080
- From MT1 and MT2: nc -w5 -z -v < hostname of DB Load Balancer > 3306
- From DB1 and DB2: nc -w5 -z -v < hostname of DB Load Balancer > 3306
Update Configuration files with your Load Balancer names
-
Update the /etc/nginx/sites-enabled/default file on each of the nginx servers to pass the requests to middle-tier ALB:
proxy_pass http://<tomcat-alb>:8080;
-
Restart nginx server on both the nodes.
-
Update /var/lib/tomcat9/webapps/ROOT/META-INF/context.xml, and jdbc url with your database load balancer with this url:
url="jdbc:mysql://<DB load balancer>:3306/<DatabaseName> "
-
Restart the Tomcat service on both nodes.
These configurations can vary depending on your application. Make the necessary changes.
The configuration of all the components of a three tier architecture that includes the load balancers is now complete.