<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8122199360699827669</id><updated>2026-11-03T04:33:21.111-08:00</updated><category term=".Net General"/><category term="Azure"/><category term="Digital Identity"/><category term="电子基盘"/><category term="Cloud Computing"/><category term="Silverlight/WPF"/><category term="Security"/><category term="Algorithms"/><category term="User Interface"/><category term="Unity"/><category term="MVC/MVVM"/><category term="WCF"/><category term="Computer Vision"/><category term="TIY"/><category term="Geneva"/><category term="Python"/><category term="Software Matrices"/><category term="Windows Phone"/><category term="嘈嘈切切"/><category term="Deployment"/><category term="English - PDC 2008"/><category term="NDepend"/><category term="Software Engineering"/><category term="jQuery"/><category term="我的中国"/><category term="疯人手札"/><category term="CAB"/><category term="Design Patterns"/><category term="Service Bus"/><category term="史海钩沉"/><category term="白玉美人"/><category term="Architecture"/><category term="Entity Framework"/><category term="Sync Framework"/><title type='text'>Haishi&#39;s Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>229</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-2673042280592905338</id><published>2019-12-09T20:12:00.005-08:00</published><updated>2019-12-10T08:16:00.366-08:00</updated><title type='text'>Bono</title><content type='html'>When we learn classic compute algorithms, we don&#39;t start with learning how a computer is built. We don&#39;t start with how a transistor works or how to build integrated circuits. Instead, we go straight with the abstraction - bits, commands, programs and such. I think we should take the same approach when we learn quantum computing. Instead of trying to understand the bizarre quantum world, we should take some quantum behaviors granted and go straight with higher-level abstracts such as qubits and quantum gates. And once we grasp these basic concepts, we should go even a level higher to use a high-level language like &lt;a href=&quot;https://docs.microsoft.com/en-us/quantum/language/?view=qsharp-preview&quot;&gt;Q#&lt;/a&gt; and focus on how quantum algorithms work, and how we can apply quantum algorithms on practical problems.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/microsoft/bono&quot;&gt;Bono&lt;/a&gt; is an open source quantum algorithm visualizer I&#39;m building in the open. This project is inspired by a few existing systems such as&amp;nbsp;&lt;a href=&quot;https://algassert.com/quirk&quot;&gt;Quirk&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://quantum-computing.ibm.com/&quot;&gt;IBM Q Experience&lt;/a&gt;, and the&amp;nbsp;&lt;a href=&quot;https://oreilly-qc.github.io/&quot;&gt;Programming Quantum Computers book&lt;/a&gt;. Bono is a Javascript-based visualizer that features:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Drag-and-drop quantum circuit editing.&lt;/li&gt;
&lt;li&gt;Dynamic circuit evaluation.&lt;/li&gt;
&lt;li&gt;Works offline in a browser. No server is needed.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Generates Q# code.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
I&#39;ve also created a&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/channel/UCt0irJo07P4MXxCYY-BYhDQ&quot;&gt;YouTube channel&lt;/a&gt; dedicated to quantum algorithms. My goal is to create an easily digestable course for regular software developers to learn about quantum computing without needing to understand any underlying quantum physics.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Bono is at its infancy. And I&#39;m still a new student in the quantum world. I intentionally develop both Bono and the video series in the open. I hope the early results can inspire collaborations so that we can explore the quantum computing world together.&lt;br /&gt;
&lt;br /&gt;
Last but not least, you can find more Q# related contents at the&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/qsharp/q-advent-calendar-2019/&quot;&gt;Q# Advent Calendar 2019&lt;/a&gt;.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/2673042280592905338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2019/12/bono.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2673042280592905338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2673042280592905338'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2019/12/bono.html' title='Bono'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-2613521290284957296</id><published>2018-04-06T11:57:00.001-07:00</published><updated>2018-04-06T11:59:27.393-07:00</updated><title type='text'>Open Service Broker API and Service Fabric Service Catalog</title><content type='html'>&lt;p&gt;A service running on cloud often has dependencies on external services such as storage and identity. Because the lifecycle management of these external service instances is separate from your application, your application must implicitly assume these services are properly configured and available when it runs. The idea of &lt;a href=&quot;https://www.openservicebrokerapi.org/&quot;&gt;Open Service Broker API&lt;/a&gt; (OSB API) is to allow applications to take control of the lifecycle of dependent services by allowing them to explicitly define and verify external service instance statuses when they run.&amp;nbsp; The API defines a set of operations for an application to provision, update, and delete external service instances as needed. Azure provides an OSB API implementation, OSB for Azure (OSBA), as an open source project &lt;a href=&quot;https://github.com/Azure/open-service-broker-azure&quot;&gt;here.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Applications can certainly talk to OSB API directly to manage external service instances. However, this requires applications to be aware of OSB API configurations, which creates an unnecessary coupling to OSB API. Instead, the OSB API community has advised using a Service Catalog to provide platform-native representation of external service instances so that the applications concern only with the hosting platform. For example, Kubernetes &lt;a href=&quot;https://kubernetes.io/docs/concepts/service-catalog/&quot;&gt;Service Catalog&lt;/a&gt; is an extension to Kubernetes control plane that allows applications to use external services with native Kubernetes artifacts and tools, without ever needing to know about OSB API. The OSB API configuration in this case is a platform configuration concern and uncoupled from the applications. &lt;/p&gt;&lt;p&gt;This blog article records my journey to enable a Service Fabric Catalog Service (SFCAT) on &lt;a href=&quot;https://github.com/Microsoft/service-fabric&quot;&gt;Service Fabric&lt;/a&gt;. The following diagram is adapted from &lt;a href=&quot;https://openservicebrokerapi.org/blog/2017/06/22/announcing-the-open-service-broker-api-v2-12&quot;&gt;this diagram&lt;/a&gt; to illustrate where SFCAT fits.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPUxyPFbFIZbecPQwuI_vLjZNAqUuvUxosU8Ifn3MVnB-7OIfzOo91yCGbZKq3avFX85BtvVX302qVcVKvwMVsXlCVCQk4D0I5CIA1IA2SQp7A-Hb_GLnGTVAffJ4rKQHRd8cmBrMbk08U/s1600-h/image%255B7%255D&quot;&gt;&lt;img width=&quot;490&quot; height=&quot;267&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7-X1dEzn_CzO8om2nZ5aeg_HAw4XY9K9I6h0QLhgkRYOgtssvOzEbxUaHdsQMNOPObJR0dubsz6r33at7zaUNaLy7vTWvud-JoWdNlOHaOy8PI6vH0scdv45bwbcG3amBky5YsXwF1liq/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1&gt;OSB Check and OSB mock&lt;/h1&gt;&lt;p&gt;When I started with SFCAT, OSBA was still under active development, and there were some discrepancies between OSBA implementation and the &lt;a href=&quot;https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md&quot;&gt;OSB API spec&lt;/a&gt;. I was afraid I would be tempted to write an OSBA-specific version, so I started to create a mock OSB API implementation that strictly followed the spec. To make sure the mock OSB API staying true to the spec, I then created a OSB checker to automate the tests against the spec. I later donated both projects to OSB API and they are now hosted under the OSB API org on GitHub: &lt;a title=&quot;https://github.com/openservicebrokerapi/osb-checker&quot; href=&quot;https://github.com/openservicebrokerapi/osb-checker&quot;&gt;https://github.com/openservicebrokerapi/osb-checker&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;To try out the OSB mock and OSB checker, follow these steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Clone &lt;a href=&quot;https://github.com/openservicebrokerapi/osb-checker.git&quot;&gt;https://github.com/openservicebrokerapi/osb-checker.git&lt;/a&gt; (assuming to your home folder)&lt;/li&gt;&lt;li&gt;Launch mock server&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;cd ~/osb-checker/2.13/mock&lt;br clear=&quot;all&quot;&gt;npm install&lt;br clear=&quot;all&quot;&gt;node mockOSB.js&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;li&gt;Run tests&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;cd&amp;nbsp; ~/osb-checker/2.13/tests&lt;br clear=&quot;all&quot;&gt;npm install&lt;br clear=&quot;all&quot;&gt;npm test&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h1&gt;Service Fabric Service Catalog (SFCAT)&lt;/h1&gt;&lt;p&gt;With a mock OSB API implementation, I moved to SFCAT implementation itself. I looked at Kubernetes control plane, and I envied how it had extension points such as &lt;a href=&quot;https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/&quot;&gt;kubectl plugins&lt;/a&gt; and &lt;a href=&quot;https://kubernetes.io/docs/concepts/overview/extending/#user-defined-types&quot;&gt;custom resources&lt;/a&gt;. So, I took the opportunity to prototype an extensible control plane for Service Fabric. The idea was to support new resource types in both backend and frontend simply by injecting some configuration files and binaries. I wanted the newly defined resource types to be “alive” (which I called “smart entities”), meaning a resource instance could monitor itself and reconcile its current state with the desired state. I believed this was a better architecture than Kubernetes &lt;a href=&quot;https://kubernetes.io/docs/concepts/overview/components/&quot;&gt;controllers&lt;/a&gt; because it was simpler and more scalable. The idea was captured in &lt;a href=&quot;http://haishibai.blogspot.com/2017/08/autonomous-state-reconciliation-with.html&quot;&gt;my earlier blog article.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I also started to create a command-line tool, &lt;em&gt;sfcat&lt;/em&gt;, to interact with the prototype control plane. It started as an OSB API specific client. Gradually, OSB-specific parts are contained into a few classes, leaving the command-line tool a generic tool to operate on any “smart entities”.&lt;/p&gt;&lt;p&gt;You can find the SFCAT source code here: &lt;a title=&quot;https://github.com/Haishi2016/sfcat&quot; href=&quot;https://github.com/Haishi2016/sfcat&quot;&gt;https://github.com/Haishi2016/sfcat&lt;/a&gt;. Once you clone the repo and deploy the application to your Service Fabric cluster, you should be able to link it to the mock server (or other OSB API implementations) by modifying the &lt;strong&gt;appsettings.json&lt;/strong&gt; file under the &lt;strong&gt;CatalogService&lt;/strong&gt; project:&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;&quot;ActorSwitches&quot;: { &lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;OSBEntityCatalogActor&quot;: { &lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;OSBEndpoint&quot;: &quot;&amp;lt;OSB API endpoint&amp;gt;&quot;,&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;OSBUser&quot;: &quot;&amp;lt;user name&amp;gt;&quot;, &lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;OSBPassword&quot;: &quot;&amp;lt;password&amp;gt;&quot; &lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br clear=&quot;all&quot;&gt;}&lt;/font&gt;&lt;/p&gt;&lt;p&gt;Then, you should be able to use &lt;em&gt;sfcat&lt;/em&gt; to interact with the control plane following the instructions here: &lt;a title=&quot;https://github.com/Haishi2016/sfcat/blob/master/docs/sfcat.md&quot; href=&quot;https://github.com/Haishi2016/sfcat/blob/master/docs/sfcat.md&quot;&gt;https://github.com/Haishi2016/sfcat/blob/master/docs/sfcat.md&lt;/a&gt;.&lt;/p&gt;&lt;h1&gt;Declarative definitions&lt;/h1&gt;&lt;p&gt;Yet, there was still a problem. Because I couldn’t extend Service Fabric application model, I couldn’t provide a way to declaratively define the external service dependencies in application manifests. So, I came up with an alternative -- I created a &lt;strong&gt;BrokeredServiceActor &lt;/strong&gt;service, which supported declarative configurations of an external service in its configuration file (the actor also supported direct OSB API connection but that was optional):&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;&amp;lt;Section Name=&quot;BrokerConfiguration&quot;&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;ServiceType&quot; Value=&quot;2e2fc314-37b6-4587-8127-8f9ee8b33fea&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;ServicePlan&quot; Value=&quot;6ddf6b41-fb60-4b70-af99-8ecc4896b3cf&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;UseCSService&quot; Value=&quot;true&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;CSEndpoint&quot; Value=&quot;http://..:8088&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;BrokerPolicy&quot; Value=&quot;ProvisionAndBind&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- To directly connect to an OSB endpoint, set UseCSService to false –&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- Other values: BindOnly –&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;OSBEndpoint&quot; Value=&quot;http://104.45.224.242&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;OSBUser&quot; Value=&quot;haishi&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;OSBPassword&quot; Value=&quot;P$ssword!&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;lt;/Section&amp;gt;&lt;/font&gt;&lt;p&gt;This gave me declarative definition. However, there was still a problem. Service Fabric didn’t allow dynamically composing a service into another application. Fortunately, I thought about that a few years ago and created the &lt;a href=&quot;https://github.com/Azure/SFNuGet&quot;&gt;SFNuGet&lt;/a&gt; project, which allowed individual Service Fabric services to be packaged, shared, discovered and dynamically composed into other applications through NuGet mechanisms. &lt;/p&gt;&lt;h1&gt;Conclusion&lt;/h1&gt;&lt;p&gt; So, I had got all the pieces – a OSB mock server, a test suite to test it, a SF control plane prototype, a command-line tool to interact with it, a service wrapper actor to support declarative definition, and a mechanism to distribute the package. SFNuGet has been released for a long time; and OSB-check is in the good hands of the community. Unfortunately, SFCAT is half-baked, and I’ve been since dragged away to other projects, leaving SFCAT untouched for several months by now. I just recently got approval to make it a public repository. I wish someone out there would be interested and drag me to the project to make some new progresses on this. Tweet to @Haishi2010 or email to &lt;a href=&quot;mailto:hbai@microsoft.com&quot;&gt;hbai@microsoft.com&lt;/a&gt; if you were interested.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/2613521290284957296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2018/04/open-service-broker-api-and-service.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2613521290284957296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2613521290284957296'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2018/04/open-service-broker-api-and-service.html' title='Open Service Broker API and Service Fabric Service Catalog'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7-X1dEzn_CzO8om2nZ5aeg_HAw4XY9K9I6h0QLhgkRYOgtssvOzEbxUaHdsQMNOPObJR0dubsz6r33at7zaUNaLy7vTWvud-JoWdNlOHaOy8PI6vH0scdv45bwbcG3amBky5YsXwF1liq/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-6600312404162199724</id><published>2017-11-16T14:15:00.001-08:00</published><updated>2017-11-16T15:18:45.608-08:00</updated><title type='text'>Setting up High available MySQL deployments on Service Fabric using containers and Docker Compose</title><content type='html'>&lt;p&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/services/service-fabric/&quot;&gt;Microsoft Service Fabric&lt;/a&gt; is a&amp;nbsp; PaaS offering that provides not only container orchestration, but also Microservices programming models and paradigms for you to build your next-gen, cloud-native Microservices on cloud. &lt;/p&gt;&lt;p&gt;This blog article examines the new &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-docker-compose&quot;&gt;Docker Compose support&lt;/a&gt;, which allows you to take an existing &lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker Compose&lt;/a&gt; file and deploy it directly onto a Linux-based Service Fabric cluster. As for scenario, I chose to deploy high-available MySQL clusters. Specifically, I’ll deploy two high-available MySQL deployments, one using &lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/replication.html&quot;&gt;MySQL Replication&lt;/a&gt;, and another using &lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster.html&quot;&gt;MySQL Cluster&lt;/a&gt;. All source code is available here: &lt;a title=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler&quot; href=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler&quot;&gt;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’ll assume you’ve done the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You’ve created a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-get-started-azure-cluster&quot;&gt;Linux-based Service Fabric cluster&lt;/a&gt; on Azure.&lt;/li&gt;&lt;li&gt;Your cluster should have the Service Fabric DNS service enabled.&lt;/li&gt;&lt;li&gt;You’ve installed and configured &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-cli&quot;&gt;sfctl&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h1&gt;MySQL Replication&lt;/h1&gt;&lt;p&gt;MySQL Replication enables data to be copied from a master MySQL server to one or more slave MySQL servers. MySQL Replication doesn’t provide automatic failovers (please correct me if I’m wrong). When master fails, you can manually promote a slave to a new master. For a more detailed description, please see &lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/replication-solutions-switch.html&quot;&gt;this MySQL doc&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;For this scenario, I’ve created two new Docker images for both master (&lt;a href=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler/MySQL-Replication/Service-Fabric/master&quot;&gt;hbai/mysql-master-sf&lt;/a&gt;) and slave (&lt;a href=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler/MySQL-Replication/Service-Fabric/slave&quot;&gt;mysql-slave-sf&lt;/a&gt;). Both container images use custom scripts as entry points to configure replication before launching the MySQL services. The images also support a &lt;strong&gt;CRATE_PET_DB&lt;/strong&gt; flag. If the flag is set, the startup scripts automatically create a simple &lt;strong&gt;&lt;em&gt;pet_db&lt;/em&gt;&lt;/strong&gt; database for testing purposes.&lt;/p&gt;&lt;h2&gt;Master&lt;/h2&gt;&lt;p&gt;The master start-up script creates a &lt;strong&gt;MYSQL_REPLICA_USER&lt;/strong&gt; and sets up its privileges for replication. It then creates a &lt;strong&gt;&lt;em&gt;pet_db&lt;/em&gt;&lt;/strong&gt; database if the &lt;strong&gt;CREATE_PET_DB&lt;/strong&gt; flag is set:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;echo &quot;CREATE USER &#39;$MYSQL_REPLICA_USER&#39;@&#39;%&#39; IDENTIFIED BY &#39;$MYSQL_REPLICA_PASS&#39;; &quot; | &quot;${mysql[@]}&quot;&lt;br&gt;
echo &quot;GRANT REPLICATION SLAVE ON *.* TO &#39;$MYSQL_REPLICA_USER&#39;@&#39;%&#39;; &quot; | &quot;${mysql[@]}&quot;&lt;br&gt;
echo &quot;GRANT REPLICATION CLIENT ON *.* TO &#39;$MYSQL_REPLICA_USER&#39;@&#39;%&#39;; &quot; | &quot;${mysql[@]}&quot;&lt;br&gt;
echo &quot;FLUSH PRIVILEGES; &quot; | &quot;${mysql[@]}&quot;&lt;/p&gt;
&lt;p&gt;if [ &quot;$CREATE_PET_DB&quot; ]; then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo &quot;CREATE DATABASE pet_db; &quot; | &quot;${mysql[@]}&quot;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo &quot;USE pet_db; CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20), species VARCHAR(20), sex CHAR(1), birth DATE, death DATE); &quot; | &quot;${mysql[@]}&quot;&lt;br&gt;
fi&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Slave&lt;/h2&gt;&lt;p&gt;The slave start-up script is a bit more complex. After creating the &lt;strong&gt;&lt;em&gt;pet_db&lt;/em&gt;&lt;/strong&gt;, it enters a loop to wait for the master to get ready. When the master is ready, it continues to configure itself as the slave to the master: &lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;if [ &quot;$CREATE_PET_DB&quot; ]; then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo &quot;CREATE DATABASE pet_db; &quot; | &quot;${mysql[@]}&quot;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo &quot;USE pet_db; CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20), species VARCHAR(20), sex CHAR(1), birth DATE, death DATE); &quot; | &quot;${mysql[@]}&quot;&lt;br&gt;
fi&lt;/p&gt;
&lt;p&gt;for i in $(seq $((MYSQL_MASTER_WAIT_TIME + 1))); do&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ! mysql &quot;-u$MYSQL_REPLICA_USER&quot; &quot;-p$MYSQL_REPLICA_PASS&quot; &quot;-h$MYSQL_MASTER&quot; -e &#39;SELECT 1;&#39; | grep -q 1; then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo &amp;gt;&amp;amp;2 &quot;Waiting for $MYSQL_REPLICA_USER@$MYSQL_MASTER&quot;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sleep 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fi&lt;br&gt;
done&lt;/p&gt;
&lt;p&gt;MasterPosition=$(mysql &quot;-u$MYSQL_REPLICA_USER&quot; &quot;-p$MYSQL_REPLICA_PASS&quot; &quot;-h$MYSQL_MASTER&quot; -e &quot;SHOW MASTER STATUS \G&quot; | awk &#39;/Position/ {print $2}&#39;)&lt;br&gt;
MasterFile=$(mysql &quot;-u$MYSQL_REPLICA_USER&quot; &quot;-p$MYSQL_REPLICA_PASS&quot; &quot;-h$MYSQL_MASTER&quot; -e &quot;SHOW MASTER STATUS \G&quot; | awk &#39;/File/ {print $2}&#39;)&lt;/p&gt;
&lt;p&gt;echo &amp;gt;&amp;amp;2 &quot;Master File: $MasterFile&quot;&lt;br&gt;
echo &amp;gt;&amp;amp;2 &quot;Master Position: $MasterPosition&quot;&lt;/p&gt;
&lt;p&gt;echo &quot;CHANGE MASTER TO MASTER_HOST=&#39;$MYSQL_MASTER&#39;, MASTER_PORT=$MYSQL_MASTER_PORT, MASTER_USER=&#39;$MYSQL_REPLICA_USER&#39;, MASTER_PASSWORD=&#39;$MYSQL_REPLICA_PASS&#39;, MASTER_LOG_FILE=&#39;$MasterFile&#39;, MASTER_LOG_POS=$MasterPosition;&quot;&amp;nbsp; | &quot;${mysql[@]}&quot;&lt;br&gt;
echo &quot;START SLAVE;&quot;&amp;nbsp; | &quot;${mysql[@]}&quot;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Compose&lt;/h2&gt;&lt;p&gt;The Docker Compose file is straightforward. In your deployments, you can change your replica user and password as needed. &lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;version: &#39;3&#39;&lt;br&gt;
services:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; master:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; image: hbai/mysql-master-sf&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; restart: unless-stopped&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ports:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 3306:3306&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; environment:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_ROOT_PASSWORD=master_passw0rd&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_REPLICA_USER=replica&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_REPLICA_PASS=replica_Passw0rd&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - CREATE_PET_DB=true&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; slave:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; image: hbai/mysql-slave-sf&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; restart: unless-stopped&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ports:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 3307:3306&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; environment:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_ROOT_PASSWORD=slave_passw0rd&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_REPLICA_USER=replica&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_REPLICA_PASS=replica_Passw0rd&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_MASTER=master&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_MASTER_WAIT_TIME=10&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_MASTER_PORT=3306&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - CREATE_PET_DB=true&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Try it out&lt;/h2&gt;&lt;p&gt;To deploy the cluster, use the following command.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;sfctl compose create --name MySqlReplication --file-path &amp;lt;path to your docker-compose.yml&amp;gt;&lt;br&gt;
&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Once deployed, you can follow these steps to do a simple replication test:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;SSH into the Service Fabric cluster node where the master container is running. By default, you should be able to SSH into each node via port 3389-3393 for a 5-node cluster.&lt;/li&gt;&lt;li&gt;Attach to the master container (you can get your container name by &lt;em&gt;sudo docker ps&lt;/em&gt;)&lt;br clear=&quot;all&quot;&gt;sudo docker exec –it &amp;lt;your container name&amp;gt; /bin/bash &lt;/li&gt;&lt;li&gt;Connect to your master database and insert a new record to the &lt;strong&gt;&lt;em&gt;pet_db &lt;/em&gt;&lt;/strong&gt;(the following sample assumes you use the default &lt;strong&gt;&lt;em&gt;master_passw0rd&lt;/em&gt;&lt;/strong&gt; as the root password):&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;p&gt;mysql -uroot -pmaster_passw0rd&lt;br&gt;
use pet_db;&lt;br&gt;
INSERT INTO pet VALUES(&#39;Puffball&#39;, &#39;Diane&#39;,&#39;hamster&#39;, &#39;f&#39;, &#39;1999-03-30&#39;, NULL);&lt;/p&gt;&lt;li&gt;SSH into the Service Fabric cluster node where the slave container runs. &lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;li&gt;Attach to the slave container.&lt;/li&gt;&lt;li&gt;Connect to the slave and select records from the &lt;strong&gt;&lt;em&gt;pet_db&lt;/em&gt;&lt;/strong&gt;. You should see the record you just inserted above has been replicated over:&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;p&gt;mysql -uroot -pslave_passw0rd&lt;br&gt;
use pet_db;&lt;br&gt;
select * from pet;&lt;/p&gt;&lt;li&gt;Optionally, you also can use command &lt;strong&gt;SHOW SLAVE STATUS\G;&lt;/strong&gt; to examine how your replica is configured.&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The following screenshot shows the view on the slave:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE2TfeptdIDfe6BZaENkNyN1qtX_s9iHy7WHM2zc0eWjqfzKTsTgYbfPb4zne1yPA8GjvppYfbnrnYbTj9fKGGX71UsKNhOQB14-cTgsbmbxiuktkloGj80vJKuns5MPvLge_tpkYwYCXO/s1600-h/image%255B4%255D&quot;&gt;&lt;img width=&quot;423&quot; height=&quot;327&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDiOqDBxfmw0PJlRvMm1zNQ0FtBs_QxZaQTZM7ULhPZK4cXvIHwLFl99eoZe6fr0xnttDTH_Ku56IIqdRHPCmKF3ppA97RWJvf3JAP7j5EfNpdCV5Hk32nv5BRj00S9GGeCWcYoXPY5I6u/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1&gt;MySQL NDB Cluster&lt;/h1&gt;&lt;p&gt;A MySQL NDB Cluster is comprised of a Management server, a number of Data Nodes and a number of SQL Nodes. The Data Nodes form an in-memory clustered storage engine (called NDB), and the SQL Nodes form a stateless API layer for database clients. Please see &lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster-overview.html&quot;&gt;this MySQL doc&lt;/a&gt; for a more detailed description of MySQL NDB Cluster.&lt;/p&gt;&lt;p&gt;It’s much harder to set up a fully automated MySQL Cluster using Docker Compose. MySQL Cluster requires management node, data node and service node to be launched in specific order. Although you can set up launching dependencies among containers, Docker Compose doesn’t wait for the applications inside containers to become ready before it moves down the dependency chain. To work around this, I’ve created a (very) simple gatekeeper container that provides a Web API for callers to check in/check out tokens. As containers launch, they send signals to the gatekeeper to report their stats. Other containers who depend on the states will wait until the states reach desired goals.&lt;/p&gt;&lt;p&gt;For this scenario, I created four custom Docker images: management (&lt;a href=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler/MySQL-Cluster/management&quot;&gt;hbai/ha&lt;/a&gt;), ndb (&lt;a href=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler/MySQL-Cluster/ndb&quot;&gt;hbai/ndb&lt;/a&gt;), mysql (&lt;a href=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler/MySQL-Cluster/mysql&quot;&gt;hbai/mysql&lt;/a&gt;) and gatekeeper (&lt;a href=&quot;https://github.com/Haishi2016/Vault818/tree/master/Nuka-Cola-Bottler/MySQL-Cluster/gatekeeper&quot;&gt;hbai/gatekeeper&lt;/a&gt;). The first three images are all based on the official MySQL Cluster image (&lt;a href=&quot;https://hub.docker.com/r/mysql/mysql-cluster/&quot;&gt;mysql/mysql-cluster&lt;/a&gt;), with custom entry point script to coordinate container launches.&lt;/p&gt;&lt;h2&gt;Gatekeeper&lt;/h2&gt;&lt;p&gt;Gatekeeper is a prototype-quality Node.js server that handles simple PUT requests and GET requests. A client can add a new token by sending a PUT request to a key route; and a client can retrieve a token by GET from the key route. There are no authentications or conflict avoidances implemented in this prototype. However, because for each of the keys there will only be one writer (and multiple readers), the gatekeeper works reasonable well in this setting. &lt;/p&gt;&lt;h2&gt;Management Node&lt;/h2&gt;&lt;p&gt;The management node launches &lt;em&gt;ndb_mgmd&lt;/em&gt; service with a custom &lt;em&gt;config.ini&lt;/em&gt; file as shown below. Please note: 1) all nodes are identified by DNS names instead of IP addresses; 2) ndb nodes are assigned with specific &lt;em&gt;ServerPort&lt;/em&gt;. We need fixed ports so that we can define necessary port bindings on containers. &lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[ndbd default]&lt;p&gt;NoOfReplicas= 2&lt;p&gt;[ndb_mgmd]&lt;p&gt;Hostname= management&lt;p&gt;[ndbd]&lt;p&gt;HostName= ndb1&lt;p&gt;ServerPort= 50501&lt;p&gt;[ndbd]&lt;p&gt;HostName= ndb2&lt;p&gt;ServerPort= 50502&lt;p&gt;[mysqld]&lt;p&gt;HostName= mysql&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In the start-up script, you’ll see a workaround to configure the host name. This is temporary workaround because of a bug in Service Fabric that causes hostnames not injected into containers correctly. This should not be necessary when the bug is fixed.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;#workaround to set hostname&lt;br&gt;
sed -i &#39;s/localhost.localdomain/management/&#39; /etc/sysconfig/network&lt;br&gt;
HH=`cat /etc/hostname`&lt;br&gt;
tail -1 /etc/hosts | sed -e &quot;s/${HH}/management/&quot; &amp;gt;&amp;gt; /etc/hosts&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;When you use the above &lt;em&gt;ini&lt;/em&gt; file that uses DNS names instead of IP addresses, &lt;em&gt;ndb_mgmd&lt;/em&gt; will do a name resolution check upon start. This puts us in a difficult situation – on one hand the management node needs to wait for other containers (ndb nodes and mysql node) to come up so that the name resolutions will pass; on the other hand other nodes are waiting for the management to start. To deal with the situation, I used a two-phase wait: When a container is launched, it sets its state to &lt;strong&gt;LAUNCHED&lt;/strong&gt;. And when it finishes initialization, it sets its state to &lt;strong&gt;READY&lt;/strong&gt;. The management node waits for the &lt;strong&gt;LAUNCHED&lt;/strong&gt; states from other containers before it does name resolutions. And the other containers wait for the &lt;strong&gt;READY&lt;/strong&gt; sate of management node before they continue.&lt;/p&gt;&lt;p&gt;&lt;em&gt;ndb_mgmd&lt;/em&gt; is launched without the &lt;em&gt;--nodeamon &lt;/em&gt;flag. This is because we need to send the &lt;strong&gt;READY&lt;/strong&gt; state after we’ve launched the &lt;em&gt;ndg_mgmd &lt;/em&gt;service.To stop the container from exiting after sending the &lt;strong&gt;READY&lt;/strong&gt; state, an infinite loop is added at the end:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;while true; do sleep 1000; done&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Data Nodes&lt;/h2&gt;&lt;p&gt;The compose file defines two data nodes, ndb1 and ndb2. A data node is initiated with a custom &lt;strong&gt;&lt;em&gt;my.cnf&lt;/em&gt;&lt;/strong&gt; file that points to the management node:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[mysqld]&lt;br&gt;
ndbcluster&lt;br&gt;
ndb-connectstring=management&lt;br&gt;
user=mysql&lt;/p&gt;
&lt;p&gt;[mysql_cluster]&lt;br&gt;
ndb_connectstring=management&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The container image takes a NDBID parameter, which will be used as a postfix to the host name. Please note this id has no connection with &lt;em&gt;NodeId&lt;/em&gt; in MySQL Cluster settings. It’s just a postfix to the host name to distinguish different data node instances.&lt;/p&gt;&lt;h2&gt;MySQL Node&lt;/h2&gt;&lt;p&gt;The MySQL node waits for all other containers before it calls the default &lt;em&gt;entrypoint.sh&lt;/em&gt; from the base image to initialize the database, and then launches the &lt;em&gt;mysqld&lt;/em&gt; service.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;while [[ &quot;$MANAGEMENT&quot; != &quot;READY&quot; ]]&amp;nbsp; || [[ &quot;$NDB1&quot; != &quot;READY&quot; ]] || [[ &quot;$NDB2&quot; != &quot;READY&quot; ]]&lt;br&gt;
do&lt;br&gt;&amp;nbsp;&amp;nbsp; MANAGEMENT=`curl -sL &lt;a href=&quot;http://gatekeeper:8180/management`&quot;&gt;http://gatekeeper:8180/management`&lt;/a&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; NDB1=`curl -sL &lt;a href=&quot;http://gatekeeper:8180/ndb1`&quot;&gt;http://gatekeeper:8180/ndb1`&lt;/a&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; NDB2=`curl -sL &lt;a href=&quot;http://gatekeeper:8180/ndb2`&quot;&gt;http://gatekeeper:8180/ndb2`&lt;/a&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; sleep 1&lt;br&gt;&amp;nbsp;&amp;nbsp; echo &quot;Waiting: management:$MANAGEMENT; ndb1:$NDB1; ndb2:$NDB2&quot;&lt;br&gt;
done&lt;/p&gt;
&lt;p&gt;/entrypoint.sh mysqld&lt;br&gt;
mysqld&lt;br&gt;
&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Compose&lt;/h2&gt;&lt;p&gt;The compose file is a bit longer in this case. Please note how the port bindings on &lt;em&gt;ndb*&lt;/em&gt; nodes match with the &lt;em&gt;config.ini &lt;/em&gt;file above.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;version: &#39;3&#39;&lt;br&gt;
services:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; gatekeeper:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; image: hbai/gatekeeper&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hostname: gatekeeper&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ports:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 8180:8180&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; management:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; image: hbai/ha:30&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hostname: management&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; depends_on:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - gatekeeper&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ports:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 1186:1186&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ndb1:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; image: hbai/ndb:8&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hostname: ndb1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; depends_on:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - gatekeeper&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - management&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ports:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 2202:2202&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 50501:50501&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; environment:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - NDBID=1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ndb2:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; image: hbai/ndb:8&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hostname: ndb2&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; depends_on:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - gatekeeper&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - management&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ports:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 2202:2202&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 50502:50502&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; environment:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - NDBID=2&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; mysql:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; image: hbai/mysql:10&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hostname: mysql&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; depends_on:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - gatekeeper&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - management&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - ndb1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - ndb2&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; environment:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - MYSQL_RANDOM_ROOT_PASSWORD=true&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ports:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 3306:3306&lt;br&gt;
networks:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; default:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; external:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name: nat&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Try it out&lt;/h2&gt;&lt;p&gt;To deploy the cluster, use the following command.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;sfctl compose create --name MySqlCluster --file-path &amp;lt;path to your docker-compose.yml&amp;gt;&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Once deployed, you can follow these steps to do a simple test:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;SSH to the node where the &lt;em&gt;mysql&lt;/em&gt; container is running.&lt;/li&gt;&lt;li&gt;Get the auto-generated root password from the &lt;em&gt;mysql&lt;/em&gt; container log:&lt;br clear=&quot;all&quot;&gt;sudo docker logs &amp;lt;container name&amp;gt; 2&amp;gt;&amp;amp;1 | grep PASSWORD&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;li&gt;Attach a shell to the &lt;em&gt;mysql&lt;/em&gt; container.&lt;/li&gt;&lt;li&gt;Launch &lt;em&gt;ndb_mgm&lt;/em&gt; and observe the cluster status:&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;ndb_mgm&lt;br clear=&quot;all&quot;&gt;show&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;The above command should return something like the following screenshot. &lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKUfoIo7VKjvZdZNJIRgvY0Quj3hxK5AtkW4wdD-JYLjKZs6J9lVsGI52_jA7dP1K_fntmu6rn-Jbak5ptp-6Ty81o9gW4mKCB-AXOcsRRgmsTQytE17ATKBrPL5zv5tnIZmqn3fxCsMhy/s1600-h/image%255B8%255D&quot;&gt;&lt;img width=&quot;428&quot; height=&quot;212&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEjsdprGKssiNE3pXCoGFe6tfQC-k94rEjU50iDOokIbe4ZvrWvCbgp1m_I6_WxJMT8-ru6JSMnVNod9G5bhZFRph_ScamWxHPNQBssmA58pKDqOo9RDI-XbdYT8Vwl2cCoGys4mNpvsqh/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;li&gt;Exit &lt;em&gt;ndb_mgm&lt;/em&gt; and launch &lt;em&gt;mysql&lt;/em&gt;:&lt;br clear=&quot;all&quot;&gt; mysql -uroot -p&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;li&gt;Enter the temporary password to log in.&lt;/li&gt;&lt;li&gt;Update the root password:&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;p&gt;ALTER USER &#39;root&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;&amp;lt;new password&amp;gt;&#39;;&lt;br&gt;
&lt;/p&gt;&lt;li&gt;Create a new database and insert a record:&lt;/li&gt;&lt;p&gt;CREATE DATABASE pet_db;&lt;br&gt;
USE pet_db; CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20), species VARCHAR(20), sex CHAR(1), birth DATE, death DATE);&lt;br&gt;
INSERT INTO pet VALUES(&#39;Puffball&#39;, &#39;Diane&#39;,&#39;hamster&#39;, &#39;f&#39;, &#39;1999-03-30&#39;, NULL);&lt;/p&gt;&lt;li&gt;In Service Fabric Explorer, restart (deactivate and then activate) the node where &lt;em&gt;mysql&lt;/em&gt; container is running. This simulates a machine crash.&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;li&gt;Attach to the container again and launch &lt;em&gt;mysql&lt;/em&gt;. Select records from the &lt;em&gt;&lt;strong&gt;pet_db&lt;/strong&gt;&lt;/em&gt; and you should see the record is preserved:&lt;/li&gt;&lt;p&gt;mysql -uroot -p&amp;lt;your new password&amp;gt;&lt;br&gt;
use pet_db;&lt;br&gt;
select * from pet;&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;The above command should return the record:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6hHNq40mWTXjQrAqpYTGvf6OFukjLE5u_SvW8qEMDYCNy4HKaanTh9-D-XpucrtbMV1PFQSQEoEjD4p3sPBILwqF7aoNRwuJ8XrG7lYs0ySiDv2uxeYudifTjy6qn3Fau6Mvt-vACPtid/s1600-h/image%255B13%255D&quot;&gt;&lt;img width=&quot;434&quot; height=&quot;153&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBvhGGhaz-c6oX_Pg2_9b-tf0V_F5Acu-ijr_nZBT0GlG0261fr351CYcKooM_zkIpiANJtah76QlUKtnDAFgiKQqUXi5ea5kJoZc5_SERqzt23xDPqRbH0RKvF-MthpHmW6gkSFrpvWyr/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;/ol&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;h1&gt;Closing Thoughts&lt;/h1&gt;&lt;p&gt;Service Fabric allows you to take your existing containerized workloads and host it on a Service Fabric cluster. And once your workload is migrated to Service Fabric, you can start enjoying Service Fabric programming models and other programming paradigms enabled by Service Fabric to write your cloud-native Microservices applications.&lt;/p&gt;&lt;p&gt;This article shows you how to construct HA MySQL clusters on Service Fabric yourself. If you want to use MySQL on Azure but don’t want to take on any responsibility of managing the infrastructure yourself, you should consider the new &lt;a href=&quot;https://azure.microsoft.com/en-us/services/mysql/&quot;&gt;Azure Database for MySQL&lt;/a&gt; offering.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/6600312404162199724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2017/11/setting-up-high-available-mysql.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6600312404162199724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6600312404162199724'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2017/11/setting-up-high-available-mysql.html' title='Setting up High available MySQL deployments on Service Fabric using containers and Docker Compose'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDiOqDBxfmw0PJlRvMm1zNQ0FtBs_QxZaQTZM7ULhPZK4cXvIHwLFl99eoZe6fr0xnttDTH_Ku56IIqdRHPCmKF3ppA97RWJvf3JAP7j5EfNpdCV5Hk32nv5BRj00S9GGeCWcYoXPY5I6u/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-5899200857922155292</id><published>2017-08-07T15:10:00.001-07:00</published><updated>2017-08-07T15:10:40.754-07:00</updated><title type='text'>Autonomous State Reconciliation with Actors</title><content type='html'>&lt;p&gt;Desired State Configuration (DSC) is a proven technique for configuration management. It’s been widely used in configuration management systems such as Chef, Puppet and PowerShell DSC. Besides, many PaaS systems, such as OpenShift and Kubernetes, use a Resource+Controller pattern where a Controller observes an object store (such as etcd) and reconciles object current states with their desired states, as shown in the following diagram. In such settings, consumers interact with the Object Store through a simple CRUD API. And storage events are observed by registered Controllers. The Controllers react to these event and take necessary actions, which often bear the goal to reconcile object states with desired states.&amp;nbsp; &lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFZ67UPMRiDbvGzOvU4IwM7eXteSR1X_U8o1hnt5gltGU1A-XGuzYvo9eQ0968Q3l45C43ENoeZlPIUac_FNwkhfUKbZdpMyxplqFX4L8F9EFDSQHAHSw31EmOfoaHWNrtxc4mS-qjgWDP/s1600-h/image%255B7%255D&quot;&gt;&lt;img width=&quot;396&quot; height=&quot;154&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1uDH5GoGVRoJNjKA2sbpE1P_Tqaq1WfPgPi4nUa-BBLf1I0Pn7eBnmYvGxMlsPudI8Eb3I1MyxyzL_lO6ZcWANsroaegAzXViqwAS2hDaD70BPmKGhvM4V_dc6xC4nSP1r-Lr4knJtoW6/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;This blog proposes an alternative implementation of the pattern using stateful actors, as shown in the following diagram: &lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2StNal2OnP57cnh59xjmq_k5X3rojNXI4lNlCXJIFbwtfFB4d7_-Nv3tkv5qBMA8-RoZBjlRkJIF8mQ__Q8vzw9vbFVo6Qw3lFcO3WbS7XMKegd1GEpTff-sGSuXZVFRgHe4qh8YklCSn/s1600-h/image%255B11%255D&quot;&gt;&lt;img width=&quot;400&quot; height=&quot;190&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig7EVwQSvhNg-zpR3auYrBvo6mryBcgwqRNJiDoJNBIrDM-9gEmuHvtHJjQ6ExJdxO3bQUuYprZloWbYAPlYR0Q7g5fqcZtOErCgV85P1nw2YbeZfnvBUJc2R7f2GBU-osUhiovp1xP6h6/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;There are two major differences between the two implementations:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;There are no separate Controllers in the second implementation. Instead, an actor encapsulates its state and its&amp;nbsp; state reconciliation logic. This makes system configuration and extension easier – instead of adding a new object type and a new matching controller type, you can register a new actor type and the actor will take care of its state maintenance autonomously. &lt;/li&gt;&lt;li&gt;The Object Store in the second implementation is actually just a stateless gateway, as it doesn’t maintain any internal state. This helps to avoid the cases when the Object Store becoming the bottle neck (or a single point of failure) of the system. You can scale out the gateway easily. And because it works directly with individual actors, you don’t have a potential bottleneck at Controller layer, either.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For actor systems that don’t support actor catalogs, you may want to implement a catalog actor that corresponds to each actor type, as shown in the following diagram. The catalog actor maintains a catalog of associated actors, and provides cross-entity/actor functionalities such as searching and transforming actors. The catalog actors can be organized in different namespaces to provide scaling as well as (logical) tenant isolation.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL-aB6XgbmpQdo1pJ9mmzffVXqkZEvzTOAZkgRFbA9KE6eb2DOCwIQhRiIbpZyRU-vBFzgGIFge4p9nfrs1i1FUqdCXuKrbuwCT4lFxa2528JSC9Lx6oI6Z5FKD1wea7Jg3AYEEhzdB2IT/s1600-h/image%255B15%255D&quot;&gt;&lt;img width=&quot;401&quot; height=&quot;130&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnDDezGYlTo_83UG1voXZTclXrg1jOQQoOI_oL0Q7bQTGuund-wvefupALxEEXU5I85wFa1Md2lU_Z1J4wpxAsZfH9Evo3OWjV1EWbrvfYf4oDleE-l5iN-ZFVODWb4mLfNsYKg4SPVnIN/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;All the actors, except for the state reconciliation logics,&amp;nbsp; are quite similar. If your actor system supports polymorphism, you can create base catalog actor and base entity actor from which you can inherit common behaviors, such as the notion of current state and desired sate. Furthermore, if you feel having a separate actor type per entity is too heavy, you can use a generic implementation where state management logics are dynamically injected, driven by some sort of manifest:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWa3r3mlq_uhF0m0EtW-_JgD3YAC7wZPX6vzHcLxIMAgSKSNK9IozjpsBunYXUOjBUhZoNJERgDYguVd53w3Uug_u7OF5tIipA__0yK19IW1y5s3xSx534Q6oxyyCNxcEdu9bjVr8jzX5t/s1600-h/image%255B19%255D&quot;&gt;&lt;img width=&quot;405&quot; height=&quot;193&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0mt7qKk-egegA7ELlcqqMlr6t0Oz8HhoqRoc7WcJUN18FH0jF95UPO7b8kLfg6wIz6AzujSqAA8tT3VptqInEOamBQtjqJiGJY72vdAURlM9yIyycLJkf_gIf8GgzHPF2dwGD3kbb7I8Y/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The actor-implementation seems to have some architectural advantages: 1) There are no centralized components. The store gateway can be scaled out easily (as it’s stateless), and states are scattered into different actor instances. 2) Great parallelism. All actors execute on their own thread/process/machine, and you can rebalance actor instances easily based on system loads. 3) Great isolation. Each actor can decide on its own reconciliation intervals, and they don’t rely on a centralized messaging/event system to get events from an external store. Furthermore, An actor (controller) won’t be able to subscribe to events from other entities types (unless they are explicitly exposed/allowed). This avoids a rogue controller tapping into arbitrary entity event streams to make damages. 4) Better resiliency. Even if the store gateway completely fails, none of the states is missing. When the gateway comes back, it can resume service as normal by referencing to catalog service by namespaces. On the other hand, a single (and complete) actor failure has a much less blast radius than a data-store failure. &lt;/p&gt;&lt;p&gt;Before I conclude this article, I have to mention that I haven’t done a performance comparison between the two implementations. On the surface, having an actor instance per entity does present some overheads, but I believe the price is small and worth paying in most cases. &lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/5899200857922155292/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2017/08/autonomous-state-reconciliation-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/5899200857922155292'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/5899200857922155292'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2017/08/autonomous-state-reconciliation-with.html' title='Autonomous State Reconciliation with Actors'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1uDH5GoGVRoJNjKA2sbpE1P_Tqaq1WfPgPi4nUa-BBLf1I0Pn7eBnmYvGxMlsPudI8Eb3I1MyxyzL_lO6ZcWANsroaegAzXViqwAS2hDaD70BPmKGhvM4V_dc6xC4nSP1r-Lr4knJtoW6/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-821445117698297986</id><published>2017-07-17T18:46:00.001-07:00</published><updated>2017-07-19T11:07:29.528-07:00</updated><title type='text'>Cloud: A New Perspective</title><content type='html'>&lt;p&gt;it’s Sunday evening at 7, and I’m still at my desk. I just finished some preparation work for a conference, and now I’m brooding over the idea of writing this article before going home for dinner. I’ve been planning to write this for a long time, to explain my view on cloud. And now, I think it’s been delayed long enough. So be it, since I’ve already started typing, let’s see how far it goes…&lt;/p&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;The *-aaS myth&lt;/h2&gt;&lt;p&gt;I’ve spoken to many customers and developers who try to adopt cloud. Regardless of the project size and type, I often see people making a difficult decision at the beginning of the project: do I do a “lift-and-shift”, or start over and write a “cloud-native” app? Or, for people who are more familiar with cloud jargon – do I do IaaS or PaaS?&amp;nbsp; &lt;/p&gt;&lt;p&gt;Do I do IaaS or PaaS? This is the exact wrong question to ask first. It takes some work to peel the onion, so please bear with me. &lt;/p&gt;&lt;p&gt;The whole set of *-aaS acronyms, IaaS, PaaS and SaaS, offers a nice, layered view of a cloud platform. At the bottom you have the infrastructures; then on top of the infrastructure you have the platform to develop and host your services; and on top of that are managed services. This is indeed a powerful and precise model of a cloud platform. Maybe just because it’s so elegant and logical, people start to believe when you put your applications on cloud, you should fit your applications into one or several of these layers. &lt;/p&gt;&lt;p&gt;Should you?&lt;/p&gt;&lt;p&gt;Let’s go back to the basics. Let’s say you have an application, and you want to run it. What do you need? Some CPU, some memory, some disk space, and some networking, maybe. When you run the application locally (or, “on-premises”), you acquire and manage compute resources yourself – to buy a PC, to rent a server rack, and so on. The big promise of cloud is that cloud can offer such compute resources to you so that you are not bogged down with such nugatory tasks. How does cloud offer these resources to you?&amp;nbsp;&amp;nbsp; Ding-ding-ding! Through one of the *-aaS thingies, of course! The catch is, however, the ways to work with different *-aaS thingies are VERY different. The world of cloud is bright and shinny, but you have to carefully pick one of the *-aaS paths because all these paths lead to a LAYER of the cloud, not the ENTIRE cloud. You might be happy with the original lift-and-shift with IaaS but find out that you have to start over anyway because PaaS is such a different paradigm. You might have invested heavily in PaaS, and some cool kids come around, containerized your application and moved it over to cloud without even touching your code. How do you know if you were picking the right path? How do you know if you’d get the return you expected? How do you know if your choice will remain sound for future iterations of your project? &lt;/p&gt;&lt;p&gt;How do we resolve this situation? It’s actually fairly easy – we just need to think about the situation from a slightly different angle - If all you need is certain amount of resources, why would you care HOW the resources is given to you? Shouldn’t you be able to just say, “hey, Mr. cloud, can you give me some resources so I can run my stuff?” Does it really matter if these resources are packaged as a virtual machine, a container, a cluster of nodes, or a managed deployment slot? No, thank you. Give me some CPU and memory and I’m good.&lt;/p&gt;&lt;h2&gt;The CMR model&lt;/h2&gt;&lt;p&gt;Cloud is a huge pool of compute resources. On top of this pool swim many different workloads. That’s what cloud is. No IaaS, no PaaS or SaaS, it’s just a pool of resources in which your workloads can live and thrive. &lt;/p&gt;&lt;p&gt;This resource pool view makes logical sense, however it’s probably too coarse to be useful. Having been working for Microsoft for 5 years, I’ve started to like acronyms. So here’s my offer – a new model to describe cloud that I call “CMR”, or Control-Mesh-Resource. The model can be illustrated by the following diagram (hey, I’m not an artist – it’s much prettier in my mind…).&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq9INnXlyEKuCJcx_3gZ3v8cfxsh37uVYMk-6t-f6fpKB0E58RKXcOVZHBbn7XDyMQSQf6a_-m_rjLDpHm_n60s8kTnbW7smkUm3Q2b_umM-BTSR4byR21cfNgIk3k-cEp2rrizGtM3fsX/s1600-h/image7&quot;&gt;&lt;img width=&quot;445&quot; height=&quot;193&quot; title=&quot;image&quot; style=&quot;display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9egT2H1Lax_-BlJZq6BZiVNnxUQt5mSDn2-Lnp6vbpu9CkxSDPJHc4iEZZ_BsjKpFfcxNSWdIuaklLdFdkv_B3MZPlN4YAWm-3pNkMoZ3lv56ZHar1VQaviSwdE71jfNCyasjXgILr8J6/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;At the top of the model is the Control Plane. The control plane takes your workload and schedule it on to some sort of resources. It offers all the good things a cloud can possibly offer you – failover, dynamic scale, high-availability and such. You give your workload to it and it takes care everything for you. The Control Plane projects your workloads onto a Compute Plane, or a Resource pool. The resource pool can be made up by ANYTHING that can carry out compute tasks. As long as your workload’s requirement is satisfied, you don’t care. Your application exists on the resource pool if the form of a Service Mesh. A Service Mesh defines a connectivity scope, a security domain and a management boundary. I should point out that in CMR model, there are no fixed clusters. Service Meshes are completely mobile and can span across many different compute resource types, including “traditional” PaaS clusters.&lt;/p&gt;&lt;p&gt;The best way to visualize the CMR model is to think the Control Plane as a pane of colored spotlights that sweep across the Resource Plan and project Service Meshes on them – that’s the CMR model. &lt;/p&gt;&lt;p&gt;CMR is a unified view of IaaS and PaaS.&amp;nbsp; When you use IaaS, you are using some APIs to provision a virtual machine and then deploy your workload to it. When you use PaaS, you use a different set of API. CMR abstracts away API differences because at the end, you are marrying your workload with some compute resources.&amp;nbsp; &lt;/p&gt;&lt;p&gt;The *-aaS model comes from a cloud platform’s perspective and is resource-centric. CMR, on the other hand, comes from a customer’s perspective and is workload-centric. The *-aaS explains the structure of a cloud platform, but it’s lacking critical details on workloads themselves. CMR takes a workload-centric view. It concerns about how workload is projected into compute resources.&lt;/p&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;Examine the cloud through the CMR lens&lt;/h2&gt;&lt;p&gt;CMR model is quite powerful. It can be used to describe many cloud architectures. I’ll attempt to explain a few here. As I’m getting tired typing, I’ll not going to spend much time expanding on each of the topics. I think they are quite self-explanatory anyway. &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Serverless – CMR isn’t concerned about how the resources are acquired. It provides the broadest view on Serverless. Under the CMR model, Serverless isn’t bound to specific frameworks or implementations. Resources are simply acquired by a generic auction process. However, what’s more important here is CMR’s Service Mesh abstraction, which allows scattered, reactive components to be treated as a holistic application. This mesh concept is missing in many existing Serverless implementations, btw. &lt;/li&gt;&lt;li&gt;Hybrid – CMR isn’t concerned about where the resources come from. As long as a resource is offered by someone, it can participate in workload auctions. In other words, an auction process may involve resources from multiple cloud platforms as well as on-premises compute resources. The characteristics of the resources may impact their candidacy for specific workload, but the overall process is exactly the same for cloud, for on-premises, and for individual machines.&lt;/li&gt;&lt;li&gt;Cluster federation – CMR doesn’t define resource offers at certain granularity. A resource provider can offer up a portion of a CPU, a container, or even an entire compute cluster. For instance, if your application is described by Docker Compose and you want to have a global deployment, you can simply ask for two K8s clusters from different regions. The two clusters form a cross-region (but loosely-coupled) Service Mesh where your application instances run.&amp;nbsp; Or, if your application needs a virtual machine, you can choose a resource that offers you the required machine. &lt;/li&gt;&lt;li&gt;Cloud agnostic – CMR offers an abstraction at very high-level. This high-level abstraction hides the differences among different clouds, different scheduling and orchestration frameworks, and workload architectures. &lt;/li&gt;&lt;li&gt;New compute options – The CMR model can be used to describe scheduling workloads on all types of compute resources – physical machines, virtual machines, containers, GPUs, ASICs, dedicated instances, shared instances, spot instances, IoT devices, … Again, as long as someone is making an offer, the resource can participate in workload distribution.&lt;/li&gt;&lt;li&gt;Intelligent edge – CMR doesn’t define a fixed Compute Plane. New compute resources can come in at anytime. And when existing resources disappears, CMR moves the “color spotlight” and projects the workload somewhere else. This model works for Intelligence Edge especially well, because it allows different device types with different technical stacks, different capacities and availabilities to be unified into a dynamic Compute Plane. Furthermore, it allows seamless transitions between cloud and edge so that applications have flexibility to allocate compute resources where it makes most sense. For example, an application may deploy ML inference in cloud at the beginning, and then moves the inference engine to capable devices for lower latency and massive scale. At the same time, the less capable devices can still take advantage of cloud-hosted inferences. &lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Key Principles of a CMR System&lt;/h2&gt;&lt;p&gt;CMR model is an abstract model. However, it’s not hard to implement CMR for real. As I’m just talking about theoretical model here, I shall not continue with suggestions on design and implementation details. However, I envision the following as some of the key principles in such a system.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Open Compute Plane. The Compute Plane shall be made up by an open (abiding to required security constraints, of course) registration system, with which agents representing resources can register and start to make offers.&lt;/li&gt;&lt;li&gt;Workload auction. The workload shall be projected to the Compute Plane through an request-offer matching auction system. &lt;/li&gt;&lt;li&gt;Service Mesh isolation. Service Mesh is the key construct to provide a multi-tenant Compute Plane. It provides connectivity, security domain and management boundary. It can also be extended to support tracing, auditing and smart routing etc.&lt;/li&gt;&lt;li&gt;Desired State management. Service Mesh state is monitored and reconciled with desired state regularly. This is the key to provide QoS offerings to applications.&lt;/li&gt;&lt;li&gt;Minimum API. The beauty of CMR model resides in simplicity. The Control Plan shall offer a minimum set of API – probably less that 10 methods. And there really aren’t many: resource registration, state report and reconciliation, workload distribution are probably the only three mandatory things.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Closing Thoughts&lt;/h2&gt;&lt;p&gt;As you can probably see, this article is posted on Monday instead of Sunday. I paused for a day because I was debating if CMR should be presented as a new model for the entire cloud, or just a unified view of PaaS. Two of me discussed for a long time. And eventually I decided since the model covers all compute on cloud, it’s from a different perspective, and it goes beyond what existing PaaS systems commonly offer,&amp;nbsp; I’ll call it a new cloud model.&lt;/p&gt;&lt;p&gt;Of course, this model didn’t come from vacuum. It’s heavily influenced by existing models, systems and designs. And the fact that it abstracts away infrastructural details does make it look like PaaS – but that’s just a byproduct of abstraction. Again, the key difference between *-aaS and CMR is that CMR takes a customer’s perspective and focuses on how workload is scheduled, not how resources are offered. I hope you find this model interesting. And I do believe an implementation of the CMR model can provide a powerful, unified compute fabric that spans across the globe. And I do think I need to leave for dinner now.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/821445117698297986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2017/07/cloud-new-perspective.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/821445117698297986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/821445117698297986'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2017/07/cloud-new-perspective.html' title='Cloud: A New Perspective'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9egT2H1Lax_-BlJZq6BZiVNnxUQt5mSDn2-Lnp6vbpu9CkxSDPJHc4iEZZ_BsjKpFfcxNSWdIuaklLdFdkv_B3MZPlN4YAWm-3pNkMoZ3lv56ZHar1VQaviSwdE71jfNCyasjXgILr8J6/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-4074625083866939067</id><published>2017-03-07T17:29:00.001-08:00</published><updated>2017-12-14T00:37:36.678-08:00</updated><title type='text'>Setting up a highly-available Minecraft server using Containers on a Linux-based Service Fabric cluster</title><content type='html'>&lt;p&gt;In the last &lt;a href=&quot;http://www.gdconf.com/&quot;&gt;GDC&lt;/a&gt;, Mark Fussell (&lt;a href=&quot;https://twitter.com/mfussell&quot;&gt;@mfussell&lt;/a&gt;) and I presented a demo of setting up a high-available Minecraft server using &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/&quot;&gt;Microsoft Service Fabric&lt;/a&gt;. The idea of the demo is to showcase how Service Fabric is used as a Docker container orchestration engine and brings certain Quality of Service (QoS) improvements to legacy applications without any modifications. I’ve got a few requests to reveal the details on how the demo was set up, so here it goes – everything you need to set up a similar demo yourself.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Disclaimer: This article uses an upcoming feature that supports container data volumes. The feature is coming soon, and I’ll update the article when this feature is released (which means if you see this the feature hasn’t been released yet). Please see product &lt;/strong&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-containers-overview#containers-and-service-fabric-roadmap&quot;&gt;&lt;strong&gt;roadmap&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; for more details.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Updates (9/7/2017): Container data volumes support is now in production&lt;/strong&gt; (&lt;a title=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-containers-volume-logging-drivers&quot; href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-containers-volume-logging-drivers&quot;&gt;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-containers-volume-logging-drivers&lt;/a&gt;).&lt;/p&gt; &lt;h1&gt;Background&lt;/h1&gt; &lt;p&gt;Many legacy applications are designed as single-tenant and single-site. For such applications, it’s a common practice to deploy a dedicated server (or server cluster) for a specific customer (tenant). In case disaster recovery (DR) is required, a passive secondary is used, with data from active site constantly replicated at certain interval, which is driven by the system’s Recovery Point Objective (RPO). When a disaster happens, the secondary is switched to primary and the data replication direction is reversed. In some systems, the secondary is never promoted. Instead, it provides read-only accesses to the data until the primary is fully restored. &lt;/p&gt; &lt;p&gt;This active-passive configuration improves system availability. However, it requires a spare set of compute resources that don’t actively take any customer traffic in most cases. An active-active configuration will be more efficient because both server instances can serve customer traffic. Unfortunately, active-active redundancy is often hard to achieve, because these legacy applications often assume they are the sole writers of the data. When multiple active instances try to operate on the same data, various of conflicts will occur and the results are often catastrophic. &lt;/p&gt; &lt;p&gt;A KPI of disaster recovery is the Recovery Time Objective (RTO), which refers to the duration for an application to be fully functional after a restore/failover request. In an ideal active-passive configuration, the RTO is minimum because the DR process is a simple address flip. Here’s the idea - if we could launch a new set of servers in a very short time, we can still meet our RTO without having to keep a spare set of servers around. This is where containers can help. One of the key characteristics of container is agility. A new container can be launched very quickly comparing to the time needed to boot up a full server. When a server fails, we simply launch a new one. And because the launching time is almost negligible, we can achieve high availability without a redundant, passive server. &lt;/p&gt; &lt;p&gt;Of course, data still needs to be replicated. And there are container data management solutions like &lt;a href=&quot;https://github.com/ClusterHQ/flocker&quot;&gt;Flocker&lt;/a&gt;. In this demo, however, I used a simpler approach – I used &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/storage/storage-dotnet-how-to-use-files&quot;&gt;Azure File storage&lt;/a&gt; as a shared storage across multiple server instances. Because there was only a single server instance (per partition – see the last section) running at any given time, I didn’t need to worry about conflicting writes from multiple writers. And because Azure File storage itself was replicated for availability, I could assume the state would remain available under Microsoft’s SLA.&lt;/p&gt; &lt;p&gt;Diagram 1 shows how the demo is configured: A containerized Minecraft server is deployed to a 5-node Service Fabric cluster. A shared Azure File volume is attached to each of the nodes, and is passed in using the &lt;em&gt;–v&lt;/em&gt; switch when the Minecraft server is launched. Azure Load Balancer is configured with a load-balancing rule over port 25565 as well as a probe that queries the same port on each node. &lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUFovE9axC0LBFEli-sg81bLCBaPnPbJ0B4uIZfWS0EnWFvo70du5DUX1pV71VmqAHhC7-YS3PNSDygDP5zHrFteAe4bInPXVxVOzBuwfQ0h7p3h2Jt44ZXV0in_4prmugE0vvDdo604xg/s1600-h/image%25255B4%25255D.png&quot;&gt;&lt;img width=&quot;699&quot; height=&quot;310&quot; title=&quot;image&quot; style=&quot;border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUKf_R0NcQl1nRMlAZrtOukiGGzRO8RFQguPf35bTaa7w2aBe_PSCHn4wEqFxMRdgT4jnlxJV07swVXP-Szv-b57fsnARByaKRAglHqmhrK59HLbR8ATtgb9P9waKFfp7oqjhla_C8GvoV/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Diagram 1: A single Minecraft server on a Service Fabric cluster&lt;/p&gt; &lt;p&gt;&lt;br&gt;&lt;/p&gt; &lt;p&gt;When the hosting node fails, two things will happen. First, Service Fabric will locate a healthy node and launch a new Minecraft server (container) instance. Second, Azure Load Balancer will detect the failed node (as well as the new node who’s started responding to probe) and route traffic accordingly.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgpC8JRg1FfB8sL-yKeW-G9l5K8Lb1zOE0pWp09lQHlRIQKQSh5qdbLPu32tRZ2KcGlw82xMZ3VT5niIG5fCMVQjKC6WahWWq4dxhWe6UKdx3Mr5rxEMAoQzSZMtbOJGmYWvw7hrenM41W/s1600-h/image%25255B8%25255D.png&quot;&gt;&lt;img width=&quot;701&quot; height=&quot;312&quot; title=&quot;image&quot; style=&quot;border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHm7pkVfuFsTTejgB3DPgIbJjrqhpGBF7MFXmeb4BTl8Gll0WZXToROEDzsfGQFgLv4GOlqjoaD2wpZx02GhIuzGjXgFEtX-yCgii12Lj06Jz41Lr9FSI-7Jj-9Xm9c981xhzXDe7LRux1/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Diagram 2: Minecraft server failover&lt;/p&gt; &lt;p&gt;&lt;br&gt;&lt;/p&gt; &lt;h1&gt;Implementation Steps&lt;/h1&gt; &lt;p&gt;This demo uses Azure File storage, Docker containers, Azure Service Fabric &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-linux-overview&quot;&gt;Linux support&lt;/a&gt;, Minecraft client, Linux shell commands as well as Azure Management Portal. To keep the article short, I’d assume you are familiar with most of the concepts.&lt;/p&gt; &lt;p&gt;&lt;br&gt;&lt;/p&gt; &lt;h2&gt;1. Provision a Linux-based Service Fabric cluster&lt;/h2&gt; &lt;p&gt;Provisioning an unsecured Service Fabric cluster is easy. You can simply use &lt;a href=&quot;https://portal.azure.com/&quot;&gt;Azure Management Portal&lt;/a&gt; to create an unsecured cluster using the creation wizard. Creating a secured cluster takes a few more steps – you’ll need to set up a Key Vault and upload a cluster certificate. Please refer to &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-creation-via-portal&quot;&gt;this article&lt;/a&gt; if you need more details. Obviously, an unsecured cluster is not meant for production use – event for dev-test it shouldn’t be long-lived if it’s on the public cloud.&lt;/p&gt; &lt;p&gt;When you create the cluster, make sure to select the Linux OS:&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhofQsdZKXBEtRf4j068wQ8fZIrJ1Y8Da0BvCj3NpEP2UkiIuG1DOjelaKyIiQq1JH7kGi45UDVemVtIh6SWXLAofXWd8kCtttZcpWgPBGllxqv9DJosn2Qg8QSyuAH5DIEObLUgUjNHuZy/s1600-h/image%25255B12%25255D.png&quot;&gt;&lt;img width=&quot;411&quot; height=&quot;397&quot; title=&quot;image&quot; style=&quot;border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXsd1IKk8texv2pv6RnPG-TJQyFHTgtmyfFyJG0fFW9sKLJgay-hE5p3oApg8DTdzkD29mdQasGVPnPV9Lx9NunQleZ90wJ7nt7frSSwAQniUDLxrCHMQYr-gKH42bsme80ugmIqfn6was/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;And allow Service Fabric to manage runtime versions:&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDukyySOGzyJpoyqwfIiDRXMP7AYsvJMKh7NQj99kX_7OP1RRdjnNjQ9cMdc5tD1psTRDZJWn49MOOyHcE4ul8cIA_iaO_G4bU4Zu2lrsgKoQ4QFiasuUlY3-OE8mnQNIkh_xeUti8oXNE/s1600-h/image%25255B16%25255D.png&quot;&gt;&lt;img width=&quot;414&quot; height=&quot;330&quot; title=&quot;image&quot; style=&quot;border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyUPrv1g-mfNDPaX9FNJn1Lg-lmH5VyR6E-bQsQfz9D7s05bTxy0HS3I1fosfNILH2NOL4qB7qeIdp9RkWl2baapGk-TFarZ65hbxhIP1jBipuDt7bpBZuiyTbt1cupEL5PUnOnyyqcgaC/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h2&gt;2. Provision an Azure File share&lt;/h2&gt; &lt;p&gt;Creating an Azure File share is also easy. In Azure Management Portal, navigate to the Storage account you want to use, click on the &lt;strong&gt;Files&lt;/strong&gt; tile on the overview blade, and then click on the &lt;strong&gt;+ File share&lt;/strong&gt; button to create a new file share. Once the file share is created, click on the share, and then click on the &lt;strong&gt;Connect &lt;/strong&gt;button. The &lt;strong&gt;Connect&lt;/strong&gt; blade displays the command to mount the share onto a Linux machine (or a Windows machine):&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWEZ_J6pvUCrMxK3YP5N0U0F9vQX_eQP0EqxHLYAQe8pG45Cy04yLG9nyQhxaCLokOpqLOJiOm-cWC6wsdltdOHMcc7_GA65V2c_bn887NS235NlM-Ul31w5cRYI2eH2Zz2WGM5dIo71eD/s1600-h/image%25255B21%25255D.png&quot;&gt;&lt;img width=&quot;953&quot; height=&quot;463&quot; title=&quot;image&quot; style=&quot;border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQqblrrwQOQl8uhyphenhyphenJ9H1Ja1_XHEanF3fbu5fEmEC6MT-NO5WpPlV9yYrzGcNhrkGSs9q5xrlQCeNQjbelx5-xkOWcGy2JRlaMW9Zt0YpNdFAZIUua7dGbUiKEbHCuTeG5NXM5ImrBiKpLA/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Now, you need to mount the share onto each of the Service Fabric cluster nodes. I’m sure this part can be automated. But here I’m going to show you the manual steps. For each of the nodes:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;SSH into the node. By default, there should be a inbound NAT rule defined that allows you to SSH into individual Service Fabric node, with port 3389 mapped to the first node, 3390 mapped to the second node and so on.  &lt;li&gt;On the node, issue the following command:&lt;br clear=&quot;all&quot;&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;sudo apt-get install cifs-utils&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;sudo mkdir /mnt/minecraftworld&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;sudo mount -t cifs //[your storage account name].file.core.windows.net/[your Azure File share name] /mnt/minecraftworld -o vers=3.0,username=…&lt;br clear=&quot;all&quot;&gt;&lt;/font&gt;&lt;br&gt;The first command is to make sure you have required software package installed. It’s probably unnecessary but doesn’t hurt to check. The second command creates a mounting point and the last command mounts the Azure File share (copy the exact command from the &lt;strong&gt;Connect&lt;/strong&gt; blade above).&lt;/p&gt; &lt;li&gt;To automatically mount the share when the machine restarts, edit the &lt;strong&gt;/etc/fstab&lt;/strong&gt; file and insert the following line at the end of the file:&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;//[your storage account name].file.core.windows.net/[your Azure File share name] /mnt/minecraftworld cifs vers=3.0,username=…&lt;/font&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;3. Creating the Minecraft application&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;The easiest way to create the demo application is to use Yeoman as instructed &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-deploy-container-linux&quot;&gt;here&lt;/a&gt;. When entering &lt;strong&gt;Image Name&lt;/strong&gt;, enter &lt;strong&gt;itzg/minecraft-server&lt;/strong&gt;, which is a Minecraft serve images on Docker Hub.  &lt;li&gt;Once the application is created, modify the generate service manifest to include a few environment variables. The &lt;strong&gt;EULA&lt;/strong&gt; variable is to auto-accept EULA. And the &lt;strong&gt;MODE&lt;/strong&gt; variable is to switch the game to creative mode (so that you don’t need to worry about surviving).&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;&amp;lt;CodePackage Name=&quot;Code&quot; Version=&quot;1.0.0&quot;&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;EntryPoint&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;ContainerHost&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;ImageName&amp;gt;itzg/minecraft-server&amp;lt;/ImageName&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Commands&amp;gt;&amp;lt;/Commands&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/ContainerHost&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/EntryPoint&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;EnvironmentVariables&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;&amp;lt;EnvironmentVariable Name=&quot;EULA&quot; Value=&quot;TRUE&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;EnvironmentVariable Name=&quot;MODE&quot; Value=&quot;creative&quot; /&amp;gt;&lt;br&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/EnvironmentVariables&amp;gt;&lt;br&gt;&amp;lt;/CodePackage&amp;gt;&lt;br clear=&quot;all&quot;&gt;&lt;/font&gt;&lt;font face=&quot;Courier New&quot;&gt;&amp;lt;Resources&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Endpoints&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Endpoint Name=&quot;MinecraftEndpoint&quot; Port=&quot;25565&quot; Protocol=&quot;tcp&quot; /&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/Endpoints&amp;gt;&lt;br clear=&quot;all&quot;&gt;&amp;lt;/Resources&amp;gt;&lt;/font&gt;&lt;br&gt; &lt;li&gt;Then, modify the application manifest to map the &lt;strong&gt;/mnt/minecraftword&lt;/strong&gt; folder to &lt;strong&gt;/data&lt;/strong&gt; volume in container:&lt;br clear=&quot;all&quot;&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot;&gt;&amp;lt;ServiceManifestImport&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;ServiceManifestRef ServiceManifestName=&quot;MinecraftServicePkg&quot; ServiceManifestVersion=&quot;1.0.0&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Policies&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;ContainerHostPolicies CodePackageRef=&quot;Code&quot;&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;PortBinding ContainerPort=&quot;25565&quot; EndpointRef=&quot;MinecraftEndpoint&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;&amp;lt;Volume Source=&quot;/mnt/minecraftworld&quot; Destination=&quot;/data&quot; /&amp;gt;&lt;/strong&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/ContainerHostPolicies&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/Policies&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/ServiceManifestImport&amp;gt;&lt;br clear=&quot;all&quot;&gt;&lt;/font&gt;&lt;/p&gt; &lt;li&gt;Now, you can use the &lt;strong&gt;install.sh&lt;/strong&gt; script to deploy the application (after you use azure CLI command: &lt;em&gt;azure servicefabric cluster connect –connection-endpoint [your cluster URL]&lt;/em&gt; to connect to your cluster).&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;4. Demo&lt;/h2&gt; &lt;p&gt;Before demo, it’s highly recommended to SSH into each node and use &lt;strong&gt;sudo docker pull itzg/minecraft-server&lt;/strong&gt; to cache the Docker image on the node’s local disk. This will make failovers much faster. Also before demo, connect to the Minecraft server and create a structure in the world so that you can show how state is persisted during failovers. For example, this is what I created for GDC:&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3h-WzpWJHOnsQpzpji6M4ya_ogMAH1zs5DbSAPtgX_tyWzEsUTHcnjX3eBvYidqSp_t6TdaOW8Ac_7xr8CFBg5JKKw1vuAYd171LzD9N3H_lMdPfCIJoSxvy0JZZYtSSWCyD0dAQVAD8A/s1600-h/image%25255B25%25255D.png&quot;&gt;&lt;img width=&quot;848&quot; height=&quot;479&quot; title=&quot;image&quot; style=&quot;border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg75XYmONxg3rDdcdpC7ebBPa4RAuTnPoIGtFYGLj9cnKdiQRCjFy-Nk428C74NFlFt6WqBmE-B9tRVX2qgKM7QdNKsuju3Q9U9nBKMYWqywWfLTQfOfhxe07UxbfGkFXptgUG7-jxWlFWK/?imgmax=800&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Now, the demo steps:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Launch Minecraft client. On title screen, click on the &lt;strong&gt;Multiplayer&lt;/strong&gt; button.  &lt;li&gt;On the multiplayer screen, click on the &lt;strong&gt;Direct Connect&lt;/strong&gt; button.  &lt;li&gt;In the &lt;strong&gt;Server Address&lt;/strong&gt; text box, enter your Minecraft server URL, which should be [your service fabric cluster name]. For example, my environment was &lt;strong&gt;&lt;em&gt;sf-linuxdemo.southcentralus.cloudapp.azure.com&lt;/em&gt;&lt;/strong&gt;. By default, port 25565 is used.  &lt;li&gt;As the game is playing, open Service Fabric explorer, identify the node where the container is running, and restart the node. This simulates a node failure.  &lt;li&gt;Observe the container moved to a new node as the game is still playing. Eventually, the connection will break because the client isn’t retrying for broken connections (it’s not expecting the server to be HA!). However, you can reconnect and the game should continue with all your states saved.&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;4. Scaling&lt;/h2&gt; &lt;p&gt;Running only one instance on a 5-node cluster seems wasteful. To scale out to multiple instances, do the following:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Partition your storage. You can either attach multiple Azure File volumes to each node, or create sub folders for each of your Minecraft server instances.  &lt;li&gt;Modify your Azure Load Balancer settings to have addition load balancing rules and probes on port 25566, 25567, 25568 and so on (or use a port range).  &lt;li&gt;Create multiple copies of your Service Fabric application package. Each copy is configured with different port, application version and attached volume (or folder).  &lt;li&gt;Now you can deploy multiple application versions, each configured for a separate world file location, to the same cluster.&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;5. Closing thoughts&lt;/h2&gt; &lt;p&gt;This demo shows the idea of a “Tenant Manager” pattern, which treats a legacy server as a deployable unit and deploys customer-specific instances (tenants) to a shared infrastructure. Without modifying the existing code, the pattern provides basics QoS characteristics such as HA and scaling.&amp;nbsp; The demo doesn’t provide a Tenant Manager implementation, but I imagine writing such a manager isn’t hard. At last, using Azure File as a shared volume worked well enough in this case because Minecraft server does occasional data syncs from memory to disk. The solution won’t work well if your application relies on high performance disk reads and writes. &lt;/p&gt; &lt;p&gt;That’s all there is to setup the interesting demo. Thank you for reading.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/4074625083866939067/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2017/03/setting-up-highly-available-minecraft.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/4074625083866939067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/4074625083866939067'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2017/03/setting-up-highly-available-minecraft.html' title='Setting up a highly-available Minecraft server using Containers on a Linux-based Service Fabric cluster'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUKf_R0NcQl1nRMlAZrtOukiGGzRO8RFQguPf35bTaa7w2aBe_PSCHn4wEqFxMRdgT4jnlxJV07swVXP-Szv-b57fsnARByaKRAglHqmhrK59HLbR8ATtgb9P9waKFfp7oqjhla_C8GvoV/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-218855297901513650</id><published>2017-01-24T21:08:00.001-08:00</published><updated>2017-01-29T16:50:24.659-08:00</updated><title type='text'>A World without Applications</title><content type='html'>&lt;p&gt;At the end of my &lt;a href=&quot;http://haishibai.blogspot.com/2017/01/understanding-microservices-right-way.html&quot;&gt;previous article&lt;/a&gt; I mentioned the idea of application-less world. Indeed, it sounds like a very strange idea. To avoid the risk of being considered insane, I’m writing this article to further explain the idea. I hope the rational behind the idea would render me a logical person. To do this, I’m going to present my thinking step by step, with some assertions that I believe are safe to make, moving towards some statements that are still subject to validations. Regardless, I hope this article can entertain your mind somehow. &lt;/p&gt; &lt;p&gt;So, here it goes:&lt;/p&gt; &lt;h2&gt;Problems with Applications&lt;/h2&gt; &lt;p&gt;Whenever we use a computer – PC, phone, tablet, ATM, and what have you – we always interact with some sort of applications. This has become so natural to us that we rarely think about it – or should we? If you put some thinking into the situation, I believe it’s not hard for you to realize that what we need is the functionalities behind these applications, not the applications themselves. In other words, application is an delivery mechanism, and the way we are engaging with applications nowadays presents several problems.&lt;/p&gt; &lt;p&gt;Problem #1: Before we want to do anything via an application, we have to find the application that can do what we will expect it to do. Unfortunately, there are no guaranteed ways for us to do that. Let’s conduct a simple experiment: Let’s say you want to remove an unexpected stranger from one of your family vacation photos. What do you do? That’s easy! Just do a search online or in an application store, you’ll likely to find a number of options. Which one is better? How about the ones with highest ratings? Hmm… it’s not FREE. So you narrow the scope down to free applications only and find some other ones. Now, which one to choose? You need to take a leap of faith. You have no idea if the application will do exactly what you want. And if it doesn’t? That’s your bad – you made the choice. Can you force the application do what you want to do? Well, the best you can do is to file a complaint and hope for an upgrade. Or, you have to repeat the process and pick a different application. I don’t know about you, but I feel that’s quite annoying.&lt;/p&gt; &lt;p&gt;Problem #2: To get an application, you have to pay. And I’m talking just about money. Even if the application is free, you have to spend energy and time to download, install, and possibly configure the application. And then, you have to learn how to use it. If you think about it, this sucks. It’s like to receive a package, you have to hire a mailman, whose whole job is to deliver this single package. And what’s worse is that after delivering the package, he stays around! Can’t an application automatically come to us when we need it, do the thing and then disappear?&amp;nbsp; Well, hold that thought and allow me to rant on. On the other hand, for application makers, the downloads, the installations, and the learning curves are all obstacles to adoption. How do we eliminate all those? I know someone will say “services”&amp;nbsp; - I’ll come back to that later.&lt;/p&gt; &lt;p&gt;Problem #3: To make applications competitive, application makers keep building up new functionalities into the applications. To use another analogy – this is like when all you want is a simple bottle opener, but all you can find are Swiss knives. This is bad to application users because they are paying much more (money or resource) than what they actually need. This is also bad to application developers because they have to maintain applications with ever-increasing complexity.&amp;nbsp; &lt;/p&gt; &lt;p&gt;Problem #4: What if you want do something complex that can’t be carried out by any existing applications? You’ll have to use multiple applications and somehow figure out a way to make everything work together. You may have to repeatedly enter same data, you may have to convert data formats between application (by using another application?), and you have to figure out a workflow to use the applications in the correct order… Can’t we make this easier?&lt;/p&gt; &lt;p&gt;All above problems should be quite obvious, which makes me wonder why such fundamental concept is so flawed.&amp;nbsp; Well, I’m not going into that discussion because I’m not a historian or philosopher. Instead, allow me to continue with the concept of “services”. &lt;/p&gt; &lt;p&gt;Services are believed to solve many of the above problems – I believe some of you still remember the “thin-client” and “browser-server architecture” hypes. However, if you look closer, services don’t solve problem 1, 3, or 4.&amp;nbsp; The only problem they can help to resolve is problem 2 – but is that so? True, you don’t have to download or install services, but there is another barrier – subscription. With the exception of a few world-class free services such as Bing and Google, you are almost always required to register, often with fees or forced disclosure of personal information, before you can consume a service. How many online accounts do you have? Let me count mine – XBOX Live, Twitter, Facebook, Google, LinkedIn, GitHub, Expedia, IEEE, Unity, Minecraft, utility, phone bill, bank… the list goes on and on. How many times did you have to sign in to different services? How many times did you lose your passwords? How many times did your relatives forget their passwords and asked for your help?&lt;/p&gt; &lt;h2&gt;Problems with Isolated Ecosystems&lt;/h2&gt; &lt;p&gt;When you buy a smart phone, you are not just buying a phone, you are joining a tribe. Each tribe gives you an application ecosystem within which you can purchase applications. There are quite large application ecosystems out there. Chances are, you will find what you need all the time – there are even applications dedicated to teaching you how to tie shoelaces. However, such ecosystems are rare – there are only a handful in the entire world. What if&amp;nbsp; - I said “IF” - Apple closes its door someday? If you were relying on your phone everyday, basically you need to learn a new way of living.&amp;nbsp; I switched from Windows Phone to Android a while ago (not because I didn’t like Windows Phone, but to get the VR goggle perk at the moment). I’m still adjusting myself and keep missing the wonderful email and calendar functions I got from Windows Phone. I, as a consumer, should not be bound to a specific ecosystem.&lt;/p&gt; &lt;p&gt;Within in these ecosystems are millions of applications. Or, to put it more precisely, millions of SILOED applications. They all work for you, but they don’t work together – please revisit problem #4 if you were not sure what I mean. All these ecosystems show a long-tail distribution in application usage and profitability. There are many applications that are terrific at certain tasks, but they are rarely used (or even discovered) because they are too specific. What get used are a bunch of generalists while true specialists remain in hibernation. So, as a user, when you have a unique request, the chances of that getting fulfilled is near to none. Why? &lt;u&gt;Because NO applications understand what you want to do. They only know what they CAN do.&lt;/u&gt; The sad thing is, though you have access to millions of applications, you have nobody to explain what you really need.&lt;/p&gt; &lt;p&gt;Last but not least, let’s don’t forget accessing to such ecosystems is a luxury privilege that not everybody has. Not everyone has smart phones. Not everyone has computers.&lt;/p&gt; &lt;h2&gt;A World without Applications&lt;/h2&gt; &lt;p&gt;In a world without application, when I need something, I simply talk to a guy I trust – let’s call this guy “Scotty”. I tell Scotty what I want and he will see it happen. Scotty is intelligent. I can explain in great details exactly what I want. I don’t need to learn how to work with Scotty – I simply talk to him in my own language, state what I want, and move on with my life. Once Scotty takes care the business, he calls back and tells me “All is good, boss!”&lt;/p&gt; &lt;p&gt;Scotty is well connected. If he finds something he can’t do, he will find some specialists to help him out. In some cases, he even organize a little bidding process to find the most efficient specialist to carry out a task. And because Scotty is so well-connected, he often makes deals and gets discounts because he brings business to them. For some difficult tasks, I’ll give Scotty an award, and he will settle with the specialists he has involved. &lt;/p&gt; &lt;p&gt;Scotty and the specialists form a new type of ecosystem. The main difference between this new ecosystem and the isolated application ecosystems is that this new ecosystem is organic. The specialists in this ecosystem are small and specific. They are also intelligent: They can discover each other, can negotiate deals, can collaborate. This ecosystem is dynamic, but you don’t need to navigate through it at all – you have Scotty. Just ask Scotty to do it for you. &lt;/p&gt; &lt;p&gt;As the technology evolves, I believe we’ve got all necessary ingredients to make this a reality. Go to wake Scotty up, he’s been waiting.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/218855297901513650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2017/01/a-world-without-applications.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/218855297901513650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/218855297901513650'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2017/01/a-world-without-applications.html' title='A World without Applications'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-2591535482131183892</id><published>2017-01-23T17:26:00.001-08:00</published><updated>2017-01-23T17:26:12.164-08:00</updated><title type='text'>Understanding Microservices: the right way</title><content type='html'>&lt;p&gt;What is Microservices? &lt;/p&gt; &lt;p&gt;Microservices is often explained as an architecture style where an application is decomposed into independent services.&amp;nbsp; The idea sounds intuitive, yet it often leads to confusion. How is Microservices different from SOA? How is it different from J2EE or DCOM? Or, even, how is it different from OOP? Just last week I saw an article saying “do you have a class, make it a Microservices!” That’s … Well, I don’t even know how to comment on it. However, I’m SURE the author who wrote that is not doing so himself, if he’s sane. &lt;/p&gt; &lt;p&gt;I believe most of the confusion comes from the fact that people are trying to understand Microservices as an architecture style. However, when examined from the pure architectural perspective, Microservices doesn’t offer much (or even anything) new. For example, both separation of concerns and loose coupling have been proven practices for decades. Then, why do we need this new “Microservices” term? To fully understand Microservices, we have to take a different perspective: &lt;/p&gt; &lt;p&gt;&lt;strong&gt;The core value proposition of Microservices resides in how you operate your application, not how you design your application.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Once you view Microservices from the right perspective, things will become perfectly clear. &lt;/p&gt; &lt;h2&gt;Why do people talk about Microservices NOW?&lt;/h2&gt; &lt;p&gt;Decomposing applications into components is not a new idea. What makes Microservices significant is the context where it lives in – the cloud. Cloud is a radically different environment from your local datacenters. To achieve higher Quality of Services (QoS), a set of different approaches need to be adopted. In turn, these approaches demand your applications to have certain characteristics. And eventually, to deliver these characteristics, you need to design your applications in certain ways. So, Microservices architecture is rooted from the needs of operating applications on cloud. Only when you fully understand how applications are operated on cloud can you fully understand Microservices.&lt;/p&gt; &lt;h3&gt;Availability&lt;/h3&gt; &lt;p&gt;System availability can be calculated by the formula: Availability = MTBF/MTBF+MTTR, where MTBF means “mean time between failure”, and MTTR means “mean time to restore”. Intuitively, if MTBF is big enough, which means the sever rarely fails, availability goes up. On the other hand, if MTTR is small enough, it’s impact on availability becomes less significant. To improve system availability, local datacenters and cloud use two different strategies: local datacenters focus on prolonging MTBF, while cloud focuses on reducing MTTR. Traditional datacenters have fewer machines, with each machine assigned to a particular role. When a server fails, finding a replacement could be hard. In other words, MTTR in local datacenters is usually quite long. This is why local datacenters try to prolong MTBF as much as possible so that the effect of long MTTR can be reduced. Cloud is a totally different story. Cloud has many commodity servers that fail more often. However, when a server fails, it’s fairly easy for a cloud platform to find another healthy server. In other words, although MTBF on cloud is shorter, MTTR is so low that it can be neglected – at least this is the case in theory.&lt;/p&gt; &lt;p&gt;Finding another server is just a small portion of MTTR. The time to migrate an application (as well as its data) to a new server is heavily affected by how the application is designed and implemented. Intuitively, the smaller the application, the easier it is to migrate it. However, many applications are big and complex by nature. How to deal with these applications? There are no better ways but to split them into smaller pieces. In this way, when a portion of the application fails, we need to migrate only the failed parts. &lt;u&gt;This operation strategy on cloud decides that a cloud application should be decomposed into smaller parts. &lt;/u&gt;&lt;/p&gt; &lt;h3&gt;Scalability&lt;/h3&gt; &lt;p&gt;Local datacenters &lt;u&gt;scale up&lt;/u&gt; applications by placing applications on more powerful machines. This is again because of the difficulties in acquiring new servers. In local datacenters, adding compute resources (memory/disk/CPU) to an existing server is much easier and quicker (when possible) than buying and installing new servers. On the other hand, cloud &lt;u&gt;scales out&lt;/u&gt; applications by splitting workload to multiple application instances – adding compute resources to a server for a specific customer is obviously too much of a management burden for a cloud vendor.&lt;/p&gt; &lt;p&gt;However, not all applications are easily scalable. Especially, many applications assume they are holding the global truth (or global state) of the entire system. When multiple instances of such applications work together, the global truth is split, causing all kinds of conflicts and confusions. There are two ways to deal with this – to eliminate the global state, or to concentrate the global state to one or a few components so it’s more manageable. Both approaches call for isolation.&amp;nbsp; The parts who hold these states are the stateful part of your application. And the rest of the application becomes stateless, which means they operate without relying on any local states. Scaling the stateless part is easy – just throw multiple instances behind a load balancer and you are done. Scaling the stateful part is harder. Usually you need to apply some partition scheme for scale, and some replication strategy for high availability. &lt;u&gt;The different nature of application parts decides that application parts should be able to operate independently.&lt;/u&gt; &lt;/p&gt; &lt;h2&gt;Microservices is all about mobilizing workloads&lt;/h2&gt; &lt;p&gt;So, to efficiently operate your applications on cloud, you need to split your applications into smaller parts; these parts should be isolated; and they should be able to operate independently. So, the notion of Microservices is really driven by how cloud operates applications, not how you design your application. The rise of containers provides a solid testament to this fact. Containers don’t demand you to change your application architecture. Instead, they help you to &lt;u&gt;mobilize&lt;/u&gt; your applications so that&amp;nbsp; the applications can be better hosted on cloud.&amp;nbsp; So, at the end, &lt;u&gt;Microservices is all about mobilizing your workloads&lt;/u&gt;. The mobilized workloads should be light-weight, self-sufficient and isolated so that they can be moved around without friction. And only when your workloads are fully mobilized can your applications take the full advantage of cloud.&lt;/p&gt; &lt;h2&gt;Additional thoughts&lt;/h2&gt; &lt;p&gt;Imagine how cloud would look like in the future – there will be no concept of machines or clusters. The whole datacenter, or even the entire cloud, is just a humongous pool of resources where your mobilized services can roam freely. And if I dare to dream further – what if these services can intelligently discover and collaborate with each other? Then, the concept of applications fades away as well. The cloud itself becomes an organic, intelligent entity that offers infinite functionalities to all users. Wouldn’t it be wonderful?&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/2591535482131183892/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2017/01/understanding-microservices-right-way.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2591535482131183892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2591535482131183892'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2017/01/understanding-microservices-right-way.html' title='Understanding Microservices: the right way'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-5345157349135404023</id><published>2016-11-24T17:54:00.001-08:00</published><updated>2017-03-08T10:08:31.993-08:00</updated><title type='text'>Tutorial: Creating a Kubernetes cluster on Microsoft Azure Container Service (from Windows client)</title><content type='html'>&lt;p&gt;This tutorial captures all the steps needed to provision and manage a new Kubernetes cluster on Microsoft Azure Container Service. It collects all steps in a single document so that you don’t need to jump through several links to get everything ready. To start, all you need is an Azure subscription.&lt;/p&gt; &lt;h2&gt;Install Azure CLI 2.0&lt;/h2&gt; &lt;p&gt;Azure CLI 2.0 (in preview when this article is written) is the next generation of Azure cross-platform command line experience. It’s built using Python. To keep my above promise true, I’m going to cover the steps of installing Python, Pip, and then Azure CLI on Windows. If you are using Linux distributions, Python and Pip are likely to be installed already.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Go to python.org and download &lt;a title=&quot;Python 2.7.12&quot; href=&quot;https://www.python.org/ftp/python/2.7.12/python-2.7.12.msi&quot;&gt;Python 2.7.12&lt;/a&gt;.  &lt;li&gt;Launch the downloaded msi and install Python. Accept all default settings except that on the &lt;strong&gt;Customize Python&lt;/strong&gt; screen, you should enable the &lt;strong&gt;Add python.exe to Path&lt;/strong&gt; option.  &lt;li&gt;Once Python (and Pip) is installed, launch a new Command Prompt window and use command: &lt;br clear=&quot;all&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&lt;br clear=&quot;all&quot;&gt;pip install –-user azure-cli&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;to install Azure CLI.  &lt;li&gt;Edit your environment variable and add &lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;%APPDATA%\Python\Scripts&lt;/font&gt; to your &lt;strong&gt;PATH&lt;/strong&gt; variable. Some documents say you should add the %APPDATA%\Python\&lt;font color=&quot;#c0504d&quot;&gt;PythonXY&lt;/font&gt;\Scripts folder instead (where XY is the Python version number, such as “27”). However this doesn’t seem to be correct with the current release.  &lt;li&gt;Now, launch a new Command Prompt window, and you should be able to use “&lt;strong&gt;az&lt;/strong&gt;” command from any folder.&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Create an Azure AD Service Principal &lt;/h2&gt; &lt;p&gt;In order to talk to Azure APIs to provision Azure resources, Kubernetes uses an Azure AD Service Principal – you can think of Service Principal as an account for a service/application instead for a human user. The following steps show you how to create a Service Principal using Azure CLI.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;In a Command Prompt window, use: &lt;br clear=&quot;all&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&lt;br clear=&quot;all&quot;&gt;az login &lt;br clear=&quot;all&quot;&gt;&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;to login to your Azure subscription. The command prompts you to open a browser, navigate to the designated address, and paste in the one-time code. Once you paste in the code, click on the &lt;strong&gt;Continue&lt;/strong&gt; button on the page to continue with the login process. Once the log-in process completes, you can continue with your &lt;strong&gt;az&lt;/strong&gt; commands in the command window.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2eU_tVqbX-ZYNmjMEUGbYFY1CIxkxFUkOobuM36EznAvwC4KcXkNdu7vVmu3qVvIQBvsIOn2L1_zl3M7i1xv9PjI5bPyT7QHrcXlSKp3qJVo0jnGYZFgCqWupXuDFrRuOJyLVFXgbQVnI/s1600-h/image%25255B4%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiewlurPYksApKSbIfokTekZrt6wEs0-IZ5uVBC1rBIKo6PpJIOjs8oA3Kdt7kftIF2DEtfF2a-qIMl3gVYSPTVd5kYmhLvUMbol3UG3EM45kemMK2kaBw6T0cRFDwYt04Rto6Vcm5k7rja/?imgmax=800&quot; width=&quot;812&quot; height=&quot;38&quot;&gt;&lt;/a&gt;  &lt;li&gt;Use command &lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;az account set –-subscription=&lt;em&gt;&amp;lt;your subscription id or name&amp;gt;&lt;/em&gt;&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;to select the Azure subscription you want to use.  &lt;li&gt;Use command&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;az ad sp create-for-rbac –-role=”Contributor” –-scopes=”/subscriptions/&amp;lt;your subscription id&amp;gt;”&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;to create a new Service Principal. If needed, you can use &lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;az account show&lt;/font&gt; to get your subscription id. The above command generates a JSON output like this:  &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;{&lt;br&gt;&amp;nbsp; &quot;appId&quot;: &quot;0f622c84-…&quot;,&lt;br&gt;&amp;nbsp; &quot;name&quot;: &quot;&lt;/font&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;http://azure-cli-…&quot;&lt;/font&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;,&lt;br&gt;&amp;nbsp; &quot;password&quot;: &quot;9290d942-…&quot;,&lt;br&gt;&amp;nbsp; &quot;tenant&quot;: &quot;72f988bf-…&quot;&lt;br&gt;}&lt;/font&gt;&lt;/p&gt; &lt;li&gt;save the results for later use.&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Create SSH Keys (on Windows)&lt;/h2&gt; &lt;p&gt;To create a Kubernetes cluster, you need a key pair. This has been covered at many places, but a promise is a promise. Since I’ve promised to cover all steps, here it goes:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Install &lt;strong&gt;Git Bash&lt;/strong&gt; – go to &lt;a href=&quot;https://git-scm.com/downloads&quot;&gt;https://git-scm.com/downloads&lt;/a&gt;, click on the “Downloads for Windows” button and install the bash shell. This is an easy way to get &lt;strong&gt;openssl&lt;/strong&gt;, which we’ll need to use to generate key pairs.  &lt;li&gt;Launch &lt;strong&gt;Git Bash&lt;/strong&gt;, and use the following command to generate a new key:&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;openssl req –x509 –nodes –days 365 –newkey rsa:2048 –keyout myPrivateKey.key –out myCert.pem&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;The above command will ask you a series of questions such as country name, organization etc. Fill in the info and continue to create the key.  &lt;li&gt;Convert the key to rsa format and constraint access to the private key file:&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;openssl rsa –in ./myPrivateKey.key –out myPrivateKey_rsa&lt;br clear=&quot;all&quot;&gt;chmod 0600 myPrivateKey_rsa&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt; &lt;li&gt;Download and launch &lt;strong&gt;PuttyGen&lt;/strong&gt;: &lt;a href=&quot;http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html&quot;&gt;&lt;a href=&quot;http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html&quot;&gt;http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html&lt;/a&gt; (no install is necessary).  &lt;li&gt;In &lt;strong&gt;PuttyGen&lt;/strong&gt;, select the &lt;strong&gt;File-&amp;gt;Load a Private Key&lt;/strong&gt; menu. Select the &lt;strong&gt;myPrivateKey_rsa&lt;/strong&gt; file you just created. When prompted, click on the &lt;strong&gt;OK&lt;/strong&gt; button.  &lt;li&gt;Copy down the text in the &lt;strong&gt;Key&lt;/strong&gt; field at the top of the window – this is the SSH public key you’ll paste into Azure Portal later.  &lt;li&gt;Click on the &lt;strong&gt;Save private key&lt;/strong&gt; button and save the private key as a &lt;strong&gt;&lt;em&gt;.PPK&lt;/em&gt;&lt;/strong&gt; file. This file will be provided to Putty if you were going to use Putty as the SSH client.&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Provision a Kubernetes Cluster&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;Login to Azure portal. Click on the&lt;strong&gt; Plus icon&lt;/strong&gt; at the upper-left corner of screen. Then on the &lt;strong&gt;New&lt;/strong&gt; blade, search for “&lt;strong&gt;Azure Container Service&lt;/strong&gt;”.  &lt;li&gt;In search results, click on the &lt;strong&gt;Azure Container Service&lt;/strong&gt; entry to launch the provision wizard. On the wizard screen, click on the &lt;strong&gt;Create&lt;/strong&gt; button to continue.  &lt;li&gt;In the &lt;strong&gt;Basics&lt;/strong&gt; section, enter a user name, paste in the public key you got from step 6 above, pick subscription, resource group and location, and then click on the &lt;strong&gt;OK&lt;/strong&gt; button to continue.  &lt;li&gt;In the &lt;strong&gt;Framework configuration&lt;/strong&gt; section, make sure &lt;strong&gt;Kubernetes&lt;/strong&gt; is selected:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyzhq4ko-5tfCxhJmut55pkdRZXil2Yh9kfDIC8rRR6hGoeOuLmcITN2pE6qbR87gu0KbRZwSkRpCQTDDGLPvFAp72XrJw-z64_IuZUvtyXyMp57sMek_CZfkwuwVp1WyYQSaeJrxCBFlV/s1600-h/image%25255B8%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSe-Ul_83Y9N0eiByW0u4ApzwquiVNsPVe1SUMZ06mfWXxJsglQUJYvAiouCE4vJOn71E3xzKmiEQ9nU0CV6hDSIkdIqpJgYxr3Q4xXWMS6-zBYhdGMjjat0-ZaPPljAuqc0ZtrRzhRrOc/?imgmax=800&quot; width=&quot;584&quot; height=&quot;229&quot;&gt;&lt;/a&gt;  &lt;li&gt;One you make the selection, you’ll be prompted to enter &lt;strong&gt;Service Principal Client ID&lt;/strong&gt; and &lt;strong&gt;Service Principal Client Secret&lt;/strong&gt;. For Service Principal Client ID, paste in the “&lt;strong&gt;name&lt;/strong&gt;” field from the Service Principal output JSON file; for Principal Client Secret, paste in the value of the “&lt;strong&gt;password&lt;/strong&gt;” field.  &lt;li&gt;In the &lt;strong&gt;Azure Container services settings&lt;/strong&gt; section, select the number and size of your agent nodes, and provide a DNS prefix.  &lt;li&gt;Follow the wizard to finish the provisioning process.&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Install and configure kubectl&lt;/h2&gt; &lt;p&gt;&lt;font color=&quot;#ff0000&quot;&gt;Supposedly, you can use Azure CLI to install and configure&lt;strong&gt; kubectl&lt;/strong&gt; on your local Windows machine. However,&amp;nbsp; due to a bug (&lt;/font&gt;&lt;a title=&quot;https://github.com/Azure/azure-cli/issues/1350&quot; href=&quot;https://github.com/Azure/azure-cli/issues/1350&quot;&gt;&lt;font color=&quot;#ff0000&quot;&gt;https://github.com/Azure/azure-cli/issues/1350&lt;/font&gt;&lt;/a&gt;&lt;font color=&quot;#ff0000&quot;&gt;) this isn’t working yet. I’ll update the article once this bug is fixed.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;font color=&quot;#000000&quot;&gt;UPDATE 3/8/2017: This bug is now marked as fixed. I haven’t verified it myself, though.&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;You can also attempt to install and configure kubectl manually, however that’s out of the scope of this article.&lt;/p&gt; &lt;h2&gt;&lt;/h2&gt; &lt;h2&gt;SSH into a master node to use kubectl&lt;/h2&gt; &lt;p&gt;Instead of using a local client, you can also SSH into a master node on the cluster and use &lt;strong&gt;kubectl&lt;/strong&gt; there. The master nodes are put behind an Azure Load Balancer, which has an associated public IP and a DNS name.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Get the master FQDN from the overview page of your cluster:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh9VwWqnHtRQGF9vdFMbfeM3E-U5Nv9JC4lge1VtppXBT5ej-keOv7-HzhdF6yGAge1fHj1HDZS48bv1HEnsdti-oVTTTG_UsYYSCyr580BiBSuPplJDstwN4VXAAWGDs4DpcTbS4H25hM/s1600-h/image%25255B12%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFUr0U9IbMFsXIJYkAenBS3HSfEUry2A8lecGoOUNwkHidTJbEJKhqDhvP56tlDUmoQ4iwXdtq92zf2_kAmpD2TGrbyY_DISNVAbJYB3mYVNeh0V5VZu1wuse_w5bOYJnvqcQg_Lw6BgjT/?imgmax=800&quot; width=&quot;630&quot; height=&quot;145&quot;&gt;&lt;/a&gt;  &lt;li&gt;Copy your private SSH key to your %HOMEPATH%\.ssh folder:&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;copy &amp;lt;path to the myPrivateKey_rsa&amp;gt; %HOMEPATH%\.ssh\id_rsa&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt; &lt;li&gt;Use a SSH client such as Git Bash to connect to the cluster: &lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;&lt;strong&gt;ssh &amp;lt;&lt;em&gt;your user name&amp;gt;&lt;/em&gt;@&lt;em&gt;&amp;lt;Master FQDN above&amp;gt;&lt;/em&gt;&lt;/strong&gt;&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;When prompted to add the address to trusted host list, type in &lt;strong&gt;yes&lt;/strong&gt; or &lt;strong&gt;y&lt;/strong&gt; and press &lt;em&gt;&lt;strong&gt;[Enter].&lt;/strong&gt;&lt;/em&gt;  &lt;li&gt;Now you can use kubectl to manage your cluster:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTaNhM3vtsZe-dqyJdVyU3mOtUHKvz85upI1VYe4oGtzycRtF5BUxNM0SR-kUCgNyCeIDZW6a8t2e9jmbD2wjV3RtQIC6eW1T3qoaTa9H2gWd4GnOOHyPontFKKr_GgrVs3_YY86s1dvjr/s1600-h/image%25255B16%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjncJjY4MB7nmKVW3JeTePGfK4qB7EbYT8vDIeRaoD1ql6_1LkojdwVZAOfWAvKDCzrbPfn1YVhjoCSDrMmJzytoAAyka0XsY3tlMSfUb4WTGZHhnXuvmJfFZLqCLQjgPfaU5VEvTMIC_EQ/?imgmax=800&quot; width=&quot;648&quot; height=&quot;93&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;(This article was written on a Thanksgiving afternoon as I waited for my wife and daughter to join me for dinner, so it was a bit rushed. I apologizes for possible errors)&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/5345157349135404023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/11/tutorial-creating-kubernetes-cluster-on.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/5345157349135404023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/5345157349135404023'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/11/tutorial-creating-kubernetes-cluster-on.html' title='Tutorial: Creating a Kubernetes cluster on Microsoft Azure Container Service (from Windows client)'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiewlurPYksApKSbIfokTekZrt6wEs0-IZ5uVBC1rBIKo6PpJIOjs8oA3Kdt7kftIF2DEtfF2a-qIMl3gVYSPTVd5kYmhLvUMbol3UG3EM45kemMK2kaBw6T0cRFDwYt04Rto6Vcm5k7rja/s72-c?imgmax=800" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-4902031678605421617</id><published>2016-11-14T18:39:00.001-08:00</published><updated>2016-11-14T19:34:48.386-08:00</updated><title type='text'>Tutorial: Adding UI to a Service Fabric Web API service</title><content type='html'>&lt;p&gt;This tutorial shows you the steps to add a UI layer on top of a &lt;strong&gt;Service Fabric Web API&lt;/strong&gt; service. The goal of this article is to bring in a HTML + JavaScript UI layer on top of the Web API service with minimum changes to the existing project. This method works the best when you want to provide a simple configuration/monitor UI for your Web API. If you want to build a full ASP.NET MVC application, you should consider the &lt;strong&gt;ASP.NET Core (Preview)&lt;/strong&gt; service type.&lt;/p&gt; &lt;h2&gt;Bringing in static HTML pages&lt;/h2&gt; &lt;p&gt;As a starting point, let’s bring in some static HTML pages to the Web API service.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create a new Service Fabric application with a &lt;strong&gt;Stateless Web API&lt;/strong&gt; service.&lt;/li&gt; &lt;li&gt;Under the service project, create a new &lt;strong&gt;wwwroot&lt;/strong&gt; folder and a simple &lt;strong&gt;index.html&lt;/strong&gt; page under it.&lt;/li&gt; &lt;li&gt;Change the property of index.html to set its &lt;strong&gt;Copy to Output Directory&lt;/strong&gt; property to&amp;nbsp; &lt;strong&gt;Copy Always&lt;/strong&gt; (with Build Action as Content).&lt;/li&gt; &lt;li&gt;Add a reference to the &lt;strong&gt;Microsoft.Owin.FileSystems&lt;/strong&gt; NuGet package and the &lt;strong&gt;Microsoft.Owin.StaticFiles&lt;/strong&gt; NuGet package.&lt;/li&gt; &lt;li&gt;Modify the &lt;strong&gt;startup.cs&lt;/strong&gt; file to add routings for static files:&lt;/li&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;var fileSystem = new PhysicalFileSystem(@&quot;.\wwwroot&quot;);&lt;br&gt;var options = new FileServerOptions&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; EnableDefaultFiles = true,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; FileSystem = fileSystem,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RequestPath = PathString.Empty&lt;br&gt;};&lt;br&gt;options.DefaultFilesOptions.DefaultFileNames = new[] { &quot;index.html&quot; };&lt;br&gt;options.StaticFileOptions.FileSystem = fileSystem;&lt;br&gt;options.StaticFileOptions.ServeUnknownFileTypes = true;&lt;br&gt;options.EnableDirectoryBrowsing = true;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;config.MapHttpAttributeRoutes();&lt;br&gt;appBuilder.UseFileServer(options);&lt;/font&gt;&lt;/p&gt; &lt;li&gt;Build and deploy the application. You should be able to access the above index.html page by navigating to the root folder (such as &lt;a href=&quot;http://localhost:&amp;lt;port&amp;gt;/&quot;&gt;http://localhost:&amp;lt;port&amp;gt;/&lt;/a&gt;)&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Adding jQuery, Angular, and Bootstrap etc.&lt;/h2&gt; &lt;p&gt;Now, you can continue to bring in JavaScript libraries such as jQuery and Angular to build up your UI. &lt;/p&gt; &lt;ol&gt; &lt;li&gt;Modify the &lt;strong&gt;index.html&lt;/strong&gt; file:&lt;/li&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&amp;lt;html lang=&quot;en&quot; xmlns=&quot;&lt;/font&gt;&lt;a href=&quot;http://www.w3.org/1999/xhtml&amp;quot;&quot;&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;http://www.w3.org/1999/xhtml&quot;&lt;/font&gt;&lt;/a&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&amp;gt;&lt;br&gt;&amp;lt;head&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script src=&quot;//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script src=&quot;//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script src=&quot;//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-route.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script src=&quot;//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-resource.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script src=&quot;//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.13.2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script src=&quot;//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br&gt;&amp;lt;/head&amp;gt;&lt;br&gt;&amp;lt;body&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div ng-app=&quot;testApp&quot;&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div ng-view&amp;gt;&amp;lt;/div&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;&lt;br&gt;&amp;lt;/body&amp;gt;&lt;br&gt;&amp;lt;/html&amp;gt;&lt;/font&gt;&lt;/p&gt; &lt;li&gt;Add an &lt;strong&gt;app.js&lt;/strong&gt; file to the &lt;strong&gt;&lt;em&gt;wwwroot&lt;/em&gt;&lt;/strong&gt; folder:&lt;/li&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;var testApp = angular.module(&quot;testApp&quot;, [&#39;ngRoute&#39;]);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;testApp.config([&#39;$routeProvider&#39;, function ($routeProvider) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $routeProvider.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; when(&#39;/ngtest&#39;, {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; templateUrl: &#39;/views/testPage1.html&#39;, controller: &#39;TestController&#39;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; });&lt;br&gt;}]);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;testApp.controller(&#39;TestController&#39;, function ($scope) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $scope.message = &quot;This is a message from the test controller&quot;;&lt;br&gt;});&lt;/font&gt;&lt;/p&gt; &lt;li&gt;Add a new &lt;strong&gt;Views&lt;/strong&gt; folder to the &lt;strong&gt;&lt;em&gt;wwwroot&lt;/em&gt;&lt;/strong&gt; folder. Create a &lt;strong&gt;testPage1.html&lt;/strong&gt; page under the Views folder:&lt;br&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&amp;lt;h1&amp;gt;This is test page with message {{message}}&amp;lt;/h1&amp;gt;&lt;/font&gt;&lt;/li&gt; &lt;li&gt;Deploy the app again. You should be able to access the Angular route by navigating to http://localhost:&amp;lt;port&amp;gt;/#/ngtest.&lt;/li&gt;&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/4902031678605421617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/11/tutorial-adding-ui-to-service-fabric.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/4902031678605421617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/4902031678605421617'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/11/tutorial-adding-ui-to-service-fabric.html' title='Tutorial: Adding UI to a Service Fabric Web API service'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-7724826575749144144</id><published>2016-11-09T18:45:00.001-08:00</published><updated>2016-11-10T12:02:14.776-08:00</updated><title type='text'>Tutorial: Writing a Windows native client for Dynamics 365</title><content type='html'> &lt;p&gt;Recently, I needed to write a Windows client demo application that connects to Dynamics 365 via its Web API. As usual, I documented the process as I went along. Overall the experience was fair. However there were a few places that required some researches. I hope this article can provide help to whoever needs to take on a similar task in the future. &lt;strong&gt;Disclaimer&lt;/strong&gt;: this document was written based on pre-release system, so details are subject to change.&lt;/p&gt; &lt;h2&gt;Navigate to your Azure AD Tenant&lt;/h2&gt; &lt;p&gt;Before you can invoke Dynamics 365 Web APIs, you need to authenticate. Dynamics 365 uses Azure AD for authentication. When you provision a new trial Dynamics 365 organization (or tenant), a corresponding Azure AD tenant is created ( I believe in a production deployment you can reuse an existing Azure AD tenant, but I haven’t tried that myself). In order to allow your client application to authenticate with the Azure AD tenant, you’ll need to register your application with the AD tenant. And to do that, you’ll need an Azure subscription. The following steps show you how to navigate to the administrative page of your Azure AD tenant from your Dynamics 365 portal. &lt;/p&gt; &lt;ol&gt; &lt;li&gt;On your Dynamics 365 portal, click on the upper-left corner and then click on the &lt;strong&gt;Admin&lt;/strong&gt; tile, which opens the &lt;strong&gt;Admin center &lt;/strong&gt;page&lt;strong&gt;. &lt;/strong&gt;The address to your Dynamics 365 organization, btw, is &lt;strong&gt;http://&lt;/strong&gt;&lt;em&gt;&amp;lt;your Dynamics 365 organization name&amp;gt;.&lt;/em&gt;&lt;strong&gt;crm.dynamics.com&lt;/strong&gt;. Make sure you are using the correct URL, and double-check if you’ve logged in to the desired tenant (by looking at upper-right corner of the page).&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLB8rV03CGUTMFXl23kawz6ZESbD3AwV8h65c3GnLqCocrh5SxIlD4IrT9pswtoBfgzJB6tPcTVQgYZRhc36aJQvzHpTUpIYy9zx3Q_DGEfEe_hNd-mPijh5hye6R6h33u9EE9P0u5cSWC/s1600-h/image%25255B6%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEt2Co4HWWRkfotsIwDVFZglBhB2qMIXeSjtg1RubMMxgV_lM3PqWphyphenhyphenoqojVdBaJ_oj9JkSgJ4NV9ix9kCT33Kbo3ud-kGEQ2m1zMA5hKc7qgaPN29JHHUAhIljqWTVmpV97VKTmQvLZX/?imgmax=800&quot; width=&quot;347&quot; height=&quot;327&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;In the left pane, click on the &lt;strong&gt;Admin centers&lt;/strong&gt; –&amp;gt; &lt;strong&gt;Azure AD&lt;/strong&gt; link. This link takes you to the Azure AD tenant corresponding to your Dynamics 365 organization. However, if you don’t already have an Azure subscription, you’ll be prompted to create one. In this case, you can follow the wizard to create a free Azure subscription, and try the link again.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmLvs4LodB9pioydtaGcrBbxFcsETnL4Qca1iT-oEo9T1RV26kQVzt5pt8VyDpny5OiDj5MKlaD6_mncTL11cc95u17u_c3WAiFqJJAbykHQ34Pq15YTO8jnex7Ym5uFE3vPuWPka98Naf/s1600-h/image%25255B10%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS4efMpg6Lzh3dFdSu329QyiGSoIf4skn0bvhlXO21Q5yWmmmHMYij1pYq6UPW94wGz8ITbWV0dmtNgWGvjplkUb7MATTwc3I3RiUBEltQFSIoQjDM55ae7DjUrAyO6dlOO-aGj-RYTs2X/?imgmax=800&quot; width=&quot;243&quot; height=&quot;247&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Once you navigate to the Azure AD page, Your Azure AD tenant should have some applications already registered under the &lt;strong&gt;APPLICATIONS&lt;/strong&gt; tab, among which is the “&lt;strong&gt;Dynamics CRM Online&lt;/strong&gt;” application – this is the application we want to access.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNFF3LPIPSZqLVoGshx5lWTSb-Kpwzz6ueBzpDBvoeyaknzEiKpkd3n8x9EG5RjbtYplR2zRz142HC1uOPUL2e6dLmwbu9ZWpfZlW9afg1GaEM2bzTirjy2Q2CaAdPJ3phY4sDP2IBc31g/s1600-h/image%25255B14%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmFoJ9MDgT7tqNFAHRTtwmEblW52vJgnvYSRmTUCFErNKMOp5RQVWJ5gWhNXOa5_qaB9Q8WkpunNFX_mndckO3zOdMIq6hO_mSsqty3dB4DqXAVQ6hN9jgUpv2nxFtvqNf5MO62zFfbsxK/?imgmax=800&quot; width=&quot;492&quot; height=&quot;154&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Register your client application with the Azure AD Tenant&lt;/h2&gt; &lt;p&gt;This part is pretty much standard for Azure AD – you need to register your client application as a trusted application in the tenant. &lt;/p&gt; &lt;ol&gt; &lt;li&gt;Click on the &lt;strong&gt;APPLICATIONS &lt;/strong&gt;tab on your Azure AD tenant page. And then, click on the &lt;strong&gt;ADD &lt;/strong&gt;icon at the bottom command bar.&lt;/li&gt; &lt;li&gt;On the dialog, select &lt;strong&gt;Add an application my organization is developing&lt;/strong&gt;.&lt;/li&gt; &lt;li&gt;Enter a &lt;strong&gt;NAME&lt;/strong&gt; for your application, select the &lt;strong&gt;NATIVE CLIENT APPLICATION&lt;/strong&gt; option, and then click on the next arrow.&lt;/li&gt; &lt;li&gt;On the &lt;strong&gt;REDIRECT URI&lt;/strong&gt; field, enter a well-formed URI, for example &lt;em&gt;http://your-app-name&lt;/em&gt;. The URI doesn’t need to be accessible in this case.&lt;/li&gt; &lt;li&gt;Click on the check icon to complete registering the application.&lt;/li&gt; &lt;li&gt;Once the application is registered, you can see the application’s &lt;strong&gt;CLIENT ID&lt;/strong&gt; as well as &lt;strong&gt;REDIRECT URI&lt;/strong&gt; from its overview page. You’ll need these information in your code.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheDSwR_APwcZW5XM5H0J0HTM4a8Azu-F6D0b3BwNX-YRk5Y-JTEiYTmnHEAttCVPX3vZt3QmRNTYacZoe3Tfil_jjfM6N7NQ4kQ8TnWHqDPS9FWnzZMTgUrMVcJNzlj3lQcAabnH2BdfiM/s1600-h/image%25255B18%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihIjwokrRMJh_NO98f2quwgRIRme2z93mhrrD_C1rESTPseJ2Y-oy4pqniGa-qIi7ya3zaZQMmIZgeNYNK6USXVCT9Vf-Ve4BdoXbqbK4SNIA881bXq1xtoDp6o0VfPgJuUaJ2LuNhGi3_/?imgmax=800&quot; width=&quot;368&quot; height=&quot;295&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Switch to the application’s &lt;strong&gt;CONFIGURE&lt;/strong&gt; tab.&lt;/li&gt; &lt;li&gt;Click on the &lt;strong&gt;Add application&lt;/strong&gt; button in the &lt;strong&gt;&lt;em&gt;permissions to other applications&lt;/em&gt;&lt;/strong&gt; section.&lt;/li&gt; &lt;li&gt;Select the &lt;strong&gt;Dynamics CRM Online&lt;/strong&gt; application to grant access to Dynamics 365.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYOXs5Ne6AVWBVe-qxI4xLA_zPf3fgkB89Klf98JBoGcA5v_mOeplOCb1prnXc_9vAsr1HYaEQQAh-w2ym4EjkrJh2WPFdjyQ3EWcV1dHMYhcrDmKviKnzH-K5imLdLiBvvzb1t01TKGQC/s1600-h/image%25255B22%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq-mmvsf9Db37Dhbrh0lhalbftgM_fUio_pm-Hw88MzENFXe8QDH9H-6unE8aQQ3yhBMAT3aFF_y9ufqxPwslCW7ZgMRAnMXFIDB6eYT5Vf5Wl6kPcALUrnQoV6k05u1KJxNv6t9npV21p/?imgmax=800&quot; width=&quot;503&quot; height=&quot;231&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Back on the application page, drop down the &lt;strong&gt;&lt;em&gt;Delegated Permission&lt;/em&gt;&lt;/strong&gt; box and check the &lt;strong&gt;Access CRM Online as organization users&lt;/strong&gt; checkbox:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmZk8T5dnrSg3828g7qgE-aVfpTfv0iA8PgB7EYEIisHKvifIrR5E-AzKKn5eMd_Fx0PLWUBPRzCA7JXcEommtVq4mdEErHNrfQ5p-XC4xKPvv31d77XBe4daPlTqcMbcqqeoDSMfrsXWt/s1600-h/image%25255B26%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgynp6dZiQy1h5WraCQKTcTFIo1fogKr70PNw2ZHi_U_MmBxtWNphMV5jIvw_rXn12SKO44_GBFbFaKcsjoCi-pBv4ZHTRwHdwbUO7qAyWucBpu0K6DMzMqoYJ5H3dCldCsvEjSmzP_-w7L/?imgmax=800&quot; width=&quot;508&quot; height=&quot;126&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Click on the &lt;strong&gt;SAVE&lt;/strong&gt; icon on the bottom command bar to save the changes.&lt;/li&gt;&lt;/ol&gt;       &lt;h2&gt;Modify your client application&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;Create a new Console application&lt;/li&gt; &lt;li&gt;Add a reference to NuGet package: &lt;strong&gt;Microsoft.IdentityModel.Clients.ActiveDirectory&lt;/strong&gt;.&lt;/li&gt; &lt;li&gt;Add the following code. Please note that I use a &lt;strong&gt;FileCache&lt;/strong&gt; class from &lt;a href=&quot;https://github.com/Azure-Samples/active-directory-dotnet-native-desktop&quot;&gt;this sample repository&lt;/a&gt; to cache the token so that I don’t need to login every time. The following code authenticates to your Dynamics 365 organization and retrieve all accounts. &lt;/li&gt; &lt;p&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;class Program&lt;br&gt;{&lt;br&gt;&amp;nbsp; private static string tenant = &quot;&amp;lt;your Azure AD tenant&amp;gt;.onmicrosoft.com&quot;;&lt;br&gt;&amp;nbsp; private static string clientId = &quot;&amp;lt;your client app client id&amp;gt;&quot;; //see step 9 above&lt;br&gt;&amp;nbsp; private static string aadInstance = &quot;&lt;/font&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;https://login.microsoftonline.com/{0}&quot;;&lt;/font&gt;&lt;br&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&amp;nbsp; private static Uri redirectURI = new Uri(&quot;&amp;lt;your client redirect url&amp;gt;&quot;); //see step 9 above&lt;br&gt;&amp;nbsp; private static string dynamicCRMTenant = &quot;&lt;/font&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;https://&amp;lt;your Dynamics 354 org name&amp;gt;.crm.dynamics.com&quot;;&lt;/font&gt;&lt;br&gt;&lt;font size=&quot;2&quot; face=&quot;Courier New&quot;&gt;&amp;nbsp; private static string authority = String.Format(CultureInfo.InstalledUICulture, aadInstance, tenant);&lt;br&gt;&amp;nbsp; static void Main(string[] args)&lt;br&gt;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string token = loginToCRM().Result;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; retrieveAccounts(token).Wait();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.ReadLine();&lt;br&gt;&amp;nbsp; }&lt;br&gt;&amp;nbsp; static async Task&amp;lt;string&amp;gt; loginToCRM()&lt;br&gt;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var authContext = new AuthenticationContext(authority, new FileCache());&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = await authContext.AcquireTokenSilentAsync(dynamicCRMTenant, clientId);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (string.IsNullOrEmpty(result.AccessToken) || result.ExpiresOn.DateTime &amp;lt; DateTime.UtcNow)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var token = await authContext.AcquireTokenAsync(dynamicCRMTenant, clientId, redirectURI, new PlatformParameters(PromptBehavior.Always));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return token.AccessToken;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return result.AccessToken;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; static async Task retrieveAccounts(string token)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpClient httpClient = new HttpClient();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(&quot;Bearer&quot;, token);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; httpClient.DefaultRequestHeaders.Add(&quot;OData-MaxVersion&quot;, &quot;4.0&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; httpClient.DefaultRequestHeaders.Add(&quot;OData-Version&quot;, &quot;4.0&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(&quot;application/json&quot;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpResponseMessage response = await httpClient.GetAsync(dynamicCRMTenant + &quot;/api/data/v8.1/accounts&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string s = await response.Content.ReadAsStringAsync();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine(s);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/font&gt;&lt;/p&gt;&lt;/ol&gt; &lt;p&gt;Btw, you can look up exposed core entity types here: &lt;a title=&quot;https://msdn.microsoft.com/en-us/library/mt608066.aspx&quot; href=&quot;https://msdn.microsoft.com/en-us/library/mt608066.aspx&quot;&gt;https://msdn.microsoft.com/en-us/library/mt608066.aspx&lt;/a&gt;. And for all entity types, you can query /api/data/v8.1/.&lt;/p&gt; &lt;p&gt;Hope this helps!&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/7724826575749144144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/11/tutorial-writing-windows-native-client.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/7724826575749144144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/7724826575749144144'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/11/tutorial-writing-windows-native-client.html' title='Tutorial: Writing a Windows native client for Dynamics 365'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEt2Co4HWWRkfotsIwDVFZglBhB2qMIXeSjtg1RubMMxgV_lM3PqWphyphenhyphenoqojVdBaJ_oj9JkSgJ4NV9ix9kCT33Kbo3ud-kGEQ2m1zMA5hKc7qgaPN29JHHUAhIljqWTVmpV97VKTmQvLZX/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-2075937854818723594</id><published>2016-08-27T17:01:00.001-07:00</published><updated>2016-09-01T11:32:01.053-07:00</updated><title type='text'>SFNuGet 0.3.1 release: multi-service packages and application parameters</title><content type='html'>&lt;p&gt;I’m glad to announce that &lt;a href=&quot;https://www.nuget.org/packages/SFNuGet/&quot;&gt;SFNuGet&lt;/a&gt; 0.3.1&amp;nbsp; is now available. This version added two major features: application parameter support and multi-service package support.&lt;/p&gt; &lt;h2&gt;Application Parameters&lt;/h2&gt; &lt;p&gt;When you add a new service to a Service Fabric application in Visual Studio, some service attributes such as instance count and partition count are externalized as application manifest parameters. This design allows you to load different parameters when you deploy your application package to&amp;nbsp; different environments. In earlier versions of SFNuGet, these attributes are hard-coded. In version 0.3.1, this attributes are externalized just like in the Visual Studio experience. For example, when you add a SFNuGet reference to a stateful service named “MyStateful”, the following parameters are automatically added to your application manifest:&lt;/p&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot;&gt;&amp;lt;Parameters&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;&lt;strong&gt;MyStateful_TargetReplicaSetSize&lt;/strong&gt;&quot; DefaultValue=&quot;3&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;&lt;strong&gt;MyStateful1_MinReplicaSetSize&lt;/strong&gt;&quot; DefaultValue=&quot;3&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Parameter Name=&quot;&lt;strong&gt;MyStateful1_PartitionCount&lt;/strong&gt;&quot; DefaultValue=&quot;1&quot; /&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/Parameters&amp;gt;&lt;/font&gt;&lt;/p&gt; &lt;h2&gt;Multi-service Packages&lt;/h2&gt; &lt;p&gt;Under the Microservices architecture, each of the services should be independent. However, there are cases that some services depend on some other services in order to function correctly. Now, SFNuGet allows you to add a reference to another SFNuGet-enabled service so that you can package your service with all necessary dependency services. &lt;/p&gt; &lt;p&gt;In the following walkthrough, I’ll create a new stateful service that depends on the &lt;a href=&quot;https://www.nuget.org/packages/HelloWorldSFNuGet/&quot;&gt;HelloWorldSFNuGet&lt;/a&gt; service package. Then, I’ll use SFNuGet to package the new service as a new NuGet package.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create a new Service Fabric application with a stateful service named &lt;strong&gt;&lt;em&gt;MyStateful&lt;/em&gt;&lt;/strong&gt;.  &lt;li&gt;In the &lt;strong&gt;&lt;em&gt;MyStateful&lt;/em&gt;&lt;/strong&gt; service project, add a reference to the &lt;a href=&quot;https://www.nuget.org/packages/SFNuGet/&quot;&gt;SFNuGet&lt;/a&gt; package and then the &lt;a href=&quot;https://www.nuget.org/packages/HelloWorldSFNuGet/&quot;&gt;HelloWorldSFNuGet (2.0.0)&lt;/a&gt; package.  &lt;li&gt;Build the &lt;strong&gt;&lt;em&gt;MyStateful&lt;/em&gt;&lt;/strong&gt; project.  &lt;li&gt;Publish the generated &lt;strong&gt;&lt;em&gt;MyStateful.1.0.0.nupkg&lt;/em&gt;&lt;/strong&gt; to a NuGet storage of your choice. In Visual Studio, you can define a NuGet source based on a folder or a shared folder by using the &lt;strong&gt;&lt;em&gt;Tools&lt;/em&gt;&lt;/strong&gt; –&amp;gt; &lt;strong&gt;&lt;em&gt;NuGet Package Manager&lt;/em&gt;&lt;/strong&gt; –&amp;gt; &lt;strong&gt;&lt;em&gt;Package Manager Settings&lt;/em&gt;&lt;/strong&gt; menu.  &lt;li&gt;Create a new Service Fabric application with a stateless service named &lt;strong&gt;&lt;em&gt;Stateless1&lt;/em&gt;&lt;/strong&gt;.  &lt;li&gt;In the Service Fabric application project, add a reference to the &lt;strong&gt;&lt;em&gt;MyStateful&lt;/em&gt;&lt;/strong&gt; NuGet package. Because VS tooling experience has been updated, please see my &lt;a href=&quot;http://haishibai.blogspot.com/2016/08/sharing-your-service-fabric-services-as.html&quot;&gt;earlier post&lt;/a&gt; on how to do this correctly.  &lt;li&gt;In the &lt;strong&gt;Preview&lt;/strong&gt; dialog, you’ll see the &lt;strong&gt;&lt;em&gt;MyStateful&lt;/em&gt;&lt;/strong&gt; package will pull down its dependencies when you install it. Click on the &lt;strong&gt;OK&lt;/strong&gt; button to install both services. &lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0b_M4jMIGYZMWG2Eav-ddW6k9UjroZlsfh-SpiYbuoFfH0NPJV8cBrllWVx72VihqOoMyxLUE1S6L6SWDxV8Nv8b6VLni-cTnHqjoxy2n-6vaBPzDLNYHPp6KZU08jDL4QnT3WEBwx2KI/s1600-h/image%25255B5%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiOQ1xxfFc6t5sryqSMNf9jCScWPIQQR2FcMDJscCsVn5dvVsPU84wO0waqy4sz2aCAZPYx1uVZffcJ6Guoh7O0C1em4mTNcy83a7sJkwn640ZSzkzrPgl7yrahs4FOXcLFr0mRStdp9eL/?imgmax=800&quot; width=&quot;379&quot; height=&quot;384&quot;&gt;&lt;/a&gt;  &lt;li&gt;Once the package is installed, you can observe both service packages are included, and your application manifest is updated accordingly.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieT0LlNgsKHNaB58VxFyjNsb0OeuarYv7GOa3KpyeF6xklN4djnXim-jHMBZQxLIYwx2khclKYWi71W0AIqrCc1Du8z1UEYcefRqpbQ58eH_OmI8PoLiMaSAR4MvWtx5tQxs5_Beag3y7Y/s1600-h/image%25255B11%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-JMnBRrJBnbYc6Ba9eJOjgOL2ATqvPcmgzQrkmKcAdaEb6XivUY5aroCToJSxJKZdBjpivR12PkgPnQUoELlslwI5YFGzdxGwYD8-7BF77JbHrvsXSlZJPiWuAyR3Wc4udJSTU880_BiR/?imgmax=800&quot; width=&quot;244&quot; height=&quot;167&quot;&gt;&lt;/a&gt;  &lt;li&gt;Publish the application. The application should publish and function without errors:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFb8nMv5el4ucGhqEBafhWGcgg2OvvsIFgpp0-OFAmrGgTwU-GFKe6FZK08dC2T0N2M7ty-leGBQSeqdJDKC2ZYfgby4y2PEwH4mUiRyb6voquBacrfVbGIwElO7P01JSiRQjHj6K-Hq1x/s1600-h/image%25255B16%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOPZVR2u7Ihzae4B25LUyTCGklQfZkE3Fac0ans4dTCmWY-sS3ouj0GR4TuXZUCjbhfFpRedPF8smlh4Uo8fxpnOHZK7zxYmekziO95Szi-V4ydjqtt0sge5Ee1A1Qhu0v2nywRBjfex8x/?imgmax=800&quot; width=&quot;788&quot; height=&quot;128&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Conclusion&lt;/h2&gt; &lt;p&gt;This release is a major milestone. I don’t anticipate many more features before the 1.0 release. For the next few weeks, the main focus will be testing, bug fixing and filling up the omitted details (such as supporting configuration overrides, auto-populating&amp;nbsp; existing parameter files etc.). Thank you for your interests in SFNuGet and I look forward to seeing your reusable Service Fabric published, discovered, and reused!&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/2075937854818723594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/08/sfnuget-031-release-multi-service.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2075937854818723594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2075937854818723594'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/08/sfnuget-031-release-multi-service.html' title='SFNuGet 0.3.1 release: multi-service packages and application parameters'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiOQ1xxfFc6t5sryqSMNf9jCScWPIQQR2FcMDJscCsVn5dvVsPU84wO0waqy4sz2aCAZPYx1uVZffcJ6Guoh7O0C1em4mTNcy83a7sJkwn640ZSzkzrPgl7yrahs4FOXcLFr0mRStdp9eL/s72-c?imgmax=800" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-5454802344529441363</id><published>2016-08-25T19:23:00.001-07:00</published><updated>2016-09-16T22:05:43.772-07:00</updated><title type='text'>Tutorial: Running Windows Server containers on Azure Virtual Machines</title><content type='html'>&lt;p&gt;This tutorial covers the steps of running a Windows Server container on an Azure virtual machine.&amp;nbsp; There are quite a few tutorials online but I haven’t seen one that covers Azure virtual machines – and it turns out you might need to do some changes to make the networking work. &lt;/p&gt; &lt;p&gt;Disclaimer: this article is based on a specific preview version of Windows (Windows Server 2016 Technical Preview 5) running as an Azure virtual machine. The steps below don’t apply to earlier/later versions, and the experience may change in the future.&lt;/p&gt; &lt;h2&gt;Getting Docker on the machine&lt;/h2&gt; &lt;p&gt;The first part of this blog is based on &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/windowscontainers/deployment/deployment&quot;&gt;this MSDN article&lt;/a&gt;. There’s also a “Windows Server 2016 with Containers Tech Preview 5” image on Azure Marketplace. If you chose to use this image, you can skip all the configuration steps in this part. &lt;/p&gt; &lt;ol&gt; &lt;li&gt;Provision a new virtual machine (Windows Server 2016 Technical Preview 5) using the Azure &lt;a href=&quot;https://portal.azure.com&quot;&gt;portal&lt;/a&gt;.  &lt;li&gt;Connect to the server via RDP.  &lt;li&gt;Launch PowerShell, and use the following commands to install the container feature:&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;Install-WindowsFeature containers&lt;br clear=&quot;all&quot;&gt;Restart-Computer –Force&lt;/font&gt;  &lt;li&gt;Once the computer reboots, connect to it via RDP again.  &lt;li&gt;Launch PowerShell, and use the following commands to download and install Docker engine and Docker client:&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;Invoke-WebRequest &quot;https://get.docker.com/builds/Windows/x86_64/docker-1.12.1.zip&quot; -OutFile &quot;$env:TEMP\docker-1.12.1.zip&quot; -UseBasicParsing&lt;br clear=&quot;all&quot;&gt;Expand-Archive -Path &quot;$env:TEMP\docker-1.12.1.zip&quot; -DestinationPath $env:ProgramFiles&lt;br clear=&quot;all&quot;&gt;[Environment]::SetEnvironmentVariable(&quot;Path&quot;, $env:Path+&quot;;C:\Program Files\Docker&quot;, [EnvironmentVariableTarget]::Machine)&lt;/font&gt;  &lt;li&gt;Restart the PowerShell session to ensure modified path is recognized.  &lt;li&gt;Use the following commands to register Docker service and then download the Windows Server Core image:&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;dockerd --register-service&lt;br clear=&quot;all&quot;&gt;start-service Docker&lt;br clear=&quot;all&quot;&gt;docker pull microsoft/windowsservercore&lt;/font&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;Fix (possible) networking problem&lt;/h2&gt; &lt;p&gt;Now comes the interesting part. Inspect the default &lt;strong&gt;nat&lt;/strong&gt; network using command line:&lt;/p&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot;&gt;docker network inspect nat&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Observe the output. If the subnet is configured as 172.*, your container networking is most likely broken. You can still create and run containers, but you won’t be able to connect to services running on these containers. This error is because Host Networking Service (HNS) is trying to create the default &lt;strong&gt;nat&lt;/strong&gt; network with prefix 172.16.0./12, which overlaps with the IP address assigned to some VM tenants in Azure. &lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhz6apbp1waav-vMx4YpRArHMkg5mjsNQenfZl4uLukG-z-RkLwTvT_vUCcDlfDHPKiIuFq5L_QI5zvW7NPqcjCJlrcuUJhV8F52-q6lao84tHQ3KStKW6QQdia6AY5MWUrtvJo7asETXY8/s1600-h/image%25255B11%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhaRUguNFu3vsmrO5WxALwKkLACVsK4forlOXKT7frQrf7uUx6SzCZPlvZ6DNchwCs96SJhm0oSnbfZZZZ_AncfSRT8tNpXjMkTDQGsvZnP-Jj4KqSwMUGBFt9qsu9szCRoiBgri1WmQyk/?imgmax=800&quot; width=&quot;244&quot; height=&quot;168&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Fixing this problem takes a few steps. I’m not sure if the following steps are optimum. And I haven’t figured out a way to permanently change the &lt;strong&gt;dockerd&lt;/strong&gt; configuration (there’s a &lt;strong&gt;c:\ProgramData\dockers\config&lt;/strong&gt; folder but there’s only a key.json file with unknown purpose). Please send me your comments if you knew how to make these steps better!&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Launch PowerShell  &lt;li&gt;Use the following commands to stop Docker service and to clean up existing network artifacts:&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;stop-service Docker&lt;br clear=&quot;all&quot;&gt;Get-ContainerNetwork | Remove-ContainerNetwork&lt;br clear=&quot;all&quot;&gt;Get-NetNat | Remove-NetNat&lt;br clear=&quot;all&quot;&gt;Get-VMSwitch|Remove-VMSwitch&lt;/font&gt;  &lt;li&gt;Now we’ll launch Docker daemon (dockerd) with a different CIDR that doesn’t overlap with 127.*. Please note the first part of the CIDR is 192.168.1.&lt;strong&gt;1&lt;/strong&gt; (instead of 192.168.1.0). From what I observe, containers will pick 192.168.1.1 as the default gateway. As the dockerd on Windows doesn’t have the --default-gateway switch, setting the address to 192.168.1.1 gives the host correct address (that matches the containers’ expectations). &lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;cd “c:\Program Files\docker”&lt;br clear=&quot;all&quot;&gt;dockerd --fixed-cidr=”192.168.1.&lt;strong&gt;1&lt;/strong&gt;/24”&lt;/font&gt;  &lt;li&gt;Now inspect the nat network again:&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;docker network inspect nat&lt;/font&gt;&lt;br clear=&quot;all&quot;&gt;You should see the subnet is configured correctly this time:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4HwsnfDLDOmDFvAEb2YY0qVS4vtqI6Th9gdKvmLgTzjVacvfn3KbUe5ceWali5rv_TzIO7GXHdK78WvoJ4JdAfKTYgXv2n6O6U56T219K00_zTe_zpIdBoqPi6IeFg1lqq5w9LoPLs_Bi/s1600-h/image%25255B8%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFmiwgfhodSqKBgPguh8B5XET1zvaGJaw3CchjGo6SB9aUc64ZIPt-ioEM8OmaNV1vAoH8oJskgxkoxqxFxVAfAQ4wnAukHiRLxNcUBvtkcZI-ppsm1nmoPnMgGFy8kzRnJDE8eYYX5OwB/?imgmax=800&quot; width=&quot;329&quot; height=&quot;244&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;After this, your new containers should be assigned with correct IP addresses and can ping the host/each other. If you still have connectivity problems, try to turn off the firewall on the host.&lt;/p&gt; &lt;p&gt;I hope this helps. And I believe the above problem will be fixed in future versions. Thank you for reading &lt;img class=&quot;wlEmoticon wlEmoticon-smile&quot; style=&quot;border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none&quot; alt=&quot;Smile&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9aNUOZSmWoYA2VLXUlos9m7F_HFYbcMw6NGguU5ffaYZcxkYjDTbYaIqtLr6-Pz0mz3Zsmyhury4n45p2_2GfZ9D82_2bzw75gA5-gI7tdwqksj1bgRHBDjxtGkv-xz9zxE1dwZsZlhHA/?imgmax=800&quot;&gt;.&lt;/p&gt; &lt;h2&gt;UPDATE:&lt;/h2&gt; &lt;p&gt;To permanently change the CIRD settings, create a &lt;strong&gt;daemon.json&lt;/strong&gt; file under %&lt;strong&gt;ProgramData%\docker\config&lt;/strong&gt; folder (see the complete file schema &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/dockerd/&quot;&gt;here&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot;&gt;{ &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;fixed-cidr&quot;: &quot;192.168.1.1/24&quot;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face=&quot;Courier New&quot;&gt;}&lt;/font&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/5454802344529441363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/08/tutorial-running-windows-server.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/5454802344529441363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/5454802344529441363'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/08/tutorial-running-windows-server.html' title='Tutorial: Running Windows Server containers on Azure Virtual Machines'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhaRUguNFu3vsmrO5WxALwKkLACVsK4forlOXKT7frQrf7uUx6SzCZPlvZ6DNchwCs96SJhm0oSnbfZZZZ_AncfSRT8tNpXjMkTDQGsvZnP-Jj4KqSwMUGBFt9qsu9szCRoiBgri1WmQyk/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-6640502158792754248</id><published>2016-08-15T16:46:00.001-07:00</published><updated>2016-08-15T16:46:48.655-07:00</updated><title type='text'>How is it made: Nginx as a pre-packaged Service Fabric service</title><content type='html'>&lt;p&gt;Last week, I published a prototype of a reusable &lt;a href=&quot;https://nginx.org/&quot;&gt;Nginx&lt;/a&gt; service as a Service Fabric service using &lt;a href=&quot;http://haishibai.blogspot.com/2016/08/sharing-your-service-fabric-services-as.html&quot;&gt;SFNuGet&lt;/a&gt;. This article provides you details on how to use this service as well as how this service is designed and implemented. I wish this article will give you some ideas on how to package existing services as a reusable Service Fabric service. &lt;/p&gt; &lt;h2&gt;Usage&lt;/h2&gt; &lt;p&gt;The following walkthrough shows you how to use the &lt;a href=&quot;https://www.nuget.org/packages/NginxPoC/&quot;&gt;NginxPoc NuGet package (1.4.0)&lt;/a&gt;, and how to conduct a couple of experiments with it. If you are only interested in how the service is designed and implemented, please skip to the next section. &lt;/p&gt; &lt;h3&gt;&lt;/h3&gt; &lt;h3&gt;&lt;/h3&gt; &lt;h3&gt;Include NginxPoC in your application&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;Create a new Service Fabric application.&lt;/li&gt; &lt;li&gt;Right-click on the solution node and select the &lt;strong&gt;“Manage NuGet Package for Solution…”&lt;/strong&gt; menu.&lt;/li&gt; &lt;li&gt;Search for “&lt;strong&gt;&lt;em&gt;nginxpoc&lt;/em&gt;&lt;/strong&gt;”. Once the package is found, select the package, and make sure your application project is checked in the list, and click on the &lt;strong&gt;Install&lt;/strong&gt; button.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOLJVdOnrsM4tbEyMCsdymNTQkNYSUaIArP5LsVlhjaF1fKtljZF2_wGTZFRBdOIt5B9o21vRst32oU8tQiebUtu2BASJ6Ud_-138BsPWpaFFfYVRvx4KxpQ5zld59H-at-_qBU7ltg3qM/s1600-h/image%25255B4%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-QA2BnKeN563ZB0u5Qn0_btGBL0sBdYXL_puZDZ44kYEHLV9_Be3nCGPw3n8BNEbQw9bqXZp-SbZqZVnGu7640yfFg4hp2rzdbH2k5tUuSLknYgvYOwrUtyLKBJy5qelpqPZbcyqIrrZ4/?imgmax=800&quot; width=&quot;577&quot; height=&quot;233&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Modify your application manifest and change the instance count of the &lt;strong&gt;Nginx&lt;/strong&gt; service to “1”.&lt;/li&gt; &lt;li&gt;Deploy you application.&lt;/li&gt; &lt;li&gt;You should observe your application is deployed correctly. Nginx is launched and you should be able to access Nginx by &lt;a href=&quot;http://localhost:88/&quot;&gt;http://localhost:88/&lt;/a&gt; if you are using a local Service Fabric cluster.&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;Rolling a configuration change&lt;/h3&gt; &lt;p&gt;Now let’s change the Nginx configuration to use the default port 80.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Modify the &lt;em&gt;&lt;strong&gt;nginx.conf&lt;/strong&gt;&lt;/em&gt; file under the &lt;strong&gt;&lt;em&gt;ApplicationPackageRoot\NginxPkg\Config\confg&lt;/em&gt;&lt;/strong&gt; folder and change the http-&amp;gt;server-&amp;gt;listen port setting from &lt;strong&gt;88&lt;/strong&gt; to &lt;strong&gt;80&lt;/strong&gt;.&lt;/li&gt; &lt;li&gt;Save the file. Right-click the application project and select the “&lt;strong&gt;&lt;em&gt;Edit Manifest Version…&lt;/em&gt;&lt;/strong&gt;” menu.&lt;/li&gt; &lt;li&gt;Update the version of &lt;strong&gt;&lt;em&gt;Config&lt;/em&gt;&lt;/strong&gt; package of &lt;strong&gt;&lt;em&gt;NginxPkg&lt;/em&gt;&lt;/strong&gt; to &lt;strong&gt;2.0.0&lt;/strong&gt;. Make sure “&lt;strong&gt;&lt;em&gt;Automatically update application and service versions&lt;/em&gt;&lt;/strong&gt;” checkbox is checked. Then, click the &lt;strong&gt;&lt;em&gt;Save&lt;/em&gt;&lt;/strong&gt; button.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj05x2wfkolHEa98nci5yyob925Fna8z32hh1RRHlBvQcAPl6x2J-rDVO9wX75iQ0DykD1lMvVTnwDua9tZCUP3QlayShmqsCeZMK8zulZCT-yn5tkvGTo4UUtwRKoMNoP5E1g3yeFEL5Id/s1600-h/image%25255B9%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYzemabRluseZt99cGXo4ImAs_7s2aiC_yCTRlw9q7wWqTB2516EsDBzTOtB4TxPk2kMjKI7kofeXkqbMm37BZy7dyxWPBk8g-6s8mxqGD2Xbt0DyuGMORgGtNIpSrH2pRmSYFujFflfj9/?imgmax=800&quot; width=&quot;341&quot; height=&quot;231&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Right-click the application project and select the “&lt;strong&gt;Package&lt;/strong&gt;” menu to create a new package.&lt;/li&gt; &lt;li&gt;Use PowerShell to trigger a rolling update: &lt;/li&gt; &lt;p&gt;Copy-ServiceFabricApplicationPackage -ApplicationPackagePath &lt;em&gt;[app package path]&lt;/em&gt; -ImageStoreConnectionString &#39;file:C:\SFDevCluster\Data\ImageStoreShare&#39; -ApplicationPackagePathInImageStore &lt;em&gt;[app name]&lt;/em&gt;\_V2&lt;br&gt;Register-ServiceFabricApplicationType -ApplicationPathInImageStore &quot;&lt;em&gt;[app name]&lt;/em&gt;\_V2&quot;&lt;br&gt;Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/&lt;em&gt;[app name]&lt;/em&gt; -ApplicationTypeVersion 2.0.0 -HealthCheckRetryTimeoutSec 60 -UpgradeReplicaSetCheckTimeoutSec 3000 -FailureAction Rollback -Monitored&lt;/p&gt; &lt;li&gt;Once the rolling update completes, Nginx is restarted at port 80.&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;Failing over&lt;/h3&gt; &lt;p&gt;When a node fails, Service Fabric automatically migrates the workload on the node to another healthy node. To test this, perform the following test:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Use Service Fabric explorer, identify the node where the Nginx service instance is deployed.&lt;/li&gt; &lt;li&gt;Deactivate the node.&lt;/li&gt; &lt;li&gt;Observe the service instance is migrated to another node where Nginx is re-launched.&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;Recovery&lt;/h3&gt; &lt;p&gt;If Nginx process crashes, the wrapper service automatically restarts it. To test this, use Task Manager to kill the two &lt;strong&gt;&lt;em&gt;nginx-1.11.3.exe&lt;/em&gt;&lt;/strong&gt; processes (one main process and one worker process) and observe the two processes are restored after a few seconds.&lt;/p&gt; &lt;h2&gt;Service Design&lt;/h2&gt; &lt;p&gt;Service Fabric supports hosting executables as &lt;a href=&quot;https://azure.microsoft.com/en-us/documentation/articles/service-fabric-deploy-existing-app/&quot;&gt;guest executables&lt;/a&gt;. Naturally, the easiest approach would be packaging &lt;a href=&quot;http://nginx.org/en/docs/windows.html&quot;&gt;Nginx for Windows&lt;/a&gt; as a guest application. The challenge, however, is to put Nginx configurations into a Service Fabric configuration package.&amp;nbsp; Because configuration packages are versioned, the path to a configuration package has the package’s version in it. We need to somehow find out which is the correct folder (there could be multiple versions uploaded) to use and then use the Nginx “-c” switch to load the correct configuration file.&amp;nbsp; To solve this problem, my solution is to create a stateless wrapper service that wraps the Nginx executable. The wrapper service launches Nginx as it starts. It also registers to configuration change event and re-launch Nginx with the updated configuration. Last but not least, it also monitors the Nginx process and restarts the process if Nginx has crashed.&lt;/p&gt; &lt;h2&gt;Service Implementation&lt;/h2&gt; &lt;p&gt;To create the service package, I followed these steps:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create a new Service Fabric Application with a stateless service named &lt;strong&gt;&lt;em&gt;Nginx&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt; &lt;li&gt;Download and unpack Nginx for Windows (1.11.3) to the &lt;strong&gt;&lt;em&gt;PackageRoot\Code&lt;/em&gt;&lt;/strong&gt; folder.&lt;/li&gt; &lt;li&gt;Add a new &lt;strong&gt;&lt;em&gt;setupACL.cmd&lt;/em&gt;&lt;/strong&gt; file under the same folder. The file contains the following line to allow &lt;strong&gt;&lt;em&gt;nginx-1.11.3.exe&lt;/em&gt;&lt;/strong&gt; to listen to incoming traffic:&lt;br clear=&quot;all&quot;&gt;&lt;font face=&quot;Courier New&quot;&gt;netsh advfirewall firewall add rule name=&quot;Nginx (port 80)&quot; dir=in action=allow program=.\nginx-1.11.3.exe enable=yes&lt;/font&gt;&lt;/li&gt; &lt;li&gt;Add a new &lt;strong&gt;&lt;em&gt;ApplicationManifest.overrides.xml&lt;/em&gt;&lt;/strong&gt; file under the &lt;strong&gt;&lt;em&gt;PackageRoot&lt;/em&gt;&lt;/strong&gt; folder. This is a partial application manifest that defines service-specific principals and policies. SFNuGet supports merging such application manifests into the final application manifest so that service authors can include these service-specific configurations (in addition to what are allowed in configuration overrides). Alternatively, you can modify &lt;em&gt;&lt;strong&gt;setupACL.cmd&lt;/strong&gt;&lt;/em&gt; to explicit grant &lt;strong&gt;&lt;em&gt;Network Services &lt;/em&gt;&lt;/strong&gt;required rights so that to avoid running the service as an administrator. &lt;/li&gt; &lt;p&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;br&gt;&amp;lt;ApplicationManifest xmlns:xsd=&quot;&lt;a href=&quot;http://www.w3.org/2001/XMLSchema&amp;quot;&quot;&gt;http://www.w3.org/2001/XMLSchema&quot;&lt;/a&gt; xmlns:xsi=&quot;&lt;a href=&quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&quot;&gt;http://www.w3.org/2001/XMLSchema-instance&quot;&lt;/a&gt; ApplicationTypeName=&quot;NginxPoCType&quot; ApplicationTypeVersion=&quot;1.0.0&quot; xmlns=&quot;&lt;a href=&quot;http://schemas.microsoft.com/2011/01/fabric&amp;quot;&quot;&gt;http://schemas.microsoft.com/2011/01/fabric&quot;&lt;/a&gt;&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;ServiceManifestImport&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;ServiceManifestRef ServiceManifestName=&quot;NginxPkg&quot; ServiceManifestVersion=&quot;1.0.0&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Policies&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;RunAsPolicy CodePackageRef=&quot;Code&quot; UserRef=&quot;Admin&quot; EntryPointType=&quot;All&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/Policies&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/ServiceManifestImport&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;Principals&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Users&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;User Name=&quot;Admin&quot;&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;MemberOf&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;SystemGroup Name=&quot;Administrators&quot; /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/MemberOf&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/User&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/Users&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/Principals&amp;gt;&lt;br&gt;&amp;lt;/ApplicationManifest&amp;gt;&lt;/p&gt; &lt;li&gt;The service implementation contains several private members:&lt;/li&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private string nginxProcessName = &quot;&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private string getConfigPath()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var codePackage = this.Context.CodePackageActivationContext.CodePackageName;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var configPackage = this.Context.CodePackageActivationContext.GetConfigurationPackageObject(&quot;Config&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var codePath = this.Context.CodePackageActivationContext.GetCodePackageObject(codePackage).Path;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Path.Combine(configPackage.Path, &quot;conf\\nginx.conf&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private bool isNginxRunning()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!string.IsNullOrEmpty(nginxProcessName))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var processes = Process.GetProcessesByName(nginxProcessName);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return processes.Length != 0;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private void launchNginxProcess(string arguments)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var codePackage = this.Context.CodePackageActivationContext.CodePackageName;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var configPackage = this.Context.CodePackageActivationContext.GetConfigurationPackageObject(&quot;Config&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var codePath = this.Context.CodePackageActivationContext.GetCodePackageObject(codePackage).Path;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var res = File.Exists(Path.Combine(codePath, &quot;nginx-1.11.3.exe&quot;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var nginxStartInfo = new ProcessStartInfo(Path.Combine(codePath, &quot;nginx-1.11.3.exe&quot;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nginxStartInfo.WorkingDirectory = codePath;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nginxStartInfo.UseShellExecute = false;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nginxStartInfo.Arguments = arguments;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var nginxProcess = new Process();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nginxProcess.StartInfo = nginxStartInfo;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nginxProcess.Start();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nginxProcessName = nginxProcess.ProcessName;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt; &lt;li&gt;The RunAsync method registers to the configuration change event, launches Nginx, and re-launch it if it crashes:&lt;/li&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected override async Task RunAsync(CancellationToken cancellationToken)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.Context.CodePackageActivationContext.ConfigurationPackageModifiedEvent += CodePackageActivationContext_ConfigurationPackageModifiedEvent;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; launchNginxProcess(&quot;-c &quot; + getConfigPath());&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (true)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (cancellationToken.IsCancellationRequested)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; launchNginxProcess(&quot;-s quit&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cancellationToken.ThrowIfCancellationRequested();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!isNginxRunning())&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; launchNginxProcess(&quot;-c &quot; + getConfigPath());&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt; &lt;li&gt;And finally, the following code handles configuration changes: &lt;/li&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private void CodePackageActivationContext_ConfigurationPackageModifiedEvent(object sender, PackageModifiedEventArgs&amp;lt;ConfigurationPackage&amp;gt; e)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; launchNginxProcess(&quot;-s quit&quot;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (isNginxRunning())&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Task.Delay(TimeSpan.FromSeconds(1));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; launchNginxProcess(&quot;-c &quot; + Path.Combine(e.NewPackage.Path, &quot;conf\\nginx.conf&quot;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;/p&gt;&lt;/ol&gt; &lt;h2&gt;Conclusion&lt;/h2&gt; &lt;p&gt;This article shows you how to wrap an existing application as a reusable Service Fabric service. I’m looking forward to seeing your services published and reused! &lt;/p&gt; &lt;p&gt;Before concluding this article, a few words on Nginx dynamic configuration – there’s a commercial &lt;a href=&quot;https://www.nginx.com/products/&quot;&gt;Nginx Plus&lt;/a&gt; that supports dynamic configuration updates via REST API. In a dynamic cloud production environment, Nginx Plus is probably better choice than the above configuration update method. &lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/6640502158792754248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/08/how-is-it-made-nginx-as-pre-packaged.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6640502158792754248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6640502158792754248'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/08/how-is-it-made-nginx-as-pre-packaged.html' title='How is it made: Nginx as a pre-packaged Service Fabric service'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-QA2BnKeN563ZB0u5Qn0_btGBL0sBdYXL_puZDZ44kYEHLV9_Be3nCGPw3n8BNEbQw9bqXZp-SbZqZVnGu7640yfFg4hp2rzdbH2k5tUuSLknYgvYOwrUtyLKBJy5qelpqPZbcyqIrrZ4/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-2580514254703414414</id><published>2016-08-10T17:38:00.001-07:00</published><updated>2016-08-10T17:38:34.680-07:00</updated><title type='text'>Sharing your Service Fabric services as NuGet packages</title><content type='html'>&lt;p&gt;Have you been hoping for easily incorporating existing services such as &lt;a href=&quot;https://www.nginx.com/resources/wiki/&quot;&gt;Nginx&lt;/a&gt;, &lt;a href=&quot;http://redis.io/&quot;&gt;Redis&lt;/a&gt; and &lt;a href=&quot;https://www.mongodb.com/&quot;&gt;MongoDB&lt;/a&gt; into your &lt;a href=&quot;https://azure.microsoft.com/en-us/services/service-fabric/&quot;&gt;Microsoft Azure Service Fabric&lt;/a&gt; applications? Have you made a reusable Service Fabric service that you are eager to share with the community? If your answer to either question is “yes”, this blog is just for you! I’m very happy to announce availability of &lt;a href=&quot;http://nuget.org/packages/SFNuGet/0.1.1&quot;&gt;SFNuGet&lt;/a&gt;, a NuGet package that allows you to &lt;strong&gt;easily&lt;/strong&gt; share, discover and reuse Service Fabric services as NuGet packages.&lt;/p&gt; &lt;p&gt;To author a Service Fabric NuGet package, simply add a reference to SFNuGet&amp;nbsp; and rebuild your service project. SFNGet automatically adds a new build target to your project that generates a NuGet package when you build your project. Then, the package can be published to any NuGet repositories so that others can make use of it. Using a Service Fabric NuGet package is a breeze as well – just add a reference to the service NuGet package and everything will be configured for you. No additional actions are needed – your application is ready to be published with the referenced service (of course you can still configure the service in whichever way you prefer). &lt;/p&gt; &lt;h2&gt;Usage Experience&lt;/h2&gt; &lt;p&gt;To use a Service Fabric NuGet package, all you need to do is to reference to the package in your Service Fabric application. The following walkthrough shows you how to add a sample “Hello World” Service Fabric NuGet package to your application.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;In Visual Studio 2015, create a new Service Fabric application, or open an existing Service Fabric application.  &lt;li&gt;Add a reference to &lt;strong&gt;&lt;em&gt;HelloWorldSFNuGet&lt;/em&gt;&lt;/strong&gt;, which contains a simple stateless service that writes outputs to event tracer.  &lt;ol&gt; &lt;li&gt;Right-click the solution and select the &lt;strong&gt;&lt;em&gt;Manage NuGet Packages for Solution&lt;/em&gt;&lt;/strong&gt; menu.  &lt;li&gt;Search for the &lt;strong&gt;&lt;em&gt;HelloWorldSFNuGet&lt;/em&gt;&lt;/strong&gt; package.  &lt;li&gt;Make sure your Service Fabric &lt;u&gt;application&lt;/u&gt; project is checked, and click on the &lt;strong&gt;&lt;em&gt;Install&lt;/em&gt;&lt;/strong&gt; button to install the package.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw38D17B-KhEOKnsITY2xQFiVdnLBmL4owdS2HBum95347rq7oUEAGJOr_r72MXw4rT1n5bg56bHf_H8cjgAATAH_dDlXqdXPLuWn2IxBjv7TP7A2Et_hqrVn6IrvC7eMiF-GXUALgwCr2/s1600-h/image%25255B3%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheuyhZWxbQgUYPmXdwtIEZehP86PuaqZUtRHeYWlaITF3dELx769cAuCVzZKsohU0aNOT9SiNiYCc26q6oiA_JEaE-OWJBa6NdveEgETDiwtDbs_GSLsBWVxIxQLPir3Y_vZut_gkdixtc/?imgmax=800&quot; width=&quot;444&quot; height=&quot;372&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;strong&gt;And that’s it!&lt;/strong&gt; You can deploy your application as it is now and the referenced service will be deployed along other services in your application. &lt;/p&gt; &lt;p&gt;Of course, if you prefer, you can use the Package Manager Console as well: &lt;font face=&quot;Courier New&quot;&gt;Install-Package HelloWorldSFNuGet&lt;/font&gt;.&lt;/p&gt; &lt;p&gt;A couple of things happen when you add the reference: first, a “&lt;strong&gt;HelloWorldSFNuGetPkg&lt;/strong&gt;” folder is added to the &lt;strong&gt;&lt;em&gt;ApplicationPackageRoot&lt;/em&gt;&lt;/strong&gt; folder. This new folder contains everything from the service package – code (binaries), configuration, data as well as the service manifest.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidt8WXY8ECtUWn2RxumVLIzi2Ij0UfAR5kS4MqTisj5iej51Io0JFoZEzIjAT36_ZH5bXuyJodivDKRmMx3nFhmiC84YRi6g-XFjdMR5ZOpT4yFW2099GXXGw8d1Tqq3eKmtguvTYwdOIK/s1600-h/image%25255B6%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHHKnl7gEderjx829J9M8bN3I-boWTby6Y4u5K_OJrGlYH2Ee69Wu8shTVkxrdl7RDUrBnmBKLIew-M3YB_qc7zUoEESjQWEsGs4tC-0V7EkWWkndCECezpMDDYS2LZ9jm_82i2-l6YAIo/?imgmax=800&quot; width=&quot;222&quot; height=&quot;114&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Second, your application manifest is updated to include the service reference. The service is also registered as a default service by default. But you can certainly remove it (or make any other configuration changes as you see fit) if you want.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr1TtwnAGb-M_oJ428njGH07qYzxb5PHQsL4B6FKhdj4F9fa6VM6tU9TGPnWNO0MmD5afi6xAn7bsReC3EkTnGhZpO2soSpJwOwccjOWo-Lr_ftZl05mcDk7-xbUriKxeF0M6zWohbuoQH/s1600-h/image%25255B10%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7YEF7h9j0RV7fn33sXVbOCmICzoME2qdrQSF7eYuCUVvV5-H-Kg2g9jGLp-L426YAuxOF6geyW4zn-pjtw2ZheuluODu1zV8glNZvdn3tGFlWeAxJSDlKjesD11FBaXRE294GWdpqnoVm/?imgmax=800&quot; width=&quot;523&quot; height=&quot;328&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Publish your application and you’ll see the reference service is published as expected:&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrfgrvmgQaE7X9k89pG4GVhMRhgpiu5TeeFuJZRi1_hjxD8Ci_dh0pSmBoxx-yw-5jM3EGYm1agjTdhTkPBIAzqckB7u1LTC0FBZuP018eMEHHY5c9DE2YPIk3DJSzC4sivBuDUL-3mBX0/s1600-h/image%25255B15%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHoKCGFzFfg1a39RM11nfVNwJsQkU5aNRKQuXtTPkhcWTt_fBKv3zOAQR2IObcN11nn4jy8sKNsVq66ap-Oi8rHU1tupPTDdngbZ3uE3T2oEwKoeKNV7MN10uftWi286OA_uChrDqUqJJj/?imgmax=800&quot; width=&quot;702&quot; height=&quot;211&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;If you don’t want the service anymore, simply uninstall the package and everything will be cleaned up for you.&lt;/p&gt; &lt;h2&gt;Authoring Experience&lt;/h2&gt; &lt;p&gt;To create a Service Fabric NuGet package, add a reference to the &lt;strong&gt;&lt;em&gt;SFNuGet&lt;/em&gt;&lt;/strong&gt; package and rebuild your service. That’s all you need to do – no code changes, no additional actions. Simply build and the package is automatically created. The following walkthrough shows you how to package a Web API service as a reusable NuGet package.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create a new Service Fabric Application with a stateless service using the &lt;strong&gt;&lt;em&gt;Stateless Web API&lt;/em&gt;&lt;/strong&gt; project. Name the service &lt;strong&gt;&lt;em&gt;MyWebAPI&lt;/em&gt;&lt;/strong&gt;. If you want to publish an existing service, simply go to step 2.  &lt;li&gt;Add a NuGet reference to &lt;strong&gt;&lt;em&gt;SFNuGet&lt;/em&gt;&lt;/strong&gt; in your &lt;u&gt;service&lt;/u&gt; project.  &lt;li&gt;Rebuild the project (or the entire solution).&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Once the project is built, you’ll find a &lt;strong&gt;&lt;em&gt;MyWebAPI.1.0.0.nupkg&lt;/em&gt;&lt;/strong&gt; under the project folder. This is the package you can share with others so they can easily include your service into their applications.&lt;/p&gt; &lt;h2&gt;Reusable Nginx service&lt;/h2&gt; &lt;p&gt;As a test, I’ve created and published a &lt;strong&gt;&lt;em&gt;NginxPoC&lt;/em&gt;&lt;/strong&gt; NuGet package using SFNuGet. You can reuse the service by simply adding a NuGet reference as shown in the above walkthrough. Before you publish your application to a local cluster, you need to change the instance count to &lt;strong&gt;1&lt;/strong&gt; from the default –&lt;strong&gt;1&amp;nbsp; &lt;/strong&gt;to avoid port conflicts. A couple of things to notice:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;In this package, the Nginx instance uses port &lt;strong&gt;88&lt;/strong&gt; instead of the default 80.  &lt;li&gt;The Nginx configuration is included in the service’s configuration package. This allows Nginx to be dynamically configured by uploading a new configuration package. However, as I haven’t got a chance to test this part myself so there could be problems.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;If you are using a local cluster, you should be able to access Nginx page by &lt;a href=&quot;http://localhost:88/&quot;&gt;http://localhost:88/&lt;/a&gt; after the application is deployed:&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJo5kMBOYcqp4tVkhpDylaTUCIQvSrzafkCjLWIFblVhZwAp-PH4ETx_nEDW93dF7-kKB31gbUtSYWM6a-bArVPLnuyGqL2oHquePy0o3NA4lYlvk4ocSfO05du3HmWp1Sm6oWmnovBe2p/s1600-h/image%25255B19%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5c_GNAWgk2Z3Gk76eSGiLhmyoXaOBmXbe_iazSog7yjLAdAES3f_i2gqG4aQ23OOh_VuvaIOqh5tAa_qAycjVUVkmZMAXlkLxSnIAHzFgNgIGRte9ytVoMCt0PMlYrgB8DTTN6D3e2VKF/?imgmax=800&quot; width=&quot;540&quot; height=&quot;205&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h2&gt;Why I did this?&lt;/h2&gt; &lt;p&gt;In the world of Microservices, one of the greatest things is &lt;a href=&quot;https://hub.docker.com/&quot;&gt;Docker Hub&lt;/a&gt;, where you can find thousands of reusable images that you can easily incorporate into your own applications. Service Fabric is still fairly new, so there isn’t such a vibrant community yet - I hope SFNuGet is a start. With SFNuGet, developers like you can easily create and share great services with the entire community. If you come from the Docker world, you can think Service Fabric NuGet packages as Docker images, while Service Fabric application manifests as Docker compose files. And if I dare to dream, the parallel concepts create opportunities for bridging the two in the future.&lt;/p&gt; &lt;p&gt;NuGet is familiar to Visual Studio developers, so I believe using NuGet creates the most natural experiences to both service authors and service consumers.&amp;nbsp; Moreover, NuGet provides a lot of desirable capabilities (that we find in Docker Hub and Trusted Registry) including versioning and public and private repos. So, NuGet is an apparent choice that allows me to build out the system without requiring any Visual Studio tooling or Service Fabric changes. Can the experience be even better? Definitely. This is where I need your help to try SFNuGet out and send me your feedbacks, which I’ll forward to our engineering teams at Microsoft.&lt;/p&gt; &lt;p&gt;By default, all SFNuGet generated packages are tagged with a “&lt;strong&gt;SFNuGet&lt;/strong&gt;” tag. This allows basic discoverability of reusable Service Fabric services in an existing NuGet community. Will there be a dedicated site as Docker Hub? I hope you – let me know if you want to build one :). &lt;/p&gt; &lt;p&gt;Some known issues/limitations and future thoughts:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;When updating application manifests, no parameters are used for instance count. In future versions, instance count will be parameterized as what VS tooling does by default.  &lt;li&gt;You can use SFNuGet to package stateless, stateful, Web API and actor services. But you can’t directly package a Guest Application service. This is because the Guest Application service doesn’t have its own service project. For Nginx, I created a wrapper service that wraps the guest executable.  &lt;li&gt;When you install SFNuGet, it reads your assembly manifest and generates a NuGet spec (nuspec) file. The file is not updated afterwards. You’ll need to manually update the file if you want to make further changes.  &lt;li&gt;NuGet.exe hangs when invoked from msbuild if the package contains many files. So in the code I have a monitor that checks the output of NuGet.exe and shuts it down when I see “Successfully created package”. This is obviously undesirable but I don’t have better alternatives at this point. You can examine the script in the &lt;strong&gt;NuGetPackage.ps1&lt;/strong&gt; file after you install SFNuGet. Please let me know if you have better solutions.  &lt;li&gt;In the future, I hope to see better VS tooling integrations. Such as making the referenced services appear under the “Sevices” node in the application project, providing a “add existing service” menu with better discovery experience, and allow NuGet packages to be referenced without unpacking, etc.  &lt;li&gt;I’m planning on allowing publishing a Docker image (starting with Windows-based images first) as a SFNuGet package. This will allow including a stateless Docker container as a guest application service into your Service Fabric application and deploy the application on a Docker-enabled Service Fabric cluster. Is this scenario valuable to you? Please let me know!&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;Call to actions&lt;/h2&gt; &lt;p&gt;Give &lt;strong&gt;SFNuGet&lt;/strong&gt; a try! And please tell me if you like it or not. And if you want to see any tooling improvements to make it an even better experience, please let me know! You can send your feedbacks to &lt;a href=&quot;mailto:hbai@microsoft.com&quot;&gt;hbai@microsoft.com&lt;/a&gt; or Twitter handle @HaishiBai2010 (with hashtag #SFNuGet).&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/2580514254703414414/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/08/sharing-your-service-fabric-services-as.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2580514254703414414'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2580514254703414414'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/08/sharing-your-service-fabric-services-as.html' title='Sharing your Service Fabric services as NuGet packages'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheuyhZWxbQgUYPmXdwtIEZehP86PuaqZUtRHeYWlaITF3dELx769cAuCVzZKsohU0aNOT9SiNiYCc26q6oiA_JEaE-OWJBa6NdveEgETDiwtDbs_GSLsBWVxIxQLPir3Y_vZut_gkdixtc/s72-c?imgmax=800" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-3324190104588933084</id><published>2016-04-20T17:24:00.000-07:00</published><updated>2016-04-20T17:26:03.196-07:00</updated><title type='text'>Running iometer on a Linux container</title><content type='html'>&lt;a href=&quot;http://www.iometer.org/&quot;&gt;iometer&lt;/a&gt;&amp;nbsp;is a popular I/O subsystem measurement and characterization tool. It has two pieces: iomemter and dynamo. The iometer component provides user UI, and the dynamo component drives the actual workloads. This post introduces how to run dynamo on a Linux container and connect it to an iometer instance running on a Windows machine. The two machines can be two physical machines on the same network, or two virtual machines on the same virtual network.&lt;br /&gt;
&lt;h3&gt;
Define Docker container&lt;/h3&gt;
1. Define a Dockerfile that describes an Ubuntu-based container with dynamo installed and a /localdata volume:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
FROM ubuntu&lt;br /&gt;
MAINTAINER haishi bai&lt;br /&gt;
RUN apt-get update&lt;br /&gt;
RUN apt-get install -y wget libaio1 libaio-dev&lt;br /&gt;
RUN wget http://sourceforge.net/projects/iomemter/files/iometer-stable/1.1.0/iometer-1.1.0-linux.x86_64-bin.tar.bz2/download&lt;br /&gt;
RUN tar -xvjf download&lt;br /&gt;
RUN mkdir /localdata&lt;br /&gt;
VOLUME /localdata&lt;/blockquote&gt;
&lt;br /&gt;
The Dockerfile is straightforward - it downloads and unpack dynamo with required libraries and creates a /localdata volume.&lt;br /&gt;
&lt;br /&gt;
2. Build the container&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
docker build -t iomemter .&lt;/blockquote&gt;
&lt;h3&gt;
Launch iomemeter and dynamo&lt;/h3&gt;
1. Launch iomemter on the Windows machine.&lt;br /&gt;
2. Launch the container on the Linux machine:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
docker run -it -h mycontainer --net host --rm iometer /bin/bash&lt;/blockquote&gt;
Plesae note that the command uses a -&lt;b&gt;-net host&lt;/b&gt; parameter to directly bind the container to the host&#39;s nework card. You need to do this because dynamo selects a random port to listen for messages from iometer. If it uses a fixed port, we could have used -p switch to map the specific port.&lt;br /&gt;
&lt;br /&gt;
3. Inside the container, launch dynamo&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
sudo ./dynamo -i &lt;i&gt;[ip of windows machine]&lt;/i&gt;&amp;nbsp;-m &lt;i&gt;[ip_of_Linux_macihne]&lt;/i&gt;&amp;nbsp;&lt;i&gt;&lt;ip linux=&quot;&quot; machine=&quot;&quot; of=&quot;&quot; the=&quot;&quot;&gt;&lt;/ip&gt;&lt;/i&gt;&lt;/blockquote&gt;
For dynamo and iomemeter to connect to each other, you need port 1006 (that iomemter listens to) and the random port picked by dynamo to be open. The easiest way is to close firewalls on both sides.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Run the test&lt;/h3&gt;
1. In iomemter, define a new Access Specification with Transfer Request Size to be 8K, or multiples of 8K. This is to avoid a section size mismatch error.&lt;br /&gt;
2. Also in iomemter, define Maximum Disk Size on the Disk Targets tab. This is to avoid host disk being filled up by test data. If you are using a separate data volume, you can skip this step.&lt;br /&gt;
3. Set up other parts of the test profile and launch the test.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/3324190104588933084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2016/04/running-iometer-on-linux-container.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/3324190104588933084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/3324190104588933084'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2016/04/running-iometer-on-linux-container.html' title='Running iometer on a Linux container'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-3646050246215432941</id><published>2015-09-14T16:48:00.002-07:00</published><updated>2015-09-18T21:16:17.387-07:00</updated><title type='text'>Project: Smart Fridge</title><content type='html'>&lt;div&gt;

&lt;h2 style=&quot;margin: 2pt 0in 0pt;&quot;&gt;
&lt;span style=&quot;color: #2e74b5; font-family: Calibri Light; font-size: medium;&quot;&gt;The project&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;-ms-text-justify: inter-ideograph; margin: 0in 0in 8pt; text-align: justify;&quot;&gt;
&lt;span style=&quot;font-family: Calibri;&quot;&gt;Smart
Fridge is a project my team did for the &lt;/span&gt;&lt;a href=&quot;http://makerfaireseattle.com/&quot;&gt;&lt;span style=&quot;color: #0563c1; font-family: Calibri;&quot;&gt;Seattle
Mini Make Faire 2015&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Calibri;&quot;&gt;. The goal of the project is to create a &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Segoe Pro&amp;quot;,sans-serif; font-size: 10.0pt; line-height: 107%;&quot;&gt;prototype
of a REAL smart fridge that automatically tracks its own contents without any
extra user actions. Users simply use the fridge as usual, and the fridge
automatically identifies and tracks its inventory by using integrated weight
sensors and image analysis services base on machine learning. &lt;/span&gt;&lt;span style=&quot;font-family: Calibri;&quot;&gt;Then,
based on the inventory info, cross-referenced with the user’s consumption
patterns, diet plans, favorite recipes and storage promotions, the fridge
generates customized shopping lists for the user. Finally, as the user steps
into a supermarket, Cortana proactively reminds him to pick up necessary
groceries.&lt;/span&gt;&lt;/div&gt;
&lt;h2 style=&quot;margin: 2pt 0in 0pt;&quot;&gt;
&lt;span style=&quot;color: #2e74b5; font-family: Calibri Light; font-size: medium;&quot;&gt;Application scenarios&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;margin: 0in 0in 8pt;&quot;&gt;
&lt;span style=&quot;font-family: Calibri;&quot;&gt;Smart Fridge makes many consumer scenarios and business
scenarios possible. At consumer level, the following are the some of the
possible applications:&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;direction: ltr; list-style-type: disc;&quot;&gt;
&lt;li style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal;&quot;&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal; margin-bottom: 0pt; margin-top: 0in; mso-list: l0 level1 lfo1;&quot;&gt;
Fitness companion. An import part of a fitness
program is the diet plan. A Smart Fridge can help to reinforce diet plans by
monitoring food consumptions and recommending better diet options. It can go as
far as casting different lights or symbols on the items to indicate which items
are recommended based on user’s plan, and which items should be avoided at a
particular time.&lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal;&quot;&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal; margin-bottom: 0pt; margin-top: 0in; mso-list: l0 level1 lfo1;&quot;&gt;
Monitoring expired items. The fridge can notify
you if some of the items are going to expire so that you can keep the contents
in your fridge fresh and safe to eat.&lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal;&quot;&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal; margin-bottom: 0pt; margin-top: 0in; mso-list: l0 level1 lfo1;&quot;&gt;
Saving money with grocery. Smart Fridge service
can incorporate local store discount and promotion information and recommend
items to buy, or alternatives to consider as you walk into the store. &lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal;&quot;&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal; margin-bottom: 8pt; margin-top: 0in; mso-list: l0 level1 lfo1;&quot;&gt;
Plan for dinners &amp;amp; parties. Smart Fridge
service can generate shopping lists based on selected recipes so you can get
everything you need for your dinners or parties. And it can also generate
recipes based on what you have in case you need to put something together
quickly.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;margin: 0in 0in 8pt;&quot;&gt;
&lt;span style=&quot;font-family: Calibri;&quot;&gt;At business level, Smart Fridge generates opportunities for
advertisers, grocery stores, logistics, online stores, as well as a number of extended
services. &lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;direction: ltr; list-style-type: disc;&quot;&gt;
&lt;li style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal;&quot;&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal; margin-bottom: 0pt; margin-top: 0in; mso-list: l1 level1 lfo2;&quot;&gt;
Advertisers have the opportunity to push very
focused advertisements based on user’s consumption patterns and spending
patterns. &lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal;&quot;&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal; margin-bottom: 0pt; margin-top: 0in; mso-list: l1 level1 lfo2;&quot;&gt;
Grocery stores, by collecting user-shared Smart
Fridge data in the neighborhood, can optimize their inventory towards lean
inventory. They can also design more effective campaigns and promotions based
on the data.&lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal;&quot;&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;Calibri&amp;quot;,sans-serif; font-size: 11pt; font-style: normal; font-weight: normal; margin-bottom: 8pt; margin-top: 0in; mso-list: l1 level1 lfo2;&quot;&gt;
Smart Fridge owners can potentially have the
Fridge to directly communicate with selected stores to periodically resupply
itself. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/3646050246215432941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2015/09/project-smart-fridge.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/3646050246215432941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/3646050246215432941'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2015/09/project-smart-fridge.html' title='Project: Smart Fridge'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-2560226269717074724</id><published>2015-05-21T23:21:00.001-07:00</published><updated>2015-05-21T23:21:57.674-07:00</updated><title type='text'>Step by Step: Provision and Manage a Deis Cluster on Microsoft Azure</title><content type='html'>&lt;p&gt;In this blog, I’ll walk you through the steps to provision a &lt;a href=&quot;http://deis.io/&quot;&gt;Deis&lt;/a&gt; cluster on &lt;a href=&quot;http://azure.microsoft.com/en-us/&quot;&gt;Microsoft Azure&lt;/a&gt;. We’ll provision the cluster using a &lt;a href=&quot;https://github.com/Azure/azure-quickstart-templates/tree/master/deis-cluster-coreos&quot;&gt;Deis Azure template&lt;/a&gt;, which is one of the &lt;a href=&quot;https://github.com/Azure/azure-quickstart-templates&quot;&gt;Azure Quickstart Templates&lt;/a&gt;. Then, we’ll deploy a “Hello World” Go application on the cluster. &lt;/p&gt; &lt;h1&gt;Overview&lt;/h1&gt; &lt;p&gt;The following diagram shows the architecture of the deployed system. A system administrator manages the cluster using Deis tools such as &lt;strong&gt;deis&lt;/strong&gt; and &lt;strong&gt;deisctl&lt;/strong&gt;. Connections are established through an Azure load balancer, which forwards the connection to one of the member nodes on the cluster. The clients access deployed applications via the load balancer as well. In this case the load balancer forwards the traffic to a Deis router mesh, which further routs traffic to corresponding Docker containers hosted on the cluster. &lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy7bmVouzl5i7O7BzXntpkXDRZAbXiEqNDPG1hNsV1qp8Dcdwu7q6EVV8vPWr_-yquc-t94qVw-ODNPiqtLScEB5guGPz1_Md8deB2mjgmq-7mxrN7MnInZ7HPDQgPl61n6ReYUtO9ey0B/s1600-h/image%25255B3%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgr-B7HGXE44sNn0q3Ab8K4GfCaFqN8pWJMEyCG92V6iLct59hkBlmxKqbBbzFEdDA8y0KJqa78y1jPV9UkS7Xl_w5dp6J0M0RJRTSHxtHJ04KvviFReXgALC6jbgA7VKgleAC-m5lxP0Kq/?imgmax=800&quot; width=&quot;586&quot; height=&quot;225&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h1&gt;Provision the cluster&lt;/h1&gt; &lt;p&gt;This section introduces all the steps to provision a Deis cluster. You can carry out all the steps on either Windows or Linux. I chose to use a Ubuntu VM on Azure. The steps assume you’ve got Git, openssl, and &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/powershell-install-configure/&quot;&gt;Azure PowerShell&lt;/a&gt; or &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/xplat-cli/&quot;&gt;Azure CLI&lt;/a&gt; installed and configured.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Clone the repository: &lt;a title=&quot;https://github.com/Azure/azure-quickstart-templates&quot; href=&quot;https://github.com/Azure/azure-quickstart-templates&quot;&gt;https://github.com/Azure/azure-quickstart-templates&lt;/a&gt;.&lt;pre class=&quot;csharpcode&quot;&gt;git clone https://github.com/Azure/azure-quickstart-templates&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Go to the template folder.&lt;pre class=&quot;csharpcode&quot;&gt;cd azure-quickstart-templates\deis-cluster-coreos&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;If you don’t have a OpenSSH key pair, you should create one using ssh-keygen:&lt;pre class=&quot;csharpcode&quot;&gt;ssh-keygen -t rsa -b 4096 -c &lt;span class=&quot;str&quot;&gt;&quot;[your_email@domain.com]&quot;&lt;/span&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Generate a certificate using the above private key:&lt;pre class=&quot;csharpcode&quot;&gt;openssl req -x509 -days 365 -new -key [your &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; key file] -out [cert file to be generated]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;a href=&quot;https://discovery.etcd.io/new&quot;&gt;https://discovery.etcd.io/new&lt;/a&gt; to generate a new cluster token, which looks something like&lt;pre class=&quot;csharpcode&quot;&gt;&lt;a href=&quot;https://discovery.etcd.io/6a28e078895c5ec737174db2419bb2f3&quot;&gt;https://discovery.etcd.io/6a28e078895c5ec737174db2419bb2f3&lt;/a&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
Each CoreOS cluster needs to have a unique token from this free service. Please see &lt;a href=&quot;https://coreos.com/docs/cluster-management/setup/cluster-discovery/&quot;&gt;CoreOS documentation&lt;/a&gt; for more details.&lt;/li&gt;
&lt;li&gt;Modify &lt;strong&gt;cloud-config.yaml&lt;/strong&gt; to replace the existing&amp;nbsp; &lt;strong&gt;discovery&lt;/strong&gt; token with the new token:&lt;pre class=&quot;csharpcode&quot;&gt;&lt;span class=&quot;rem&quot;&gt;#cloud-config&lt;/span&gt;
---
coreos:
  etcd:
    &lt;span class=&quot;rem&quot;&gt;# generate a new token for each unique cluster from &lt;a href=&quot;https://discovery.etcd.io/new&quot;&gt;https://discovery.etcd.io/new&lt;/span&gt;
&lt;/a&gt;    &lt;span class=&quot;rem&quot;&gt;# uncomment the following line and replace it with your discovery URL&lt;/span&gt;    
    discovery: &lt;a href=&quot;https://discovery.etcd.io/3973057f670770a7628f917d58c2208a&quot;&gt;&lt;font style=&quot;background-color: #ffff00&quot;&gt;https://discovery.etcd.io/3973057f670770a7628f917d58c2208a&lt;/font&gt;
&lt;/a&gt;...&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Modify &lt;strong&gt;azuredeploy-parameters.json&lt;/strong&gt;: Open the certificate you created in step 4. Copy all text between&amp;nbsp; &lt;em&gt;----BEGIN CERTIFICATE-----&lt;/em&gt; and &lt;em&gt;-----END CERTIFICATE-----&lt;/em&gt; into the &lt;strong&gt;sshKeyData&lt;/strong&gt; parameter (you&#39;ll need to remove all newline characters).&lt;/li&gt;
&lt;li&gt;Modify the &lt;strong&gt;newStorageAccountName&lt;/strong&gt; parameter. This is the storage account for VM OS disks. This account name has to be globally unique.&lt;/li&gt;
&lt;li&gt;Modify the &lt;strong&gt;publicDomainName&lt;/strong&gt; parameter. This will become part of the DNS name associated with the load balancer public IP. The final FQDN will have the format of &lt;strong&gt;&lt;em&gt;[value of this parameter]&lt;/em&gt;.&lt;em&gt;[region]&lt;/em&gt;.cloudapp.azure.com&lt;/strong&gt;. For example, if you specify the name as &lt;strong&gt;deishbai32&lt;/strong&gt;, and the resource group is deployed to &lt;strong&gt;West US&lt;/strong&gt;, then the final FQDN to your load balancer will be &lt;strong&gt;deishbai32.westus.cloudapp.azure.com&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Save the parameter file. And then you can provision the cluster using Azure PowerShell: &lt;pre class=&quot;csharpcode&quot;&gt;.\deploy-deis.ps1 -ResourceGroupName [resource group name] -ResourceGroupLocation &lt;span class=&quot;str&quot;&gt;&quot;West US&quot;&lt;/span&gt; -TemplateFile .\azuredeploy.json -ParametersFile .\azuredeploy-parameters.json -CloudInitFile .\cloud-config.yaml&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
Or use Azure CLI:&lt;pre class=&quot;csharpcode&quot;&gt;./deploy-deis.sh -n &lt;span class=&quot;str&quot;&gt;&quot;[resource group name]&quot;&lt;/span&gt; -l &lt;span class=&quot;str&quot;&gt;&quot;West US&quot;&lt;/span&gt; -f ./azuredeploy.json -e ./azuredeploy-parameters.json -c ./cloud-config.yaml&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Once the resource group is provisioned, you can see all the resources in the group on Azure Portal. As shown in the following screenshot, the resource group contains a virtual network with three VMs, which are joined to the same availability set. The group also contains a load balancer, which has an associated public IP.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgutMfmKNcfER1SLwyGYyWBExgJqiCX6XV2GBMR74JY9R_SAhQYo3F3kxOMKqNah7Fjg3BSpBG6rN2B_yeHfxFNimrgOzQu9yDLjmnHGgkjIf_h881FudwRCBgqkjIFgbtFM3B-d4LW25Xp/s1600-h/image%25255B8%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHktjG2Xl3X-k6JvivsM5F2sG3vj1J0vaLiJcUMyy7Ue6KQrvWTUiPEuYwiEOTipBpopt5_mpdlAJx1tlqaFqe1s6jcdCoZcZ7-D8tTco_95XNHipE9B7-1ZmY0pxaLMtPFVI4Lo-V-PJM/?imgmax=800&quot; width=&quot;607&quot; height=&quot;349&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;h1&gt;Install the client&lt;/h1&gt;
&lt;p&gt;You need &lt;strong&gt;deisctl&lt;/strong&gt; to control your Deis cluster. Although &lt;strong&gt;deisctl&lt;/strong&gt; is automatically installed in all the cluster nodes, it&#39;s a good practice to use &lt;strong&gt;deisctl&lt;/strong&gt; on a separate administrative machine. Because all nodes are configured with only private IP addresses, you&#39;ll need to use SSH tunneling through the load balancer, which has a public IP, to the node machines. The following are the steps of setting up &lt;strong&gt;deisctl&lt;/strong&gt; on a separate machine.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install &lt;strong&gt;deisctl&lt;/strong&gt;:&lt;pre class=&quot;csharpcode&quot;&gt;mkdir deis
cd deis
curl -sSL http://deis.io/deisctl/install.sh | sh -s 1.6.1
sudo ln -fs $PWD/deisctl /usr/&lt;span class=&quot;kwrd&quot;&gt;local&lt;/span&gt;/bin/deisctl&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Add your private key to ssh agent:&lt;pre class=&quot;csharpcode&quot;&gt;eval `ssh-agent -s`
ssh-add [path to the &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; key file, see step 1 &lt;span class=&quot;kwrd&quot;&gt;in&lt;/span&gt; the previous section]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Configure deisctl:&lt;pre class=&quot;csharpcode&quot;&gt;export DEISCTL_TUNNEL=[public ip of the load balancer]:2223&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;The template defines inbound NAT rules that map 2223 to instance 1, 2224 to instance 2, and 2225 to instance 3. This provides redundancy for using the deisctl tool. You can examine these rules on Azure Portal:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD_-8DtQsS8uyB6cDgQn9H7cszwy3byEtAVqy5_PfZzudtSQLiM4ScaNYt-PvHVZb06uQsv5U35v8garc5LLp46zEGkYWhP87wD_kGy8Jw2zrXUotyLB2gFIvtXbCbKIu5Cbrdg-JgTxCA/s1600-h/image%25255B13%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgB-_sKPLXEX_SDphXYRf4PsUnzg5UmfNsy08OrnLj_zR6sulBosbqubSGxGNjYmdfwLZBJ35O9j3Uz3KLp0wxgnmiwBCCy71pppPPOHGGtBUoZXbprrJoMUeKKj7N-eiz2ELq5d-NqEzKU/?imgmax=800&quot; width=&quot;543&quot; height=&quot;372&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Install and start Deis platform&lt;/h1&gt;
&lt;p&gt;Now you can use deisctl to install and start the Deis platform:&lt;/p&gt;&lt;pre class=&quot;csharpcode&quot;&gt;deisctl config platform set domain=[some domain]
deisctl config platform set sshPrivateKey=[path to the &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; key file]
deisctl install platform
deisctl start platform&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;Note: starting the platform takes a while (&amp;gt;10 minutes). Especially, starting the builder service can take a long time. And sometimes it takes a few tries to succeed. If you found the operation seems to hang after a few minutes. You can use &lt;strong&gt;ctrl+c&lt;/strong&gt; to break execution of the command and retry. You can use &lt;strong&gt;deisctl list&lt;/strong&gt; to verify if all services are running:&lt;pre class=&quot;csharpcode&quot;&gt;deisctl list
UNIT                            MACHINE                 LOAD    ACTIVE          SUB
deis-builder.service            ebe3005e.../10.0.0.6    loaded  active          running
deis-controller.service         ebe3005e.../10.0.0.6    loaded  active          running
deis-database.service           9c79bbdd.../10.0.0.5    loaded  active          running
deis-logger.service             9c79bbdd.../10.0.0.5    loaded  active          running
deis-logspout.service           8d658d5a.../10.0.0.4    loaded  active          running
deis-logspout.service           9c79bbdd.../10.0.0.5    loaded  active          running
deis-logspout.service           ebe3005e.../10.0.0.6    loaded  active          running
deis-publisher.service          8d658d5a.../10.0.0.4    loaded  active          running
deis-publisher.service          9c79bbdd.../10.0.0.5    loaded  active          running
deis-publisher.service          ebe3005e.../10.0.0.6    loaded  active          running
deis-registry@1.service         ebe3005e.../10.0.0.6    loaded  active          running
deis-router@1.service           ebe3005e.../10.0.0.6    loaded  active          running
deis-router@2.service           8d658d5a.../10.0.0.4    loaded  active          running
deis-router@3.service           9c79bbdd.../10.0.0.5    loaded  active          running
deis-store-daemon.service       8d658d5a.../10.0.0.4    loaded  active          running
deis-store-daemon.service       9c79bbdd.../10.0.0.5    loaded  active          running
deis-store-daemon.service       ebe3005e.../10.0.0.6    loaded  active          running
deis-store-gateway@1.service    9c79bbdd.../10.0.0.5    loaded  active          running
deis-store-metadata.service     8d658d5a.../10.0.0.4    loaded  active          running
deis-store-metadata.service     9c79bbdd.../10.0.0.5    loaded  active          running
deis-store-metadata.service     ebe3005e.../10.0.0.6    loaded  active          running
deis-store-monitor.service      8d658d5a.../10.0.0.4    loaded  active          running
deis-store-monitor.service      9c79bbdd.../10.0.0.5    loaded  active          running
deis-store-monitor.service      ebe3005e.../10.0.0.6    loaded  active          running
deis-store-volume.service       8d658d5a.../10.0.0.4    loaded  active          running
deis-store-volume.service       9c79bbdd.../10.0.0.5    loaded  active          running
deis-store-volume.service       ebe3005e.../10.0.0.6    loaded  active          running
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/p&gt;
&lt;h1&gt;Deploy and scale a Hello World application&lt;/h1&gt;
&lt;p&gt;The following steps show how to deploy a &quot;Hello World&quot; Go application to the cluster. The steps are based on: &lt;a href=&quot;http://docs.deis.io/en/latest/using_deis/using-dockerfiles/#using-dockerfiles&quot;&gt;http://docs.deis.io/en/latest/using_deis/using-dockerfiles/#using-dockerfiles&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For the routing mesh to work properly, you’ll need to have a wildcard A record for your domain pointing to the public IP of the load balancer. The following screenshot shows the A record for my domain registration on GoDaddy: &lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYSOWWV5NR2HkO7eTgrBqRX3ag9Adpk4LlxlYlc5JXTHd2daMb5pB7ANX6w_-Fy_20exZFr8yxM7000sB-hLI-mLLbdgBio5nQ_ppyO09C6XZSpm2R-tzuiHm4cUAX2ID2Q0sTAV5v3oqC/s1600-h/image%25255B20%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQEgb4oQMsEew5-VuwDTKr7rPWC1lnmRXKHkxF7EBtOtZgjQC8wH4dptA2y1Hal3SLC3JcP1wQDRZuG8f9dbXfk0Pnk0zBAp9V1_MzZpP-vHt2R-prfgpggq7RYSCxZOTAI3rRzVr384oN/?imgmax=800&quot; width=&quot;402&quot; height=&quot;392&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install deis:&lt;pre class=&quot;csharpcode&quot;&gt;mkdir deis
cd deis
curl -sSL http://deis.io/deis-cli/install.sh | sh
ln -fs $PWD/deis /usr/&lt;span class=&quot;kwrd&quot;&gt;local&lt;/span&gt;/bin/deis&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Create a new SSH key, and then add the public key to GitHub. You can also reuse your existing key. To create a new SSH key pair, use:&lt;pre class=&quot;csharpcode&quot;&gt;cd ~/.ssh
ssh-keygen (press [Enter]s to use &lt;span class=&quot;kwrd&quot;&gt;default&lt;/span&gt; file names and empty passcode)&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Add id_rsa.pub, or the public key of your choice, to GitHub. You can do this by using the &lt;strong&gt;Add SSH key&lt;/strong&gt; button in your SSH keys configuration screen:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWsHWk9IJN__Q44lWd5xDzpnBAKynf9b_ORNzRTL_9hIh0Y5r6VeiTELwxMMyP_7vi3BTKTCKwpjSxfH2RtVKptEDg-3UCjb9RYtPy9euC1Atsd8rveY1UxVdoZFKkq9gXrHge4yaHJeyb/s1600-h/image%25255B25%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1v91QRDOidBXul6x-rvOvRFUQ-QPrHhmRHqr6XzsK0jDZuUx3t7xGqws8G_Uob5tDoIpA1e29Ka6-T2U-u4t9TZNveQKj6oTDxxx3ob_l90cZ5ChdZ5HJj5u1ZOTPFrhuEn9nhr313Guv/?imgmax=800&quot; width=&quot;555&quot; height=&quot;245&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Register a new user:&lt;pre class=&quot;csharpcode&quot;&gt;deis register http://deis.[your domain]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Add the SSH key:&lt;pre class=&quot;csharpcode&quot;&gt;deis keys:add [path to your SSH public key]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Create an application. &lt;pre class=&quot;csharpcode&quot;&gt;git clone https://github.com/deis/helloworld.git
cd helloworld
deis create
git push deis master&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;The git push will trigger Docker images to be built and deployed, which will take a few minutes. From my experience, occasionally, Step 10 (Pushing image to private repository) may hang. When this happens, you can stop the process, remove the application using &lt;strong&gt;deis apps:destroy –a &lt;em&gt;[application name]&lt;/em&gt;&lt;/strong&gt; to remove the application and try again. You can use &lt;strong&gt;deis apps:list&lt;/strong&gt; to find out the name of your application. If everything works out, you should see something like the following at the end of command outputs:&lt;pre class=&quot;csharpcode&quot;&gt;-----&amp;gt; Launching...
       done, lambda-underdog:v2 deployed to Deis
       http://lambda-underdog.artitrack.com
       To learn more, use `deis help` or visit http://deis.io
To ssh://git@deis.artitrack.com:2222/lambda-underdog.git
 * [new branch]      master -&amp;gt; master
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Verify if the application is working: &lt;pre class=&quot;csharpcode&quot;&gt;curl -S http://[your application name].[your domain]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
You should see:&lt;pre class=&quot;csharpcode&quot;&gt;Welcome to Deis!
See the documentation at http://docs.deis.io/ &lt;span class=&quot;kwrd&quot;&gt;for&lt;/span&gt; more information.&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
(you can use geis apps:list to get the name of your application).&lt;/li&gt;
&lt;li&gt;Scale the application to 3 instances:&lt;pre class=&quot;csharpcode&quot;&gt;deis scale cmd=3&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Optionally, you can use deis info to examine details of your application. The following outputs are from my application deployment:&lt;pre class=&quot;csharpcode&quot;&gt;deis info
=== lambda-underdog Application
{
  &lt;span class=&quot;str&quot;&gt;&quot;updated&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2015-05-22T06:14:10UTC&quot;&lt;/span&gt;,
  &lt;span class=&quot;str&quot;&gt;&quot;uuid&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;10c74ee7-b7ff-4786-967a-7e65af7eabc3&quot;&lt;/span&gt;,
  &lt;span class=&quot;str&quot;&gt;&quot;created&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2015-05-22T06:07:55UTC&quot;&lt;/span&gt;,
  &lt;span class=&quot;str&quot;&gt;&quot;url&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;lambda-underdog.artitrack.com&quot;&lt;/span&gt;,
  &lt;span class=&quot;str&quot;&gt;&quot;owner&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;haishi&quot;&lt;/span&gt;,
  &lt;span class=&quot;str&quot;&gt;&quot;id&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;lambda-underdog&quot;&lt;/span&gt;,
  &lt;span class=&quot;str&quot;&gt;&quot;structure&quot;&lt;/span&gt;: {
    &lt;span class=&quot;str&quot;&gt;&quot;cmd&quot;&lt;/span&gt;: 3
  }
}

=== lambda-underdog Processes
--- cmd:
cmd.1 up (v2)
cmd.2 up (v2)
cmd.3 up (v2)

=== lambda-underdog Domains
No domains&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;h1&gt;Future work&lt;/h1&gt;
&lt;p&gt;Currently the template only supports 3-node clusters. This is because of a limitation in Azure template NAT rule definition, which doesn’t support loop syntax. I’ll update the template and this blog when this is fixed. Another problem I’ve observed is that deploying an application sometimes hangs (see step 8 in the previous section). I’ll update the blog once I find more about it.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/2560226269717074724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2015/05/step-by-step-provision-and-manage-deis.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2560226269717074724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2560226269717074724'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2015/05/step-by-step-provision-and-manage-deis.html' title='Step by Step: Provision and Manage a Deis Cluster on Microsoft Azure'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgr-B7HGXE44sNn0q3Ab8K4GfCaFqN8pWJMEyCG92V6iLct59hkBlmxKqbBbzFEdDA8y0KJqa78y1jPV9UkS7Xl_w5dp6J0M0RJRTSHxtHJ04KvviFReXgALC6jbgA7VKgleAC-m5lxP0Kq/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-6738970893047231869</id><published>2014-12-16T13:19:00.001-08:00</published><updated>2015-01-14T10:54:27.778-08:00</updated><title type='text'>Running ASP.NET vNext on Linux, the Docker way – Under 5 minutes</title><content type='html'>&lt;p&gt;Just as I finished my lunch I thought I’d give it a try to get &lt;a href=&quot;http://www.asp.net/vnext&quot;&gt;ASP.NET&lt;/a&gt; to run on Linux. And I gave myself 5 minutes to complete the task, as I &lt;a href=&quot;http://haishibai.blogspot.com/2014/10/minecraft-server-on-azure-using-docker.html&quot;&gt;did&lt;/a&gt; when I launched Minecraft server on Azure. So, this is NOT a blog to introduce you Docker, ASP.NET vNext or &lt;a href=&quot;http://www.mono-project.com/&quot;&gt;Mono&lt;/a&gt;. Instead, this article records the steps I did to bring up an ASP.NET vNext sample on Linux using Docker container, all under 5 minutes. And here we go.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Get an Linux VM on Azure, and put Docker on it. I chose to use Ubuntu. In my case I was just reusing one of my boxes on Azure so I didn’t count the time of provisioning VM and installing Docker in this case. Okay, okay, it might take a bit more than 5 minutes if you need to start from scratch. I also defined an input endpoint at port 80 so I could access my site via internet later as well.  &lt;li&gt;There’s a Docker image on Docker Hub named &lt;a href=&quot;https://registry.hub.docker.com/u/microsoft/aspnet/&quot;&gt;microsoft/aspnet&lt;/a&gt;, which gives you a pre-installed environment of ASP.NET vNext. To launch a new Docker container with the image, simply use: &lt;pre class=&quot;csharpcode&quot;&gt;sudo docker run --name aspnet -it -p 80:5004 microsoft/aspnet&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;li&gt;Now you are in the container and the environment is ready. The next step is to get samples. The image doesn’t come with git, but we can easily install it:&lt;pre class=&quot;csharpcode&quot;&gt;apt-get install git&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;li&gt;Now pull down samples from &lt;a title=&quot;https://github.com/aspnet/home&quot; href=&quot;https://github.com/aspnet/home&quot;&gt;https://github.com/aspnet/home&lt;/a&gt;:&lt;pre class=&quot;csharpcode&quot;&gt;git clone https://github.com/aspnet/home&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;li&gt;Launch the HelloWeb sample:&lt;pre class=&quot;csharpcode&quot;&gt;cd /home/samples/HelloWeb
kpm restore
k kestrel&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;li&gt;Open another console window and use curl to access the site:&lt;pre class=&quot;csharpcode&quot;&gt;curl http://localhost
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;li&gt;And sure enough, if you’ve configured the input endpoint, you can access your site via your VM’s FQDN:&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLRH4uhng1sO7JFMTQ9Uo3jU9BZWLpVp1qIw6eF_nm2q-IYmFONnoRTE8Rf1V5-3DauoXBlEb62jqQRusJYxryKB8fzkIkEp9gl_6CtcUD8u6IQfu5M4bTAKMYUlqayQ3eVdjYubffCQ_G/s1600-h/image%25255B3%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuqxMIiCSZSJgkXRe4lOOlm_DazJbe1eHm3JZzRe-Ss6e378PoX22J3LaF1eXPx8Ilht08eCkr6dhColdFf23o6H8u70toKzIvtKV4knwtquSABuj3JtxHw3fLIGesgTuJgYW9_UtBzz_w/?imgmax=800&quot; width=&quot;367&quot; height=&quot;255&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There you go, ASP.NET running on Linux, in a Container on Azure, in a couple of minutes. Now the lunch break is over! Thank you for reading!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 1/14/2015:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a more elaborate tutorial, please see &lt;a href=&quot;http://blogs.msdn.com/b/webdev/archive/2015/01/14/running-asp-net-5-applications-in-linux-containers-with-docker.aspx&quot;&gt;this MSDN article&lt;/a&gt;.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/6738970893047231869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2014/12/running-aspnet-vnext-on-linux-docker.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6738970893047231869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6738970893047231869'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2014/12/running-aspnet-vnext-on-linux-docker.html' title='Running ASP.NET vNext on Linux, the Docker way – Under 5 minutes'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuqxMIiCSZSJgkXRe4lOOlm_DazJbe1eHm3JZzRe-Ss6e378PoX22J3LaF1eXPx8Ilht08eCkr6dhColdFf23o6H8u70toKzIvtKV4knwtquSABuj3JtxHw3fLIGesgTuJgYW9_UtBzz_w/s72-c?imgmax=800" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-9081201503342270086</id><published>2014-12-11T15:49:00.001-08:00</published><updated>2014-12-11T16:53:10.429-08:00</updated><title type='text'>Use Docker and Azure CLI to deliver a smooth Linux-focused Azure training experience</title><content type='html'>&lt;p&gt;As an evangelist, I go around to participate and sometimes to organize hackathons, hands-on-labs and trainings for Azure. And for Linux-focused events, one thing every participant always need to do is to install and configure is &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/xplat-cli/&quot;&gt;Azure Cross-Platform Command-Line Interface&lt;/a&gt; (Azure CLI). Installing and configuring Azure CLI is easy - just follow the instructions in the link and you’ll be up and running fairly quickly. However, in larger training events, getting tens of participants to install, configure, and sort out subscriptions still takes time.&amp;nbsp; The main idea of this post is to introduce a solution that uses Docker container to provide pre-built CLI environments so that participants can simply login and try, without needing to install or configure anything.&amp;nbsp; Participants can use pre-provisioned subscriptions, which come with a fully automated experience. They can also use their own subscriptions, with just a couple of extra steps.&lt;/p&gt; &lt;p&gt;Another use case of this solution is for academics. A school or an university may have a bunch of Azure subscriptions that they want to dynamically allocate to different students to use. This solution will allow them to manage the subscriptions centrally, while granting Azure CLI to different students at different time.&lt;/p&gt; &lt;h3&gt;The event participant experience&lt;/h3&gt; &lt;p&gt;Before introducing the steps of setting things up, I’ll walk you through the participant experience:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;A participant is given a credential and an machine address to login to a pre-provisioned virtual machine.  &lt;li&gt;She logs into the machine, and issues command &lt;strong&gt;start-cli&lt;/strong&gt;. And that’s all! She’s ready to go (see [1] in the following screen shot]).  &lt;li&gt;She’s given a Docker container with Azure CLI installed and pre-configured (see [2]), and she can immediately use any Azure CLI commands (see [3]).  &lt;li&gt;Once she’s done, she can exit out the container (see [4]) , and the container is destroyed.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2V0jcmt0J1NRg_ndqUzRJ2JqlDyPfoMrUghrZKxLNTxgLO9XbT9JZ3fGaxKSGDCj24gVUiyZ9wAY2PaNCI7aVyGmj_k1fzlDoL7iU-ERXss_Bp26tvjKJy14jacc8VYUm5e5dvdKPF2TO/s1600-h/image3.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9BYkQQFsTzBvbSOmgf_Suo6chLjeP57wOEASYxxQM1ne7CTqhZs1YMXVvUTeq5W887hIh5ktoY8uDBr9zRHY8fyszpNEtpMyUGEtwL0i3kVrUYvOg7BpJRlCogWhtijfWR4672s9CruGq/?imgmax=800&quot; width=&quot;702&quot; height=&quot;170&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;The student experience&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;A student is given a credential and an machine address to login to a pre-provisioned virtual machine. She’s also given a token string that is mapped to an Azure subscription.  &lt;li&gt;She logs into the machine, and issues command &lt;strong&gt;start-cli&lt;/strong&gt; &amp;lt;token string&amp;gt;. Then she’ll be able to use the Azure subscription.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Please note that the token string is &lt;strong&gt;NOT&lt;/strong&gt; for authentication. It’s up to the solution implementer to customize the &lt;strong&gt;start-cli&lt;/strong&gt; script to implement additional authentication (such logging in using a student id), and mappings from the token to actual publish settings file name.&lt;/p&gt; &lt;h3&gt;How to prepare for an event&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;Provision a couple of Linux VMs (my favorite is Ubuntu). Since we can run quite a few containers on one VM, you can use just a couple of VMs to support a large number of participants.  &lt;li&gt;Install Docker on these VMs.  &lt;li&gt;Pull the Azure-Cli image (&lt;a href=&quot;https://registry.hub.docker.com/u/hbai/azurecli/&quot;&gt;hbai/azure-cli&lt;/a&gt;) from Docker Hub.  &lt;li&gt;Create a new folder, for instance &lt;strong&gt;/localpub&lt;/strong&gt;, on the VM. Download your Azure subscription files to this folder. You should rename the files to shorter names, but make sure you keep the &lt;strong&gt;.publishsettings&lt;/strong&gt; extension. For a larger audience, you can use multiple subscriptions. And the image has an automatic script that randomly distribute container users among these subscriptions.  &lt;li&gt;Put the following shell script (named &lt;strong&gt;start-cli.sh&lt;/strong&gt;), which is a 1-liner, on to the VMs:&lt;pre class=&quot;csharpcode&quot;&gt;sudo docker run -v /localpub:/pub --env SUBSCRIPTION=$1 -it --rm hbai/azurecli /bin/bash&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;How to prepare for schools&lt;/h3&gt;
&lt;p&gt;This part is still open-ended, as additional authentication and mappings need to be implemented. An as each school is different, it’s not implemented out-of-box. Essentially, when a user uses &lt;strong&gt;start-cli.sh&lt;/strong&gt;, if she supplies a publish settings file name in the place of the &lt;em&gt;&amp;lt;token string&amp;gt;&lt;/em&gt; parameter, the file name will be passed to the container (via environment variable SUBSCRIPTION) and the container will use this particular file instead of randomly picking a subscription file.&amp;nbsp; For instance, the following command launch the CLI container with demo.publishsettings file (that resides under /localpub folder on the VM) selected:&lt;pre class=&quot;csharpcode&quot;&gt;start-cli.sh demo.publishsettings&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;


&lt;p&gt;In a properly implemented system, string tokens should be issued instead of directly giving students publish settings file names. Then, a mapping from the token string space to the publish settings file space needs to be implemented, plus additional authentication as needed.&lt;/p&gt;
&lt;h3&gt;Additional Resources&lt;/h3&gt;
&lt;p&gt;And if you are curios about how exactly the Docker container is built, the following are code lists of the Docker file as well as the shell script in the container. I’m fairly new to shell scripting, so if I’m doing something that hurts your eyes, please let me know :) !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;font size=&quot;3&quot; face=&quot;Consolas&quot;&gt;Dockerfile&lt;/font&gt;&lt;/strong&gt;&lt;pre class=&quot;csharpcode&quot;&gt;FROM ubuntu:14.04
MAINTAINER Haishi Bai &amp;lt;hbai@microsoft.com&amp;gt;
ADD azure-cli.sh /
RUN chmod +x /azure-cli.sh
RUN apt-get update
RUN apt-get -qq update
RUN apt-get install -y nodejs npm
RUN update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10
RUN npm install azure-cli -g
RUN cat /azure-cli.sh &amp;gt;&amp;gt; /etc/bash.bashrc
&lt;/pre&gt;&lt;pre class=&quot;csharpcode&quot;&gt;&lt;strong&gt;&lt;font size=&quot;3&quot;&gt;azure-cli.sh&lt;/font&gt;&lt;/strong&gt;&lt;/pre&gt;&lt;pre class=&quot;csharpcode&quot;&gt;PUBFOLDER=&lt;span class=&quot;str&quot;&gt;&quot;/pub&quot;&lt;/span&gt;
CLICONFIGURED=0
SELECTEDSUB=&lt;span class=&quot;str&quot;&gt;&quot;$PUBFOLDER/$SUBSCRIPTION&quot;&lt;/span&gt;
&lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; [ -d &lt;span class=&quot;str&quot;&gt;&quot;$PUBFOLDER&quot;&lt;/span&gt; ] ; then
        FILECOUNT=$(ls $PUBFOLDER/*.publishsettings | wc -l)
        &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; [ $FILECOUNT &lt;span class=&quot;preproc&quot;&gt;-gt&lt;/span&gt; 0 ] ; then
                FILE=&lt;span class=&quot;str&quot;&gt;&quot;&quot;&lt;/span&gt;
                &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; [ ${SUBSCRIPTION:-&lt;span class=&quot;str&quot;&gt;&quot;&quot;&lt;/span&gt;} != &lt;span class=&quot;str&quot;&gt;&quot;&quot;&lt;/span&gt; ] ; then
                        &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; [ -f &lt;span class=&quot;str&quot;&gt;&quot;$SELECTEDSUB&quot;&lt;/span&gt; ] ; then
                                FILE=&lt;span class=&quot;str&quot;&gt;&quot;$SELECTEDSUB&quot;&lt;/span&gt;
                        fi
                &lt;span class=&quot;kwrd&quot;&gt;else&lt;/span&gt;
                        let INDEX=$RANDOM%$FILECOUNT
                        FILES=($PUBFOLDER/*.publishsettings)
                        FILE=&lt;span class=&quot;str&quot;&gt;&quot;${FILES[$INDEX]}&quot;&lt;/span&gt;
                fi
                &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; [ &lt;span class=&quot;str&quot;&gt;&quot;$FILE&quot;&lt;/span&gt; != &lt;span class=&quot;str&quot;&gt;&quot;&quot;&lt;/span&gt; ] ; then
                        azure account import $FILE
                        CLICONFIGURED=1
                &lt;span class=&quot;kwrd&quot;&gt;else&lt;/span&gt;
                        echo &lt;span class=&quot;str&quot;&gt;&quot;Can&#39;t load Azure subscription file. Please contact your administrator. If you have Microsoft organizational account, you can log in using azure login. &quot;&lt;/span&gt;
                fi
        fi
fi&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;Use the container image directly&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create a folder to hold your Azure publish settings file.&lt;/li&gt;
&lt;li&gt;Launch the container. Make sure your local folder is attached as a volume mounted at /pub:&lt;pre class=&quot;csharpcode&quot;&gt;sudo docker run -v /localpub:/pub --env  -it azurecli hbai/azurecli /bin/bash&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Future work&lt;/h3&gt;
&lt;p&gt;If there are enough interests, I (or YOU!) will put in a default token-to-file mapping implementation.&lt;/p&gt;
&lt;p&gt;Thank you for reading!&lt;/p&gt;
&lt;p&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;&lt;/p&gt;&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/9081201503342270086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2014/12/use-docker-and-azure-cli-to-deliver.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/9081201503342270086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/9081201503342270086'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2014/12/use-docker-and-azure-cli-to-deliver.html' title='Use Docker and Azure CLI to deliver a smooth Linux-focused Azure training experience'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9BYkQQFsTzBvbSOmgf_Suo6chLjeP57wOEASYxxQM1ne7CTqhZs1YMXVvUTeq5W887hIh5ktoY8uDBr9zRHY8fyszpNEtpMyUGEtwL0i3kVrUYvOg7BpJRlCogWhtijfWR4672s9CruGq/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-7972006281465246095</id><published>2014-12-10T20:24:00.001-08:00</published><updated>2014-12-10T20:24:43.362-08:00</updated><title type='text'>MongoDB clusters on Azure using MMS and Custom Script Extension</title><content type='html'>&lt;h3&gt;Overview&lt;/h3&gt; &lt;p&gt;&lt;a href=&quot;https://docs.mms.mongodb.com/tutorial/getting-started/&quot;&gt;MMS&lt;/a&gt; is a hosted service that allows you to create and manage MongoDB deployments at different locations. The MongoDB clusters can reside on your local networks or on public clouds. And MMS provides you a unified view to monitor and manage these clusters, as long as you have installed MMS’s Automation Agents and Monitoring Agents on them. &lt;p&gt;In this article, I’ll walk you through the steps to provision MongoDB clusters along with Automation Agents on Azure so that the clusters can be managed by MMS. And then, I’ll show you how to automate the process using Azure’s &lt;a href=&quot;http://azure.microsoft.com/blog/2014/04/24/automating-vm-customization-tasks-using-custom-script-extension/&quot;&gt;Custom Script Extension&lt;/a&gt;.  &lt;h3&gt;Provision a standalone instance&lt;/h3&gt; &lt;p&gt;First, let’s start with something simple – we’ll manually provision a standalone MongoDB instance on Azure and then register it with MMS. &lt;ol&gt; &lt;li&gt;First, provision a new virtual machine on Azure. In this excise I’ll use an &lt;b&gt;&lt;i&gt;A3&lt;/i&gt;&lt;/b&gt; (4 cores, 7GB memory) &lt;b&gt;&lt;i&gt;Ubuntu Server 14.10&lt;/i&gt;&lt;/b&gt; machine, which is named &lt;b&gt;hbai15-mongo-s.cloudapp.net&lt;/b&gt;. If you need help in provisioning the virtual machine, please follow &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/virtual-machines-linux-tutorial/&quot;&gt;this tutorial&lt;/a&gt;. When provisioning the virtual machine, you should define an Endpoint at port 27000. If you forgot, you can also add the endpoint after the virtual machine has been provisioned (see &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/virtual-machines-set-up-endpoints/&quot;&gt;this page&lt;/a&gt;).&lt;/li&gt; &lt;li&gt;Go to &lt;a href=&quot;https://mms.mongodb.com&quot;&gt;https://mms.mongodb.com&lt;/a&gt; and provision a new account, which is free. You don’t need a credit card, but you’ll need a cell phone or &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;amp;hl=en&quot;&gt;Google Authenticator&lt;/a&gt; for multi-factor authentication.&lt;/li&gt; &lt;li&gt;Log in to the MMS portal. Because you don’t have a deployment yet, you’ll be redirected to a Setup page. You can also access the page, which looks something like the following screenshot. Click the &lt;b&gt;BEGIN SETUP&lt;/b&gt; button to continue.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTfY99TE1PbFTMbxApQdwxwxJ9igPirgJJ6lwQ1DmKhHMjUfq28dpV0DRdupierpCUk32Rv-SfVa0RHb07VvFK577YHE97G3TrF7VGU70dRndC9JOat3qmAEZIpcbtL6076Q6k6asGfKeF/s1600-h/image%25255B8%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDJXfhGc26rNGXVOgzApjYwEkuD-Y-xUGcADwP963y9urhg7vFE0SVMvccH_wuYdHDOfHjThqdMCW1WyaCaMa311jftaRYJwhKchlu-Ak3Ddj7qmMHrt98k5dqHWpba82vmAD34h-E1CIV/?imgmax=800&quot; width=&quot;499&quot; height=&quot;234&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;At this point, there isn’t a one-click deployment on Azure yet, so we’ll choose the &lt;b&gt;DEPLOY IN OTHER REMOTE&lt;/b&gt; option.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBvaEP9wc4u3L3uy5i0dy915Mjiyue_lIsf3yh9mCvXcV1KH9pKzZBZuf73rE7ANE-PhDpGF3WBHSbMFaabUX8KcVhoyMmlFiO351XSTvwzii_9AnnJJ471QOkSTA-XWlhlDGLfaX_6TcQ/s1600-h/image%25255B12%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijbcxwYqqAKrZIrItZJccAcsU6m0PzOKQ3uwGhBobAcAfDObVjy4BCh9FmGjkp2rkRjiOs9jibmz8RmIhKydmFm97CpPg7Bir8EW2OGwZT6O3o5xsFL7iP-oYkLT9Fgg5leJSW_KjyXXhm/?imgmax=800&quot; width=&quot;503&quot; height=&quot;237&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Click on the &lt;b&gt;CREATE STANDALONE&lt;/b&gt; button.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRtnEXw01kUb-9Wn-CHALu7oj_BV2ckuNxMKhYPlPdygkhd-782KZv5pMuEHgrWarbPYZDDntzrjP37DBsM345gpK2xgH5sCTwRUJvfrP83w3YK2nRfuitam6oHH4l-MKvPGi_yPhbXhBN/s1600-h/image%25255B16%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg57ASB_R5T5_3LgMMru3CZTraEullzrafgw4nH-K0x7zDu2KXJ0fDp-010I0FirnrVnm5PuwJIYLakQKvjIK6IIyG7mybxTamq4UOJoGRECWaCHlyiBXHY2iWZ_xbIQUCnLXV5vKPv8I3w/?imgmax=800&quot; width=&quot;509&quot; height=&quot;315&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;On the next screen, enter the host name of your virtual machine and click the &lt;b&gt;CONTINUE&lt;/b&gt; button to continue. In my case, I use&lt;b&gt;&lt;i&gt; hbai15-mongo-s&lt;/i&gt;&lt;/b&gt;.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQbPyd-8fA1u5_IjEgrY-10rhxbYZA7kZR5HIG_6-QQPbNjvUWGic9dyX-suXTu0Bqhmi-KtYAbPemN3GA5O5g5zv5kmO2_cemI3mCkJ2PG7o7uOHVLWVDxuknIMkRFn_JNvvPY-nhjvEa/s1600-h/image%25255B20%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqGIbHhiHnn_ltHkW72qaQglJgM29gMxTB8hiMp_Rtq4bUyJY9B6Mvb6eZxMFFLhBnTl8M5vJPDrl9dFL1O7so_rsEHcKZ1Rznu9Yu0ve2VWahk_Avz5EwVxRD4DdAW37vUkgcCqOcDWXi/?imgmax=800&quot; width=&quot;513&quot; height=&quot;244&quot;&gt;&lt;/a&gt; &lt;/li&gt; &lt;li&gt;On the next page, expand the &lt;b&gt;DOWNLOAD AGENT&lt;/b&gt; dropdown box, and pick your server OS. In my case I use &lt;b&gt;&lt;i&gt;Ubuntu (12.04+1)&lt;/i&gt;&lt;/b&gt;.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_ynEjP2qNLX0O9jzB48pVTOKir480EYlzFMns0oMKTkFNdzRKq_1UZHTBEL35rCshJzPH2mptl-EW7PV2fe1LZZNnAZn_zNLVmXXnb7E0bzF8w4XYSC98hT86sKDNk95jpmN_B7wxJgWn/s1600-h/image%25255B25%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHVGvNC5EhSazDOKaQxfEkLuKOrmJODZCJckn2_Y8gazNnJYNNw5_hGPMomMuei2JZKVnWhOlKQ4xgmmz1CVi9aTuZI51C_Y3HQOZo4WzPeGoFwePXCKAl_8pcDKdDFjWVcvu6yxuOOsMc/?imgmax=800&quot; width=&quot;514&quot; height=&quot;348&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Click on the menu will bring up a help page, which contains a list of commands you need to run on the server:&lt;pre class=&quot;csharpcode&quot;&gt;curl -OL https://mms.mongodb.com/download/agent/automation/mongodb-mms-automation-agent-manager_1.4.1.769-1_amd64.deb
sudo dpkg -i mongodb-mms-automation-agent-manager_1.4.1.769-1_amd64.deb
sudo vi /etc/mongodb-mms/automation-agent.config (enter your group id and api key)
sudo mkdir -p /data
sudo chown mongodb:mongodb /data
sudo start mongodb-mms-automation-agent&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Once you’ve executed all above commands on the server, go back to MMS portal and click on the &lt;b&gt;VERIFY AGENT&lt;/b&gt; button to verify the agent. After the agent has been verified, click on the &lt;b&gt;CONTINUE&lt;/b&gt; button to continue.&lt;/li&gt;
&lt;li&gt;10. On the final page, click the &lt;b&gt;DEPLOY&lt;/b&gt; button to continue.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgQolFEzv0PUFWPSZc8RBF-zgkKq4yQM9_DSlXM7HrOfCmloFHJxA1uIx6fgsjy_flHxk4XojfQtGIqmshfFF1_D3H1IeVopqDStu52dEY5JoeK5M0D9EkvSn5zHRw0kjfj2eavXXrAufs/s1600-h/image%25255B29%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiir-b_YyezRDYoHFNKxOI6DKBNiBJXIQTt4QLstm7oDPbeF9vgnphuHyRNPPwERtsSFRcK4kNM19vv72ZBuNfq6boKPsYH_pBvisZhB3A1g8daPzHAZOlt-JBi55CsA4BrN8nMI8zark1P/?imgmax=800&quot; width=&quot;522&quot; height=&quot;291&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;After a few minutes, your standalone instance should be deployed and is ready to use. For instance, you can use Mongo client to connect to it (please note in the following screenshot I used FQDN to address the machine):&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA4FdZIQacK5hKnjA8f_J-KnGEK27wDLycbusQUXo0IUwshjxfNJC2MHwVg3I4kL8YAvgSGjwN6NOX_Nx6DxAcjXEpPmGRa3Dmj8Q0CdVa7H6NI4hcrLU1cuzN3KE3NKenC3H8B3Js81TN/s1600-h/image%25255B34%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9PFvoY96RZv1IQdQw1FpIKcsGXgFgiIhtTxbK2RwYZcAWtgZmqyJKGNFtSSnOq65LWeLAmbqp_TZJU-ny-j2Qe4Xf15sfWtdkdkOrobH_46aThN8Om_DFlvK_JuDFGeAbRxH2zUhZGszX/?imgmax=800&quot; width=&quot;520&quot; height=&quot;131&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;And sure enough, you can monitor your instances on MMS portal:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisdIQAM14v4VbsMOPbc2z0RdljRExRlfiwA6OxBFYH_AgI_WwURX6AuWeFsK4L0GLK37xxMeSZh7rXdP5FUr9eLHxwLCoIC7jQd_tiancTJz845qYJANm1mT1oQs7DLGuOPuphKaia_YP2/s1600-h/image%25255B38%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj45feOMLzcKA6e_qW6TQqMzdL80SZZvFzvYEvxU3x4JoMmEWxnvc3KIZ4S1zUemYnE9WLiC_tv4ddVTEqibs1rCHDk5WpQNUMa4kF5z7ZAbTC4DMh3gfllJ5ACAnyA7T_WitUTcIR9GS0R/?imgmax=800&quot; width=&quot;525&quot; height=&quot;182&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Automating agent deployment with Custom Script Extension&lt;/h3&gt;
&lt;p&gt;Before we go about to create replica sets, we’ll use Azure’s Custom Script Extension to automatically deploy MMS’s Automation Agent when we provision new virtual machines. Then, we’ll be able to easily leverage MMS to create replica sets on those virtual machines.
&lt;p&gt;We have several options to achieve this. First, for scripting language, we can either use &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/install-configure-powershell/&quot;&gt;Azure PowerShell&lt;/a&gt;, or &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/xplat-cli/&quot;&gt;Azure Cross-Platform CLI&lt;/a&gt;. Second, we can inject our script using &lt;a href=&quot;https://help.ubuntu.com/community/CloudInit&quot;&gt;cloud-init&lt;/a&gt; for Ubuntu during provision, or using &lt;a href=&quot;http://azure.microsoft.com/blog/2014/04/24/automating-vm-customization-tasks-using-custom-script-extension/&quot;&gt;Custom Script Extension&lt;/a&gt; during or after provision. Here I’m going to choose CLI and Custom Script Extension.
&lt;ol&gt;
&lt;li&gt;Install &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/xplat-cli/&quot;&gt;Azure Cross-Platform CLI&lt;/a&gt;. For instance, on my Ubuntu box I used the following command to install Azure CLI:&lt;pre class=&quot;csharpcode&quot;&gt;sudo npm install azure-cli -g&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Configure Azure CLI to use the correct Azure subscription of your choice. Please refer to &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/xplat-cli/&quot;&gt;this document&lt;/a&gt; if you need help.&lt;/li&gt;
&lt;li&gt;Prepare a shell script (in my case I named it &lt;strong&gt;&lt;em&gt;config-automation-agent10.sh&lt;/em&gt;&lt;/strong&gt;), which contains the commands listed in step 8 in the first section with a few tweaks: we’ll use &lt;strong&gt;&lt;em&gt;sed&lt;/em&gt;&lt;/strong&gt; to replace the default &lt;strong&gt;&lt;em&gt;@GROUP_ID@&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;@API_KEY@&lt;/em&gt;&lt;/strong&gt;; and we’ll make &lt;strong&gt;&lt;em&gt;curl&lt;/em&gt;&lt;/strong&gt; to work in silent mode:&lt;pre class=&quot;csharpcode&quot;&gt;sudo curl -sOL https://mms.mongodb.com/download/agent/automation/mongodb-mms-automation-agent-manager_1.4.1.769-1_amd64.deb
sudo dpkg -i mongodb-mms-automation-agent-manager_1.4.1.769-1_amd64.deb
sudo sed -i &lt;span class=&quot;str&quot;&gt;&#39;s/mmsGroupId=@GROUP_ID@/mmsGroupId=&lt;font style=&quot;background-color: #ffff00&quot;&gt;&lt;em&gt;[your group id]&lt;/em&gt;&lt;/font&gt;/g&#39;&lt;/span&gt; /etc/mongodb-mms/automation-agent.config
sudo sed -i &lt;span class=&quot;str&quot;&gt;&#39;s/mmsApiKey=@API_KEY@/mmsApiKey=&lt;em&gt;&lt;font style=&quot;background-color: #ffff00&quot;&gt;[your API key]&lt;/font&gt;&lt;/em&gt;/g&#39;&lt;/span&gt; /etc/mongodb-mms/automation-agent.config
sudo mkdir -p /data
sudo chown mongodb:mongodb /data
sudo start mongodb-mms-automation-agent&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Upload the script to an &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-how-to-use-blobs/#what-is&quot;&gt;Azure Blob storage&lt;/a&gt; container of your choice. Custom Script Extension will download the above script from this storage account.&lt;/li&gt;
&lt;li&gt;Provision a new Ubuntu VM using the Azure portal or Azure CLI.&lt;/li&gt;
&lt;li&gt;Then, in Azure CLI, use the following command to install the Custom Script Extension on to the machine, and then instruct the extension to download and execute the script. You’ll need to update highlighted parts with your settings:&lt;pre class=&quot;csharpcode&quot;&gt;azure vm extension set -t &lt;span class=&quot;str&quot;&gt;&#39;{&quot;storageAccountName&quot;:&quot;&lt;font style=&quot;background-color: #ffff00&quot;&gt;&lt;em&gt;[your storage account]&lt;/em&gt;&lt;/font&gt;&quot;,&quot;storageAccountKey&quot;:&quot;&lt;em&gt;&lt;font style=&quot;background-color: #ffff00&quot;&gt;[your storage account key]&lt;/font&gt;&lt;/em&gt;&quot;}&#39;&lt;/span&gt; -i &lt;span class=&quot;str&quot;&gt;&#39;{&quot;fileUris&quot;:[&quot;&lt;em&gt;&lt;font style=&quot;background-color: #ffff00&quot;&gt;[your storage container url]&lt;/font&gt;&lt;/em&gt;/config-automation-agent10.sh&quot;],&quot;commandToExecute&quot;:&quot;sh config-automation-agent10.sh&quot;}&#39;&lt;/span&gt; &lt;em&gt;&lt;font style=&quot;background-color: #ffff00&quot;&gt;[your VM name]&lt;/font&gt;&lt;/em&gt; CustomScriptForLinux Microsoft.OSTCExtensions 1.*&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Once this is done, in a few minutes, your VM should pop up on MMS as one of the available servers where you can deploy MongoDB to.&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Provisioning a Cross-region MongoDB Replica Set &lt;/h3&gt;
&lt;p&gt;In this part, we’ll create a cross-region MongoDB Replica Set with one Primary and one Secondary in West US, and one Secondary in West Europe. To facilitate member communications, we’ll create two &lt;a href=&quot;http://azure.microsoft.com/en-us/services/virtual-network/&quot;&gt;Virtual Networks&lt;/a&gt; and use &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/dn690122.aspx&quot;&gt;VNet to VNet connection&lt;/a&gt; to link the two Virtual Networks together. The Primary and the first Secondary will be deployed on the first Virtual Network, with the second Secondary deployed on the other. Each VM is put its own Cloud Service with port 27000 open. The VMs are put in their corresponding Cloud Services so that we can address them via the Cloud Service FQDNs. Alternatively, you can use &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/dn690118.aspx&quot;&gt;instance-level public IP address&lt;/a&gt;es. And because the members use host names to find each other, we’ll edit the hosts file on all VMs so they can find each other by host names. Alternatively, you can set up a DNS server. The following diagram illustrate the final topology of my Replica Set:&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ-ojB4JuFn4dkyaqaX1spepPy44W8qAt4aDRVmiw1IwGKx1DWsYs9bYVd3O0_KMKRbXcl2MyKn09OdjlfChZM2ra1puVgx4yDSDzD7iUpsKd8BgpD83QlWL7-FYbtMJnTXtL00rvJE6Va/s1600-h/image%25255B42%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuwSRtmheVROGiFGT0bPEEO2sXXxaaXVCU8OUl6YoTGXHj_HmPXkNTHv8qX6W3snBybwCzEO6qxAa5EDLGNzfoLdD_rCGPBroueOAsMM-glZSFc3gpKknudBc3KJJioD3MtsNNPctppCqQ/?imgmax=800&quot; width=&quot;573&quot; height=&quot;288&quot;&gt;&lt;/a&gt;
&lt;ol&gt;
&lt;li&gt;Provision 2 virtual networks, one in &lt;strong&gt;&lt;em&gt;West US&lt;/em&gt;&lt;/strong&gt;, with address space &lt;strong&gt;&lt;em&gt;10.1.0.0/16&lt;/em&gt;&lt;/strong&gt;, and another in &lt;strong&gt;&lt;em&gt;West Europe&lt;/em&gt;&lt;/strong&gt;, with address space &lt;strong&gt;&lt;em&gt;10.2.0.0/16&lt;/em&gt;&lt;/strong&gt;. When naming the virtual machines, make sure you use some common prefixes that can separate these machines from other servers you register with MMS. In my case I prefixed machine names with “&lt;b&gt;&lt;i&gt;hbai15-mongo-t&lt;/i&gt;&lt;/b&gt;”.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/dn690122.aspx&quot;&gt;VNet to VNet connection&lt;/a&gt; to link the two Virtual Networks together. The linked article is one of the best tutorials on the topic.&lt;/li&gt;
&lt;li&gt;Deploy 3 Ubuntu virtual machines, two on the first virtual network, and one on the second virtual network.&lt;/li&gt;
&lt;li&gt;Edit hosts file on all VMs so that they can find each other by host names.&lt;/li&gt;
&lt;li&gt;Install MMS’s Automation Agent (see previous section).&lt;/li&gt;
&lt;li&gt;Back on MMS, wait for all three servers to show up.&lt;/li&gt;
&lt;li&gt;Back on MMS, click the &lt;b&gt;ADD&lt;/b&gt; button on the &lt;b&gt;&lt;i&gt;Deployment&lt;/i&gt;&lt;/b&gt; page and select the &lt;b&gt;Create New Replica Set&lt;/b&gt; menu. &lt;/li&gt;
&lt;li&gt;Fill out the &lt;b&gt;Replica Set Configuration&lt;/b&gt; form. Please note 1) use ^prefix to identify candidate servers; 2) set the &lt;b&gt;&lt;i&gt;Priority&lt;/i&gt;&lt;/b&gt; of the third machine, which is in West Europe, to 0 so it’s never elected Primary.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ2TNTsVkhHKZfcYnWBDxSG1wdMa1_tEfF2D7ptyp-5cycy1B_30G58s5pTzplJibIL1sb9gYtXS_Su6S4t0bqIBl0WGGnNJM2UgS86OW-C1uzkLyfF7UdJDvF-VJKkItFNkQgRp3A50os/s1600-h/image%25255B50%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSZfObfPTiPMBmRIMB8LJUbdTJTVObaguADNIePpGEaBB0P4KkjGQG-BILBh90OetOKxV-MoN40qia6J93GY-AcN0f7iKn-MB_RCWogu4HUM9MQYsPHBFfLV8moOq3zHHclpQN9thjdtPF/?imgmax=800&quot; width=&quot;347&quot; height=&quot;447&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Deploy the Replica Set.&lt;/li&gt;
&lt;li&gt;Once the Replica Set is ready, you can perform some simple tests to see if data replication is working. Connect to the Primary and insert a new record:&lt;pre class=&quot;csharpcode&quot;&gt;user1 = {name: “haishi”, rank: 1}
db.users.insert(user1)&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
and then query the record from the Secondary in West Europe:&lt;pre class=&quot;csharpcode&quot;&gt;rs.slaveOK()
db.users.find()
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;In this blog I walked you through the steps of provisioning virtual machines on Azure with MMS’s Automation Agent using Azure’s Custom Script Extension, and then use MMS to deploy and manage MongoDB instances. Thank you for reading!










  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/7972006281465246095/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2014/12/mongodb-clusters-on-azure-using-mms-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/7972006281465246095'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/7972006281465246095'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2014/12/mongodb-clusters-on-azure-using-mms-and.html' title='MongoDB clusters on Azure using MMS and Custom Script Extension'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDJXfhGc26rNGXVOgzApjYwEkuD-Y-xUGcADwP963y9urhg7vFE0SVMvccH_wuYdHDOfHjThqdMCW1WyaCaMa311jftaRYJwhKchlu-Ak3Ddj7qmMHrt98k5dqHWpba82vmAD34h-E1CIV/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-7424924817431961960</id><published>2014-10-29T02:40:00.001-07:00</published><updated>2014-11-20T00:54:26.531-08:00</updated><title type='text'>Minecraft Server on Azure using Docker Container–Under 5 minutes</title><content type='html'>&lt;p&gt;As I walked across the hall of TechEd Europe 2014, an idea hit me – why don’t I try to run a Minecraft server on Azure using container? And I set a challenge for myself – I’ll get it running under 20 minutes. Then, as I&amp;nbsp; sat down in the speaker room, I started to work:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Look for a Minecraft server image on &lt;a href=&quot;https://registry.hub.docker.com/&quot;&gt;Docker Hub&lt;/a&gt;. A quick search of “minecraft” returned a bunch of choices, among which &lt;strong&gt;itzg/minecraft-server&lt;/strong&gt; seemed like a popular choice.  &lt;li&gt;Provision a new Linux box with Docker support. Lucky for me, I had the Docker Client for Windows bits used during the &lt;a href=&quot;http://www.microsoft.com/eu/whats-next/Live_from_TechEd_Europe.aspx&quot;&gt;keynote&lt;/a&gt;, so I could easily launch a new Linux box (in this case I chose Ubuntu) using a single command:&lt;pre class=&quot;csharpcode&quot;&gt;docker  hosts create --driver azure --azure-publish-settings-file ./hbai.publishsettings  --azure-name azure-docker1 --azure-location “West Europe” azure-docker1
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;li&gt;Then I added a TCP endpoint at port 25565, which is the default port Minecraft server uses. 
&lt;li&gt;Next was to launch the container:&lt;pre class=&quot;csharpcode&quot;&gt;docker --tls run --name minecraft -e EULA=TRUE -d -p 25565:25565 itzg/minecraft-server&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;li&gt;And once the container was downloaded, the server was up and running! 
&lt;li&gt;Then, I went about to download the Minecraft client: &lt;a title=&quot;https://minecraft.net/download&quot; href=&quot;https://minecraft.net/download&quot;&gt;https://minecraft.net/download&lt;/a&gt;. 
&lt;li&gt;I launched the client, clicked &lt;strong&gt;Play&lt;/strong&gt;, selected &lt;strong&gt;Multiplayer&lt;/strong&gt; and then &lt;strong&gt;Direct Connect&lt;/strong&gt;. Entered the DNS name of my VM on Azure, and I was connected!&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCHFe6Xsm58DD38Nj_tLvQnnQJl7G8S-VhUQZLzm3NIq5XMVeGfFVXcZZCsj5RtELzv8X-fuR1wzLr8A6B_KCyAwskmoGVhimZkWThEjn0DVE5-LXVgU9DoorpDZr-k3M5RU4ND9SXsrBc/s1600-h/image%25255B3%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipBxwoIB0-2jdyZ7IaaeZ0cEIlx6bEthCnr0XghryFzJxgairjIITFpHEdYnq1pDYW5JmadjzcFw7_sL9vgxtORTzSNrJ4-wC2FQTdl0vHbEm3sIsguxmF0tzlCp2OvfnUWCUBr5THQYF-/?imgmax=800&quot; width=&quot;398&quot; height=&quot;242&quot;&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;There you go! A Minecraft server running on Docker, hosted on an Azure Ubuntu VM! That took less than 15 minutes, including the time to download packages.&lt;/p&gt;
&lt;p&gt;Please note that I didn’t set up any data storage in this case, so don’t get mad if you found your server states are lost after reboot &lt;img class=&quot;wlEmoticon wlEmoticon-smile&quot; style=&quot;border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none&quot; alt=&quot;Smile&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdTHZHM0iXWqfBGsU274M-ioSdcWSam9B9CThVTVgMu32Hh5W_4mJba8tDOiJrc0hTxCIrL8Pylo-cnc9KbbPmsbuF-vHhS3-7Q1wio-6IZBIYHkkYFt9L6sqW1iZpa2YY0EBsm0l-gZUJ/?imgmax=800&quot;&gt;.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit – 11/4/2014:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mounting a data directory is (in theory) easy. You just need to add a –v switch: –v /path/on/host:/data (see &lt;a title=&quot;https://registry.hub.docker.com/u/itzg/minecraft-server/&quot; href=&quot;https://registry.hub.docker.com/u/itzg/minecraft-server/&quot;&gt;https://registry.hub.docker.com/u/itzg/minecraft-server/&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;An unrelated note – if you want to ssh into the host, you can use command :&lt;/p&gt;&lt;pre class=&quot;csharpcode&quot;&gt;ssh -i .\.docker\hosts\&lt;em&gt;[your host name]&lt;/em&gt;\id_rsa tcuser@&lt;em&gt;[your host name]&lt;/em&gt;.cloudapp.net&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
The client uses .docker\hosts folder to track hosts being managed by the client, and you can find ssh keys for each of the host under its corresponding folder. At the moment when this edit is made, by default the user name is &lt;strong&gt;tcuser&lt;/strong&gt;. 
&lt;p&gt;&lt;strong&gt;Edit – 11/18/2014&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Docker CLI for Windows has been announced today: &lt;a title=&quot;http://azure.microsoft.com/blog/2014/11/18/docker-cli-for-windows-clients/&quot; href=&quot;http://azure.microsoft.com/blog/2014/11/18/docker-cli-for-windows-clients/&quot;&gt;http://azure.microsoft.com/blog/2014/11/18/docker-cli-for-windows-clients/&lt;/a&gt;.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/7424924817431961960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2014/10/minecraft-server-on-azure-using-docker.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/7424924817431961960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/7424924817431961960'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2014/10/minecraft-server-on-azure-using-docker.html' title='Minecraft Server on Azure using Docker Container–Under 5 minutes'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipBxwoIB0-2jdyZ7IaaeZ0cEIlx6bEthCnr0XghryFzJxgairjIITFpHEdYnq1pDYW5JmadjzcFw7_sL9vgxtORTzSNrJ4-wC2FQTdl0vHbEm3sIsguxmF0tzlCp2OvfnUWCUBr5THQYF-/s72-c?imgmax=800" height="72" width="72"/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-2104313976280520244</id><published>2014-10-09T17:33:00.001-07:00</published><updated>2014-10-09T17:33:32.745-07:00</updated><title type='text'>Walkthrough: Use boot2docker on Windows</title><content type='html'>&lt;p&gt;&lt;a href=&quot;http://boot2docker.io/&quot;&gt;Boot2docker&lt;/a&gt; is a lightweight application that helps you to run Docker containers on Mac and Windows machines. This blog is a walkthrough to help you get it going on your Windows machine. First, I’ll go through the steps of getting boot2docker installed. Then, I’ll go through steps to enable fie sharing between your host machine and boot2docker, as well as between your host machine and a Docker container.&lt;/p&gt; &lt;h3&gt;&lt;/h3&gt; &lt;h3&gt;Installation&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;At the time when this blog is written, boot2docker relies on VirtualBox to host the &lt;a href=&quot;http://tinycorelinux.net/&quot;&gt;Tiny Core Linux&lt;/a&gt; vm that boot2docker depends on. If your Windows machine has Hyper-V enabled, there might be problems to get VirtualBox running. Although it’s possible to make Hyper-V and VirtualBox running side-by-side, the exact steps are out of scope of this blog. The easiest way is to disable the Hyper-V feature for now in &lt;strong&gt;&lt;em&gt;Control Panel&lt;/em&gt;&lt;/strong&gt; –&amp;gt;&lt;strong&gt;&lt;em&gt; Programs and Features&lt;/em&gt;&lt;/strong&gt; –&amp;gt; &lt;strong&gt;&lt;em&gt;Turn Windows features on or off&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt; &lt;li&gt;Download and install boot2docker for Windows from &lt;a href=&quot;https://github.com/boot2docker/windows-installer/releases&quot;&gt;this link&lt;/a&gt;. Simply follow the install wizard to complete the installation using all default settings.&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;Launch&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;By default, the installation wizard creates two desktop shortcuts: &lt;strong&gt;Boot2Docker Start&lt;/strong&gt; and Oracle &lt;strong&gt;VM VirtualBox&lt;/strong&gt;. To launch boot2docker, double-click &lt;strong&gt;Boot2Docker Start&lt;/strong&gt;.&lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOn-PDmBTOR9RYESjF-acapa6X5AIF8bZePoPFRC2P49U7KJUfiprC7qpB7tq6ecC38NYRbN2sUTXf4zVMxE2upD7AbQTDimVAi9AGMt6bag_tDF2NC-sdtuSi0fV_3fjD1mn6OgWwZHR9/s1600-h/image%25255B3%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhU_nJnN7y1H3q64lVkcZUkJmq8h9kBI35SUu2roTWF7WuW7IO_1b2uuuhh3lVfUS72xyxUbne92LlIH9oP_HIGoUdNH8OfI6FsFNm-WiAEVpj5OjgtFKhN9qKdFgChoSMDyHvPwVr0X7AT/?imgmax=800&quot; width=&quot;403&quot; height=&quot;207&quot;&gt;&lt;/a&gt;&lt;/li&gt; &lt;li&gt;If the VM fails to launch, you can manually launch it from &lt;strong&gt;&lt;em&gt;VirtualBox&lt;/em&gt;&lt;/strong&gt; and restart boot2docker.&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;&lt;/h3&gt; &lt;h3&gt;Share files between your host and boot2docker&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;In boot2docker, download cifs-utils, for example:&lt;pre class=&quot;csharpcode&quot;&gt;wget &lt;a href=&quot;http://distro.ibiblio.org/tinycorelinux/5.x/x86/tcz/cifs-utils.tcz&quot;&gt;http://distro.ibiblio.org/tinycorelinux/5.x/x86/tcz/cifs-utils.tcz&lt;/a&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Install cifs-utils:&lt;pre class=&quot;csharpcode&quot;&gt;tce-load -i cifs-utils.tcz&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Then, create a new folder, for instance /mnt/datashare:&lt;pre class=&quot;csharpcode&quot;&gt;sudo mkdir /mnt/datashare&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;On the host Windows machine, create a new folder, and then share it with yourself. In the following example I assume the shared folder is at &lt;strong&gt;//&lt;/strong&gt;&lt;em&gt;&amp;lt;your machine’s ip&amp;gt;&lt;/em&gt;&lt;strong&gt;/shared&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Now, you are ready to mount the Windows folder on boot2docker: &lt;pre class=&quot;csharpcode&quot;&gt;sudo mount -t cifs //&amp;lt;your machine&#39;s ip&amp;gt;/shared /mnt/datashare -o user=&amp;lt;your user name&amp;gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
The &amp;lt;your machine’s ip&amp;gt; should be your machine’s IP address, and &amp;lt;your user name&amp;gt; should be your Windows account name. If the user is from a domain, enter the name WITHOUT the domain name. You’ll be asked to enter your password, then the Windows folder will be mounted at /mnt/datashare.&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Create a volume container from a Windows folder&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;If you want to have a volume container based on a Windows folder, you can use the following commands, as introduced on &lt;a href=&quot;https://github.com/boot2docker/boot2docker#folder-sharing&quot;&gt;this page&lt;/a&gt;:&lt;pre class=&quot;csharpcode&quot;&gt;docker run -v /data --name my-data busybox &lt;span class=&quot;kwrd&quot;&gt;true&lt;/span&gt;
docker run --rm -v /usr/local/bin/docker:/docker -v /var/run/docker.sock:/docker.sock svendowideit/samba my-data&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Once the last command succeeds, it gives you some information on how to connect: &lt;br clear=&quot;all&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ1Jt9vC8jvTCZL5Zht-xeLA7gaMezj_O_Oa-AQD2RctwwrZlBnsrNXjvQH11kABOeRCu9yTJjteXH3s_2TwYCWDO9Tgxr0SbGNzzX49KaY-8m630UBUPeEaKL1_E88cpA2KoE3pudJAmS/s1600-h/image%25255B7%25255D.png&quot;&gt;&lt;img title=&quot;image&quot; style=&quot;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&quot; border=&quot;0&quot; alt=&quot;image&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEha1fq1pik-OVQ3mM6IQcplIJSyok0IjYR-o31jcRDQy09OUx0mUgmqmzSFQDn0Pe3LxUGEvv1Emfc_UAOLWDe_E9sxjJEYL4GPS8b0KtIPa7doxfU0RcH_MbRXSw-izNz_DMT-BpWXEb/?imgmax=800&quot; width=&quot;466&quot; height=&quot;146&quot;&gt;&lt;/a&gt;&amp;nbsp;&lt;br clear=&quot;all&quot;&gt;Then, you should be able to access this volume from Windows by //&amp;lt;your boot2docker ip&amp;gt;/data. Note here the IP address is your boot2docker IP, not your hosting machine IP.&lt;/li&gt;
&lt;li&gt;And you can also mount this volume container when you launch a new Docker container. For instance, the following command start a new Ubuntu container, which will have access to the volume by /data:&lt;pre class=&quot;csharpcode&quot;&gt;docker run -it --volumes-from my-data ubuntu&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/2104313976280520244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2014/10/walkthrough-use-boot2docker-on-windows.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2104313976280520244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/2104313976280520244'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2014/10/walkthrough-use-boot2docker-on-windows.html' title='Walkthrough: Use boot2docker on Windows'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhU_nJnN7y1H3q64lVkcZUkJmq8h9kBI35SUu2roTWF7WuW7IO_1b2uuuhh3lVfUS72xyxUbne92LlIH9oP_HIGoUdNH8OfI6FsFNm-WiAEVpj5OjgtFKhN9qKdFgChoSMDyHvPwVr0X7AT/s72-c?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-6280904667799731600</id><published>2014-09-26T10:40:00.001-07:00</published><updated>2014-09-26T11:00:07.148-07:00</updated><title type='text'>Tutorial: Building an Azure Template</title><content type='html'>&lt;h3&gt;Overview&lt;/h3&gt; &lt;p&gt;An Azure Template is a JSON file that captures the infrastructure requirements of your application. An Azure Template defines a number of related resources that are required by your application. Each resource represents a manageable entity, such as an Azure Website, a virtual machine, a SQL database, an identity, a load balancer, or an autos-scaling rule, etc. When an Azure Template is deployed on Azure, Azure Resource Manager ensures that all resources are brought to the specified states so that your application runs under the exact desired environments.  &lt;p&gt;In this tutorial, we’ll walk you through of steps of authoring and deploying a simple Azure Template. The template defines a web application with a frontend running on Azure Websites and a database running on Azure SQL Database. The goal of this tutorial is to walk you through the structure of an Azure Template. In your actual projects, you should use graphical UIs to edit your templates to avoid manual editing errors. &lt;h3&gt;Preparing the environment&lt;/h3&gt; &lt;p&gt;At the time when this blog is written, the easiest way to get started with Azure Template editing is to use Visual Studio with &lt;a href=&quot;http://blogs.msdn.com/b/rmattsampson/archive/2014/08/19/azure-resource-manager-tools-for-visual-studio.aspx&quot;&gt;Azure Resource Manager Tools (Preview) for Visual Studio&lt;/a&gt;. In addition, you’ll also need an active Azure subscription. You can get a free trial subscription at &lt;a href=&quot;http://www.azure.com/&quot;&gt;www.azure.com&lt;/a&gt;. &lt;h3&gt;Getting started&lt;/h3&gt; &lt;p&gt;Azure provides a rich set of Azure Templates in its Azure Gallery. In this tutorial we’ll use an existing template from the gallery to get the editing experience started. But we’ll clear the template so that we can start from scratch.  &lt;ol&gt; &lt;li&gt;In Visual Studio, select the &lt;b&gt;FILE&lt;/b&gt; –&amp;gt; &lt;b&gt;New&lt;/b&gt; –&amp;gt; &lt;b&gt;Project…&lt;/b&gt; menu.&lt;/li&gt; &lt;li&gt;On the &lt;b&gt;&lt;i&gt;New Project&lt;/i&gt;&lt;/b&gt; dialog, select the &lt;b&gt;Cloud&lt;/b&gt; –&amp;gt; &lt;b&gt;Cloud App from the Azure Gallery&lt;/b&gt; template, enter a Name for your project, and then click the &lt;b&gt;OK&lt;/b&gt; button.&lt;/li&gt; &lt;li&gt;On the &lt;b&gt;Select Azure Template&lt;/b&gt; dialog, select the &lt;b&gt;Website + SQL&lt;/b&gt; template, and click the &lt;b&gt;OK&lt;/b&gt; button.&lt;/li&gt; &lt;li&gt;On the &lt;b&gt;New ASP.NET Project&lt;/b&gt; dialog, select &lt;b&gt;MVC&lt;/b&gt; template, and click the &lt;b&gt;OK&lt;/b&gt; button.&lt;br clear=&quot;all&quot;&gt;Once the wizard completes, you’ll have an ASP.NET website and an Azure Template that is ready to be deployed to Azure. Under the solution’s &lt;b&gt;&lt;i&gt;DeploymentItems\Templates&lt;/i&gt;&lt;/b&gt; folder, you’ll find an Azure Template named &lt;b&gt;WebSiteSQLDatabase1.json. &lt;/b&gt;This template already contains all necessary resources for you to deploy your website to Azure. However, for the sake of learning, we’ll clear the template to a bare minimum. &lt;/li&gt; &lt;li&gt;Modify the template so the code looks like the following code list:&lt;pre class=&quot;csharpcode&quot;&gt;{
    &lt;span class=&quot;str&quot;&gt;&quot;$schema&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;West US&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;resources&quot;&lt;/span&gt;: [
    ]
}&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
 In this minimum template file, the &lt;b&gt;&lt;i&gt;$schema&lt;/i&gt;&lt;/b&gt; property indicates the schema version of the template file. The &lt;b&gt;contentVersion&lt;/b&gt; property indicates the version of the template file itself. The main element of the file is the &lt;b&gt;resources&lt;/b&gt; array, which contains all the resources to be defined in the group. &lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;NOTE: There are several other top-level properties such as &lt;b&gt;parameters&lt;/b&gt;, &lt;b&gt;variables&lt;/b&gt;, &lt;b&gt;tags&lt;/b&gt; and &lt;b&gt;outputs&lt;/b&gt;. We’ll cover some of these properties later in this tutorial.&lt;br clear=&quot;all&quot;&gt;&lt;/li&gt;
&lt;li&gt;Save the template. And then, in &lt;b&gt;&lt;i&gt;Solution Explorer&lt;/i&gt;&lt;/b&gt;, right-click on the template and select the &lt;b&gt;Deploy Template…&lt;/b&gt; menu. On the &lt;b&gt;Deploy Template &lt;/b&gt;dialog, select the Azure subscription, storage account, and deployment location you want to use, then click the &lt;b&gt;Deploy&lt;/b&gt; button to deploy the template to Azure.&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;NOTE: Leave the &lt;b&gt;Template parameters file&lt;/b&gt; field at its default value. We’ll come back to this later. &lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;The deployment should succeed after a short delay. You can observe the deployment traces in the &lt;b&gt;&lt;i&gt;Output&lt;/i&gt;&lt;/b&gt; window. Deployment of an Azure Template is an idempotent operation so that you can deploy the same template as many times as you like. You can think an Azure Template as a definition of the desired states of your application resources. And Azure will make sure the resources are brought to specified states when you deploy the template.&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Defining a Resource&lt;/h3&gt;
&lt;p&gt;In previous section you’ve deployed an empty Azure Template to Azure. In this part you’ll add a SQL Database resource to the template. You define a new resource by adding a new child element to the &lt;b&gt;resources&lt;/b&gt; array. 
&lt;p&gt;A resource definition is a JSON object with three mandatory properties: &lt;b&gt;name&lt;/b&gt;, &lt;b&gt;type&lt;/b&gt; and &lt;b&gt;apiVersion&lt;/b&gt;. The &lt;b&gt;name&lt;/b&gt; property identifies the resource. And the &lt;b&gt;type&lt;/b&gt; property and the &lt;b&gt;apiVersion&lt;/b&gt; property combined specifies which version of what Resource Provider to use. Azure Resource Manager has an extensible architecture, in which different Resource Providers can be plugged into the system to provide capabilities of managing a variety type of resources. For example, for SQL Database servers, the corresponding Resource Provider &lt;b&gt;type&lt;/b&gt; is &lt;b&gt;&lt;i&gt;Microsoft.Sql/servers&lt;/i&gt;&lt;/b&gt;, and the current &lt;b&gt;apiVersion&lt;/b&gt; is “&lt;b&gt;&lt;i&gt;2.0&lt;/i&gt;&lt;/b&gt;”. 
&lt;ol&gt;
&lt;li&gt;Modify your Azure Template to include the definition of a SQL Database server resource. In the following code list, a new SQL Database server named &lt;b&gt;mysqlserver&lt;/b&gt; has been added to the &lt;b&gt;resources&lt;/b&gt; array.&lt;pre class=&quot;csharpcode&quot;&gt; 1:{
 2:     &lt;span class=&quot;str&quot;&gt;&quot;$schema&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#&quot;&lt;/span&gt;,
 3:     &lt;span class=&quot;str&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;,
 4:     &lt;span class=&quot;str&quot;&gt;&quot;resources&quot;&lt;/span&gt;: [
 5:         {
 6:             &lt;span class=&quot;str&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2.0&quot;&lt;/span&gt;,
 7:             &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;mysqlserver&quot;&lt;/span&gt;,
 8:             &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;Microsoft.Sql/servers&quot;&lt;/span&gt;,
 9:             &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;West US&quot;&lt;/span&gt;,
10:             &lt;span class=&quot;str&quot;&gt;&quot;properties&quot;&lt;/span&gt;: {
11:                 &lt;span class=&quot;str&quot;&gt;&quot;administratorLogin&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;admin&quot;&lt;/span&gt;,
12:                 &lt;span class=&quot;str&quot;&gt;&quot;administratorLoginPassword&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;password$123&quot;&lt;/span&gt;
13:             }
14:         }
15:     ]
16:}
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
In above template, line 5 to line 14 defines a SQL Database server resource. In addition to the &lt;b&gt;&lt;i&gt;apiVersion&lt;/i&gt;&lt;/b&gt;, &lt;b&gt;&lt;i&gt;name&lt;/i&gt;&lt;/b&gt;, and &lt;b&gt;&lt;i&gt;type&lt;/i&gt;&lt;/b&gt; properties, you also see a &lt;b&gt;location&lt;/b&gt; property and a &lt;b&gt;properties&lt;/b&gt; property. These two properties, along with a &lt;b&gt;tags&lt;/b&gt; property, are defined by a Resource Provider Contract, which defines the communication protocol between Azure Resource Manager and Resource Providers. The &lt;b&gt;properties&lt;/b&gt; list is defined and consumed by the specific Resource Provider. In this case, SQL Server Resource Provider has defined two properties (among others) that represent administrator login (&lt;i&gt;administratorLogin&lt;/i&gt;) and administrator password (&lt;i&gt;administratorPassword&lt;/i&gt;). &lt;/li&gt;
&lt;li&gt;Deploy the modified template. Once the template has been successfully deployed, you should be able to observe on Azure Management Portal that the new SQL Database server has been provisioned.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;In above template we put password as plain text, which is not a recommended practice. We’ll fix this when we discuss &lt;b&gt;parameters&lt;/b&gt;.
&lt;h3&gt;Defining a Child Resource&lt;/h3&gt;
&lt;p&gt;Next we’ll define a SQL Database on the newly defined SQL Database server. Naturally, a SQL Database belongs to a SQL Database server. In other words, a SQL Database is a child resource of a SQL Database server resource. And this is exactly how you would define it – to add a child element to the server JSON object we just defined.
&lt;ol&gt;
&lt;li&gt;Modify the template as shown in the following code list (line 14 – 28):&lt;pre class=&quot;csharpcode&quot;&gt; 1:{
 2:     &lt;span class=&quot;str&quot;&gt;&quot;$schema&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#&quot;&lt;/span&gt;,
 3:     &lt;span class=&quot;str&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;,
 4:     &lt;span class=&quot;str&quot;&gt;&quot;resources&quot;&lt;/span&gt;: [
 5:         {
 6:             &lt;span class=&quot;str&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2.0&quot;&lt;/span&gt;,
 7:             &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;mysqlserver&quot;&lt;/span&gt;,
 8:             &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;Microsoft.Sql/servers&quot;&lt;/span&gt;,
 9:             &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;West US&quot;&lt;/span&gt;,
10:             &lt;span class=&quot;str&quot;&gt;&quot;properties&quot;&lt;/span&gt;: {
11:                 &lt;span class=&quot;str&quot;&gt;&quot;administratorLogin&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;admin&quot;&lt;/span&gt;,
12:                 &lt;span class=&quot;str&quot;&gt;&quot;administratorLoginPassword&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;password$123&quot;&lt;/span&gt;
13:             },
14:             &lt;span class=&quot;str&quot;&gt;&quot;resources&quot;&lt;/span&gt;: [
15:                 {
16:                      &lt;span class=&quot;str&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2.0&quot;&lt;/span&gt;,
17:                      &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;mysqldatabase&quot;&lt;/span&gt;,
18:                      &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;databases&quot;&lt;/span&gt;,
19:                       &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;West US&quot;&lt;/span&gt;,
20:                      &lt;span class=&quot;str&quot;&gt;&quot;dependsOn&quot;&lt;/span&gt;: [
21:                           &lt;span class=&quot;str&quot;&gt;&quot;[concat(&#39;Microsoft.Sql/servers/&#39;, &#39;mysqlserver&#39;)]&quot;&lt;/span&gt;
22:                      ],
23:                      &lt;span class=&quot;str&quot;&gt;&quot;properties&quot;&lt;/span&gt;: {
24:                          &lt;span class=&quot;str&quot;&gt;&quot;edition&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;Web&quot;&lt;/span&gt;,
25:                          &lt;span class=&quot;str&quot;&gt;&quot;maxSizeBytes&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;1073741824&quot;&lt;/span&gt;
26:                      }
27:                 }
28:             ]
29:         }
30:     ]
31:}&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
In above template, line 15 to line 27 defines the SQL Database resource to be added to the SQL Database server resource. You can see all resource definitions in Azure Templates follow a consistent format. In this new resource definition, we define a SQL Database named “mysqldatabase”, and specify it should be a Web edition with maximum size of 1G. &lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;NOTE: You’ll need to pick a different name other than &lt;b&gt;&lt;i&gt;mysqldatabase&lt;/i&gt;&lt;/b&gt; as the name is already taken.&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;Please note the &lt;b&gt;dependsOn&lt;/b&gt; property in line 20. The &lt;b&gt;dependsOn&lt;/b&gt; property is used to define dependencies among resources. In this particular case, the property specifies that the SQL Database resource depends on the SQL Database server resource – this makes sense because a SQL Database server has to be provisioned first before a SQL Database can be added to it. When an Azure Template is deployed, Azure Resource Manger builds up a dependency graph based on these dependencies and ensures the resources are configured in the correct order. &lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;NOTE: We’ll see another type of dependency by references later in this tutorial. &lt;/li&gt;
&lt;li&gt;As an added exercise, define another child resource under the SQL Database Server resource to enable accesses to the sever from all IP addresses:&lt;pre class=&quot;csharpcode&quot;&gt;{
    &lt;span class=&quot;str&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;firewallrules&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;AllowAllWindowsAzureIps&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;West US&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;dependsOn&quot;&lt;/span&gt;: [
        &lt;span class=&quot;str&quot;&gt;&quot;[concat(&#39;Microsoft.Sql/servers/&#39;, &#39;mysqlserver&#39;)]&quot;&lt;/span&gt;
    ],
    &lt;span class=&quot;str&quot;&gt;&quot;properties&quot;&lt;/span&gt;: {
        &lt;span class=&quot;str&quot;&gt;&quot;endIpAddress&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;255.255.255.255&quot;&lt;/span&gt;,
        &lt;span class=&quot;str&quot;&gt;&quot;startIpAddress&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;
    }
}
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;Using Variables&lt;/h3&gt;
&lt;p&gt;In our current version of template, the SQL Database Server name “mysqlserver” has appeared in several different places. Since it’s not a good practice to repeat string literals in your template, you can use variables to correct this.
&lt;p&gt;In an Azure Template, you can define an arbitrary JSON key/value dictionary named &lt;b&gt;variables&lt;/b&gt;. And then, you can refer to any defined values in your template by the syntax &lt;b&gt;[variables(‘&lt;i&gt;[key]&lt;/i&gt;’)]. &lt;/b&gt;A property value can be a simple type or a complex type. As long as you follow standard JSON syntax, you can define very complex properties if you’d like. However in this tutorial we’ll use only string properties.&lt;b&gt;&lt;/b&gt;
&lt;ol&gt;
&lt;li&gt;Add a &lt;b&gt;variables&lt;/b&gt; element to the template root:&lt;pre class=&quot;csharpcode&quot;&gt;…
&lt;span class=&quot;str&quot;&gt;&quot;variables&quot;&lt;/span&gt;: {
    &lt;span class=&quot;str&quot;&gt;&quot;serverName&quot;&lt;/span&gt;:&lt;span class=&quot;str&quot;&gt;&quot;mysqlserver&quot;&lt;/span&gt;
},
…
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;Once a variable is defined, you can refer it in your template code. For instance, instead &lt;pre class=&quot;csharpcode&quot;&gt;&lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;mysqlserver&quot;&lt;/span&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
use:&lt;pre class=&quot;csharpcode&quot;&gt;&lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;[variables(&#39;serverName&#39;)]&quot;&lt;/span&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
And instead of &lt;pre class=&quot;csharpcode&quot;&gt;&lt;span class=&quot;str&quot;&gt;&quot;[concat(&#39;Microsoft.Sql/servers/&#39;, &#39;mysqlserver&#39;)]&quot;&lt;/span&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
use:&lt;pre class=&quot;csharpcode&quot;&gt;&quot;[concat(&lt;span class=&quot;str&quot;&gt;&#39;Microsoft.Sql/servers/&#39;&lt;/span&gt;, variables(&lt;span class=&quot;str&quot;&gt;&#39;serverName&#39;&lt;/span&gt;))]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;Using Parameters&lt;/h3&gt;
&lt;p&gt;It’s often useful to get an Azure Template parameterized. For instance, instead of fixing the SQL Database server name in the script, you can define the server name as a parameter so that the template can be used to deploy multiple instances of identically configured servers. Another use case of parameters is to separate secure information such as passwords from the template. In the following steps, we’ll define both a &lt;b&gt;&lt;i&gt;serverLocation&lt;/i&gt;&lt;/b&gt; parameter and an &lt;b&gt;&lt;i&gt;adminPassword&lt;/i&gt;&lt;/b&gt; parameter.
&lt;ol&gt;
&lt;li&gt;To define parameters, add a new &lt;b&gt;parameters&lt;/b&gt; element to the template root. Each parameter is defined as a complex type with a &lt;b&gt;type&lt;/b&gt; property and optional &lt;b&gt;allowedValues&lt;/b&gt; and &lt;b&gt;defaultValue&lt;/b&gt; properties. In current schema the supported types are: &lt;b&gt;&lt;i&gt;int&lt;/i&gt;&lt;/b&gt;, &lt;b&gt;&lt;i&gt;string&lt;/i&gt;&lt;/b&gt;, &lt;b&gt;&lt;i&gt;securestring&lt;/i&gt;&lt;/b&gt; and &lt;b&gt;&lt;i&gt;bool&lt;/i&gt;&lt;/b&gt;. The following code segment defines a string-typed &lt;b&gt;serverLocation&lt;/b&gt; parameter and an &lt;b&gt;&lt;i&gt;adminPasswword&lt;/i&gt;&lt;/b&gt; parameter as a secure string.&lt;pre class=&quot;csharpcode&quot;&gt;…
&lt;span class=&quot;str&quot;&gt;&quot;parameters&quot;&lt;/span&gt;: {
    &lt;span class=&quot;str&quot;&gt;&quot;serverLocation&quot;&lt;/span&gt;: {
        &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;string&quot;&lt;/span&gt;
    } ,
    &lt;span class=&quot;str&quot;&gt;&quot;adminPassword&quot;&lt;/span&gt;: {
        &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;securestring&quot;&lt;/span&gt;
    }
},
…
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
When a parameterized template is deployed, Azure reads parameter values from a separate parameter file, which is another JSON file with a list of parameter values. You can create multiple parameter files for different environments. This helps you to make sure all your environments are configured in a consistent manor. &lt;/li&gt;
&lt;li&gt;In Visual Studio, replace the content of &lt;b&gt;WebSiteSQLDatabase1.dev.json&lt;/b&gt; with the following code list:&lt;pre class=&quot;csharpcode&quot;&gt;{
    &lt;span class=&quot;str&quot;&gt;&quot;$schema&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentParameters.json#&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;parameters&quot;&lt;/span&gt;: {
        &lt;span class=&quot;str&quot;&gt;&quot;serverLocation&quot;&lt;/span&gt;: {
            &lt;span class=&quot;str&quot;&gt;&quot;value&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;West Us&quot;&lt;/span&gt;
        }
    }
}&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
Note we are not hard-coding the &lt;b&gt;&lt;i&gt;adminPassword&lt;/i&gt;&lt;/b&gt; value in this file. We’ll manually enter the value during deployment.&lt;/li&gt;
&lt;li&gt;Then, replaces all “West US” constants in your template to parameter references:&lt;pre class=&quot;csharpcode&quot;&gt;[parameters(&lt;span class=&quot;str&quot;&gt;&#39;serverLocation&#39;&lt;/span&gt;)]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
And replace all &quot;password$123&quot; with &lt;pre class=&quot;csharpcode&quot;&gt;[parameters(&lt;span class=&quot;str&quot;&gt;&#39;adminPassword&#39;&lt;/span&gt;)]&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/li&gt;
&lt;li&gt;In Solution Explorer, right-click the template and select the &lt;b&gt;Deploy Template&lt;/b&gt; menu. On &lt;b&gt;the Deploy Templat&lt;/b&gt;e dialog, you can choose which template parameter file to use. Click the &lt;b&gt;Edit Parameters&lt;/b&gt; button and enter a value for the &lt;b&gt;&lt;i&gt;adminPassword&lt;/i&gt;&lt;/b&gt; parameter.&lt;/li&gt;
&lt;li&gt;Click the &lt;b&gt;Deploy&lt;/b&gt; button to deploy the template again. The template should be deployed correctly.&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Resource Dependencies&lt;/h3&gt;
&lt;p&gt;In the “Defining a Child Resource” section you’ve seen how you can add a dependency to a resource. In this part you’ll extend the template to include a definition of a web site, which is to be deployed on Azure Websites. In this case, the web site resource depends on an Azure Websites host plan resource. 
&lt;ol&gt;
&lt;li&gt;Add the following resource definitions to your template:&lt;pre class=&quot;csharpcode&quot;&gt;{
    &lt;span class=&quot;str&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2014-04-01&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;Microsoft.Web/serverFarms&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;myhostplan&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;[parameters(&#39;serverLocation&#39;)]&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;properties&quot;&lt;/span&gt;: {
        &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;myhostplan&quot;&lt;/span&gt;,
        &lt;span class=&quot;str&quot;&gt;&quot;sku&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;Free&quot;&lt;/span&gt;
    }
},
{
    &lt;span class=&quot;str&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2014-04-01&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;Microsoft.Web/Sites&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;mywebsite&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;[parameters(&#39;serverLocation&#39;)]&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;dependsOn&quot;&lt;/span&gt;: [
        &lt;span class=&quot;str&quot;&gt;&quot;[concat(&#39;Microsoft.Web/serverFarms/&#39;, &#39;myhostplan&#39;)]&quot;&lt;/span&gt;
    ],
    &lt;span class=&quot;str&quot;&gt;&quot;properties&quot;&lt;/span&gt;: {
        &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;mywebsite&quot;&lt;/span&gt;,
        &lt;span class=&quot;str&quot;&gt;&quot;serverFarm&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;myhostplan&quot;&lt;/span&gt;
    }
}
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;br clear=&quot;all&quot;&gt;NOTE: You’ll need to pick a different name other than &lt;b&gt;&lt;i&gt;mywebsite&lt;/i&gt;&lt;/b&gt; for your website as the name is already taken.&lt;br clear=&quot;all&quot;&gt;&lt;br clear=&quot;all&quot;&gt;In the above code segment, the first resource definition defines a free-tier Azure Website host plan. This shows that in addition to the resources you own, you can also use resource definitions to create references to other PaaS and SaaS services. The second resource definition defines the web site to be provisioned on the selected host plan. And the dependency is described by the &lt;b&gt;dependsOn&lt;/b&gt; property just like you’ve seen earlier in this tutorial. &lt;br clear=&quot;all&quot;&gt;A second type of dependencies is created by &lt;b&gt;&lt;i&gt;references&lt;/i&gt;&lt;/b&gt;. In a resource definition, you can refer to a property from another resource definition, creating a dependency between the two. For example, in our case we’d like to enter a connection string to the SQL Database into the &lt;b&gt;&lt;i&gt;web.config&lt;/i&gt;&lt;/b&gt; file of our website. And in this connection string we’ll need to refer to the fully qualified domain name (FQDN) of the database server. Obviously the website resource provider has no place to understand how to find that name, but the SQL Database resource provider should be able to provide it. And this is done by the &lt;b&gt;reference&lt;/b&gt; keyword.&lt;/li&gt;
&lt;li&gt;Add a new child resource that represents a web.config file to the website resource, as shown in the bolded part of the following code list:&lt;pre class=&quot;csharpcode&quot;&gt;…
{
    &lt;span class=&quot;str&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;2014-04-01&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;Microsoft.Web/Sites&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;mywebsite&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;location&quot;&lt;/span&gt;: &lt;span class=&quot;str&quot;&gt;&quot;[parameters(&#39;serverLocation&#39;)]&quot;&lt;/span&gt;,
    &lt;span class=&quot;str&quot;&gt;&quot;dependsOn&quot;&lt;/span&gt;: [
        &lt;span class=&quot;str&quot;&gt;&quot;[concat(&#39;Microsoft.Web/serverFarms/&#39;, &#39;myhostplan&#39;)]&quot;&lt;/span&gt;
    ],
    properties&lt;span class=&quot;str&quot;&gt;&quot;: {
        &quot;&lt;/span&gt;name&lt;span class=&quot;str&quot;&gt;&quot;: &quot;&lt;/span&gt;mywebsiteddf&lt;span class=&quot;str&quot;&gt;&quot;,
        &quot;&lt;/span&gt;serverFarm&lt;span class=&quot;str&quot;&gt;&quot;: &quot;&lt;/span&gt;myhostplan&lt;span class=&quot;str&quot;&gt;&quot;
    },
    &quot;&lt;/span&gt;resources&lt;span class=&quot;str&quot;&gt;&quot;: [{
        &quot;&lt;/span&gt;apiVersion&lt;span class=&quot;str&quot;&gt;&quot;: &quot;&lt;/span&gt;2014-04-01&lt;span class=&quot;str&quot;&gt;&quot;,
        &quot;&lt;/span&gt;type&lt;span class=&quot;str&quot;&gt;&quot;: &quot;&lt;/span&gt;config&lt;span class=&quot;str&quot;&gt;&quot;,
        &quot;&lt;/span&gt;name&lt;span class=&quot;str&quot;&gt;&quot;: &quot;&lt;/span&gt;web&lt;span class=&quot;str&quot;&gt;&quot;,
        &quot;&lt;/span&gt;dependsOn&lt;span class=&quot;str&quot;&gt;&quot;: [
            &quot;&lt;/span&gt;[concat(&lt;span class=&quot;str&quot;&gt;&#39;Microsoft.Web/Sites/&#39;&lt;/span&gt;, &lt;span class=&quot;str&quot;&gt;&#39;mywebsiteddf&#39;&lt;/span&gt;)]&lt;span class=&quot;str&quot;&gt;&quot;
        ],
        &quot;&lt;/span&gt;properties&lt;span class=&quot;str&quot;&gt;&quot;: {
            &quot;&lt;/span&gt;connectionStrings&lt;span class=&quot;str&quot;&gt;&quot;: [{
                &quot;&lt;/span&gt;ConnectionString&lt;span class=&quot;str&quot;&gt;&quot;: &quot;&lt;/span&gt;[concat(&lt;span class=&quot;str&quot;&gt;&#39;Data Source=tcp:&#39;&lt;/span&gt;, reference(concat(&lt;span class=&quot;str&quot;&gt;&#39;Microsoft.Sql/servers/&#39;&lt;/span&gt;, variables(&lt;span class=&quot;str&quot;&gt;&#39;serverName&#39;&lt;/span&gt;))).fullyQualifiedDomainName, &lt;span class=&quot;str&quot;&gt;&#39;,1433;Initial Catalog=mytestdatabase;User Id=admin@&#39;&lt;/span&gt;, variables(&lt;span class=&quot;str&quot;&gt;&#39;serverName&#39;&lt;/span&gt;), &lt;span class=&quot;str&quot;&gt;&#39;;Password=&#39;&lt;/span&gt;, parameters(&lt;span class=&quot;str&quot;&gt;&#39;adminPassword&#39;&lt;/span&gt;), &lt;span class=&quot;str&quot;&gt;&#39;;&#39;&lt;/span&gt;)]&lt;span class=&quot;str&quot;&gt;&quot;,
                &quot;&lt;/span&gt;Name&lt;span class=&quot;str&quot;&gt;&quot;: &quot;&lt;/span&gt;DefaultConnection&lt;span class=&quot;str&quot;&gt;&quot;,
                &quot;&lt;/span&gt;Type&quot;: 2
            }]
        }
    }]
}
…&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
Please note how the connection string uses &lt;b&gt;reference(&lt;i&gt;[SQL Database Server resource name]&lt;/i&gt;)&lt;/b&gt; to get a reference to the SQL Database server resource, and then its &lt;b&gt;&lt;i&gt;fullQualifiedDomainName&lt;/i&gt;&lt;/b&gt; to get the server’s fully qualified domain name.&lt;/li&gt;
&lt;li&gt;Deploy the template again. You should see the website provisioned with the connection string correctly set.&lt;/li&gt;&lt;/ol&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this tutorial we walked through the steps of creating a Azure Template that defines both a website and SQL Database. And we studied how to link them together using resource dependencies. We also covered how to use variables and parameters in Azure Template and how to use different parameter files for different deployment environments.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/6280904667799731600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2014/09/tutorial-building-azure-template.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6280904667799731600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/6280904667799731600'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2014/09/tutorial-building-azure-template.html' title='Tutorial: Building an Azure Template'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8122199360699827669.post-3728480267675921829</id><published>2014-09-19T13:32:00.001-07:00</published><updated>2014-09-29T12:50:59.184-07:00</updated><title type='text'>Enabling Hybrid Scenarios on Microsoft Azure</title><content type='html'>&lt;p&gt;Although cloud adoption has been phenomenal across the entire IT landscape, we can’t ignore the fact that there are much more compute and storage resources still running on local networks. A key criterion to evaluate the practicality of a cloud platform resides in not only what kind of workloads it can run and what services it provides, but also in how well the platform supports working with on-premises resources. Hybrid connection is a key decision driver to many companies who want to adopt cloud because it’s unrealistic for them to abandon whatever they have locally and migrate everything to cloud,&amp;nbsp; given associated risk and cost. Furthermore, in some of the scenarios, such as managing automated production lines, migrating everything to cloud is simply infeasible as some devices have to be physically placed in local environments. &lt;/p&gt; &lt;p&gt;Microsoft Azure has been really strong in supporting hybrid scenarios. It provides not only connectivities at network level, but also various integration points at application level. The strong hybrid support provides Microsoft’s customers great flexibilities in their journey to the cloud. They can pick and chose the services that are most relevant to their businesses, and be assured that they can orchestrate these services with their on-premises resources to deliver continuous business solutions as they take steps to migrate to cloud. In this blog article, I’ll&amp;nbsp; provide a brief survey of various technologies Microsoft Azure provides to enable different hybrid scenarios. Specially, I’ll cover the following scenarios:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Extend your local network into the cloud  &lt;li&gt;Attach workstations (PC or Mac) to your virtual networks on cloud  &lt;li&gt;Expose local services to cloud  &lt;li&gt;Access on-premises resources from cloud  &lt;li&gt;Bridge multiple networks&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Instead of covering all the technical details, this blog focus mostly on scenarios themselves and links to external articles to provide detailed configuration steps. As there is a lot to talk about, let’s dive right in:&lt;/p&gt; &lt;h3&gt;1. Extend your local network into the cloud&lt;/h3&gt; &lt;p&gt;Microsoft Azure &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/jj156007.aspx&quot;&gt;Virtual Network&lt;/a&gt; not only allows you to create a virtual network on cloud, but also allows you to connect the cloud-based virtual network to your on-premises network via a &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/virtual-networks-create-site-to-site-cross-premises-connectivity/&quot;&gt;Site-to-Site&lt;/a&gt; or a &lt;a href=&quot;http://msdn.microsoft.com/library/azure/dn133792.aspx&quot;&gt;Point-to-Site&lt;/a&gt; connection. To create a Site-to-Site VPN connection, you’ll need a public IPv4 address and a &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/jj156075.aspx&quot;&gt;supported VPN device&lt;/a&gt;. You can also use Routing and Remote Access Service (&lt;a href=&quot;http://msdn.microsoft.com/library/azure/dn133801.aspx&quot;&gt;RRAS&lt;/a&gt;) to replace the VPN device. The benefit of using Site-to-Site connection is that you only need to configure a VPN appliance (hardware or software) at the edge of your network, and all the computers located in your local network segment and in your virtual networks on cloud can communicate with each other as if they were on the same local network. For instance, you can join a virtual machine running on Azure to your local domain, or do the reverse by setting up &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/jj156090.aspx&quot;&gt;a high-available AD on Azure&lt;/a&gt; and joining your local machines to it.&amp;nbsp; The VPN connections use IPSec over the Internet. If you want higher speed, dedicated connections, you can also leverage Azure’s &lt;a href=&quot;http://azure.microsoft.com/en-us/services/expressroute/&quot;&gt;ExpressRoute&lt;/a&gt; to establish private connections directly to Azure.&lt;/p&gt; &lt;h3&gt;2. Attach workstations (PC or Mac) to your virtual networks on cloud&lt;/h3&gt; &lt;p&gt;Point-to-Site connections are perfect for you to attach your laptops or local computers to the virtual network on Azure for troubleshooting, debugging, testing, development, and commands &amp;amp; control. For a Windows-based PC, you can directly download a VPN client after you’ve configured your virtual network gateway and a certificate for authentication. For workstations that support only L2TP (such as Mac Book), you can set up a RRAS server (or similar servers such as OpenVPN Access Server) on your virtual network and then configure the VPN connection (see &lt;a href=&quot;http://blogs.msdn.com/b/lighthouse/archive/2013/07/30/how-deploy-sstp-and-l2tp-vpn-in-windows-azure-windows-server-2012.aspx&quot;&gt;this article&lt;/a&gt;). &lt;/p&gt; &lt;p&gt;For a device that doesn’t support VPN connections, you’ll need to use Site-to-Site connection. And if Site-to-Site connections are unavailable, you can set up a local RRAS server. Both configurations require a public IP address. If you can’t acquire a public IP address, what you can do is to set up a Windows Server, join the server to your virtual network, and then use netsh to forward the traffic (more on this below).&lt;/p&gt; &lt;h3&gt;3. Expose local services to cloud&lt;/h3&gt; &lt;p&gt;Sometimes you need to expose a locally hosted services to cloud. A common challenge of implementing such scenario is addressing – these services don’t have public IP addresses, or even fixed IP addresses. How can we make these services accessible in this case? There are several options depends on your specific scenarios. First, at the most basic level, you can set up a Virtual Network to bridge the connection. If the service appliance doesn’t support VPN connections, you can also use a local Windows server to forward traffic using netsh: &lt;pre class=&quot;csharpcode&quot;&gt;netsh interface portproxy add v4tov4 listenport=&lt;em&gt;[the port that the &lt;span class=&quot;kwrd&quot;&gt;local&lt;/span&gt; server listens to]&lt;/em&gt; 
connectport=&lt;em&gt;[port exposed to  the clients, can be the same as the server port] &lt;/em&gt;
connectaddress=&lt;em&gt;[&lt;span class=&quot;kwrd&quot;&gt;local&lt;/span&gt; IP address of &lt;span class=&quot;kwrd&quot;&gt;local&lt;/span&gt; server]&lt;/em&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;


&lt;p&gt;Second, if you are hosting a WCF service, you can use &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-hybrid-app-using-service-bus-relay/&quot;&gt;Service Bus Relay&lt;/a&gt; to acquire a relay endpoint, via which the clients can connect to your service over the Internet. Third, if you want to serve up a locally hosted service as a REST API, you can consider &lt;a href=&quot;http://azure.microsoft.com/en-us/services/api-management/&quot;&gt;API Management&lt;/a&gt;, which provides transformation, throttling, authentication and many other features that help you to expose a legacy service as a RESTful API to the Internet. &lt;/p&gt;
&lt;h3&gt;4. Access on-premises resources from cloud&lt;/h3&gt;
&lt;p&gt;In additional to Virtual Network, you can also use Azure &lt;a href=&quot;http://azure.microsoft.com/en-us/documentation/articles/integration-hybrid-connection-overview/&quot;&gt;Hybrid Connection&lt;/a&gt; to connect to your local resources from your Azure &lt;a href=&quot;http://azure.microsoft.com/en-us/services/websites/&quot;&gt;Websites&lt;/a&gt; and &lt;a href=&quot;http://azure.microsoft.com/en-us/services/mobile-services/?b=14-38&quot;&gt;Mobile Services&lt;/a&gt;. For example, you can access your locally hosted SQL Server, MySQL, Oracle databases, or other HTTP/HTTPS services from your Azure Website. What’s mentioned in section 3 also applies to this section as well.&lt;/p&gt;
&lt;h3&gt;5. Bridge multiple networks&lt;/h3&gt;
&lt;p&gt;Microsoft Azure also supports &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/dn690124.aspx&quot;&gt;Multi-Site VPN&lt;/a&gt;, which allows you to connect multiple local networks together. This feature is especially useful when you need to integrate resources across multiple site locations. Furthermore, Azure allows you to connect different virtual networks together using the &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/azure/dn690122.aspx&quot;&gt;VNet to VNet Connection&lt;/a&gt;. These two features give you great flexibility to integrate and extend your networks as you fit and enable lots of cross-premise scenarios.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Microsoft Azure provides tons of options that help you to achieve a large number of hybrid scenarios at both networking level and application level. Only Microsoft Azure allows smooth transition and live collaboration among your local resources and your cloud-based resources. If you haven’t subscribed to Azure yet, you can start with a free trial subscription &lt;a href=&quot;http://azure.microsoft.com/en-us/&quot;&gt;today&lt;/a&gt;! &lt;/p&gt;
&lt;p&gt;Thank you for reading, and have a nice weekend!&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://haishibai.blogspot.com/feeds/3728480267675921829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://haishibai.blogspot.com/2014/09/enabling-hybrid-scenarios-on-microsoft.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/3728480267675921829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8122199360699827669/posts/default/3728480267675921829'/><link rel='alternate' type='text/html' href='http://haishibai.blogspot.com/2014/09/enabling-hybrid-scenarios-on-microsoft.html' title='Enabling Hybrid Scenarios on Microsoft Azure'/><author><name>Haishi</name><uri>http://www.blogger.com/profile/05217897042910076935</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry></feed>