{"action":"created","comment":{"url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/comments/24003072","id":24003072,"diff_hunk":"@@ -0,0 +1,197 @@\n+---\n+layout: post\n+title: \"OpenStack Orchestration In Depth, Part IV: Scaling\"\n+date: 2015-02-04 07:15\n+comments: true\n+author: Miguel Grinberg\n+published: true\n+categories:\n+  - rackspace-private-cloud\n+  - Orchestration\n+  - openstack-heat\n+bio:\n+  Miguel Grinberg is a software engineer with a background in web technologies and REST APIs.\n+  He is the author of the book \"Flask Web Development\" from O'Reilly Media, and has a blog at\n+  http://blog.miguelgrinberg.com, where he writes about a variety of topics including web\n+  development, robotics, photography and the occasional movie review. Miguel works as a\n+  Software Developer with the Rackspace Private Cloud team. He lives in Portland,\n+  Oregon with his wife, four kids, two dogs and a cat. Follow @miguelgrinberg on Twitter.\n+---\n+\n+This is the fourth and last article in my series on OpenStack orchestration with Heat. In the previous articles, I gave you a gentle [introduction to Heat](/blog/openstack-orchestration-in-depth-part-1-introduction-to-heat), and then I showed you some techniques to orchestrate the deployment of [single](/blog/openstack-orchestration-in-depth-part-2-single-instance-deployments/) and [multiple](/blog/openstack-orchestration-in-depth-part-3-multi-instance-deployments/) instance applications on the cloud, all done with generic and reusable components.\n+\n+Today I'm going to discuss how Heat can help with one of the most important topics in cloud computing: scalability. Like in my previous articles, I'm going to give you actual examples that you can play with on your OpenStack cloud, so make sure you have an environment where to run tests, be it a [Rackspace Private Cloud](http://www.rackspace.com/cloud/private/), [DevStack](http://devstack.org/) or any other OpenStack distribution that includes Heat.\n+\n+<!-- more -->\n+\n+## Vertical and Horizontal Scaling\n+\n+Let's say you have deployed an application to the cloud and after some time, the application has acquired a constantly growing user base. You realize it is starting to feel sluggish during peak hours. What can you do to improve response times for your users?\n+\n+The obvious solution is to use a more powerful instance. You can resize the compute instance where your application runs to a larger flavor, so that it gets more CPUs, more RAM or more disk space, whatever is necessary to bring response times back to a reasonable level. This is called *vertical scaling*. This type of scaling works well up to some point; but you will eventually reach the maximum supported number of CPUs, RAM or disk that an instance can have, and once you do, you have no way to scale more. If the load generated by your users continues to grow beyond that, then you will need to find a different way to scale.\n+\n+The other approach to scalability is to have your application running on multiple servers. If you figure out a way to run your application on two or more instances, and then add a load balancer that distributes client requests evenly among them, then your application will be able to handle much larger volumes of clients. This is called *horizontal scaling*. With this type of scaling it is going to take longer to reach a limit, since you can continue to add servers behind the load balancer until you run out of compute resources in your cloud. But even then, you can always add more hardware to your cloud and push the limit further.\n+\n+The nice thing about vertical scaling is that all applications can benefit from it; it is esentially a computer upgrade, so things work the same, but faster. Horizontal scaling, on the other side, requires applications to be carefully designed. For example, consider applications that write data to disk files. When one instance updates some file on disk, all the other instances become outdated. For things to work, data files that change may need to be replicated to all the instances, or else the load balancer could be configured to send a given client always to the same instance, so that the client specific files are always accessible. While Horizontal scaling gives you more freedom to grow, it is not a practical solution for all types of applications.\n+\n+The main purpose of this article is to show you some of the tricks Heat offers to scale applications horizontally, which presents the most challenges. The application that I'm going to deploy and scale is a very simple Flask web server that shows the hostname of the server that handled the request when you connect to it. For those familiar with Flask, below is the entire source code of this application:\n+\n+    import socket\n+    from flask import Flask\n+\n+    hostname = socket.gethostname()\n+    app = Flask(__name__)\n+\n+    @app.route('/')\n+    def index():\n+        return 'Hello from {0}!'.format(hostname)\n+\n+    if __name__ == '__main__':\n+        app.run(debug=True)\n+\n+The reason I chose to use such a simple application is that with this application it is easy to test that the load balancer is working. Connecting to the application through the load balancer and refreshing the page should show a changing hostname, as each time the page will be handled by a different server.\n+\n+Continuing with the component approach I introduced in the previous article, I have created a template that deploys this application, which I have called \"tiny\", using gunicorn and nginx. I'm not going to show you the complete template here, since it uses all the same techniques I showed you in Parts 2 and 3 of this series. If you are interested in studying this template in detail, you can find it in file [lib/tiny.yaml](https://raw.githubusercontent.com/miguelgrinberg/heat-tutorial/master/lib/tiny.yaml) in the Github repository.\n+\n+The first example I'm going to show you is the deployment of this little server as a single instance application, so that you can familiarize with it and ensure that you can get it to work on your cloud in its simplest form. After you clone the [github project](https://github.com/miguelgrinberg/heat-tutorial) to your local disk, you can launch this application as follows:\n+\n+    (venv) $ heat stack-create tiny-single -f heat_4a.yaml -e lib/env.yaml\n+\n+As the examples in previous articles, this template has parameters that allow you to configure what image, flavor, key pair and public network to use, so add those with `-P` if you need to. Once the application is deployed, you can check the outputs to find the public IP address that was assigned to it. If you connect to this address with your web browser you will receive a page with a content similar to this:","path":"src/site_source/_posts/2015-02-04-openstack-orchestration-in-depth-part-4-scaling.md","position":60,"original_position":60,"commit_id":"a9f9d0dc10b6c0eb8fe6823a96a05881f2ae8446","original_commit_id":"a9f9d0dc10b6c0eb8fe6823a96a05881f2ae8446","user":{"login":"catlook","id":5709104,"avatar_url":"https://avatars.githubusercontent.com/u/5709104?v=3","gravatar_id":"","url":"https://api.github.com/users/catlook","html_url":"https://github.com/catlook","followers_url":"https://api.github.com/users/catlook/followers","following_url":"https://api.github.com/users/catlook/following{/other_user}","gists_url":"https://api.github.com/users/catlook/gists{/gist_id}","starred_url":"https://api.github.com/users/catlook/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/catlook/subscriptions","organizations_url":"https://api.github.com/users/catlook/orgs","repos_url":"https://api.github.com/users/catlook/repos","events_url":"https://api.github.com/users/catlook/events{/privacy}","received_events_url":"https://api.github.com/users/catlook/received_events","type":"User","site_admin":false},"body":"... If you connect to this address with your web browser, you will receive ...","created_at":"2015-02-03T13:33:27Z","updated_at":"2015-02-03T13:33:27Z","html_url":"https://github.com/rackerlabs/developer.rackspace.com/pull/1046#discussion_r24003072","pull_request_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046","_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/comments/24003072"},"html":{"href":"https://github.com/rackerlabs/developer.rackspace.com/pull/1046#discussion_r24003072"},"pull_request":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046"}}},"pull_request":{"url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046","id":28523221,"html_url":"https://github.com/rackerlabs/developer.rackspace.com/pull/1046","diff_url":"https://github.com/rackerlabs/developer.rackspace.com/pull/1046.diff","patch_url":"https://github.com/rackerlabs/developer.rackspace.com/pull/1046.patch","issue_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/issues/1046","number":1046,"state":"open","locked":false,"title":"heat post #4","user":{"login":"miguelgrinberg","id":2715854,"avatar_url":"https://avatars.githubusercontent.com/u/2715854?v=3","gravatar_id":"","url":"https://api.github.com/users/miguelgrinberg","html_url":"https://github.com/miguelgrinberg","followers_url":"https://api.github.com/users/miguelgrinberg/followers","following_url":"https://api.github.com/users/miguelgrinberg/following{/other_user}","gists_url":"https://api.github.com/users/miguelgrinberg/gists{/gist_id}","starred_url":"https://api.github.com/users/miguelgrinberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miguelgrinberg/subscriptions","organizations_url":"https://api.github.com/users/miguelgrinberg/orgs","repos_url":"https://api.github.com/users/miguelgrinberg/repos","events_url":"https://api.github.com/users/miguelgrinberg/events{/privacy}","received_events_url":"https://api.github.com/users/miguelgrinberg/received_events","type":"User","site_admin":false},"body":"Final installment of my Heat series!","created_at":"2015-02-03T03:03:33Z","updated_at":"2015-02-03T13:33:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fd5107c569128dd07f049a3d4f4723a8979e03d9","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046/commits","review_comments_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046/comments","review_comment_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/comments/{number}","comments_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/issues/1046/comments","statuses_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/statuses/a9f9d0dc10b6c0eb8fe6823a96a05881f2ae8446","head":{"label":"miguelgrinberg:heat-post-4","ref":"heat-post-4","sha":"a9f9d0dc10b6c0eb8fe6823a96a05881f2ae8446","user":{"login":"miguelgrinberg","id":2715854,"avatar_url":"https://avatars.githubusercontent.com/u/2715854?v=3","gravatar_id":"","url":"https://api.github.com/users/miguelgrinberg","html_url":"https://github.com/miguelgrinberg","followers_url":"https://api.github.com/users/miguelgrinberg/followers","following_url":"https://api.github.com/users/miguelgrinberg/following{/other_user}","gists_url":"https://api.github.com/users/miguelgrinberg/gists{/gist_id}","starred_url":"https://api.github.com/users/miguelgrinberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miguelgrinberg/subscriptions","organizations_url":"https://api.github.com/users/miguelgrinberg/orgs","repos_url":"https://api.github.com/users/miguelgrinberg/repos","events_url":"https://api.github.com/users/miguelgrinberg/events{/privacy}","received_events_url":"https://api.github.com/users/miguelgrinberg/received_events","type":"User","site_admin":false},"repo":{"id":26247362,"name":"developer.rackspace.com","full_name":"miguelgrinberg/developer.rackspace.com","owner":{"login":"miguelgrinberg","id":2715854,"avatar_url":"https://avatars.githubusercontent.com/u/2715854?v=3","gravatar_id":"","url":"https://api.github.com/users/miguelgrinberg","html_url":"https://github.com/miguelgrinberg","followers_url":"https://api.github.com/users/miguelgrinberg/followers","following_url":"https://api.github.com/users/miguelgrinberg/following{/other_user}","gists_url":"https://api.github.com/users/miguelgrinberg/gists{/gist_id}","starred_url":"https://api.github.com/users/miguelgrinberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miguelgrinberg/subscriptions","organizations_url":"https://api.github.com/users/miguelgrinberg/orgs","repos_url":"https://api.github.com/users/miguelgrinberg/repos","events_url":"https://api.github.com/users/miguelgrinberg/events{/privacy}","received_events_url":"https://api.github.com/users/miguelgrinberg/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/miguelgrinberg/developer.rackspace.com","description":"Gen 3 of the developer.rackspace.com Portal","fork":true,"url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com","forks_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/forks","keys_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/keys{/key_id}","collaborators_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/teams","hooks_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/hooks","issue_events_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/issues/events{/number}","events_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/events","assignees_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/assignees{/user}","branches_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/branches{/branch}","tags_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/tags","blobs_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/git/refs{/sha}","trees_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/git/trees{/sha}","statuses_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/statuses/{sha}","languages_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/languages","stargazers_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/stargazers","contributors_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/contributors","subscribers_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/subscribers","subscription_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/subscription","commits_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/commits{/sha}","git_commits_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/git/commits{/sha}","comments_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/comments{/number}","issue_comment_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/issues/comments/{number}","contents_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/contents/{+path}","compare_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/compare/{base}...{head}","merges_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/merges","archive_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/downloads","issues_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/issues{/number}","pulls_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/pulls{/number}","milestones_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/milestones{/number}","notifications_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/labels{/name}","releases_url":"https://api.github.com/repos/miguelgrinberg/developer.rackspace.com/releases{/id}","created_at":"2014-11-06T01:04:21Z","updated_at":"2014-11-03T17:17:36Z","pushed_at":"2015-02-03T03:02:23Z","git_url":"git://github.com/miguelgrinberg/developer.rackspace.com.git","ssh_url":"git@github.com:miguelgrinberg/developer.rackspace.com.git","clone_url":"https://github.com/miguelgrinberg/developer.rackspace.com.git","svn_url":"https://github.com/miguelgrinberg/developer.rackspace.com","homepage":null,"size":56041,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"dev"}},"base":{"label":"rackerlabs:master","ref":"master","sha":"402402241867c02f31e64d18a4eaf1caede8f718","user":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":17407497,"name":"developer.rackspace.com","full_name":"rackerlabs/developer.rackspace.com","owner":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rackerlabs/developer.rackspace.com","description":"Gen 3 of the developer.rackspace.com Portal","fork":false,"url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com","forks_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/forks","keys_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/teams","hooks_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/hooks","issue_events_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/issues/events{/number}","events_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/events","assignees_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/assignees{/user}","branches_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/branches{/branch}","tags_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/tags","blobs_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/git/refs{/sha}","trees_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/statuses/{sha}","languages_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/languages","stargazers_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/stargazers","contributors_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/contributors","subscribers_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/subscribers","subscription_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/subscription","commits_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/commits{/sha}","git_commits_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/git/commits{/sha}","comments_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/comments{/number}","issue_comment_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/issues/comments/{number}","contents_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/contents/{+path}","compare_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/merges","archive_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/downloads","issues_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/issues{/number}","pulls_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls{/number}","milestones_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/milestones{/number}","notifications_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/labels{/name}","releases_url":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/releases{/id}","created_at":"2014-03-04T15:51:08Z","updated_at":"2015-02-02T19:19:30Z","pushed_at":"2015-02-02T19:19:30Z","git_url":"git://github.com/rackerlabs/developer.rackspace.com.git","ssh_url":"git@github.com:rackerlabs/developer.rackspace.com.git","clone_url":"https://github.com/rackerlabs/developer.rackspace.com.git","svn_url":"https://github.com/rackerlabs/developer.rackspace.com","homepage":null,"size":64763,"stargazers_count":37,"watchers_count":37,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":57,"mirror_url":null,"open_issues_count":100,"forks":57,"open_issues":100,"watchers":37,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046"},"html":{"href":"https://github.com/rackerlabs/developer.rackspace.com/pull/1046"},"issue":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/issues/1046"},"comments":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/issues/1046/comments"},"review_comments":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046/comments"},"review_comment":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/pulls/1046/commits"},"statuses":{"href":"https://api.github.com/repos/rackerlabs/developer.rackspace.com/statuses/a9f9d0dc10b6c0eb8fe6823a96a05881f2ae8446"}}}}