├── 1- Create VPC
├── create_vpc.tf
├── provider.tf
└── variables.tf
├── 2- Create AMI
├── apache-ami.json
├── config
│ └── userdata.sh
├── info.txt
├── playbook
│ ├── ansible.cfg
│ ├── inventory.txt
│ ├── main.yml
│ ├── roles
│ │ └── apache-tomcat
│ │ │ ├── defaults
│ │ │ └── main.yml
│ │ │ ├── handlers
│ │ │ └── main.yml
│ │ │ ├── tasks
│ │ │ └── main.yml
│ │ │ └── templates
│ │ │ ├── server.xml.j2
│ │ │ └── tomcat.service.j2
│ └── variables.yml
└── vars.json
├── 3 - Create ASG
├── cloud-config.yml
├── create_autoscaling_group.tf
├── create_elb.tf
├── create_key.tf
├── create_security_groups.tf
├── fetch_id.tf
├── key.pub
├── outputs.tf
├── provider.tf
└── vars.tf
├── Independent apache-tomcat Configuration
├── ansible.cfg
├── inventory.txt
├── main.yml
├── roles
│ └── apache-tomcat
│ │ ├── defaults
│ │ └── main.yml
│ │ ├── handlers
│ │ └── main.yml
│ │ ├── tasks
│ │ └── main.yml
│ │ └── templates
│ │ ├── server.xml.j2
│ │ └── tomcat.service.j2
└── variables.yml
└── README.md
/1- Create VPC/create_vpc.tf:
--------------------------------------------------------------------------------
1 | /*
2 | This terraform script will create a VPC and perform the following actions
3 | 1) Create Internet Gateway for public subnets
4 | 2) Create Public Subnets in multiple AZs and update route rules to use Internet Gateway
5 | 3) Create a NAT gateway service in a single AZ to be associated with
6 | 4) Create Private Subnets and update route rules to use NAT Gateway service
7 | */
8 |
9 | /*
10 | Create a VPC and setup an AWS Internet Gateway
11 | */
12 |
13 | resource "aws_vpc" "default" {
14 | cidr_block = "${var.vpc_cidr}"
15 | enable_dns_hostnames = true
16 | tags {
17 | Name = "kris-test-vpc"
18 | }
19 | }
20 |
21 | data "aws_availability_zones" "apache" {}
22 |
23 | resource "aws_internet_gateway" "default" {
24 | vpc_id = "${aws_vpc.default.id}"
25 | }
26 |
27 | /*
28 | Setup public Subnets
29 | */
30 | resource "aws_subnet" "zone-a-public" {
31 | vpc_id = "${aws_vpc.default.id}"
32 |
33 | cidr_block = "${var.public_subnet_cidr_a}"
34 | availability_zone = "${data.aws_availability_zones.apache.names[0]}"
35 |
36 | tags {
37 | Name = "public_subneta"
38 | }
39 | }
40 |
41 | resource "aws_subnet" "zone-b-public" {
42 | vpc_id = "${aws_vpc.default.id}"
43 |
44 | cidr_block = "${var.public_subnet_cidr_b}"
45 | availability_zone = "${data.aws_availability_zones.apache.names[1]}"
46 |
47 | tags {
48 | Name = "public_subnetb"
49 | }
50 | }
51 |
52 | resource "aws_subnet" "zone-c-public" {
53 | vpc_id = "${aws_vpc.default.id}"
54 |
55 | cidr_block = "${var.public_subnet_cidr_c}"
56 | availability_zone = "${data.aws_availability_zones.apache.names[2]}"
57 |
58 | tags {
59 | Name = "public_subnetc"
60 | }
61 | }
62 |
63 | /* Public Subnet Route Rules
64 | */
65 | resource "aws_route_table" "zone-a-public" {
66 | vpc_id = "${aws_vpc.default.id}"
67 |
68 | route {
69 | cidr_block = "0.0.0.0/0"
70 | gateway_id = "${aws_internet_gateway.default.id}"
71 | }
72 |
73 | tags {
74 | Name = "route_public_subneta"
75 | }
76 | }
77 |
78 | resource "aws_route_table_association" "zone-a-public" {
79 | subnet_id = "${aws_subnet.zone-a-public.id}"
80 | route_table_id = "${aws_route_table.zone-a-public.id}"
81 | }
82 |
83 | resource "aws_route_table" "zone-b-public" {
84 | vpc_id = "${aws_vpc.default.id}"
85 |
86 | route {
87 | cidr_block = "0.0.0.0/0"
88 | gateway_id = "${aws_internet_gateway.default.id}"
89 | }
90 |
91 | tags {
92 | Name = "route_public_subnetb"
93 | }
94 | }
95 |
96 | resource "aws_route_table_association" "zone-b-public" {
97 | subnet_id = "${aws_subnet.zone-b-public.id}"
98 | route_table_id = "${aws_route_table.zone-b-public.id}"
99 | }
100 |
101 | resource "aws_route_table" "zone-c-public" {
102 | vpc_id = "${aws_vpc.default.id}"
103 |
104 | route {
105 | cidr_block = "0.0.0.0/0"
106 | gateway_id = "${aws_internet_gateway.default.id}"
107 | }
108 |
109 | tags {
110 | Name = "route_public_subnetc"
111 | }
112 | }
113 |
114 | resource "aws_route_table_association" "zone-c-public" {
115 | subnet_id = "${aws_subnet.zone-c-public.id}"
116 | route_table_id = "${aws_route_table.zone-c-public.id}"
117 | }
118 |
119 | /*
120 | AWS NAT Gateway Setup in a Public Subnet
121 | */
122 |
123 | resource "aws_eip" "natEIP" {
124 | vpc = true
125 | }
126 |
127 | resource "aws_nat_gateway" "nat_gateway_public" {
128 | allocation_id = "${aws_eip.natEIP.id}"
129 | subnet_id = "${aws_subnet.zone-b-public.id}"
130 | depends_on = ["aws_internet_gateway.default"]
131 | }
132 |
133 |
134 | /*
135 | Private Subnet setup
136 | */
137 |
138 | resource "aws_subnet" "zone-a-private" {
139 | vpc_id = "${aws_vpc.default.id}"
140 |
141 | cidr_block = "${var.private_subnet_cidr_a}"
142 | availability_zone = "${data.aws_availability_zones.apache.names[0]}"
143 |
144 | tags {
145 | Name = "Private Subnet Zone A"
146 | }
147 | }
148 |
149 | resource "aws_subnet" "zone-b-private" {
150 | vpc_id = "${aws_vpc.default.id}"
151 |
152 | cidr_block = "${var.private_subnet_cidr_b}"
153 | availability_zone = "${data.aws_availability_zones.apache.names[1]}"
154 |
155 | tags {
156 | Name = "Private Subnet Zone B"
157 | }
158 | }
159 |
160 | resource "aws_subnet" "zone-c-private" {
161 | vpc_id = "${aws_vpc.default.id}"
162 |
163 | cidr_block = "${var.private_subnet_cidr_c}"
164 | availability_zone = "${data.aws_availability_zones.apache.names[2]}"
165 |
166 | tags {
167 | Name = "Private Subnet Zone C"
168 | }
169 | }
170 |
171 | /*
172 | Private Subnet Route Rules to be routed to NAT gateway
173 | */
174 |
175 | resource "aws_route_table" "zone-a-private" {
176 | vpc_id = "${aws_vpc.default.id}"
177 |
178 | route {
179 | cidr_block = "0.0.0.0/0"
180 | nat_gateway_id = "${aws_nat_gateway.nat_gateway_public.id}"
181 | }
182 |
183 | tags {
184 | Name = "Private Subnet A"
185 | }
186 | }
187 |
188 | resource "aws_route_table_association" "zone-a-private" {
189 | subnet_id = "${aws_subnet.zone-a-private.id}"
190 | route_table_id = "${aws_route_table.zone-a-private.id}"
191 | }
192 |
193 |
194 | resource "aws_route_table" "zone-b-private" {
195 | vpc_id = "${aws_vpc.default.id}"
196 |
197 | route {
198 | cidr_block = "0.0.0.0/0"
199 | nat_gateway_id = "${aws_nat_gateway.nat_gateway_public.id}"
200 | }
201 |
202 | tags {
203 | Name = "Private Subnet B"
204 | }
205 | }
206 |
207 | resource "aws_route_table_association" "zone-b-private" {
208 | subnet_id = "${aws_subnet.zone-b-private.id}"
209 | route_table_id = "${aws_route_table.zone-b-private.id}"
210 | }
211 |
212 | resource "aws_route_table" "zone-c-private" {
213 | vpc_id = "${aws_vpc.default.id}"
214 |
215 | route {
216 | cidr_block = "0.0.0.0/0"
217 | nat_gateway_id = "${aws_nat_gateway.nat_gateway_public.id}"
218 | }
219 |
220 | tags {
221 | Name = "Private Subnet C"
222 | }
223 | }
224 |
225 | resource "aws_route_table_association" "zone-c-private" {
226 | subnet_id = "${aws_subnet.zone-c-private.id}"
227 | route_table_id = "${aws_route_table.zone-c-private.id}"
228 | }
--------------------------------------------------------------------------------
/1- Create VPC/provider.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = "${var.aws_region}"
3 | access_key = "${var.access_key}"
4 | secret_key = "${var.secret_key}"
5 | }
--------------------------------------------------------------------------------
/1- Create VPC/variables.tf:
--------------------------------------------------------------------------------
1 | variable "access_key" {
2 | default = ""
3 | }
4 |
5 | variable "secret_key" {
6 | default = ""
7 | }
8 |
9 | variable "aws_region" {
10 | description = "Region for VPC"
11 | default = "ap-southeast-2"
12 | }
13 |
14 | variable "vpc_cidr" {
15 | description = "CIDR for the whole VPC"
16 | default = "192.168.0.0/22"
17 | }
18 |
19 | /*
20 | Divide 192.168.0.0/25 into AZ specific public subnets.
21 | 3 for now for 3 AZs
22 | */
23 |
24 | variable "public_subnet_cidr_a" {
25 | description = "CIDR for the Public Subnet"
26 | default = "192.168.0.0/27"
27 | }
28 |
29 | variable "public_subnet_cidr_b" {
30 | description = "CIDR for the Public Subnet"
31 | default = "192.168.0.32/27"
32 | }
33 |
34 | variable "public_subnet_cidr_c" {
35 | description = "CIDR for the Public Subnet"
36 | default = "192.168.0.64/27"
37 | }
38 |
39 | /*
40 | Divide 192.168.0.128/25 into AZ specific private subnets.
41 | 3 for now for 3 AZs
42 | */
43 |
44 | variable "private_subnet_cidr_a" {
45 | description = "CIDR for the Private Subnet"
46 | default = "192.168.0.128/27"
47 | }
48 |
49 | variable "private_subnet_cidr_b" {
50 | description = "CIDR for the Private Subnet"
51 | default = "192.168.0.160/27"
52 | }
53 |
54 | variable "private_subnet_cidr_c" {
55 | description = "CIDR for the Private Subnet"
56 | default = "192.168.0.192/27"
57 | }
--------------------------------------------------------------------------------
/2- Create AMI/apache-ami.json:
--------------------------------------------------------------------------------
1 | {
2 | "builders": [
3 | {
4 | "access_key": "{{user `aws_access_key`}}",
5 | "ami_name": "apache-tomcat-{{user `image_version`}}",
6 | "instance_type": "{{user `aws_instance_type`}}",
7 | "region": "{{user `aws_region`}}",
8 | "secret_key": "{{user `aws_secret_key`}}",
9 | "source_ami": "{{user `aws_ami_image`}}",
10 | "ssh_username": "ubuntu",
11 | "tags": {
12 | "Name":"apache-tomcat-{{user `image_version`}}",
13 | "OS_Version": "Ubuntu 16.04",
14 | "Description": "Golden image for apache tomcat"
15 | },
16 | "type": "amazon-ebs"
17 |
18 | }
19 | ],
20 | "provisioners": [
21 | {
22 | "type": "shell",
23 | "inline": [
24 | "sleep 20",
25 | "sudo apt-get update",
26 | "sudo apt-get install python-simplejson -yq",
27 | "sudo apt-get install python -yq"
28 | ]
29 | },
30 | {
31 | "type": "ansible",
32 | "playbook_file": "playbook/main.yml"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/2- Create AMI/config/userdata.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sudo apt-get install python-simplejson -y
--------------------------------------------------------------------------------
/2- Create AMI/info.txt:
--------------------------------------------------------------------------------
1 | 1. please use below for builder if you don't want to specify the source-ami yourself.
2 |
3 | Just replace the whole ["source_ami": "{{user `aws_ami_image`}}", ]part with below:
4 |
5 |
6 | "source_ami_filter": {
7 | "filters": {
8 | "virtualization-type": "hvm",
9 | "name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
10 | "root-device-type": "ebs"
11 | },
12 | "owners": ["099720109477"],
13 | "most_recent": true
14 | },
15 |
16 | 2. If you want to add user data:
17 | "user_data_file": "config/userdata.sh"
18 |
19 |
--------------------------------------------------------------------------------
/2- Create AMI/playbook/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = inventory.txt
3 | host_key_checking = False
4 | log_path=/tmp/ansible.log
5 | deprecation_warnings = False
6 | command_warnings = False
--------------------------------------------------------------------------------
/2- Create AMI/playbook/inventory.txt:
--------------------------------------------------------------------------------
1 | [hosts]
--------------------------------------------------------------------------------
/2- Create AMI/playbook/main.yml:
--------------------------------------------------------------------------------
1 | - hosts: all
2 | become: yes
3 | vars_files:
4 | - variables.yml
5 | roles:
6 | - role: apache-tomcat
--------------------------------------------------------------------------------
/2- Create AMI/playbook/roles/apache-tomcat/defaults/main.yml:
--------------------------------------------------------------------------------
1 | package_list:
2 | - apache2
3 | - mysql-server
4 | - default-jdk
5 | - authbind
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/2- Create AMI/playbook/roles/apache-tomcat/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - name: restart apache2
4 | service:
5 | name: apache2
6 | state: restarted
7 |
8 | - name: restart tomcat
9 | service:
10 | name: tomcat
11 | state: restarted
--------------------------------------------------------------------------------
/2- Create AMI/playbook/roles/apache-tomcat/tasks/main.yml:
--------------------------------------------------------------------------------
1 | - name: install packages - apache2, mysql, authbind, java
2 | apt: name={{ item }} update_cache=yes state=present
3 | with_items:
4 | - "{{package_list}}"
5 |
6 | - name: install mod_jk
7 | apt: name=libapache2-mod-jk state=present
8 | notify:
9 | - restart apache2
10 |
11 | - name: ensure mysql is running and starts on boot
12 | service: name=mysql state=started enabled=true
13 |
14 | - name: create tomcat group
15 | group:
16 | state: present
17 | name: tomcat
18 |
19 | - name: create tomcat user (nobody should login to tomcat account)
20 | user:
21 | name: tomcat
22 | group: tomcat
23 | shell: /bin/false
24 | home: /opt/tomcat
25 |
26 | - name: create directory for tomcat
27 | file:
28 | path: /opt/tomcat
29 | state: directory
30 |
31 | - name: download tomcat package
32 | get_url: url={{tomcat_url}} dest='/tmp/'
33 |
34 | - name: unarchive tomcat package
35 | shell: "tar xzvf /tmp/apache-tomcat-8*tar.gz -C /opt/tomcat --strip-components=1"
36 |
37 | - name: change tomcat directory permissions
38 | file:
39 | path: /opt/tomcat
40 | state: directory
41 | owner: tomcat
42 | group: tomcat
43 | recurse: yes
44 |
45 | - name: change mode for conf directory
46 | file:
47 | path: /opt/tomcat/conf
48 | state: directory
49 | owner: tomcat
50 | group: tomcat
51 | recurse: yes
52 | mode: "g+x"
53 |
54 | - name: create systemd service file for tomcat
55 | template:
56 | src: tomcat.service.j2
57 | dest: /etc/systemd/system/tomcat.service
58 |
59 | - name: Reload systemd daemon & start tomcat
60 | systemd:
61 | daemon_reload: yes
62 | name: tomcat
63 | enabled: yes
64 | state: started
65 |
66 | - name: replace tomcat server.xml
67 | template:
68 | src: server.xml.j2
69 | dest: /opt/tomcat/conf/server.xml
70 | notify:
71 | - restart tomcat
72 |
73 | - name: configure apache
74 | replace: dest=/etc/apache2/sites-enabled/000-default.conf regexp='^' replace="JkMount /* ajp13_worker \n "
75 | notify:
76 | - restart apache2
77 |
78 | - name: setup simple tomcat application
79 | get_url: url=https://community.jboss.org/servlet/JiveServlet/download/588259-27006/clusterjsp.war dest='/opt/tomcat/webapps'
80 | notify:
81 | - restart tomcat
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/2- Create AMI/playbook/roles/apache-tomcat/templates/server.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
18 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
41 |
46 |
47 |
48 |
53 |
54 |
55 |
56 |
60 |
61 |
62 |
69 |
72 |
73 |
79 |
87 |
96 |
102 |
114 |
115 |
116 |
117 |
118 |
119 |
124 |
125 |
128 |
129 |
130 |
133 |
136 |
137 |
139 |
140 |
144 |
146 |
147 |
148 |
150 |
151 |
153 |
156 |
157 |
160 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/2- Create AMI/playbook/roles/apache-tomcat/templates/tomcat.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Apache Tomcat Web Application Container
3 | After=network.target
4 |
5 | [Service]
6 | Type=forking
7 |
8 | Environment=JAVA_HOME=/usr/lib/jvm/{{ java_home }}/jre
9 | Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
10 | Environment=CATALINA_HOME=/opt/tomcat
11 | Environment=CATALINA_BASE=/opt/tomcat
12 | Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC'
13 | Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'
14 |
15 | ExecStart=/opt/tomcat/bin/startup.sh
16 | ExecStop=/opt/tomcat/bin/shutdown.sh
17 |
18 | User=tomcat
19 | Group=tomcat
20 | UMask=0007
21 | RestartSec=10
22 | Restart=always
23 |
24 | [Install]
25 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/2- Create AMI/playbook/variables.yml:
--------------------------------------------------------------------------------
1 | java_home: java-1.8.0-openjdk-amd64
2 | tomcat_url: http://apache.mirror.digitalpacific.com.au/tomcat/tomcat-8/v8.5.24/bin/apache-tomcat-8.5.24.tar.gz
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/2- Create AMI/vars.json:
--------------------------------------------------------------------------------
1 | {
2 | "aws_access_key": "",
3 | "aws_secret_key": "",
4 | "aws_region": "ap-southeast-2",
5 | "aws_ami_image": "ami-cab258a8",
6 | "aws_instance_type": "t2.small",
7 | "image_version" : "0.1"
8 | }
9 |
--------------------------------------------------------------------------------
/3 - Create ASG/cloud-config.yml:
--------------------------------------------------------------------------------
1 | #cloud-config
2 | users:
3 | - name: username
4 | ssh-authorized-keys:
5 | - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3SDe0254+8pHwDn9oKfAla5wbM0izvp0wQ3lUUd2frnvLqpW91zGU0hIaHIrv4uukU64HpahpJxB1PXHTnr/VAZ01QEf96trzGWuDqKLVa/yCKRum//+DsVHdquM3TOwdZt3BXVBVe3dOWe7UqeTDzEZi8ncTgt224mDKGxIW1h53fhSu6H7g0nLXl7RnFFhfw4xuDKGAVFJ6CllQtlKWGxlTpZRsX5e/4HD2smqNl0sCSCNNOpzVp9W88RkPZF8p6cKK4uwDs6vNO62J11wkzFNjW85gZFHzzJwu2e/53swnxw==
6 | sudo: ['ALL=(ALL) NOPASSWD:ALL']
7 | shell: /bin/bash
8 | home: /home/username
9 | groups: wheel,sudo
10 |
--------------------------------------------------------------------------------
/3 - Create ASG/create_autoscaling_group.tf:
--------------------------------------------------------------------------------
1 | resource "aws_launch_configuration" "apache" {
2 | name = "apache-launch-configuration"
3 | image_id = "${data.aws_ami.apache.id}"
4 | instance_type = "${var.instance_type}"
5 | security_groups = ["${aws_security_group.apache.id}"]
6 | user_data = "${file("cloud-config.yml")}"
7 | key_name = "${aws_key_pair.kris.id}"
8 | }
9 |
10 | resource "aws_autoscaling_group" "apachescale" {
11 | name = "apacge-asg"
12 | launch_configuration = "${aws_launch_configuration.apache.id}"
13 | availability_zones = ["${data.aws_availability_zones.apache.names}"]
14 | max_size = "${var.max_instance_asg}"
15 | min_size = 3
16 | desired_capacity = 3
17 | wait_for_elb_capacity = 3
18 | health_check_type = "ELB"
19 | load_balancers = ["${aws_elb.apachelb.id}"]
20 | vpc_zone_identifier = ["${data.aws_subnet.public_a.id}","${data.aws_subnet.public_b.id}","${data.aws_subnet.public_c.id}"]
21 | tag {
22 | key = "Name"
23 | value = "apachetomcat"
24 | propagate_at_launch = "true"
25 | }
26 | }
27 |
28 | resource "aws_autoscaling_policy" "apache-memory-up" {
29 | adjustment_type = "ChangeInCapacity"
30 | autoscaling_group_name = "${aws_autoscaling_group.apachescale.id}"
31 | name = "apache-memory-up"
32 | scaling_adjustment = 1
33 | cooldown = 300
34 | }
35 |
36 | resource "aws_autoscaling_policy" "apache-cpu-up" {
37 | adjustment_type = "ChangeInCapacity"
38 | autoscaling_group_name = "${aws_autoscaling_group.apachescale.id}"
39 | name = "apache-cpu-up"
40 | scaling_adjustment = 1
41 | cooldown = 300
42 | }
43 |
44 | resource "aws_cloudwatch_metric_alarm" "memory-alarm-up" {
45 | alarm_name = "memory-alarm-up"
46 | comparison_operator = "GreaterThanOrEqualToThreshold"
47 | evaluation_periods = 2
48 | metric_name = "MemoryUtilization"
49 | namespace = "AWS/EC2"
50 | period = 120
51 | threshold = 80
52 | statistic = "Average"
53 | alarm_actions = ["${aws_autoscaling_policy.apache-memory-up.arn}"]
54 | dimensions {AutoScalingGroupName = "${aws_autoscaling_group.apachescale.id}"}
55 |
56 | }
57 |
58 | resource "aws_cloudwatch_metric_alarm" "cpu-alarm-up" {
59 | alarm_name = "cpu-alarm-up"
60 | comparison_operator = "GreaterThanOrEqualToThreshold"
61 | evaluation_periods = 2
62 | metric_name = "CPUUtilization"
63 | namespace = "AWS/EC2"
64 | period = 120
65 | threshold = 80
66 | statistic = "Average"
67 | alarm_actions = ["${aws_autoscaling_policy.apache-cpu-up.arn}"]
68 | dimensions {AutoScalingGroupName = "${aws_autoscaling_group.apachescale.id}"}
69 |
70 | }
71 |
72 | resource "aws_autoscaling_policy" "apache-memory-down" {
73 | adjustment_type = "ChangeInCapacity"
74 | autoscaling_group_name = "${aws_autoscaling_group.apachescale.id}"
75 | name = "apache-memory-down"
76 | scaling_adjustment = -1
77 | cooldown = 300
78 | }
79 |
80 | resource "aws_autoscaling_policy" "apache-cpu-down" {
81 | adjustment_type = "ChangeInCapacity"
82 | autoscaling_group_name = "${aws_autoscaling_group.apachescale.id}"
83 | name = "apache-cpu-down"
84 | scaling_adjustment = -1
85 | cooldown = 300
86 | }
87 |
88 | resource "aws_cloudwatch_metric_alarm" "memory-alarm-down" {
89 | alarm_name = "memory-alarm-down"
90 | comparison_operator = "LessThanOrEqualToThreshold"
91 | evaluation_periods = 2
92 | metric_name = "MemoryUtilization"
93 | namespace = "AWS/EC2"
94 | period = 120
95 | threshold = 20
96 | statistic = "Average"
97 | alarm_actions = ["${aws_autoscaling_policy.apache-memory-down.arn}"]
98 | dimensions {AutoScalingGroupName = "${aws_autoscaling_group.apachescale.id}"}
99 | }
100 |
101 | resource "aws_cloudwatch_metric_alarm" "cpu-alarm-down" {
102 | alarm_name = "cpu-alarm-down"
103 | comparison_operator = "LessThanOrEqualToThreshold"
104 | evaluation_periods = 2
105 | metric_name = "CPUUtilization"
106 | namespace = "AWS/EC2"
107 | period = 120
108 | threshold = 20
109 | statistic = "Average"
110 | alarm_actions = ["${aws_autoscaling_policy.apache-cpu-down.arn}"]
111 | dimensions {AutoScalingGroupName = "${aws_autoscaling_group.apachescale.id}"}
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/3 - Create ASG/create_elb.tf:
--------------------------------------------------------------------------------
1 |
2 |
3 | resource "aws_elb" "apachelb" {
4 | name = "apache-elb"
5 |
6 | subnets = ["${data.aws_subnet.public_a.id}","${data.aws_subnet.public_b.id}","${data.aws_subnet.public_c.id}"]
7 | security_groups = ["${aws_security_group.elb.id}"]
8 | listener {
9 | instance_port = 80
10 | instance_protocol = "http"
11 | lb_port = 80
12 | lb_protocol = "http"
13 | }
14 | health_check {
15 | healthy_threshold = 2
16 | interval = 10
17 | target = "TCP:80"
18 | timeout = 3
19 | unhealthy_threshold = 3
20 | }
21 | connection_draining = true
22 | }
--------------------------------------------------------------------------------
/3 - Create ASG/create_key.tf:
--------------------------------------------------------------------------------
1 | resource "aws_key_pair" "kris" {
2 | key_name = "kris-key-pair"
3 | public_key = "${file("key.pub")}"
4 | }
--------------------------------------------------------------------------------
/3 - Create ASG/create_security_groups.tf:
--------------------------------------------------------------------------------
1 | resource "aws_security_group" "apache" {
2 | name = "apache-security"
3 | description = "Allow incoming HTTP connections."
4 |
5 | ingress {
6 | from_port = 22
7 | to_port = 22
8 | protocol = "tcp"
9 | cidr_blocks = ["${var.port_22_cidr}"]
10 | }
11 |
12 | ingress {
13 | from_port = 80
14 | to_port = 80
15 | protocol = "tcp"
16 | security_groups = ["${aws_security_group.elb.id}"]
17 | }
18 |
19 | ingress {
20 | from_port = 8080
21 | to_port = 8080
22 | protocol = "tcp"
23 | security_groups = ["${aws_security_group.elb.id}"]
24 | }
25 | egress {
26 | from_port = 0
27 | to_port = 0
28 | protocol = "-1"
29 | cidr_blocks = ["0.0.0.0/0"]
30 | }
31 |
32 | vpc_id = "${data.aws_subnet.public_a.vpc_id}"
33 |
34 | tags {
35 | Name = "ApacheTomcat"
36 | }
37 | }
38 |
39 |
40 | resource "aws_security_group" "elb" {
41 | name = "elb-security"
42 |
43 | ingress {
44 | from_port = 80
45 | to_port = 80
46 | protocol = "tcp"
47 | cidr_blocks = ["0.0.0.0/0"]
48 | }
49 |
50 | ingress {
51 | from_port = 443
52 | to_port = 443
53 | protocol = "tcp"
54 | cidr_blocks = ["0.0.0.0/0"]
55 | }
56 |
57 | egress {
58 | from_port = 0
59 | to_port = 0
60 | protocol = "-1"
61 | cidr_blocks = ["0.0.0.0/0"]
62 | }
63 |
64 | tags {
65 | Name = "ELBApache"
66 | }
67 |
68 | vpc_id = "${data.aws_subnet.public_a.vpc_id}"
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/3 - Create ASG/fetch_id.tf:
--------------------------------------------------------------------------------
1 | data "aws_ami" "apache" {
2 | most_recent = true
3 | owners = ["self"]
4 | filter {
5 | name = "tag:Name"
6 | values = ["${var.filter_ami}"]
7 | }
8 | }
9 |
10 | data "aws_subnet" "public_a" {
11 |
12 | filter {
13 | name = "tag:${var.subnet_tag_name}"
14 | values = ["${var.subnet_tag_value}a"]
15 | }
16 |
17 | }
18 |
19 | data "aws_subnet" "public_b" {
20 |
21 | filter {
22 | name = "tag:${var.subnet_tag_name}"
23 | values = ["${var.subnet_tag_value}b"]
24 | }
25 |
26 | }
27 |
28 | data "aws_subnet" "public_c" {
29 |
30 | filter {
31 | name = "tag:${var.subnet_tag_name}"
32 | values = ["${var.subnet_tag_value}c"]
33 | }
34 |
35 | }
36 |
37 | data "aws_availability_zones" "apache" {}
38 |
--------------------------------------------------------------------------------
/3 - Create ASG/key.pub:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/3 - Create ASG/outputs.tf:
--------------------------------------------------------------------------------
1 | //
2 | //output "ami_id" {
3 | // value = "${data.aws_ami.apache.id}"
4 | //}
5 | //
6 | //
7 | //output "subnet_ida" {
8 | // value = "${data.aws_subnet.public_a.id}"
9 | //}
10 | //
11 | //
12 | //output "subnet_idb" {
13 | // value = "${data.aws_subnet.public_b.id}"
14 | //}
15 | //
16 | //
17 | //output "subnet_idc" {
18 | // value = "${data.aws_subnet.public_c.id}"
19 | //}
20 |
21 | output "ELB address to hit on brower" {value = "${aws_elb.apachelb.dns_name}"}
22 |
23 |
--------------------------------------------------------------------------------
/3 - Create ASG/provider.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = "${var.aws_region}"
3 | access_key = "${var.access_key}"
4 | secret_key = "${var.secret_key}"
5 | }
--------------------------------------------------------------------------------
/3 - Create ASG/vars.tf:
--------------------------------------------------------------------------------
1 |
2 |
3 | variable "access_key" {
4 | default = ""
5 | }
6 |
7 | variable "secret_key" {
8 | default = ""
9 | }
10 |
11 | variable "aws_region" {
12 | default = "ap-southeast-2"
13 | }
14 |
15 | variable "filter_ami" {
16 | default = "apache-tomcat-0.1"
17 | }
18 |
19 | variable "subnet_tag_name" {
20 | default = "Name"
21 | }
22 |
23 | variable "subnet_tag_value" {
24 | default = "public_subnet"
25 | }
26 |
27 | variable "instance_type" {
28 | default = "t2.small"
29 | }
30 |
31 | variable "port_22_cidr" {
32 | description = "CIDR for the whole VPC"
33 | default = "203.13.146.0/24"
34 | }
35 |
36 | variable "max_instance_asg" {
37 | default = "5"
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = inventory.txt
3 | host_key_checking = False
4 | log_path=/tmp/ansible.log
5 | deprecation_warnings = False
6 | command_warnings = False
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/inventory.txt:
--------------------------------------------------------------------------------
1 | [hosts]
2 |
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/main.yml:
--------------------------------------------------------------------------------
1 | - hosts: all
2 | become: yes
3 | vars_files:
4 | - variables.yml
5 | roles:
6 | - role: apache-tomcat
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/roles/apache-tomcat/defaults/main.yml:
--------------------------------------------------------------------------------
1 | package_list:
2 | - apache2
3 | - mysql-server
4 | - default-jdk
5 | - authbind
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/roles/apache-tomcat/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - name: restart apache2
4 | service:
5 | name: apache2
6 | state: restarted
7 |
8 | - name: restart tomcat
9 | service:
10 | name: tomcat
11 | state: restarted
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/roles/apache-tomcat/tasks/main.yml:
--------------------------------------------------------------------------------
1 | - name: install packages - apache2, mysql, authbind, java
2 | apt: name={{ item }} update_cache=yes state=present
3 | with_items:
4 | - "{{package_list}}"
5 |
6 | - name: install mod_jk
7 | apt: name=libapache2-mod-jk state=present
8 | notify:
9 | - restart apache2
10 |
11 | - name: ensure mysql is running and starts on boot
12 | service: name=mysql state=started enabled=true
13 |
14 | - name: create tomcat group
15 | group:
16 | state: present
17 | name: tomcat
18 |
19 | - name: create tomcat user (nobody should login to tomcat account)
20 | user:
21 | name: tomcat
22 | group: tomcat
23 | shell: /bin/false
24 | home: /opt/tomcat
25 |
26 | - name: create directory for tomcat
27 | file:
28 | path: /opt/tomcat
29 | state: directory
30 |
31 | - name: download tomcat package
32 | get_url: url={{tomcat_url}} dest='/tmp/'
33 |
34 | - name: unarchive tomcat package
35 | shell: "tar xzvf /tmp/apache-tomcat-8*tar.gz -C /opt/tomcat --strip-components=1"
36 |
37 | - name: change tomcat directory permissions
38 | file:
39 | path: /opt/tomcat
40 | state: directory
41 | owner: tomcat
42 | group: tomcat
43 | recurse: yes
44 |
45 | - name: change mode for conf directory
46 | file:
47 | path: /opt/tomcat/conf
48 | state: directory
49 | owner: tomcat
50 | group: tomcat
51 | recurse: yes
52 | mode: "g+x"
53 |
54 | - name: create systemd service file for tomcat
55 | template:
56 | src: tomcat.service.j2
57 | dest: /etc/systemd/system/tomcat.service
58 |
59 | - name: Reload systemd daemon & start tomcat
60 | systemd:
61 | daemon_reload: yes
62 | name: tomcat
63 | enabled: yes
64 | state: started
65 |
66 | - name: replace tomcat server.xml
67 | template:
68 | src: server.xml.j2
69 | dest: /opt/tomcat/conf/server.xml
70 | notify:
71 | - restart tomcat
72 |
73 | - name: configure apache
74 | replace: dest=/etc/apache2/sites-enabled/000-default.conf regexp='^' replace="JkMount /* ajp13_worker \n "
75 | notify:
76 | - restart apache2
77 |
78 | - name: setup simple tomcat application
79 | get_url: url=https://community.jboss.org/servlet/JiveServlet/download/588259-27006/clusterjsp.war dest='/opt/tomcat/webapps'
80 | notify:
81 | - restart tomcat
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/roles/apache-tomcat/templates/server.xml.j2:
--------------------------------------------------------------------------------
1 |
2 |
18 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
41 |
46 |
47 |
48 |
53 |
54 |
55 |
56 |
60 |
61 |
62 |
69 |
72 |
73 |
79 |
87 |
96 |
102 |
114 |
115 |
116 |
117 |
118 |
119 |
124 |
125 |
128 |
129 |
130 |
133 |
136 |
137 |
139 |
140 |
144 |
146 |
147 |
148 |
150 |
151 |
153 |
156 |
157 |
160 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/roles/apache-tomcat/templates/tomcat.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Apache Tomcat Web Application Container
3 | After=network.target
4 |
5 | [Service]
6 | Type=forking
7 |
8 | Environment=JAVA_HOME=/usr/lib/jvm/{{ java_home }}/jre
9 | Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
10 | Environment=CATALINA_HOME=/opt/tomcat
11 | Environment=CATALINA_BASE=/opt/tomcat
12 | Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC'
13 | Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'
14 |
15 | ExecStart=/opt/tomcat/bin/startup.sh
16 | ExecStop=/opt/tomcat/bin/shutdown.sh
17 |
18 | User=tomcat
19 | Group=tomcat
20 | UMask=0007
21 | RestartSec=10
22 | Restart=always
23 |
24 | [Install]
25 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/Independent apache-tomcat Configuration/variables.yml:
--------------------------------------------------------------------------------
1 | java_home: java-1.8.0-openjdk-amd64
2 |
3 | tomcat_url: http://apache.mirror.digitalpacific.com.au/tomcat/tomcat-8/v8.5.24/bin/apache-tomcat-8.5.24.tar.gz
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # apache-tomcat-automation
2 |
3 | ## Overview
4 |
5 | We aim to create a Highly Available apache-tomcat cluster with ansible, packer and terraform. This automation is supported currently for **Ubuntu** only.
6 |
7 | There are 3 parts of this automation:
8 | 1. Using **Terraform** creating a VPC with public and private subnets.
9 | 2. Create an ami which has apache, tomcat & one sample application configured using **Packer** and **Ansible**. The AJP connection is used between apache & tomcat.
10 | 3. Create ELB, security groups and autoscaling group with scaling policies using **Terraform**
11 |
12 | - The **Ansible** playbook to install and configure Apache-Tomcat is included independently as well in case user does not want to use packer.
13 | - I've used Amazon Linux box to setup the automation instance where I've configured Terraform, Ansible & Packer.
14 | ## Prerequisites
15 |
16 | - ##### We are tagging the resources getting created through terraform and packer. These resources are called later during Autoscaling Group Creating using terraform **data resources**.
17 |
18 | - ##### Use of tags reduces the human errors and simplify the code. Hence, in case VPC is not created using the given Terraform code then the subnets must be tagged as below:
19 | Name: `tag_name`a\b\c
20 | ```YAML
21 | Example for tag named "public_subnet":
22 | For subnet of Public AZ 1:
23 | Name: public_subneta
24 |
25 | For subnet of Public AZ 2:
26 | Name: public_subnetb
27 |
28 | For subnet of Public AZ 3:
29 | Name: public_subnetc
30 | ```
31 | - ##### For packer automation to create AMI, we are using Ubuntu 16.04 OS. User need to get this AMI ID and enter in Packer variables file manually. In case user want to avoid doing it then Packer and auto fetch the AMI ID, the code for that is given in **info.txt** file inside **Create AMI** directory.
32 |
33 | ### Creating VPC Using Terraform
34 |
35 | The code given in **1-Create VPC** is use to setup a new VPC. It creates:
36 | 1. A VPC
37 | 2. Three Public Subnets. They are created with tag `Name = public_subneta \ public_subnetb \ public_subnetc`
38 | 3. Three Private Subnets.
39 |
40 | To setup Terraform:
41 | 1. Download Terraform (linux) from: https://www.terraform.io/downloads.html
42 | 2. Extract the zip and move the terraform binary in /usr/sbin directory
43 | #### Terraform variables
44 | The following variables can be modified by the user. User need to edit **1-Create VPC/variables.tf** file
45 |
46 | - `access_key`: AWS access key for user's project
47 | - `secret_key`: AWS secret Key for User's project
48 | - `aws_region`: Region to use Default is ap-southeast-2
49 | - `vpc_cidr`: CIDR for whole VPC, default is 192.168.0.0/22
50 | - `public_subnet_cidr_a\b\c`: CIDRs for the public subnets. The default values are given in the variables.tf file.
51 | - `private_subnet_cidr_a\b\c`: CIDRs for the private subnets. The default values are given in the variables.tf file.
52 |
53 | #### Running Terraform
54 |
55 | Once all the info is correctly entered in variables.tf file user need to run below from **1-Create VPC**:
56 | - To initialize terraform: `terraform init`
57 | - To check the different resources being created: `terraform plan`
58 | - To create the resources: `terraform apply`
59 |
60 |
61 | ### Creating AMI Using Packer
62 |
63 | Creating AMI part consist of Packer and Ansible which are integrated together. This part focuses on Packer configs.
64 | The default AMI is created with Tag `Name = apache-tomcat-0.1`
65 |
66 | To setup Packer:
67 | 1. Download Packer (linux) from: https://www.packer.io/downloads.html
68 | 2. Extract the zip and move the packer binary in /usr/sbin directory
69 | #### Packer Variables
70 |
71 | The following variables can be modified by the user. User need to edit **2-Create AMI/vars.json** file
72 | - `aws_access_key`: AWS access key for user's project
73 | - `aws_secret_key`: AWS secret Key for User's project
74 | - `aws_region`: Region to use Default is ap-southeast-2
75 | - `aws_ami_image`: Ubuntu ami Id for the specific region.
76 | - `aws_instance_type`: The instance type with which the ami will be created by packer.
77 | - `image_version`: In case more images are created using the same automation this can be used to add versions. Default value is **0.1**. AMI created is **Name = apache-tomcat-0.1**
78 |
79 | ```JSON
80 | Below is the example config available:
81 | {
82 | "aws_access_key": "",
83 | "aws_secret_key": "",
84 | "aws_region": "ap-southeast-2",
85 | "aws_ami_image": "ami-cab258a8",
86 | "aws_instance_type": "t2.small",
87 | "image_version" : "0.1"
88 | }
89 | ```
90 |
91 | #### Running Packer
92 |
93 | Go to the directory 2-Create AMI and run:
94 | - packer build -var-file=vars.json apache-ami.json
95 |
96 | #### Integration with Ansible
97 |
98 | To Integrate packer with Ansible:
99 | 1. The ansible code is copied ino the playbook directory inside Create AMI directory
100 | 2. The ansible provisioner is then used to integrate ansible with packer.
101 | 3. Once a system is created with AMI the apache & tomcat services will be up and running.
102 |
103 |
104 | ### Ansible Playbook to install and configure apache & tomcat
105 |
106 | 1. The ansible playbook is integrated with Packer. As well same playbook is provided separately "Independent apache-tomcat Configuration.
107 | 2. The ansible version tested is 2.3.1 but playbook should also support 2.4
108 | 3. The playbook does below:
109 | - Install and configures apache
110 | - Install and configure tomcat (version 8) and Create tomcat service file.
111 | - Ensure that only AJP connector is used between Apache & Tomcat.
112 | - Deploy a sample application "clusterjsp" in the end of automation.
113 |
114 | #### Ansible variables
115 | The following variables can be modified by the user. User need to edit **2-Create AMI/playbook/variables.yml** file
116 | - `java_home`: The java home being used to create tomcat service file. (Default: java-1.8.0-openjdk-amd64 )
117 | - `tomcat_url`: The url to download the tomcat .tar.gz file. (Default: http://apache.mirror.digitalpacific.com.au/tomcat/tomcat-8/v8.5.24/bin/apache-tomcat-8.5.24.tar.gz)
118 |
119 | #### Running ansible playbook independently
120 | 1. All the instances where apache should be installed should have their IP's mentioned in "inventory.txt" file.
121 | 2. Run below command from "Independent apache-tomcat Configuration":
122 | - ansible-playbook main.yml
123 | 3. The ansible logs will be created in **/tmp/ansible.log** file
124 | 4. The ansible configurations are mentioned in "ansible.cfg" file. User can modify them as per need basis.
125 |
126 |
127 | ### Creating Autoscaling Group using Terraform
128 |
129 | 1. Here we are using Terraform to create:
130 | - Creates Security Groups
131 | - Creates public key for user to use with launch configuration.
132 | - Creates Elastic Load balancer
133 | - Creates Launch Configuration & Autoscaling Group
134 | - Creates policies for Autoscaling group for Increase & Decrease of number of instance as per CPU and Memory usage
135 | - Creates CPU & Memory based Cloud Watch Metrics to be used with policy of autoscaling group.
136 |
137 | 2. Info about Autoscaling Group Created:
138 |
139 | - The Desired and Minimum number of instances are 3.
140 | - The maximum number of instances is 5.
141 | - One additional instance is created when the CPU or Memory threshold crosses 80% with cool down of 300 seconds.
142 | - One instance is removed when the CPU or Memory threshold drops below 20%.
143 |
144 | 3. In the end of terraform run, the ELB DNS is generated as output. That can be used to test the ASG created.
145 | #### Terraform variables & Files
146 |
147 | The following variables can be modified by the user. User need to edit **3- Create ASG/vars.tf** file
148 | - `access_key`: AWS access key for user's project
149 | - `secret_key`: AWS secret Key for User's project
150 | - `aws_region`: Region to use Default is ap-southeast-2
151 | - `filter_ami`: The tag name for AMI being used. The default is **apache-tomcat-0.1** (This is also the default tag created by packer)
152 | - `subnet_tag_name`: This is the name of Tag's **Key** being used. The default is **Name**.
153 | - `subnet_tag_value`: This is the tag's **value** being used. The default is **public_subnet** (This is also the default subnet name created by VPC code)
154 | - `instance_type`: The size of instance to be launched with autoscaling group.
155 | - `port_22_cidr`: The cidr for the Ip range which will be used for ssh to this instances. (E.g. 103.11.226.0/24)
156 | - `max_instance_asg`: The max limit for the instances in autoscaling group. (Default = 5)
157 |
158 | For creation of key part:
159 | 1. Enter the public key in **key.pub** file.
160 | 2. If user does not want to create new key.pub then update the "key_name" part of "aws_launch_configuration" part in **3-Create ASG/create_autoscaling_group.tf** file :
161 | ```YAML
162 | resource "aws_launch_configuration" "apache" {
163 | key_name = "enter_your_key_name"
164 | }
165 | ```
166 |
167 | #### Running Terraform
168 |
169 | Once all the info is correctly entered in vars.tf file user need to run below from **2-Create ASG** directory:
170 | - To initialize terraform: `terraform init`
171 | - To check the different resources being created: `terraform plan`
172 | - To create the resources: `terraform apply`
173 |
174 |
175 | ## Testing the final setup
176 |
177 | Take the output of ELB DNS created in the last terrafrom run, append clusterjsp to it and hit the url in brouwer:
178 | http://ELB_DNS_GENERATED/clusterjsp
179 |
180 | example: http://apache-elb-993864931.ap-southeast-2.elb.amazonaws.com/clusterjsp/
181 |
182 |
--------------------------------------------------------------------------------