Hi all, new employee here.
To start learning about ansible and AWS, I created a small setup that does the following:
- create an aws instance
- install tomcat on it
- deploy a .war file from S3 and edit its contents
While this is a very simple exercise, it does touch on some interesting topics about ansible and aws.
To start, we need something like this:
- an S3 bucket to store our .war files
- a nat host so ansible tower can manage servers from our vpc in the remote vpc
- (and in our case an S3 read-only IAM policy as our version of ansible doesn’t support policy creation yet)
Tower config
To allow our tower to connect through the nat host we need some extra config.
In the root of our project we need an ansible.cfg file containing:
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=16m -F /opt/ansible/ssh/
This allows ansible to use the ssh configuration stored on the tower server. This is what it looks like:
Host [nat instance public ip]
User [nat instance user]
IdentityFile [nat instance_pem file]
Host [sandbox private subnet].*
User [sandbox user]
StrictHostKeyChecking no
IdentityFile [sandbox instance pem file]
ProxyCommand ssh -i [nat instance pem file] -o StrictHostKeyChecking=no [nat_instance_user]@[nat_instance_public_ip] nc %h %p
I won’t detail the tower configuration of the project/inventory/job/… here in detail as it’s quite straightforward.
Ansible playbook
So, we need two playbooks. The first one sets up a node and installs tomcat.
## site.yml
---
- name: Setup EC2
hosts: tower-via-local
gather_facts: no
roles:
- infrastructure
- name: install tomcat
remote_user: ec2-user
sudo: yes
hosts: launched_servers
roles:
- tomcat
The second one deploys the war and edits a file.
### deploy.yml
---
- name: deploy app
remote_user: ec2-user
sudo: yes
hosts: tag_Name_tomcat
roles:
- deploy_app
We need to split these up as both playbooks need a different inventory to work with:
- provision & install: ‘tower-via-local’ which is located in our VPC
- configuration: here we get the sandbox vpc ec2 instances and select a specific tagname we gave during provisioning
In theory, you could work with a callback to tower for the second part, but then you need to set up a way for the client to reach the server through the nat instance. As we don’t need this functionality right now, we’re skipping this step.
One of the nice things about tower is you can create simple forms provide variables to our playbooks. In this case I made two forms to provide the number of instances and the name of the war file.
# roles/infrastructure/tasks/main.yml
---
- name: Create the tomcat security group
ec2_group:
description: "Open ssh and tomcat ports"
name: "tomcat-sg"
region: "{{ region }}"
rules:
# only allow ssh access from the nat instance
- proto: tcp
from_port: 22
to_port: 22
cidr_ip: "{{ nat_ip }}/32"
# open tomcat to the world
- proto: tcp
from_port: 8080
to_port: 8080
cidr_ip: 0.0.0.0/0
purge_rules: yes
state: present
vpc_id: "{{ vpc_id }}"
register: tomcat_sg
# NOTE: only from ansible > 2.0
# ansible can't create a role from an existing policy, so we're obligated to upload a new one (stored in roles/infrastructure/files)
#- name: Create S3 read-only access
# iam_policy:
# iam_name: s3-tomcat
# iam_type: role
# policy_document: s3-ro.json
# policy_name: s3-ro
# state: present
- name: Launch base server
ec2:
assign_public_ip: yes
group_id: "{{ tomcat_sg.group_id }}"
image: "{{ tomcat_ami }}"
instance_type: "{{ tomcat_instance_type }}"
instance_profile_name: s3-tomcat
exact_count: "{{ tomcat_instance_count }}"
count_tag: { "Name": "tomcat" }
key_name: "{{ tomcat_kp }}"
region: "{{ region }}"
vpc_subnet_id: "{{ poc_subnet }}"
wait: no
assign_public_ip: yes
instance_tags: {
"Name": "tomcat",
}
register: base_server
# because wait_for doesn't use the ssh config we have to delegate this taks to the nat host. Otherwise tower wil try to connect to port 22 directly
- name: Check if we can ssh to the instance
wait_for:
host: "{{ item.private_ip }}"
port: 22
state: started
with_items: base_server.instances
when: item.state != "terminated"
delegate_to: "{{ nat_ip }}"
# add all servers to a temporary group we can use to install tomcat. We need this group as in the current playbook we have 'tower-via-local' as inventory
- name: Add servers to temporary group
add_host:
hostname: "{{ item.private_ip }}"
groupname: launched_servers
with_items: base_server.instances
when: item.state != "terminated"
The next role just installs tomcat
---
- name: Install basic software
yum:
name: "{{ item }}"
state: present
with_items:
- java-1.8.0-openjdk
- tomcat8
Ok, so now we have some servers ready to go. The next playbook uses a different inventory
# roles/deploy_app/tasks/main.yml
---
# our war file is
- name: Deploy war file
s3:
bucket: "{{ war_bucket }}"
object: "{{ war_file }}"
dest: "{{ war_deploy_path }}/{{ war_file }}"
mode: get
overwrite: no
register: war_downloaded
- name: Set correct permissions
file:
path: "{{ war_deploy_path }}/{{ war_file }}"
owner: tomcat
group: tomcat
when: war_downloaded.changed
register: war_deployed
- name: Restart tomcat
service:
name: tomcat8
state: restarted
when: war_deployed.changed
# here we cheat a little. The sample.war I'm deploying contains an index.html that we want to edit. We just wait untill the war is unpacked and the file is available
- name: Wait until war is deployed
wait_for:
path: "{{ war_deploy_path }}/{{ app_name }}/index.html"
- name: Edit index file
lineinfile:
dest: "{{ war_deploy_path }}/{{ app_name }}/index.html"
regexp: '^Sample "Hello, World" Application '
line: 'Sample "Hello, from cloudar" Application '
lineinfile:
dest: "{{ war_deploy_path }}/{{ app_name }}/index.html"
regexp: '^Sample "Hello, World" Application
'
line: 'Sample "Hello, from cloudar" Application
'
when: war_deployed.changed
This concludes our little exercise! Feel free to leave comments, I’m sure there is still room for improvement.
Jaypal
How to deploy ear,war and jar files with ansible playbooks
Thanks
Jaypal
Bert Mertens
Hi Jaypal,
The blog post you responded to describes how to deploy WAR files on Apache Tomcat.
The site.yml playbook first creates and prepares an EC2 instance using the infrastructure role and then installs Tomcat using the tomcat role.
When the server has been prepared, you can run the deploy.yml playbook.
This will then deploy the WAR file from an S3 bucket.
Concerning JAR and EAR files, please see for example following link (https://stackoverflow.com/questions/1594667/war-vs-ear-file) describing the differences.
You’ll be able to find plenty more guides online on setting up infrastructure and deployment guides.
Feel free to reach out to our sales department if you need any assistance.
Kind regards,
Bert