diff --git a/changelogs/fragments/webapp_in_HA.yaml b/changelogs/fragments/webapp_in_HA.yaml new file mode 100644 index 00000000..74cf75cb --- /dev/null +++ b/changelogs/fragments/webapp_in_HA.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: +- "Add a playbook to deploy a simple flask web app into high availability architecture (https://github.com/redhat-cop/cloud.aws_ops/pull/97)." diff --git a/playbooks/webapp/README.md b/playbooks/webapp/README.md index 5b612ee3..bf7bdef3 100644 --- a/playbooks/webapp/README.md +++ b/playbooks/webapp/README.md @@ -85,7 +85,7 @@ To delete the webapp: ### Common * **operation** (str): Operation for the webapp playbook to perform, either `create` or `delete`. Default: `create` -* **resource_prefix** (str): (Required) A prefix to prepend to the name of all AWS resources created for the webapp +* **resource_prefix** (str): A prefix to prepend to the name of all AWS resources created for the webapp. Default: `ansible-test` * **resource_tags** (dict, elements dict): Tags to apply to all AWS resources created for the webapp. Default: `prefix: "{{ resource_prefix }}"` * **aws_access_key** (str): (Required) AWS access key ID for user account with the above permissions * **aws_secret_key** (str): (Required) AWS secret access key for user account with the above permissions @@ -157,6 +157,25 @@ To delete the webapp: ``` * **deploy_flask_app_force_init** (bool): Whether to drop existing tables and create new ones when deploying the webapp database. Default: `false` +### webapp deployment in HA architecture + +`webapp_ha_aurora.yaml` playbook deploys the flask app to a cross region high availability architecture. The playbook replicates the app deployment to a second region. The backend is an Aurora global cluster. For adding the write forwarding feature, aurora-mysql can be used. Default db engine is aurora-postgresql. The app in each region is configured to access the associated Aurora cluster. In front of the two regions, route53 records are added to provide cross region DNS (failover scenario). + +Along with the [above](https://github.com/redhat-cop/cloud.aws_ops/blob/main/playbooks/webapp/README.md#playbook-variables) variables, following variables are needed for this playbook: + +* **rds_instance_class** (str): DB instance class for the aurora db instances. Default: `db.r5.large` +* **rds_global_cluster_name** (str): Name of the global cluster. Default: "{{ resource_prefix }}-global-cluster" +* **rds_primary_cluster_name** (str): Name of the primary cluster. Default: "{{ resource_prefix }}-primary-cluster" +* **rds_primary_cluster_region** (str): Primary Region. Default: `us-west-2` +* **rds_primary_cluster_instance_name** (str): Name of primary db instance. Default: "{{ resource_prefix }}-primary-instance" +* **rds_replica_cluster_name** (str): Name of the replica cluster. Default: "{{ resource_prefix }}-replica-cluster" +* **rds_replica_cluster_region** (str): Replica Region. Default: `us-east-2` +* **rds_replica_cluster_instance_name** (str): Name of the replica db instance. Default: "{{ resource_prefix }}-replica-instance" + +#### vars for route53 records +* **route53_zone_name** (str): (required) Route53 Zone name. +* **route53_subdomain** (str): Sub domain name for the application url. Default: "flaskapp" + ## Example Usage Create a `credentials.yaml` file with the folling contents: @@ -187,3 +206,16 @@ ansible-playbook migrate_webapp.yaml -e "@credentials.yaml" -e "dest_region=my-n ``` Note: migrating a webapp does not delete the app resources from the source region by default. To delete the source webapp, set var `delete_source: true`. + +To deploy the app in a high availability architecture, run: + +```bash +ansible-playbook webapp_ha_aurora.yaml -e "@credentials.yaml" -e "operation=create" +``` + +To delete the webapp resources created by the above playbook, run: + +```bash +ansible-playbook webapp_ha_aurora.yaml -e "@credentials.yaml" -e "operation=delete" +``` + diff --git a/playbooks/webapp/files/run_app.yaml b/playbooks/webapp/files/run_app.yaml index 080d3d26..62033421 100644 --- a/playbooks/webapp/files/run_app.yaml +++ b/playbooks/webapp/files/run_app.yaml @@ -24,6 +24,9 @@ ansible.builtin.yum: name: - podman + sslverify: false + validate_certs: false + update_cache: true state: present - name: Pull image from private registry diff --git a/playbooks/webapp/migrate_webapp.yaml b/playbooks/webapp/migrate_webapp.yaml index 016bc232..83886ad1 100644 --- a/playbooks/webapp/migrate_webapp.yaml +++ b/playbooks/webapp/migrate_webapp.yaml @@ -2,7 +2,6 @@ - name: Migrate webapp hosts: localhost gather_facts: false - vars_files: - vars/main.yaml diff --git a/playbooks/webapp/tasks/add_route53_records.yaml b/playbooks/webapp/tasks/add_route53_records.yaml new file mode 100644 index 00000000..70935d5f --- /dev/null +++ b/playbooks/webapp/tasks/add_route53_records.yaml @@ -0,0 +1,68 @@ +--- +- name: Add Route53 configurations + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key | default(omit) }}" + aws_secret_key: "{{ aws_secret_key | default(omit) }}" + security_token: "{{ security_token | default(omit) }}" + block: + + - name: Add route53 health check for the load balancer in primary region + amazon.aws.route53_health_check: + health_check_name: "healthchk-lb-primary" + fqdn: "{{ primary_lb.elb.dns_name }}" + port: 5000 + type: HTTP + use_unique_names: true + state: present + register: healthchk_primary_result + + - name: Add route53 health check for the load balancer in replica region + amazon.aws.route53_health_check: + health_check_name: "healthchk-lb-replica" + fqdn: "{{ replica_lb.elb.dns_name }}" + port: 5000 + type: HTTP + use_unique_names: true + state: present + register: healthchk_replica_result + + - name: Pause for 30 secs for the health check status to be in sync + ansible.builtin.pause: + seconds: 30 + + - name: Add an alias record that points to an aws ELB in the primary region + amazon.aws.route53: + state: present + zone: "{{ route53_zone_name }}" + record: "{{ route53_subdomain }}.{{ route53_zone_name }}" + type: A + value: "{{ primary_lb.elb.dns_name }}" + alias: true + identifier: "primary-record" + failover: "PRIMARY" + health_check: "{{ healthchk_primary_result.health_check.id }}" + alias_hosted_zone_id: "{{ primary_lb.elb.hosted_zone_id }}" + register: alias_record_primary_result + + - name: Add an alias record that points to an aws ELB in the replica region + amazon.aws.route53: + state: present + zone: "{{ route53_zone_name }}" + record: "{{ route53_subdomain }}.{{ route53_zone_name }}" + type: A + value: "{{ replica_lb.elb.dns_name }}" + alias: true + identifier: "replica-record" + failover: "SECONDARY" + health_check: "{{ healthchk_replica_result.health_check.id }}" + alias_hosted_zone_id: "{{ replica_lb.elb.hosted_zone_id }}" + register: alias_record_replica_result + + - name: Pause for 30 secs for the alias records to be active + ansible.builtin.pause: + seconds: 30 + + - name: Get Application URL + ansible.builtin.debug: + msg: "Application url: {{ route53_subdomain }}.{{ route53_zone_name }}:{{ deploy_flask_app_listening_port }}" diff --git a/playbooks/webapp/tasks/create.yaml b/playbooks/webapp/tasks/create.yaml index db893d63..c885e592 100644 --- a/playbooks/webapp/tasks/create.yaml +++ b/playbooks/webapp/tasks/create.yaml @@ -1,15 +1,11 @@ --- -- name: Set 'region' variable - ansible.builtin.set_fact: - region: "{{ region | default(aws_region) }}" - - name: Create resources playbook module_defaults: group/aws: aws_access_key: "{{ aws_access_key | default(omit) }}" aws_secret_key: "{{ aws_secret_key | default(omit) }}" security_token: "{{ security_token | default(omit) }}" - region: "{{ region }}" + region: "{{ region | default(aws_region) }}" block: - name: Get image ID to create an instance amazon.aws.ec2_ami_info: @@ -147,61 +143,64 @@ state: present register: rds_sg - - name: Get RDS instance info - amazon.aws.rds_instance_info: - db_instance_identifier: "{{ rds_identifier }}" - register: rds_result - - - name: Create RDS instance - when: rds_result.instances | length == 0 + - name: RDS creation + when: not "aurora" in rds_engine block: - - name: Create RDS instance (PostGreSQL Database) - amazon.aws.rds_instance: - force_update_password: true - wait: true - allocated_storage: "{{ rds_allocated_storage_gb }}" - backup_retention_period: 0 - db_instance_class: "{{ rds_instance_class }}" + - name: Get RDS instance info + amazon.aws.rds_instance_info: db_instance_identifier: "{{ rds_identifier }}" - db_name: "{{ rds_instance_name }}" - engine: "{{ rds_engine }}" - engine_version: "{{ rds_engine_version }}" - master_user_password: "{{ deploy_flask_app_rds_master_password }}" - master_username: "{{ deploy_flask_app_rds_master_username }}" - monitoring_interval: 0 - storage_type: standard - skip_final_snapshot: true - db_subnet_group_name: "{{ rds_subnet_group_name }}" - vpc_security_group_ids: - - "{{ rds_sg.group_id }}" - when: rds_snapshot_arn is not defined - - - name: Create RDS instance from snapshot (PostGreSQL Database) - amazon.aws.rds_instance: - force_update_password: true - wait: true - allocated_storage: "{{ rds_allocated_storage_gb }}" - backup_retention_period: 0 - db_instance_class: "{{ rds_instance_class }}" + register: rds_result + + - name: Create RDS instance + when: rds_result.instances | length == 0 + block: + - name: Create RDS instance (PostGreSQL Database) + amazon.aws.rds_instance: + force_update_password: true + wait: true + allocated_storage: "{{ rds_allocated_storage_gb }}" + backup_retention_period: 0 + db_instance_class: "{{ rds_instance_class }}" + db_instance_identifier: "{{ rds_identifier }}" + db_name: "{{ rds_instance_name }}" + engine: "{{ rds_engine }}" + engine_version: "{{ rds_engine_version }}" + master_user_password: "{{ deploy_flask_app_rds_master_password }}" + master_username: "{{ deploy_flask_app_rds_master_username }}" + monitoring_interval: 0 + storage_type: standard + skip_final_snapshot: true + db_subnet_group_name: "{{ rds_subnet_group_name }}" + vpc_security_group_ids: + - "{{ rds_sg.group_id }}" + when: rds_snapshot_arn is not defined + + - name: Create RDS instance from snapshot (PostGreSQL Database) + amazon.aws.rds_instance: + force_update_password: true + wait: true + allocated_storage: "{{ rds_allocated_storage_gb }}" + backup_retention_period: 0 + db_instance_class: "{{ rds_instance_class }}" + db_instance_identifier: "{{ rds_identifier }}" + engine: "{{ rds_engine }}" + engine_version: "{{ rds_engine_version }}" + master_user_password: "{{ deploy_flask_app_rds_master_password }}" + master_username: "{{ deploy_flask_app_rds_master_username }}" + monitoring_interval: 0 + storage_type: standard + skip_final_snapshot: true + db_subnet_group_name: "{{ rds_subnet_group_name }}" + vpc_security_group_ids: + - "{{ rds_sg.group_id }}" + creation_source: snapshot + db_snapshot_identifier: "{{ rds_snapshot_arn }}" + when: rds_snapshot_arn is defined + + - name: Get RDS instance info + amazon.aws.rds_instance_info: db_instance_identifier: "{{ rds_identifier }}" - engine: "{{ rds_engine }}" - engine_version: "{{ rds_engine_version }}" - master_user_password: "{{ deploy_flask_app_rds_master_password }}" - master_username: "{{ deploy_flask_app_rds_master_username }}" - monitoring_interval: 0 - storage_type: standard - skip_final_snapshot: true - db_subnet_group_name: "{{ rds_subnet_group_name }}" - vpc_security_group_ids: - - "{{ rds_sg.group_id }}" - creation_source: snapshot - db_snapshot_identifier: "{{ rds_snapshot_arn }}" - when: rds_snapshot_arn is defined - - - name: Get RDS instance info - amazon.aws.rds_instance_info: - db_instance_identifier: "{{ rds_identifier }}" - register: rds_result + register: rds_result - name: Set 'sshkey_file' variable ansible.builtin.set_fact: @@ -219,7 +218,17 @@ mode: 0400 when: rsa_key is changed + - name: Check if the vm exists + amazon.aws.ec2_instance_info: + filters: + instance-type: "{{ bastion_host_type }}" + key-name: "{{ deploy_flask_app_sshkey_pair_name }}" + vpc-id: "{{ vpc.vpc.id }}" + instance-state-name: running + register: vm_result + - name: Create a virtual machine + when: vm_result.instances | length == 0 amazon.aws.ec2_instance: name: "{{ deploy_flask_app_bastion_host_name }}" instance_type: "{{ bastion_host_type }}" diff --git a/playbooks/webapp/tasks/create_aurora_db_cluster.yaml b/playbooks/webapp/tasks/create_aurora_db_cluster.yaml new file mode 100644 index 00000000..e92c4b9f --- /dev/null +++ b/playbooks/webapp/tasks/create_aurora_db_cluster.yaml @@ -0,0 +1,81 @@ +--- +- name: Create resources playbook + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key | default(omit) }}" + aws_secret_key: "{{ aws_secret_key | default(omit) }}" + security_token: "{{ security_token | default(omit) }}" + block: + - name: Get security group id - primary region + amazon.aws.ec2_security_group_info: + filters: + group-name: "{{ rds_secgroup_name }}" + region: "{{ rds_primary_cluster_region }}" + register: rds_primary_sg + + - name: Get security group id - replica region + amazon.aws.ec2_security_group_info: + filters: + group-name: "{{ rds_secgroup_name }}" + region: "{{ rds_replica_cluster_region }}" + register: rds_replica_sg + + - name: Create Aurora db cluster + ansible.builtin.include_role: + name: cloud.aws_ops.create_rds_global_cluster + vars: + create_rds_global_cluster_operation: create + create_rds_global_cluster_engine: "{{ rds_engine }}" + create_rds_global_cluster_engine_version: "{{ rds_engine_version }}" + create_rds_global_cluster_instance_class: "{{ rds_instance_class }}" + create_rds_global_cluster_master_username: "{{ deploy_flask_app_rds_master_username }}" + create_rds_global_cluster_master_user_password: "{{ deploy_flask_app_rds_master_password }}" + create_rds_global_cluster_global_cluster_name: "{{ rds_global_cluster_name }}" + create_rds_global_cluster_primary_cluster_name: "{{ rds_primary_cluster_name }}" + create_rds_global_cluster_primary_cluster_region: "{{ rds_primary_cluster_region }}" + create_rds_global_cluster_primary_cluster_instance_name: "{{ rds_primary_cluster_instance_name }}" + create_rds_global_cluster_replica_cluster_name: "{{ rds_replica_cluster_name }}" + create_rds_global_cluster_replica_cluster_region: "{{ rds_replica_cluster_region }}" + create_rds_global_cluster_replica_cluster_instance_name: "{{ rds_replica_cluster_instance_name }}" + create_rds_global_cluster_db_subnet_group_name: "{{ rds_subnet_group_name }}" + create_rds_global_cluster_primary_cluster_db_name: "{{ rds_instance_name }}" + create_rds_global_cluster_primary_cluster_vpc_security_group_ids: + - "{{ rds_primary_sg.security_groups[0].group_id }}" + create_rds_global_cluster_replica_cluster_vpc_security_group_ids: + - "{{ rds_replica_sg.security_groups[0].group_id }}" + + - name: Get primary instance info + amazon.aws.rds_instance_info: + db_instance_identifier: "{{ rds_primary_cluster_instance_name }}" + region: "{{ rds_primary_cluster_region }}" + register: primary_instance_info_result + + - name: Get primary cluster info + amazon.aws.rds_cluster_info: + db_cluster_identifier: "{{ rds_primary_cluster_name }}" + region: "{{ rds_primary_cluster_region }}" + register: primary_cluster_info_result + + - name: Get replica cluster info + amazon.aws.rds_cluster_info: + db_cluster_identifier: "{{ rds_replica_cluster_name }}" + region: "{{ rds_replica_cluster_region }}" + register: replica_cluster_info_result + + - name: Get replica instance info + amazon.aws.rds_instance_info: + db_instance_identifier: "{{ rds_replica_cluster_instance_name }}" + region: "{{ rds_replica_cluster_region }}" + register: replica_instance_info_result + + - name: Get global db info + amazon.aws.rds_global_cluster_info: + global_cluster_identifier: "{{ rds_global_cluster_name }}" + region: "{{ rds_primary_cluster_region }}" + register: global_cluster_info + + - name: Assert that primary and replica cluster are part of global db + ansible.builtin.assert: + that: + - global_cluster_info.global_clusters[0].global_cluster_members[0].db_cluster_arn == primary_cluster_info_result.clusters[0].db_cluster_arn + - global_cluster_info.global_clusters[0].global_cluster_members[1].db_cluster_arn == replica_cluster_info_result.clusters[0].db_cluster_arn diff --git a/playbooks/webapp/tasks/delete.yaml b/playbooks/webapp/tasks/delete.yaml index 57a07e0d..1ae28294 100644 --- a/playbooks/webapp/tasks/delete.yaml +++ b/playbooks/webapp/tasks/delete.yaml @@ -1,10 +1,13 @@ --- - name: Delete resources playbook + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key | default(omit) }}" + aws_secret_key: "{{ aws_secret_key | default(omit) }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ region | default(aws_region) }}" block: - - name: Set 'region' variable - ansible.builtin.set_fact: - region: "{{ region | default(aws_region) }}" - name: Get vpc information amazon.aws.ec2_vpc_net_info: @@ -12,7 +15,7 @@ tag:Name: "{{ vpc_name }}" register: vpc - - name: Deelete resources + - name: Delete resources when: vpc.vpcs | length == 1 block: - name: Set 'vpc_id' variable @@ -25,6 +28,7 @@ instance-type: "{{ bastion_host_type }}" key-name: "{{ deploy_flask_app_sshkey_pair_name }}" vpc-id: "{{ vpc_id }}" + instance-state-name: running register: bastion - name: Delete EC2 instances with dependant Resources @@ -53,6 +57,7 @@ register: running - name: Delete workers + when: running.instances | length != 0 amazon.aws.ec2_instance: instance_ids: "{{ running.instances | map(attribute='instance_id') | list }}" wait: true @@ -142,4 +147,3 @@ name: "{{ vpc_name }}" cidr_block: "{{ vpc_cidr }}" state: absent - ignore_errors: true diff --git a/playbooks/webapp/tasks/delete_route53_records.yaml b/playbooks/webapp/tasks/delete_route53_records.yaml new file mode 100644 index 00000000..feaed1ec --- /dev/null +++ b/playbooks/webapp/tasks/delete_route53_records.yaml @@ -0,0 +1,75 @@ +--- +- name: Add Route53 configurations + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key | default(omit) }}" + aws_secret_key: "{{ aws_secret_key | default(omit) }}" + security_token: "{{ security_token | default(omit) }}" + block: + + - name: Get health check info + amazon.aws.route53_info: + query: health_check + register: health_check_info + + + - name: Delete route53 health check for the load balancer in primary region + amazon.aws.route53_health_check: + health_check_id: "{{ item.id }}" + state: absent + when: + - item.health_check_config.port == 5000 + - item.health_check_config.type == 'HTTP' + - item.health_check_config.fully_qualified_domain_name | regex_search('.*elb.amazonaws.com') + loop: "{{ health_check_info.health_checks }}" + + - name: Get hosted zone details + amazon.aws.route53_info: + query: hosted_zone + register: hosted_zone_info + + - name: Set facts for deletion + ansible.builtin.set_fact: + hosted_zone_id: "{{ item.id }}" + when: item.name[:-1] == route53_zone_name + loop: "{{ hosted_zone_info.hosted_zones }}" + + - name: Get alias hosted zone record record details + amazon.aws.route53_info: + query: record_sets + hosted_zone_id: "{{ hosted_zone_id }}" + register: record_info + + - name: Delete alias record that points to an aws ELB in the primary region + amazon.aws.route53: + state: absent + zone: "{{ route53_zone_name }}" + record: "{{ route53_subdomain }}.{{ route53_zone_name }}." + type: A + alias: true + alias_hosted_zone_id: "{{ item.alias_target.hosted_zone_id }}" + value: "{{ item.alias_target.dns_name[:-1] }}" + identifier: "primary-record" + health_check: "{{ item.health_check_id }}" + failover: "PRIMARY" + when: + - item.set_identifier is defined + - item.set_identifier == "primary-record" + loop: "{{ record_info.resource_record_sets }}" + + - name: Delete alias record that points to an aws ELB in the secondary region + amazon.aws.route53: + state: absent + zone: "{{ route53_zone_name }}" + record: "{{ route53_subdomain }}.{{ route53_zone_name }}." + type: A + alias: true + identifier: "replica-record" + failover: "SECONDARY" + alias_hosted_zone_id: "{{ item.alias_target.hosted_zone_id }}" + value: "{{ item.alias_target.dns_name[:-1] }}" + health_check: "{{ item.health_check_id }}" + when: + - item.set_identifier is defined + - item.set_identifier == "replica-record" + loop: "{{ record_info.resource_record_sets }}" diff --git a/playbooks/webapp/vars/main.yaml b/playbooks/webapp/vars/main.yaml index 8a2ff513..b04589a8 100644 --- a/playbooks/webapp/vars/main.yaml +++ b/playbooks/webapp/vars/main.yaml @@ -1,8 +1,7 @@ --- # Variables for create.yaml -aws_region: us-east-1 +resource_prefix: "ansible-test" dest_region: us-east-2 -resource_prefix: "ansible-demo-test" vpc_name: "{{ resource_prefix }}-vpc" vpc_cidr: 10.1.0.0/16 subnet_cidr: @@ -13,7 +12,7 @@ resource_tags: prefix: "{{ resource_prefix }}" operation: create -image_filter: Fedora-Cloud-Base-35-* +image_filter: Fedora-Cloud-Base-35-*gp2-0 public_secgroup_name: "{{ resource_prefix }}-sg" rds_subnet_group_name: "{{ resource_prefix }}-rds-sg" rds_secgroup_name: "{{ resource_prefix }}-rds-sec" @@ -36,10 +35,11 @@ deploy_flask_app_bastion_host_required_packages: - python-virtualenv - sshpass - git + - gcc - podman - httpd-tools - - ansible -deploy_flask_app_workers_instance_type: t3.micro + - ansible-core +deploy_flask_app_workers_instance_type: t2.xlarge deploy_flask_app_workers_user_name: fedora deploy_flask_app_number_of_workers: 2 deploy_flask_app_listening_port: 5000 @@ -55,3 +55,20 @@ deploy_flask_app_local_registry_pwd: testing123 deploy_flask_app_local_registry_port: "{{ deploy_flask_app_listening_port }}" deploy_flask_app_rds_master_password: L#5cH2mgy_ deploy_flask_app_rds_master_username: ansible + +# vars to create aurora db cluster +# Global cluster parameters ================================ +rds_global_cluster_name: "{{ resource_prefix }}-global-cluster" + +# Primary cluster parameters ================================ +rds_primary_cluster_name: "{{ resource_prefix }}-primary-cluster" +rds_primary_cluster_region: us-west-2 +rds_primary_cluster_instance_name: "{{ resource_prefix }}-primary-instance" + +# Replica cluster parameters ================================ +rds_replica_cluster_name: "{{ resource_prefix }}-replica-cluster" +rds_replica_cluster_region: us-east-2 +rds_replica_cluster_instance_name: "{{ resource_prefix }}-replica-instance" + +# vars for route53 records +route53_subdomain: "flaskapp" diff --git a/playbooks/webapp/webapp.yaml b/playbooks/webapp/webapp.yaml index 58973e44..0f169154 100644 --- a/playbooks/webapp/webapp.yaml +++ b/playbooks/webapp/webapp.yaml @@ -2,7 +2,6 @@ - name: Webapp hosts: localhost gather_facts: false - vars_files: - vars/main.yaml @@ -18,7 +17,6 @@ - name: Deploy Flask App hosts: localhost gather_facts: false - vars_files: - vars/main.yaml diff --git a/playbooks/webapp/webapp_ha_aurora.yaml b/playbooks/webapp/webapp_ha_aurora.yaml new file mode 100644 index 00000000..3997385a --- /dev/null +++ b/playbooks/webapp/webapp_ha_aurora.yaml @@ -0,0 +1,142 @@ +--- +- name: webapp HA + hosts: localhost + gather_facts: false + vars_files: + - vars/main.yaml + + tasks: + - name: Create resources and Deploy App + when: operation == "create" + block: + - name: Create resources in primary region + ansible.builtin.include_tasks: tasks/create.yaml + vars: + region: "{{ creation_region }}" + rds_instance_class: db.r5.large + rds_engine: aurora-postgresql + loop: + - "{{ rds_primary_cluster_region }}" + - "{{ rds_replica_cluster_region }}" + loop_control: + loop_var: creation_region + + - name: Create Aurora db cluster + ansible.builtin.import_tasks: tasks/create_aurora_db_cluster.yaml + vars: + rds_instance_class: db.r5.large + rds_engine: aurora-postgresql + + # ================= Deploy App in the primary region ================= + + + - name: Get VPC info from primary region + amazon.aws.ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + region: "{{ rds_primary_cluster_region }}" + register: primary_vpc + + - name: Get primary private subnet for workers + amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: "{{ primary_vpc.vpcs[0].id }}" + cidr: "{{ subnet_cidr[1] }}" + region: "{{ rds_primary_cluster_region }}" + register: primary_private_subnet + + - name: Get VM info in the primary region + amazon.aws.ec2_instance_info: + filters: + "tag:Name": "{{ deploy_flask_app_bastion_host_name }}" + instance-state-name: [ "running"] + region: "{{ rds_primary_cluster_region }}" + register: primary_vm_result + + - name: Deploy app in primary region + ansible.builtin.include_role: + name: cloud.aws_ops.deploy_flask_app + vars: + deploy_flask_app_private_subnet_id: "{{ primary_private_subnet.subnets[0].id }}" + deploy_flask_app_vpc_id: "{{ primary_vpc.vpcs[0].id }}" + deploy_flask_app_vm_info: "{{ primary_vm_result }}" + deploy_flask_app_rds_info: "{{ primary_instance_info_result }}" + deploy_flask_app_region: "{{ rds_primary_cluster_region }}" + + - name: Get load balancer name from the primary region + ansible.builtin.set_fact: + primary_lb: "{{ deploy_flask_app_lb_result }}" + + # ================= Deploy App in the replica region ================= + + - name: Get VPC info from replica region + amazon.aws.ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + region: "{{ rds_replica_cluster_region }}" + register: replica_vpc + + - name: Get VM info in the replica region + amazon.aws.ec2_instance_info: + filters: + "tag:Name": "{{ deploy_flask_app_bastion_host_name }}" + instance-state-name: [ "running"] + region: "{{ rds_replica_cluster_region }}" + register: replica_vm_result + + - name: Get replica private subnet for workers + amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: "{{ replica_vpc.vpcs[0].id }}" + cidr: "{{ subnet_cidr[1] }}" + region: "{{ rds_replica_cluster_region }}" + register: replica_private_subnet + + - name: Deploy app in replica region + ansible.builtin.include_role: + name: cloud.aws_ops.deploy_flask_app + vars: + deploy_flask_app_private_subnet_id: "{{ replica_private_subnet.subnets[0].id }}" + deploy_flask_app_vpc_id: "{{ replica_vpc.vpcs[0].id }}" + deploy_flask_app_vm_info: "{{ replica_vm_result }}" + deploy_flask_app_rds_info: "{{ replica_instance_info_result }}" + deploy_flask_app_region: "{{ rds_replica_cluster_region }}" + + - name: Get load balancer name from the replica region + ansible.builtin.set_fact: + replica_lb: "{{ deploy_flask_app_lb_result }}" + + - name: Add Route53 configurations + ansible.builtin.include_tasks: tasks/add_route53_records.yaml + + # ================================================================================ + + - name: Delete resources + when: operation == "delete" + block: + + - name: Delete Route 53 records and health checks + ansible.builtin.import_tasks: tasks/delete_route53_records.yaml + + - name: Delete Aurora DB + ansible.builtin.include_role: + name: cloud.aws_ops.create_rds_global_cluster + vars: + create_rds_global_cluster_operation: delete + create_rds_global_cluster_global_cluster_name: "{{ rds_global_cluster_name }}" + create_rds_global_cluster_primary_cluster_name: "{{ rds_primary_cluster_name }}" + create_rds_global_cluster_primary_cluster_region: "{{ rds_primary_cluster_region }}" + create_rds_global_cluster_primary_cluster_instance_name: "{{ rds_primary_cluster_instance_name }}" + create_rds_global_cluster_replica_cluster_name: "{{ rds_replica_cluster_name }}" + create_rds_global_cluster_replica_cluster_region: "{{ rds_replica_cluster_region }}" + create_rds_global_cluster_replica_cluster_instance_name: "{{ rds_replica_cluster_instance_name }}" + + - name: Delete all resources + ansible.builtin.include_tasks: tasks/delete.yaml + vars: + region: "{{ deletion_region }}" + loop: + - "{{ rds_primary_cluster_region }}" + - "{{ rds_replica_cluster_region }}" + loop_control: + loop_var: deletion_region diff --git a/roles/deploy_flask_app/files/run_app.yaml b/roles/deploy_flask_app/files/run_app.yaml index 080d3d26..b221927b 100644 --- a/roles/deploy_flask_app/files/run_app.yaml +++ b/roles/deploy_flask_app/files/run_app.yaml @@ -24,6 +24,7 @@ ansible.builtin.yum: name: - podman + update_cache: True state: present - name: Pull image from private registry diff --git a/roles/deploy_flask_app/meta/main.yaml b/roles/deploy_flask_app/meta/main.yaml index e8b3ab42..3bf1568b 100644 --- a/roles/deploy_flask_app/meta/main.yaml +++ b/roles/deploy_flask_app/meta/main.yaml @@ -1,3 +1,4 @@ --- dependencies: - role: cloud.aws_ops.aws_setup_credentials +allow_duplicates: true diff --git a/roles/deploy_flask_app/tasks/setup.yaml b/roles/deploy_flask_app/tasks/setup.yaml index d543353c..55b68b47 100644 --- a/roles/deploy_flask_app/tasks/setup.yaml +++ b/roles/deploy_flask_app/tasks/setup.yaml @@ -26,7 +26,7 @@ vpc_id: "{{ deploy_flask_app_vpc_id }}" rds_info: host: "{{ deploy_flask_app_rds_info.instances.0.endpoint.address }}" - name: "{{ deploy_flask_app_rds_info.instances.0.dbname | default('mysampledb123') }}" + name: "{{ deploy_flask_app_rds_info.instances.0.db_name | default('mysampledb123') }}" master_user_password: "{{ deploy_flask_app_rds_master_password | default('L#5cH2mgy_') }}" master_username: "{{ deploy_flask_app_rds_master_username | default('ansible') }}" register: deploy_flask_app_setup diff --git a/roles/deploy_flask_app/templates/vars.yaml.j2 b/roles/deploy_flask_app/templates/vars.yaml.j2 index 9f61ae53..bc6a211d 100644 --- a/roles/deploy_flask_app/templates/vars.yaml.j2 +++ b/roles/deploy_flask_app/templates/vars.yaml.j2 @@ -3,6 +3,7 @@ registry_host_port: "{{ deploy_flask_app_setup.add_host.host_vars.host_config.pr registry_login: user: "{{ deploy_flask_app_local_registry_user }}" password: "{{ deploy_flask_app_local_registry_pwd }}" +rds_listening_port: "{{ rds_listening_port }}" application_dir: "{{ deploy_flask_app_config.app_dir }}" application_env: "{{ deploy_flask_app_config.env }}" application_db: