{"id":3342,"date":"2015-06-05T17:41:12","date_gmt":"2015-06-05T23:41:12","guid":{"rendered":"http:\/\/benincosa.com\/?p=3342"},"modified":"2015-06-15T11:51:08","modified_gmt":"2015-06-15T17:51:08","slug":"continuous-delivery-of-a-simple-web-application-tutorial-part-3","status":"publish","type":"post","link":"https:\/\/benincosa.com\/?p=3342","title":{"rendered":"Continuous Delivery of a Simple Web Application Tutorial &#8211; Part 3"},"content":{"rendered":"<p><a href=\"http:\/\/benincosa.com\/?p=3319\">In Part 1<\/a> we discussed the architecture of what we&#8217;re trying to build.<\/p>\n<p><a href=\"http:\/\/benincosa.com\/?p=3330\">In Part 2<\/a> we created our development server with Ansible.<\/p>\n<p>In Part 3 we will finish off Ansible with creating our Load Balancers and WebServers. \u00a0Then we&#8217;ll setup Git and check everything in.<\/p>\n<p><a href=\"http:\/\/benincosa.com\/?p=3352\">In Part 4<\/a> we will finish it all off with Jenkins configuration and watch as our code continuously updates.<\/p>\n<p>At this point, all we&#8217;ve done is create the development environment, but we haven&#8217;t configured any of it. \u00a0We&#8217;re going to take a break from that for a little bit and set up our web servers and load balancers. \u00a0This should be pretty straight forward.<\/p>\n<h3>Load Balancers<\/h3>\n<p>Cisco OpenStack Private Cloud \/ Metacloud actually comes with several predefined images. \u00a0One of those is the MC-VLB, which is a preconfigured server running HAProxy with the Snapt front end. \u00a0You can see all the documentation for managing the HAProxy via the GUI using <a href=\"http:\/\/kb.snapt.net\/balancer\/snapt-balancer-manual\/\">their documentation<\/a>.<\/p>\n<p>We&#8217;re just going to configure it with Ansible. \u00a0We&#8217;ve created a file in our ansible directory called load-balancers.yml. \u00a0This file contains the following:<\/p>\n<pre class=\"lang:sh decode:true \">- name: Ensure Load Balancer is up\r\n  connection: local\r\n  hosts: local\r\n  vars_files:\r\n   - vars\/metacloud_vars.yml\r\n\r\n  tasks: \r\n  - name: Ensure Load Balancers A and B are up. \r\n    nova_compute:\r\n      state: present\r\n      auth_url: \"{{ lookup('env', 'OS_AUTH_URL') }}\"\r\n      login_username: \"{{ lookup('env', 'OS_USERNAME') }}\"\r\n      login_password: \"{{ lookup('env', 'OS_PASSWORD') }}\"\r\n      login_tenant_name: \"{{ lookup('env', 'OS_TENANT_NAME') }}\"\r\n      name: load-balancer-a\r\n      image_name: mc-vlb-3.6-x86_64-20140929\r\n      key_name: \"{{ keypair }}\"\r\n      flavor_id: 1734b486-93e0-477d-9e95-08cfe587be45\r\n      meta: \r\n        group: load-balancers\r\n      security_groups: \"{{ security_group }}\"\r\n      floating_ip_pools:\r\n      - \"{{ floating_ip_pool }}\"\r\n    with_items:\r\n      - load-balancer-a\r\n      - load-balancer-b\r\n\r\n- name: Ensure Load Balancers are configured\r\n  hosts: load-balancers\r\n  sudo: true\r\n  roles: \r\n   - load-balancer<\/pre>\n<p>We are using the encrypted vars\/metacloud_vars.yml file to pass in the appropriate values. \u00a0The flavor-id corresponds to what we saw in the GUI. \u00a0Its actually a flavor size created specifically for this load balancer image.<\/p>\n<p>Once the VM is up, then we give it the role load-balancer. \u00a0This goes to the roles\/load-balancer\/tasks\/main.yml\u00a0task. \u00a0This task looks as follows:<\/p>\n<pre class=\"lang:sh decode:true \">- name: Place files for HAProxy config\r\n  copy: src=..\/files\/haproxy.cfg dest=\/etc\/haproxy\r\n\r\n- name: Ensure HAProxy is started\r\n  service: name=haproxy state=restarted<\/pre>\n<p>Pretty simple as it just copies our config and restarts the load balancers. \u00a0This is one case where we&#8217;re not using containers in this setup. \u00a0We could have just created our own image using nginx to do it or even haproxy, but we thought it was worth taking a look at the instance to see what Metacloud provided.<\/p>\n<p>The key to this is the \/etc\/haproxy\/haproxy.cfg file. \u00a0This file is as follows:<\/p>\n<pre class=\"lang:sh decode:true \">frontend http\r\nbind *:80\r\nmode http\r\ndefault_backend web-backend\r\ntimeout client 50000\r\n\r\nbackend web-backend\r\ntimeout connect 5000\r\ntimeout server 50000\r\nbalance roundrobin\r\nmode http\r\nserver web01 10.2.3.4:80 check\r\nserver web02 10.2.3.5:80 check\r\nserver web03 10.2.3.6:80 check<\/pre>\n<p>This configuration should highlight one of the glaring problems with our environment. \u00a0We&#8217;ve put the web servers (which we haven&#8217;t even created yet!) in this static file. \u00a0What if we want to add more? \u00a0What if we get different IP addresses? While this blog won&#8217;t go over the solutions, I&#8217;d welcome any comments.<\/p>\n<p>Now running:<\/p>\n<pre class=\"lang:sh decode:true \">ansible-playbook load-balancers.yml<\/pre>\n<p>Our load balancers will come on line and be ready to serve traffic to our web instances. \u00a0Let&#8217;s create those now.<\/p>\n<h3>Web Servers<\/h3>\n<p>Calling these instances &#8216;web servers&#8217; is probably not correct. \u00a0They, in fact will be running docker containers that have the appropriate web services on them. \u00a0These servers will look just like the development server we created in the previous blog.<\/p>\n<pre class=\"lang:sh decode:true\">- name: Ensure Web Servers are Up\r\n  connection: local\r\n  hosts: local\r\n  vars_files:\r\n   - vars\/metacloud_vars.yml\r\n\r\n  tasks: \r\n  - name: Ensure webservers are up\r\n    nova_compute:\r\n      state: present\r\n      auth_url: \"{{ lookup('env', 'OS_AUTH_URL') }}\"\r\n      login_username: \"{{ lookup('env', 'OS_USERNAME') }}\"\r\n      login_password: \"{{ lookup('env', 'OS_PASSWORD') }}\"\r\n      login_tenant_name: \"{{ lookup('env', 'OS_TENANT_NAME') }}\"\r\n      name: \"{{ item }}\"\r\n      image_name: \"{{ coreos_image_name }}\"\r\n      key_name: \"{{ keypair }}\"\r\n      # 3 is m1.large\r\n      flavor_id: 3\r\n      meta: \r\n        group: web-servers\r\n      security_groups: \"{{ security_group }}\"\r\n      user_data:  \"{{ lookup('file', 'files\/web-config.sh') }}\"\r\n    with_items:\r\n      - web01\r\n      - web02\r\n      - web03<\/pre>\n<p>This script should look very similar to what you saw in deploying the development server. \u00a0The server boots up and it runs the web-config.sh script. \u00a0This script is exactly the same as the one in part 1 except at the very end of it, it brings up the latest application container:<\/p>\n<pre class=\"lang:sh decode:true\">docker run -p 80:3000 -d --name lawngnomed ci:5000\/vallard\/lawngnomed:latest<\/pre>\n<p>This is nice because its completely automated. \u00a0The server goes up and the latest web service starts. \u00a0We could remove the instance and create a new one and it would get the latest.<\/p>\n<p>As long as there is one server up, our website lawngnomed.com will stay up. \u00a0By putting java on it, Jenkins can use it to run commands and by putting Ansible on it we can configure it if we need to.<\/p>\n<p>Since you haven&#8217;t created the ci:5000\/vallard\/lawngnomed:latest docker image, yours probably won&#8217;t work. \u00a0But you could give it a docker hub image instead to make sure it gets something and then starts running.<\/p>\n<p>Let&#8217;s bring up the web servers in their current state:<\/p>\n<pre class=\"lang:sh decode:true\">ansible-playbook web-servers.yml<\/pre>\n<h3>Taking Stock<\/h3>\n<p>At this point we have accomplished 3 things:<\/p>\n<ol>\n<li>Development Server with all the services are installed<\/li>\n<li>Load Balancers are up and pointing to web servers<\/li>\n<li>Web servers are ready, but don&#8217;t have any images yet to run.<\/li>\n<\/ol>\n<p>Our next step is to start configuring all those services. \u00a0This is where our Ansible work is done. \u00a0We are using it solely for creating the environment.<\/p>\n<h3>Gitlab configuration<\/h3>\n<p>Navigating to our public IP address and port 10080, or if you put DNS and are using the nginx reverse proxy, we can now see the login screen. \u00a0The default root password is\u00a0<strong>5iveL!fe<\/strong>. \u00a0We are using some of the great containers that were built by <a href=\"https:\/\/github.com\/sameersbn\">Sameer Naik<\/a>.<\/p>\n<p><a href=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/login.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3346\" src=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/login.png\" alt=\"login\" width=\"994\" height=\"253\" \/><\/a><\/p>\n<p>We will be forced to create a new password.<\/p>\n<p><a href=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/create-password.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3347\" src=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/create-password.png\" alt=\"create password\" width=\"593\" height=\"406\" \/><\/a><\/p>\n<p>Then we need to lock things down. \u00a0Since we don&#8217;t want just everyone to sign up we can go to the settings page (click the gears in the top right side) and disable some things:<\/p>\n<p><a href=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/settings.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3348\" src=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/settings.png\" alt=\"settings\" width=\"2308\" height=\"1550\" \/><\/a><\/p>\n<p>From here we can add users by clicking the &#8216;Users&#8217; item in the left sidebar.<\/p>\n<p><a href=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/Add-users.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3349\" src=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/Add-users.png\" alt=\"Add users\" width=\"2380\" height=\"522\" \/><\/a><\/p>\n<p>I created my vallard user and that is where I&#8217;ll upload my code. \u00a0Log out as root (unless you need to add more users) and log in with your main account.<\/p>\n<p>The first thing you&#8217;ll want to do is create a project. \u00a0You may want to create two projects. \u00a0One for infrastructure as code (the Ansible scripts we&#8217;ve done) and another for the actual application. \u00a0Clicking on the cat icon in the top left side will take you to the dashboard. \u00a0From there you can create the new projects. \u00a0Once you create them you are given instructions on how to set up a git environment. \u00a0They look like this:<\/p>\n<pre class=\"lang:sh decode:true \">mkdir lawngnomed-infra\r\ncd lawngnomed-infra\r\ngit init\r\ntouch README.md\r\ngit add README.md\r\ngit commit -m \"first commit\"\r\ngit remote add origin ssh:\/\/git@&lt;some-ip&gt;:10022\/vallard\/lawngnomed-infra.git\r\ngit push -u origin master<\/pre>\n<p>The first problem you will have if you do this is that you haven&#8217;t put your ssh key into Gitlab. \u00a0Click on the profile settings icon in the top right (the little person) and click on SSH keys. \u00a0Here you can upload your own.<\/p>\n<p>Protip: \u00a0run the following command to copy the public key on your mac to the paste buffer:<\/p>\n<pre class=\"lang:sh decode:true \">cat ~\/.ssh\/id_rsa.pub | pbcopy<\/pre>\n<p>Entering this in the screen should then allow you to do your first git push.<\/p>\n<p><a href=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/ssh.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3350\" src=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/06\/ssh.png\" alt=\"ssh\" width=\"1420\" height=\"193\" \/><\/a><\/p>\n<h4>The Jenkins User<\/h4>\n<p>At this point you may want to decide whether or not to create a Jenkins user to do the continuous integration. \u00a0We created a Jenkins user and gave that user its own SSH key as well as a login to the Cisco OpenStack dashboard. \u00a0Since we created this new user, we also created a keypair for him so that he could get into the instances he created. \u00a0Copy the jenkins ssh key-pair to a safe place as we&#8217;ll be using it soon. \u00a0Add the Jenkins user to your project so that he can check out the code and see it.<\/p>\n<h3>End of Part 3<\/h3>\n<p>If you got to this part, hopefully you have pushed your Ansible code we created into Gitlab. \u00a0You also may have created a Jenkins user that can be used for our continuous integration. \u00a0Please let me know if you had any issues, comments, suggestions, or questions along the way. \u00a0I want to help.<\/p>\n<p>In the last part 4 we will go over configuring Jenkins and integrating it into Gitlab. \u00a0Then we will create some tasks to automatically run to test our systems.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Part 1 we discussed the architecture of what we&#8217;re trying to build. In Part 2 we created our development server with Ansible. In Part 3 we will finish off Ansible with creating our Load Balancers and WebServers. \u00a0Then we&#8217;ll setup Git and check everything in. In Part 4 we will finish it all off&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3342"}],"collection":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3342"}],"version-history":[{"count":2,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3342\/revisions"}],"predecessor-version":[{"id":3376,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3342\/revisions\/3376"}],"wp:attachment":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3342"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3342"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3342"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}