{"action":"created","comment":{"url":"https://api.github.com/repos/ePages-de/epages-docs/pulls/comments/33029396","id":33029396,"diff_hunk":"@@ -0,0 +1,46 @@\n+---\n+layout: post\n+title: \"Infrastructure as Code\"\n+date: \"2015-06-25 09:00:00\"\n+icon: wrench\n+tags: infrastructure, configuration management, continuous integration, jenkins\n+categories: tech-stories\n+authors: [\"Jens Fischer\", \"Dirk Jablonski\"]\n+---\n+\n+Here at ePages we heavily rely on [Continuous Integration (CI)](http://www.martinfowler.com/articles/continuousIntegration.html) to make sure our code is always in a good shape. A mixed language stack mainly composed of `Perl`, `Java` and `JavaScript` spread over multiple teams brings in different build processes and toolings needed to produce the artifacts our software is composed of, e.g. `make`, `gradle` and `grunt`. [Jenkins](https://jenkins-ci.org/) is the software we use as our CI tool. For distributing the load created by each build process we leverage the [master/slave feature](https://wiki.jenkins-ci.org/display/JENKINS/Step+by+step+guide+to+set+up+master+and+slave+machines) provided by Jenkins. We do not run all build jobs on all Jenkins instances, but already spread out specific jobs to dedicated Jenkins instances. Nevertheless some teams still share their build infrastructure, thus lacking independance e.g. when it comes to introducing new Jenkins plugins or other tools needed for their build process.\n+\n+# Jenkins configuration\n+\n+Jenkins makes it very easy to introduce new functionality by installing one of the [many available plugins](https://wiki.jenkins-ci.org/display/JENKINS/Plugins). Unfortunately not every plugin turns out to work as expected or in the worst case can potentially bring down the whole Jenkins instance. It is a bad idea to evaluate unknown plugins by installing them into your production CI infrastructure. Even making changes to a job configuration without adding any new plugins can lead to broken build jobs, negatively affecting the productivity of whole teams.\n+\n+## The solution\n+\n+We were looking for ways to make it safe and easy to improve our build infrastructure. Using version control like [GitHub](https://github.com/) for all of our source code it was natural to treat our infrastructure the same way and be able to roll back to a previous version without any hassle. Versioning whole images of Jenkins virtual servers didn't sound like the best idea and plugins like [JobConfigHistory](https://wiki.jenkins-ci.org/display/JENKINS/JobConfigHistory+Plugin) didn't go far enough.\n+\n+\n+## Server provisioning using Ansible and Vagrant\n+\n+We started by tackling the perceived root problem: the installation of the Jenkins master, including the required plugins.  The idea is to be able to set up a Jenkins master from scratch by just running a single script, while still being able to use the same facilities to update the installation, e.g. with new plugins, using the same technology. To achieve this, we decided to use [Ansible](http://www.ansible.com/home), mainly due to existing know-how.\n+\n+Once the basic installation of Jenkins master and it's prerequisites is done, the interesting part begins: installing & configuring plugins. The installation of the additional plugins is done by utilizing the [Jenkins-CLI](https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI). This allows you to provide plugin names and versions to be installed. The tricky part is getting the configuring of the server right. For this, we have so far not found a more clever solution than just to create it locally (but templated) and copy it over to the master using the according Ansible task. The addition of nodes can be done via Jenkins-CLI again.\n+\n+With the toolset described so far, the basic setup can be easily re-created any time. And since this is so easy now, we usually test our setup scripts by installing them to local [Vagrant](https://www.vagrantup.com/) machines. Especially for the global Jenkins configuration, this is an essential step, which is even easier and faster due to Ansible's capability of appliying only changes to an already provisioned system. Just re-apply the playbook, and changes in configuration are active - and you are sure they are based on your version-managed infrastructure code!","path":"_posts/2015-06-25-infrastructure-as-code.md","position":28,"original_position":28,"commit_id":"6176ffa9b20897ed6bd30c66f8090a6a6be53a9d","original_commit_id":"6176ffa9b20897ed6bd30c66f8090a6a6be53a9d","user":{"login":"BirgitBader","id":10460958,"avatar_url":"https://avatars.githubusercontent.com/u/10460958?v=3","gravatar_id":"","url":"https://api.github.com/users/BirgitBader","html_url":"https://github.com/BirgitBader","followers_url":"https://api.github.com/users/BirgitBader/followers","following_url":"https://api.github.com/users/BirgitBader/following{/other_user}","gists_url":"https://api.github.com/users/BirgitBader/gists{/gist_id}","starred_url":"https://api.github.com/users/BirgitBader/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BirgitBader/subscriptions","organizations_url":"https://api.github.com/users/BirgitBader/orgs","repos_url":"https://api.github.com/users/BirgitBader/repos","events_url":"https://api.github.com/users/BirgitBader/events{/privacy}","received_events_url":"https://api.github.com/users/BirgitBader/received_events","type":"User","site_admin":false},"body":"... can be easily recreated....\r\n\r\n... of applying...\r\n\r\nJust reapply...","created_at":"2015-06-23T10:51:43Z","updated_at":"2015-06-23T10:51:43Z","html_url":"https://github.com/ePages-de/epages-docs/pull/134#discussion_r33029396","pull_request_url":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134","_links":{"self":{"href":"https://api.github.com/repos/ePages-de/epages-docs/pulls/comments/33029396"},"html":{"href":"https://github.com/ePages-de/epages-docs/pull/134#discussion_r33029396"},"pull_request":{"href":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134"}}},"pull_request":{"url":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134","id":38318192,"html_url":"https://github.com/ePages-de/epages-docs/pull/134","diff_url":"https://github.com/ePages-de/epages-docs/pull/134.diff","patch_url":"https://github.com/ePages-de/epages-docs/pull/134.patch","issue_url":"https://api.github.com/repos/ePages-de/epages-docs/issues/134","number":134,"state":"open","locked":false,"title":"Blog: Infrastructure as Code","user":{"login":"jensfischerhh","id":396285,"avatar_url":"https://avatars.githubusercontent.com/u/396285?v=3","gravatar_id":"","url":"https://api.github.com/users/jensfischerhh","html_url":"https://github.com/jensfischerhh","followers_url":"https://api.github.com/users/jensfischerhh/followers","following_url":"https://api.github.com/users/jensfischerhh/following{/other_user}","gists_url":"https://api.github.com/users/jensfischerhh/gists{/gist_id}","starred_url":"https://api.github.com/users/jensfischerhh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jensfischerhh/subscriptions","organizations_url":"https://api.github.com/users/jensfischerhh/orgs","repos_url":"https://api.github.com/users/jensfischerhh/repos","events_url":"https://api.github.com/users/jensfischerhh/events{/privacy}","received_events_url":"https://api.github.com/users/jensfischerhh/received_events","type":"User","site_admin":false},"body":"First complete draft of blog post.","created_at":"2015-06-23T09:44:07Z","updated_at":"2015-06-23T10:51:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a6a7e3d96388e148a432a6a65b1a604c022ce3e6","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134/commits","review_comments_url":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134/comments","review_comment_url":"https://api.github.com/repos/ePages-de/epages-docs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ePages-de/epages-docs/issues/134/comments","statuses_url":"https://api.github.com/repos/ePages-de/epages-docs/statuses/6176ffa9b20897ed6bd30c66f8090a6a6be53a9d","head":{"label":"jensfischerhh:feature/InfrastructureAsCode","ref":"feature/InfrastructureAsCode","sha":"6176ffa9b20897ed6bd30c66f8090a6a6be53a9d","user":{"login":"jensfischerhh","id":396285,"avatar_url":"https://avatars.githubusercontent.com/u/396285?v=3","gravatar_id":"","url":"https://api.github.com/users/jensfischerhh","html_url":"https://github.com/jensfischerhh","followers_url":"https://api.github.com/users/jensfischerhh/followers","following_url":"https://api.github.com/users/jensfischerhh/following{/other_user}","gists_url":"https://api.github.com/users/jensfischerhh/gists{/gist_id}","starred_url":"https://api.github.com/users/jensfischerhh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jensfischerhh/subscriptions","organizations_url":"https://api.github.com/users/jensfischerhh/orgs","repos_url":"https://api.github.com/users/jensfischerhh/repos","events_url":"https://api.github.com/users/jensfischerhh/events{/privacy}","received_events_url":"https://api.github.com/users/jensfischerhh/received_events","type":"User","site_admin":false},"repo":{"id":37450426,"name":"epages-docs","full_name":"jensfischerhh/epages-docs","owner":{"login":"jensfischerhh","id":396285,"avatar_url":"https://avatars.githubusercontent.com/u/396285?v=3","gravatar_id":"","url":"https://api.github.com/users/jensfischerhh","html_url":"https://github.com/jensfischerhh","followers_url":"https://api.github.com/users/jensfischerhh/followers","following_url":"https://api.github.com/users/jensfischerhh/following{/other_user}","gists_url":"https://api.github.com/users/jensfischerhh/gists{/gist_id}","starred_url":"https://api.github.com/users/jensfischerhh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jensfischerhh/subscriptions","organizations_url":"https://api.github.com/users/jensfischerhh/orgs","repos_url":"https://api.github.com/users/jensfischerhh/repos","events_url":"https://api.github.com/users/jensfischerhh/events{/privacy}","received_events_url":"https://api.github.com/users/jensfischerhh/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jensfischerhh/epages-docs","description":"Source of ePages Developer Portal","fork":true,"url":"https://api.github.com/repos/jensfischerhh/epages-docs","forks_url":"https://api.github.com/repos/jensfischerhh/epages-docs/forks","keys_url":"https://api.github.com/repos/jensfischerhh/epages-docs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jensfischerhh/epages-docs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jensfischerhh/epages-docs/teams","hooks_url":"https://api.github.com/repos/jensfischerhh/epages-docs/hooks","issue_events_url":"https://api.github.com/repos/jensfischerhh/epages-docs/issues/events{/number}","events_url":"https://api.github.com/repos/jensfischerhh/epages-docs/events","assignees_url":"https://api.github.com/repos/jensfischerhh/epages-docs/assignees{/user}","branches_url":"https://api.github.com/repos/jensfischerhh/epages-docs/branches{/branch}","tags_url":"https://api.github.com/repos/jensfischerhh/epages-docs/tags","blobs_url":"https://api.github.com/repos/jensfischerhh/epages-docs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jensfischerhh/epages-docs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jensfischerhh/epages-docs/git/refs{/sha}","trees_url":"https://api.github.com/repos/jensfischerhh/epages-docs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jensfischerhh/epages-docs/statuses/{sha}","languages_url":"https://api.github.com/repos/jensfischerhh/epages-docs/languages","stargazers_url":"https://api.github.com/repos/jensfischerhh/epages-docs/stargazers","contributors_url":"https://api.github.com/repos/jensfischerhh/epages-docs/contributors","subscribers_url":"https://api.github.com/repos/jensfischerhh/epages-docs/subscribers","subscription_url":"https://api.github.com/repos/jensfischerhh/epages-docs/subscription","commits_url":"https://api.github.com/repos/jensfischerhh/epages-docs/commits{/sha}","git_commits_url":"https://api.github.com/repos/jensfischerhh/epages-docs/git/commits{/sha}","comments_url":"https://api.github.com/repos/jensfischerhh/epages-docs/comments{/number}","issue_comment_url":"https://api.github.com/repos/jensfischerhh/epages-docs/issues/comments{/number}","contents_url":"https://api.github.com/repos/jensfischerhh/epages-docs/contents/{+path}","compare_url":"https://api.github.com/repos/jensfischerhh/epages-docs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jensfischerhh/epages-docs/merges","archive_url":"https://api.github.com/repos/jensfischerhh/epages-docs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jensfischerhh/epages-docs/downloads","issues_url":"https://api.github.com/repos/jensfischerhh/epages-docs/issues{/number}","pulls_url":"https://api.github.com/repos/jensfischerhh/epages-docs/pulls{/number}","milestones_url":"https://api.github.com/repos/jensfischerhh/epages-docs/milestones{/number}","notifications_url":"https://api.github.com/repos/jensfischerhh/epages-docs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jensfischerhh/epages-docs/labels{/name}","releases_url":"https://api.github.com/repos/jensfischerhh/epages-docs/releases{/id}","created_at":"2015-06-15T07:34:31Z","updated_at":"2015-06-15T07:34:32Z","pushed_at":"2015-06-23T09:42:37Z","git_url":"git://github.com/jensfischerhh/epages-docs.git","ssh_url":"git@github.com:jensfischerhh/epages-docs.git","clone_url":"https://github.com/jensfischerhh/epages-docs.git","svn_url":"https://github.com/jensfischerhh/epages-docs","homepage":"https://developer.epages.com/","size":10850,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"ePages-de:develop","ref":"develop","sha":"ef905ac4e19caa6906ddb051d184a7eab740019b","user":{"login":"ePages-de","id":1125854,"avatar_url":"https://avatars.githubusercontent.com/u/1125854?v=3","gravatar_id":"","url":"https://api.github.com/users/ePages-de","html_url":"https://github.com/ePages-de","followers_url":"https://api.github.com/users/ePages-de/followers","following_url":"https://api.github.com/users/ePages-de/following{/other_user}","gists_url":"https://api.github.com/users/ePages-de/gists{/gist_id}","starred_url":"https://api.github.com/users/ePages-de/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ePages-de/subscriptions","organizations_url":"https://api.github.com/users/ePages-de/orgs","repos_url":"https://api.github.com/users/ePages-de/repos","events_url":"https://api.github.com/users/ePages-de/events{/privacy}","received_events_url":"https://api.github.com/users/ePages-de/received_events","type":"Organization","site_admin":false},"repo":{"id":30595510,"name":"epages-docs","full_name":"ePages-de/epages-docs","owner":{"login":"ePages-de","id":1125854,"avatar_url":"https://avatars.githubusercontent.com/u/1125854?v=3","gravatar_id":"","url":"https://api.github.com/users/ePages-de","html_url":"https://github.com/ePages-de","followers_url":"https://api.github.com/users/ePages-de/followers","following_url":"https://api.github.com/users/ePages-de/following{/other_user}","gists_url":"https://api.github.com/users/ePages-de/gists{/gist_id}","starred_url":"https://api.github.com/users/ePages-de/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ePages-de/subscriptions","organizations_url":"https://api.github.com/users/ePages-de/orgs","repos_url":"https://api.github.com/users/ePages-de/repos","events_url":"https://api.github.com/users/ePages-de/events{/privacy}","received_events_url":"https://api.github.com/users/ePages-de/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ePages-de/epages-docs","description":"Source of ePages Developer Portal","fork":false,"url":"https://api.github.com/repos/ePages-de/epages-docs","forks_url":"https://api.github.com/repos/ePages-de/epages-docs/forks","keys_url":"https://api.github.com/repos/ePages-de/epages-docs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ePages-de/epages-docs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ePages-de/epages-docs/teams","hooks_url":"https://api.github.com/repos/ePages-de/epages-docs/hooks","issue_events_url":"https://api.github.com/repos/ePages-de/epages-docs/issues/events{/number}","events_url":"https://api.github.com/repos/ePages-de/epages-docs/events","assignees_url":"https://api.github.com/repos/ePages-de/epages-docs/assignees{/user}","branches_url":"https://api.github.com/repos/ePages-de/epages-docs/branches{/branch}","tags_url":"https://api.github.com/repos/ePages-de/epages-docs/tags","blobs_url":"https://api.github.com/repos/ePages-de/epages-docs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ePages-de/epages-docs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ePages-de/epages-docs/git/refs{/sha}","trees_url":"https://api.github.com/repos/ePages-de/epages-docs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ePages-de/epages-docs/statuses/{sha}","languages_url":"https://api.github.com/repos/ePages-de/epages-docs/languages","stargazers_url":"https://api.github.com/repos/ePages-de/epages-docs/stargazers","contributors_url":"https://api.github.com/repos/ePages-de/epages-docs/contributors","subscribers_url":"https://api.github.com/repos/ePages-de/epages-docs/subscribers","subscription_url":"https://api.github.com/repos/ePages-de/epages-docs/subscription","commits_url":"https://api.github.com/repos/ePages-de/epages-docs/commits{/sha}","git_commits_url":"https://api.github.com/repos/ePages-de/epages-docs/git/commits{/sha}","comments_url":"https://api.github.com/repos/ePages-de/epages-docs/comments{/number}","issue_comment_url":"https://api.github.com/repos/ePages-de/epages-docs/issues/comments{/number}","contents_url":"https://api.github.com/repos/ePages-de/epages-docs/contents/{+path}","compare_url":"https://api.github.com/repos/ePages-de/epages-docs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ePages-de/epages-docs/merges","archive_url":"https://api.github.com/repos/ePages-de/epages-docs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ePages-de/epages-docs/downloads","issues_url":"https://api.github.com/repos/ePages-de/epages-docs/issues{/number}","pulls_url":"https://api.github.com/repos/ePages-de/epages-docs/pulls{/number}","milestones_url":"https://api.github.com/repos/ePages-de/epages-docs/milestones{/number}","notifications_url":"https://api.github.com/repos/ePages-de/epages-docs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ePages-de/epages-docs/labels{/name}","releases_url":"https://api.github.com/repos/ePages-de/epages-docs/releases{/id}","created_at":"2015-02-10T14:20:56Z","updated_at":"2015-05-28T09:54:48Z","pushed_at":"2015-06-23T09:44:08Z","git_url":"git://github.com/ePages-de/epages-docs.git","ssh_url":"git@github.com:ePages-de/epages-docs.git","clone_url":"https://github.com/ePages-de/epages-docs.git","svn_url":"https://github.com/ePages-de/epages-docs","homepage":"https://developer.epages.com/","size":13205,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":7,"forks":5,"open_issues":7,"watchers":1,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134"},"html":{"href":"https://github.com/ePages-de/epages-docs/pull/134"},"issue":{"href":"https://api.github.com/repos/ePages-de/epages-docs/issues/134"},"comments":{"href":"https://api.github.com/repos/ePages-de/epages-docs/issues/134/comments"},"review_comments":{"href":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134/comments"},"review_comment":{"href":"https://api.github.com/repos/ePages-de/epages-docs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ePages-de/epages-docs/pulls/134/commits"},"statuses":{"href":"https://api.github.com/repos/ePages-de/epages-docs/statuses/6176ffa9b20897ed6bd30c66f8090a6a6be53a9d"}}}}