Learning Ansible like a Command Line addict (Part 2)

Please see part 1

Everything I’ve read about Ansible talks about these big fancy “playbooks” but all I know how to do is run a ping from the command line to check if my servers exist. Do I have to copy an existing playbook and hope it does what I want?

Yea, forget that! Let’s try running something simple, like a file copy. Remember, I set this up as my jenkins user, but whatever user you’re using, log in as them to do the next parts.

First, we need a file to copy. I started with a dummy file in my home directory, hello.txt and some text inside “hello ansible!” So to run a copy command from ansible, you have to type:

ansible -m copy -a "src=./hello.txt dest=./" webservers

And you get a ton of awesome output!

jenkins@deploy:~$ ansible -m copy -a "src=./hello.txt dest=./" webservers
web02 | SUCCESS => {
 "changed": true, 
 "checksum": "af53119865a6f135b4ab851cb2b580cdc8e9f075", 
 "dest": "./hello.txt", 
 "gid": 1001, 
 "group": "deploy", 
 "md5sum": "0b2f7c2ec4095f9b8466c0913f1af3f3", 
 "mode": "0644", 
 "owner": "deploy", 
 "size": 15, 
 "src": "/home/deploy/.ansible/tmp/ansible-tmp-1497728808.43-244137364817946/source", 
 "state": "file", 
 "uid": 1001
}
web01 | SUCCESS => {
 "changed": true, 
 "checksum": "af53119865a6f135b4ab851cb2b580cdc8e9f075", 
 "dest": "./hello.txt", 
 "gid": 1001, 
 "group": "deploy", 
 "md5sum": "0b2f7c2ec4095f9b8466c0913f1af3f3", 
 "mode": "0644", 
 "owner": "deploy", 
 "size": 15, 
 "src": "/home/deploy/.ansible/tmp/ansible-tmp-1497728808.42-136193633452143/source", 
 "state": "file", 
 "uid": 1001
}

You’ll see on each of the webservers that there is now a hello.txt file in the deploy user’s home directory. Sweet! But a copy command is pretty slow when you’re dealing with a website’s worth of files. Maybe we can use rsync to speed this process up a bit?

NOTE: I did not have rsync on any of the servers in question here, so stuff blew up and I had to install it. sudo apt-get install rsync before you suffer the same fate as me!

To test, create a directory of project files (test-directory was mine) and move the hello.txt into it. I created a few other files in there too just to see how it worked. Then just run

ansible -m synchronize -a "src=./test-directory/ dest=./ archive=yes rsync_opts=--exclude=.git" webservers

And this will use rsync to copy the contents of the test-directory into the home directory of the deploy user on each of the servers.

Now we have a pretty decent way to deploy versioned releases of the code to our webservers. However, we’ll need a way to tell our webserver where the new directory is. I use symlinks which makes it simple. Just point the Apache or nginx config file at the symlink, and when you do a release you can swap it out. Often times you don’t even need to restart the server.

Here’s the list of the commands I used on my Jenkins CI deploy tool.

ansible -m file -a "path=project1/${BUILD_TAG} state=directory" webservers
ansible -m synchronize -a "src=./ dest=project1/${BUILD_TAG}/ archive=yes rsync_opts=--exclude=.git" webservers
ansible -m file -a "path=project1-live state=absent" webservers
ansible -m file -a "src=project1/${BUILD_TAG} dest=project1-live state=link" webservers

This worked pretty awesome… at least until I deployed 20 times and ran out of disk space. I wasn’t cleaning up any of my old builds! I found a helpful but non-functioning answer on Stack Overflow (https://stackoverflow.com/questions/27500262/ansible-how-to-keep-only-3-release) and I hit a wall. I couldn’t figure out how to run “with_items” without building out a playbook.

I struggled for a few days, but it suddenly clicked: I know enough about Ansible now to be able to build a playbook that does exactly what I want.

With those previous commands, I created an identical playbook in the jenkins home directory.

---
- hosts: webservers
 tasks:
 - name: create versioned directory
 file: path=project1/{{ build_tag }} state=directory

- name: sync files to folder
 synchronize: src=./ dest=project1/{{ build_tag }}/ archive=yes rsync_opts=--exclude.git

- name: delete symlink
 file: path=project1-live state=absent

- name: link new site
 file: src=project1/${{ build_tag }} dest=project1-live state=link

Then in Jenkins I just had to run:

ansible-playbook ${JENKINS_HOME}/project1-playbook.yml --extra-vars="build_tag=${BUILD_TAG}"

And it does exactly the same thing as before!

Now I can make some modifications to this playbook to clean up old deploy versions, and we should be good to go. Here’s my final playbook and Jenkins build command.

Created an “ansible-playbooks” directory in jenkins home, and then added the file ~jenkins/ansible-playbooks/webdeploy-playbook.yml

---
- hosts: "{{ server }}"
 tasks:
 - name: create versioned directory
 file: path={{ deploy_folder }}/{{ build_tag }} state=directory

- name: sync files to folder
 synchronize: src=./ dest={{ deploy_folder }}/{{ build_tag }}/ archive=yes rsync_opts=--exclude=.git

- name: delete symlink
 file: path={{ symlink_name }} state=absent

- name: link new site
 file: src={{ deploy_folder }}/{{ build_tag }} dest={{ symlink_name }} state=link

- name: get list of old releases
 shell: "ls -t {{ deploy_folder }} | tail -n +{{ releases_to_keep | int + 1 }}"
 register: ls_output

- name: delete old versions
 file: name={{ deploy_folder }}/{{ item }} state=absent
 with_items: "{{ ls_output.stdout_lines }}"

And put this in as the build step for the jenkins project.

ansible-playbook ${JENKINS_HOME}/ansible-playbooks/webdeploy-playbook.yml --extra-vars="build_tag=${BUILD_TAG} deploy_folder=project1 symlink_name=project1-live server=webservers releases_to_keep=3"

This was a pretty tough way to go about learning it, but hopefully this helps out my fellow command line junkies trying to learn Ansible playbooks.

Advertisements
Learning Ansible like a Command Line addict (Part 2)

One thought on “Learning Ansible like a Command Line addict (Part 2)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s