{"id":6737,"date":"2018-01-29T14:53:15","date_gmt":"2018-01-29T22:53:15","guid":{"rendered":"\/developerblog\/?p=6737"},"modified":"2020-03-20T09:12:58","modified_gmt":"2020-03-20T16:12:58","slug":"orchestrating-turn-servers-cloud-deployment","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/orchestrating-turn-servers-cloud-deployment\/","title":{"rendered":"Orchestrating TURN Servers for Cloud Deployment"},"content":{"rendered":"<h2>Background<\/h2>\n<p>We recently collaborated with <a href=\"http:\/\/www.aveva.com\">Aveva<\/a>,\u00a0an industrial design and management software company. The goal for\u00a0Aveva was to improve its remote rendering service and extend this technology to support an ever-growing list of platforms and devices including HoloLens. As a result of our collaboration, we built\u00a0<a href=\"https:\/\/github.com\/CatalystCode\/3dtoolkit\">3DToolkit<\/a>, a\u00a0toolkit for creating powerful cloud-based 3D experiences which stream to low-powered devices that are traditionally out of reach.<\/p>\n<p>One of the key decisions we had to make was the video transport technology. After investigating different options,\u00a0<a href=\"https:\/\/codelabs.developers.google.com\/codelabs\/webrtc-web\/#0\">WebRTC<\/a> emerged as the best choice for this project. WebRTC is designed to work peer-to-peer. In our case, one of the peers is a cloud server that streams video, and the other peer is a client device that might need to traverse <a href=\"http:\/\/en.wikipedia.org\/wiki\/NAT_traversal\" target=\"_blank\" rel=\"noopener noreferrer\">NAT gateways<\/a> and firewalls. For these cases, WebRTC APIs use <a href=\"https:\/\/www.html5rocks.com\/en\/tutorials\/webrtc\/infrastructure\/\">STUN servers<\/a> to get the IP address of the device, and <a href=\"https:\/\/www.html5rocks.com\/en\/tutorials\/webrtc\/infrastructure\/\">TURN servers<\/a> to function as relay servers.\u00a0This code story will cover how we orchestrated TURN servers for cloud deployment in our work with Aveva.<\/p>\n<h2>The Challenge<\/h2>\n<p>There are <a href=\"https:\/\/gist.github.com\/mondain\/b0ec1cf5f60ae726202e\">free STUN servers<\/a> available to use but maintaining TURN servers that relay video traffic is\u00a0resource intensive. As part of our\u00a0solution, we had to set up and maintain\u00a0our own TURN servers. The challenge was to create a simple process for deployment and to find an implementation of TURN that would satisfy the following requirements:<\/p>\n<ol>\n<li>Supports STUN server functionality<\/li>\n<li>Compliant with base TURN and STUN specs <a href=\"https:\/\/tools.ietf.org\/html\/rfc5766\">RFC 5766<\/a>, <a href=\"https:\/\/tools.ietf.org\/html\/rfc3489\">RFC 3489<\/a>, <a href=\"https:\/\/tools.ietf.org\/html\/rfc5389\">RFC 5389<\/a>, <a href=\"https:\/\/tools.ietf.org\/html\/rfc6062\">RFC 6062<\/a>.<\/li>\n<li>Free and open source<\/li>\n<li>Can be easily set up on the Linux platform<\/li>\n<li>Works over both TCP and UDP protocols<\/li>\n<li>Supports TURN authentication mechanisms such as long-term credentials and time-limited secret-based authentication<\/li>\n<li>Uses a database to store authentication data in a central location<\/li>\n<li>Supports protocols\u00a0TLS, DTLS with a real world certificate<\/li>\n<li>Supports load balancing<\/li>\n<\/ol>\n<h2>The Solution<\/h2>\n<p>After investigating different implementations of TURN servers that would\u00a0satisfy all of the above requirements we decided to use <a href=\"https:\/\/github.com\/coturn\/coturn\">coturn<\/a>, an open source and fully spec-compliant TURN server.<!--more--><\/p>\n<h3>Simplest TURN server<\/h3>\n<p>To run the first simple version of TURN server that satisfies first 5 requirements, we deployed an Ubuntu virtual machine in Azure, added rules to open ports 3478 and 5349, connected to the machine remotely via SSH and ran the following commands:<\/p>\n<pre class=\"lang:default decode:true\">sudo apt-get update \r\nsudo apt-get install -y dnsutils \r\nsudo apt-get install -y coturn\r\nsudo rm -rf \/var\/lib\/apt\/lists\/*<\/pre>\n<p>Next, we created a file with the script listed below. It prints configuration information to <span class=\"lang:default decode:true crayon-inline \">\/etc\/turnserver.conf<\/span>\u00a0, such as listening, relay and external IPs and a reference to a self-signed certificate. It also adds credentials to the SQLite database that is running on the same server using the\u00a0<a href=\"https:\/\/github.com\/coturn\/coturn\/blob\/master\/README.turnadmin\">turnadmin<\/a> command.<\/p>\n<pre class=\"lang:vim decode:true \">internalIp=\"$(ip a | grep -Eo 'inet (addr:)?([0-9]*\\.){3}[0-9]*' | grep -Eo '([0-9]*\\.){3}[0-9]*' | grep -v '127.0.0.1')\"\r\nexternalIp=\"$(dig +short myip.opendns.com @resolver1.opendns.com)\"\r\n\r\necho \"listening-port=3478\r\ntls-listening-port=5349\r\nlistening-ip=\"$internalIp\"\r\nrelay-ip=\"$internalIp\"\r\nexternal-ip=\"$externalIp\"\r\nrealm=$3\r\nserver-name=$3\r\nlt-cred-mech\r\nuserdb=\/var\/lib\/turn\/turndb\r\n# use real-valid certificate\/privatekey files\r\ncert=\/etc\/ssl\/turn_server_cert.pem\r\npkey=\/etc\/ssl\/turn_server_pkey.pem\r\n \r\nno-stdout-log\"  | tee \/etc\/turnserver.conf\r\n\r\n\r\nturnadmin -a -u $1 -p $2 -r $3\r\n\r\nturnserver<\/pre>\n<p>The script requires 3 parameters (username, password, and realm) that will be sent as authentication data to the TURN server. Running the script will start the TURN server.<\/p>\n<pre class=\"lang:default decode:true\">sudo bash deploy.sh user password123 somerealm.com<\/pre>\n<p>To validate that TURN server works, we used\u00a0<a href=\"https:\/\/webrtc.github.io\/samples\/src\/content\/peerconnection\/trickle-ice\/\">WebRTC Trickle ICE page<\/a>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2018\/01\/Screen-Shot-2018-01-08-at-4.51.26-PM.png\" alt=\"Image Screen Shot 2018 01 08 at 4 51 26 PM\" width=\"722\" height=\"627\" class=\"aligncenter size-full wp-image-10821\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/Screen-Shot-2018-01-08-at-4.51.26-PM.png 722w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2018\/01\/Screen-Shot-2018-01-08-at-4.51.26-PM-300x261.png 300w\" sizes=\"(max-width: 722px) 100vw, 722px\" \/><\/p>\n<h3>Docker container with simple TURN server<\/h3>\n<p>To make our server reusable and easy to deploy in any operating system and environment, we Dockerized the above scripts. This image is available in <a href=\"https:\/\/hub.docker.com\/r\/zolochevska\/turn-server\/\">Docker Hub<\/a>.<\/p>\n<p>With this setup, we can run the Docker container on any Unix VM with Docker installed:<\/p>\n<pre class=\"lang:default decode:true \">sudo docker run -d -p 3478:3478 -p 3478:3478\/udp --restart=always zolochevska\/turn-server username password realm<\/pre>\n<h3>More advanced TURN server in Azure<\/h3>\n<p>To be able to use a TURN server in a production environment, we needed it to satisfy the last four items in our requirements list. This situation leads to a much more complicated deployment process, as it\u00a0requires:<\/p>\n<ul>\n<li>deploying a special kind of server that would balance the\u00a0load for TURN servers instead of relaying traffic<\/li>\n<li>deploying as many VMs with a Docker container as needed to properly balance the load<\/li>\n<li>setting up a PostgreSQL database to store\u00a0authentication data for long-term credentials and\/or time-limited secret-based authentication<\/li>\n<li>installing the certificate to every instance of the server.<\/li>\n<\/ul>\n<p>We selected Azure as our hosting environment for the set of load-balanced TURN servers and have used a number of its services such as <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/virtual-machines\/\">Ubuntu VM<\/a>, <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/postgresql\/\">Azure Database for PostreSQL<\/a>, <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/key-vault\/\">Azure KeyVault<\/a>, and\u00a0<a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/virtual-network\/\">Azure Virtual Network<\/a>.\u00a0To automate the process of deployment to Azure, we developed scripts and <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-resource-manager\/resource-group-authoring-templates\">ARM templates<\/a>\u00a0that make it possible to set up load-balanced TURN servers ready for production within 10 minutes.<\/p>\n<p>The <a href=\"https:\/\/github.com\/anastasiia-zolochevska\/coturn-to-azure-deployment\">project&#8217;s GitHub page<\/a>\u00a0contains steps for deployment on either a Unix or Windows working environment. The main step is automated deployment via ARM template of\u00a0<em>x<\/em> number of VMs and running the specified Docker container\u00a0on them.<\/p>\n<p>We shared a\u00a0<a href=\"https:\/\/hub.docker.com\/r\/zolochevska\/3dsrelay\/\">Docker image with\u00a0coturn server<\/a>\u00a0set up the way it works for our project on Docker Hub. If you want to make modifications, you are welcome to fork <a href=\"https:\/\/github.com\/anastasiia-zolochevska\/3dsrelay\">the original repo on Github,<\/a>\u00a0create a Docker image based on it and specify it as a parameter for your deployment template.<\/p>\n<p>The following screenshot gives insight into the settings that configure deployment of TURN servers.<\/p>\n<p><!-- TODO - missing image -->\n<!-- \n\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-6812\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/Screen-Shot-2018-01-09-at-1.56.18-PM.png\" alt=\"\" width=\"700\" height=\"825\" \/><\/p>\n\n --><\/p>\n<h2>Conclusion<\/h2>\n<p>The scripts, templates and Docker images that we created\u00a0made it possible to set up a production-ready, load-balanced coturn implementation of TURN server to Azure within 10 minutes.\u00a0It was proven to work at scale with a massive video transfer for our use case.\u00a0\u00a0At the Aveva World Summit in October 2017, we were able to demonstrate the detailed rendering of a 400 million polygon model of an oil platform from both on-premises and Azure-based rendering services streamed into a range of devices, including HoloLens.<\/p>\n<p>Everything we built for this project is open-sourced and we welcome feedback in the comments below and contributions on GitHub.<\/p>\n<h3>Resources<\/h3>\n<ul>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/3dtoolkit\">3DToolkit main GitHub repo<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/anastasiia-zolochevska\/coturn-to-azure-deployment\">Repo with scripts, templates and instructions for deploying TURN server<\/a><\/li>\n<li><a href=\"https:\/\/hub.docker.com\/r\/zolochevska\/turn-server\/\">Docker image for the simple coturn server<\/a><\/li>\n<li><a href=\"https:\/\/hub.docker.com\/r\/zolochevska\/3dsrelay\/\">Docker image for coturn server that supports load balancing, secret-based authentication, and certificates<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>We worked with Aveva to build 3DToolkit, a toolkit for creating powerful cloud-based 3D experiences that stream on low-powered devices with WebRTC.<\/p>\n","protected":false},"author":21350,"featured_media":13045,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[10,15,16],"tags":[156,342,363,385],"class_list":["post-6737","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-app-services","category-containers","category-devops","tag-docker","tag-stun","tag-turn","tag-webrtc"],"acf":[],"blog_post_summary":"<p>We worked with Aveva to build 3DToolkit, a toolkit for creating powerful cloud-based 3D experiences that stream on low-powered devices with WebRTC.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/6737","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21350"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=6737"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/6737\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/13045"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=6737"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=6737"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=6737"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}