{"id":6326,"date":"2018-01-18T09:00:02","date_gmt":"2018-01-18T17:00:02","guid":{"rendered":"\/developerblog\/?p=6326"},"modified":"2020-03-20T09:05:59","modified_gmt":"2020-03-20T16:05:59","slug":"continuous-delivery-service-fabric-via-github-travis-ci-docker-compose","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/continuous-delivery-service-fabric-via-github-travis-ci-docker-compose\/","title":{"rendered":"Continuous Delivery for Service Fabric via Github, Travis CI and Docker Compose"},"content":{"rendered":"<p>We recently worked with <a href=\"http:\/\/ascoderu.ca\/\">Ascoderu<\/a>, a small tech-for-good non-profit registered in Canada and the Congo DRC. Ascoderu is currently developing the Lokole project, which includes a hardware access point and suite of open source Linux cloud services to enable communities to have rich communications via email for just $1 per community per day. Ascoderu is targeting rural communities in sub-Saharan Africa with this technology. Access to efficient communication in rural African areas has been recognized as an important factor for sustainable development by the United Nations&#8217; Sustainable Development <a href=\"https:\/\/sustainabledevelopment.un.org\/sdg9\">Goal 9<\/a>. The African Innovation Foundation also recently recognized Lokole&#8217;s importance for the continent, calling it one of the <a href=\"http:\/\/innovationprizeforafrica.org\/news06-14-17.html\">top 10 innovations<\/a> solving Africa&#8217;s problems.<\/p>\n<p>Ascoderu is a small non-profit and relies on volunteer development through tech-for-good hackathons, open-source events, university clubs, and so forth. As such, the continuity and the average tenure of development staff is short, which makes it essential that tasks such as deploying to production be as simple as possible.<\/p>\n<p>We previously <a href=\"https:\/\/www.microsoft.com\/developerblog\/2018\/01\/09\/deploying-a-linux-python-web-application-to-service-fabric-via-docker-compose\/\">wrote a code story<\/a> about how we simplified infrastructure management with Ascoderu using <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/service-fabric\/\">Azure Service Fabric<\/a>\u00a0and <a href=\"https:\/\/docs.docker.com\/compose\/\">Docker Compose<\/a>. This article follows up on that work and will describe how we leveraged Service Fabric, Docker Compose, Github, and\u00a0<a href=\"https:\/\/docs.travis-ci.com\/user\/for-beginners\/\">Travis CI<\/a> to build a one-click continuous delivery pipeline that deploys Ascoderu&#8217;s open-source Linux cloud services.<\/p>\n<p><!--more--><\/p>\n<h2>Continuous delivery for Azure Service Fabric via Travis CI<\/h2>\n<p>In our <a href=\"https:\/\/www.microsoft.com\/developerblog\/2018\/01\/09\/deploying-a-linux-python-web-application-to-service-fabric-via-docker-compose\/\">previous article<\/a>, we covered how to set up a Linux Service Fabric cluster and how to deploy an application to the cluster using Docker Compose. This article assumes that you already have a Service Fabric cluster and an application that you deployed to the cluster using Docker Compose, as described in the earlier article.\u00a0 In this article, we will show how to build on that work and set up continuous delivery for Service Fabric via Github and Travis CI so that whenever a new tag is created in the project (e.g. via authoring a release on the project&#8217;s Github page), the changes are automatically built and deployed to the Service Fabric cluster.<\/p>\n<p>We&#8217;re using Travis CI as the continuous delivery service in this article since it&#8217;s very popular and free for open-source projects;\u00a0 however, the approach outlined below also applies to any continuous delivery service that has support for secrets and custom Bash scripts.<\/p>\n<p>Note that Service Fabric already has a continuous delivery integration with <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/service-fabric\/service-fabric-tutorial-deploy-app-with-cicd-vsts#configure-continuous-delivery-with-vsts\">Visual Studio Team Services<\/a>\u00a0but, like many open source projects, Ascoderu&#8217;s code is hosted on Github, so we cannot leverage this integration.<\/p>\n<p>The diagram below shows the workflow based on Github and Travis CI that we&#8217;ll implement:<\/p>\n<p><figure id=\"attachment_6377\" aria-labelledby=\"figcaption_attachment_6377\" class=\"wp-caption aligncenter\" ><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-servicefabric-flow.png\" alt=\"Image travis docker servicefabric flow\" width=\"1180\" height=\"475\" class=\"aligncenter size-full wp-image-10832\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-servicefabric-flow.png 1180w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-servicefabric-flow-300x121.png 300w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-servicefabric-flow-1024x412.png 1024w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-servicefabric-flow-768x309.png 768w\" sizes=\"(max-width: 1180px) 100vw, 1180px\" \/><figcaption id=\"figcaption_attachment_6377\" class=\"wp-caption-text\">Diagram showing continuous delivery flow via Github, Travis CI, Docker Compose, and Service Fabric.<\/figcaption><\/figure><\/p>\n<p>Travis CI has built-in support for publishing to a number of systems when a build tagged as a release is successful. For example, Travis supports <a href=\"https:\/\/docs.travis-ci.com\/user\/deployment\/pypi\/\">publishing a Python package to PyPI<\/a>\u00a0or <a href=\"https:\/\/docs.travis-ci.com\/user\/deployment\/npm\/\">publishing a NodeJS module to NPM<\/a>. Currently, there is no off-the-shelf support for publishing to a Service Fabric cluster but it&#8217;s easy to implement it ourselves as shown in the rest of this section.<\/p>\n<p>First, ensure that the <em>.travis.yml<\/em> file that configures the Travis CI environment installs an up-to-date version of Docker and runs our script to publish to Service Fabric whenever a build for a tag was successful:<\/p>\n<pre title=\"Excerpt from .travis.yml for Service Fabric deployment\" class=\"lang:yaml decode:true\"># the snippet below is an excerpt from the file .travis.yml\r\n\r\n# ensure that docker is available on the travis machine\r\nservices:\r\n  - docker\r\n\r\n# ensure that the docker version is up to date so that we're sure that\r\n# we can build any sort of docker-compose file that may be in the project\r\n# e.g., the default ubuntu docker version can't build v3 compose files\r\nsudo: required\r\nbefore_deploy:\r\n  - sudo apt-get update\r\n  - sudo apt-get -y -o Dpkg::Options::=\"--force-confnew\" install docker-ce\r\n\r\n# wire-up the script to build the application and deploy it to service fabric\r\n# the script is run whenever a tag is added to the application's repository\r\ndeploy:\r\n  - provider: script\r\n    script: deploy-to-service-fabric.sh\r\n    on:\r\n      repo: your_github_organization\/your_repository_name\r\n      tags: true<\/pre>\n<p>Next, create the <em>deploy-to-service-fabric.sh<\/em> script referenced in the <em>.travis.yml<\/em> file. The script builds our application, publishes the built Docker images, and deploys the changes to the Service Fabric cluster:<\/p>\n<pre class=\"lang:sh decode:true \"># the snippet below is an excerpt from the file deploy-to-service-fabric.sh\r\n\r\n#!\/usr\/bin\/env bash\r\n\r\n# double check that the details for how to connect to docker and the service\r\n# fabric cluster are provided; you can set these in the travis ui at\r\n# https:\/\/travis-ci.org\/your_github_organization\/your_repository_name\/settings\r\n# the variables SERVICE_FABRIC_NAME and SERVICE_FABRIC_LOCATION should be set\r\n# to the values of $cluster_name and $location created during the initial\r\n# cluster setup described in our earlier article\r\nif [ -z \"$DOCKER_USERNAME\" ] || [ -z \"$DOCKER_PASSWORD\" ]; then\r\n  echo \"No docker credentials configured\" &gt;&amp;2; exit 1\r\nfi\r\nif [ -z \"$SERVICE_FABRIC_NAME\" ] || [ -z \"$SERVICE_FABRIC_LOCATION\" ]; then\r\n  echo \"No service fabric credentials configured\" &gt;&amp;2; exit 1\r\nfi\r\n\r\n# install the service fabric command line tool (sfctl), re-using the travis\r\n# python virtual environment to avoid having to set up a new one\r\npy_env=\"$HOME\/virtualenv\/python$TRAVIS_PYTHON_VERSION\"\r\npip=\"$py_env\/bin\/pip\"\r\nsfctl=\"$py_env\/bin\/sfctl\"\r\n${pip} install sfctl\r\n\r\n# as during the initial deployment to the cluster, we must materialize\r\n# all environment variables used in the docker compose file\r\ncompose_file=\"$(mktemp)\"\r\ndocker-compose config &gt; \"$compose_file\"\r\n\r\n# create a new build of the application and publish it to the docker registry so\r\n# that the nodes in the service fabric cluster can find the updated application\r\n# this assumes that the travis build step didn't already create these assets\r\ndocker login --username \"$DOCKER_USERNAME\" --password \"$DOCKER_PASSWORD\"\r\ndocker-compose -f \"$compose_file\" build\r\ndocker-compose -f \"$compose_file\" push\r\n\r\n# connect to the service fabric cluster using the certificate provided via the\r\n# travis encrypted file\r\nsf_host=\"$SERVICE_FABRIC_NAME.$SERVICE_FABRIC_LOCATION.cloudapp.azure.com\"\r\nREQUESTS_CA_BUNDLE=\"$cert_file\" ${sfctl} cluster select \\\r\n  --endpoint \"https:\/\/$sf_host:19080\" \\\r\n  --pem \"cert.pem\" \\\r\n  --no-verify\r\n\r\n# deploy the new build to the service fabric cluster, in rolling-upgrade mode to\r\n# ensure up-time; the deployment will run asynchronously and you can monitor the\r\n# progress in the cluster management ui\r\n${sfctl} compose upgrade \\\r\n  --deployment-name \"$SERVICE_FABRIC_NAME\" \\\r\n  --file-path \"$compose_file\"<\/pre>\n<p>The script above is general-purpose so it can be re-used for any application that is hosted on GitHub, uses Travis CI and deploys to Service Fabric. The script assumes two preconditions:<\/p>\n<ol>\n<li>The certificate required to connect to the Service Fabric cluster is made available under the name <em>cert.pem<\/em> in the script&#8217;s working directory<\/li>\n<li>Username and password credentials to publish Docker images are provided via environment variables<\/li>\n<\/ol>\n<p>To satisfy the first precondition and make the Service Fabric certificate available to the continuous delivery script, we can use the <a href=\"https:\/\/github.com\/travis-ci\/travis.rb#installation\">Travis command line tool<\/a>\u00a0to encrypt the certificate and then commit the encrypted file to the Github repository, whose contents are always available during the Travis CI build:<\/p>\n<pre class=\"lang:sh decode:true\"># encrypt the certificate using travis' key; this command will also modify\r\n# the .travis.yml configuration file to automatically decrypt the certificate\r\n# whenever a build is run\r\ntravis encrypt-file cert.pem --add\r\n\r\n# make sure to never commit the original certificate!\r\necho \"cert.pem\" &gt;&gt; .gitignore\r\n\r\n# commit the encrypted certificate to make it available to travis\r\ngit add cert.pem.enc .travis.yml\r\ngit commit -m \"Adding deployment secret\"\r\ngit push<\/pre>\n<p>More information about encrypting files for Travis CI is available in the <a href=\"https:\/\/docs.travis-ci.com\/user\/encrypting-files\/\">Travis documentation<\/a>.<\/p>\n<p>To satisfy the second precondition and make the Docker credentials available to the script, we use the Travis website to configure the required environment variables as shown in the screenshot below.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-screenshot.png\" alt=\"Image travis docker screenshot\" width=\"2600\" height=\"2083\" class=\"aligncenter size-full wp-image-10831\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-screenshot.png 2600w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-screenshot-300x240.png 300w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-screenshot-1024x820.png 1024w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-screenshot-768x615.png 768w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-screenshot-1536x1231.png 1536w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/travis-docker-screenshot-2048x1641.png 2048w\" sizes=\"(max-width: 2600px) 100vw, 2600px\" \/><\/p>\n<p>More information about providing environment variables to Travis CI is available in the <a href=\"https:\/\/docs.travis-ci.com\/user\/environment-variables\/\">Travis documentation<\/a>.<\/p>\n<p>With the deployment script now set up, the cluster certificate made available to Travis CI, and Docker credentials provided via environment variables, our continuous delivery pipeline is now complete. Whenever a tag gets added to the repository (e.g., by authoring a release on Github via\u00a0<em>https:\/\/github.com\/your_github_organization\/your_repository_name\/releases\/new<\/em>), the changes will be automatically built, new Docker images will be published, and Service Fabric will take care of upgrading the running applications to reflect the new images (see the <a href=\"https:\/\/travis-ci.org\/ascoderu\/opwen-cloudserver\/builds\/303620604\">sample log<\/a> for a release build in the Ascoderu project). Note that Service Fabric uses a <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/service-fabric\/service-fabric-application-upgrade#rolling-upgrades-overview\">rolling upgrade<\/a> mechanism to ensure availability of the service during the deployment.<\/p>\n<h2>Summary<\/h2>\n<p>This code story covered how to create a continuous delivery pipeline for Service Fabric via Github and Travis CI. Using this setup, the Ascoderu non-profit is now able to deploy changes to production with a simple one-click workflow driven off of Github releases. This method enables anyone in Ascoderu to easily and confidently author releases, including developers new to the project.<\/p>\n<p>The approach outlined in this article is fully generalized and therefore reusable for any project that is hosted on Github and uses Docker Compose with Azure Service Fabric. Try it in your project and tell us about your results in the comments below!<\/p>\n<h2>Resources<\/h2>\n<ul>\n<li><a href=\"https:\/\/github.com\/ascoderu\/opwen-cloudserver\/blob\/0f2efe273b8590799aeb5de7252146c42a04e06a\/travis\/cd.sh\">Service Fabric enabled Travis CI continuous delivery script for Ascoderu project<\/a><\/li>\n<li><a href=\"https:\/\/www.microsoft.com\/developerblog\/2018\/01\/09\/deploying-a-linux-python-web-application-to-service-fabric-via-docker-compose\/\">Article on how to deploy Linux Python applications to Service Fabric via Docker Compose<\/a><\/li>\n<li><a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/service-fabric\/\">More information about Service Fabric<\/a><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article describes how to use GitHub, Travis CI and Docker Compose to build a simple continuous delivery pipeline to deploy Linux Docker containers to a Service Fabric cluster of Linux hosts.<\/p>\n","protected":false},"author":21408,"featured_media":13043,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[15,16],"tags":[156,325,362],"class_list":["post-6326","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-containers","category-devops","tag-docker","tag-service-fabric","tag-travis-ci"],"acf":[],"blog_post_summary":"<p>This article describes how to use GitHub, Travis CI and Docker Compose to build a simple continuous delivery pipeline to deploy Linux Docker containers to a Service Fabric cluster of Linux hosts.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/6326","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21408"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=6326"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/6326\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/13043"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=6326"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=6326"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=6326"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}