istio.io/archive/v0.7/blog/2018/egress-tcp.html

80 lines
48 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="en" itemscope itemtype="https://schema.org/WebPage"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><meta name="theme-color" content="#466BB0"/><meta name="title" content="Consuming External TCP Services"><meta name="description" content="Describes a simple scenario based on Istio Bookinfo sample"><meta name="og:title" content="Consuming External TCP Services"><meta name="og:description" content="Describes a simple scenario based on Istio Bookinfo sample"><meta name="og:url" content="/blog/2018/egress-tcp.html"><meta name="og.site_name" content="Istio"><title>Istioldie 0.7 / Consuming External TCP Services</title><script> window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date; ga('create', 'UA-98480406-2', 'auto'); ga('send', 'pageview'); </script> <script async src='https://www.google-analytics.com/analytics.js'></script><link rel="alternate" type="application/rss+xml" title="Istio Blog RSS" href="/v0.7/feed.xml"><link rel="shortcut icon" href="/v0.7/favicons/favicon.ico" ><link rel="apple-touch-icon" href="/v0.7/favicons/apple-touch-icon-180x180.png" sizes="180x180"><link rel="icon" type="image/png" href="/v0.7/favicons/favicon-16x16.png" sizes="16x16"><link rel="icon" type="image/png" href="/v0.7/favicons/favicon-32x32.png" sizes="32x32"><link rel="icon" type="image/png" href="/v0.7/favicons/android-36x36.png" sizes="36x36"><link rel="icon" type="image/png" href="/v0.7/favicons/android-48x48.png" sizes="48x48"><link rel="icon" type="image/png" href="/v0.7/favicons/android-72x72.png" sizes="72x72"><link rel="icon" type="image/png" href="/v0.7/favicons/android-96x196.png" sizes="96x196"><link rel="icon" type="image/png" href="/v0.7/favicons/android-144x144.png" sizes="144x144"><link rel="icon" type="image/png" href="/v0.7/favicons/android-192x192.png" sizes="192x192"><link rel="manifest" href="/v0.7/manifest.json"><meta name="apple-mobile-web-app-title" content="Istio"><meta name="application-name" content="Istio"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,500,500italic,700,700italic,900,900italic"><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.6/css/all.css"><link rel="stylesheet" href="/v0.7/css/light_theme.css" title="light"><link rel="alternate stylesheet" href="/v0.7/css/dark_theme.css" title="dark"> <script src="/v0.7/js/styleSwitcher.min.js"></script></head><body class="language-unknown theme-unknown"><header role="banner"><nav class="navbar navbar-expand-sm navbar-dark fixed-top bg-dark justify-content-between"> <a class="navbar-brand" href="/v0.7/" style="visibility: visible"> <img class="logo" src="/v0.7/img/istio-logo.svg" alt="Istio Logo"/> <span class="brand-name">Istioldie 0.7</span> </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button><div class="collapse navbar-collapse justify-content-end" id="navbarCollapse"><ul id="navbar-links" class="navbar-nav active"><li class="nav-item"> <a class="nav-link " href="/v0.7/docs/">Docs</a></li><li class="nav-item"> <a class="nav-link active" href="/v0.7/blog/2018/traffic-mirroring.html">Blog</a></li><li class="nav-item"> <a class="nav-link " href="/v0.7/help/">Help</a></li><li class="nav-item"> <a class="nav-link " href="/v0.7/community.html">Community</a></li><li class="nav-item"> <a class="nav-link " href="/v0.7/about/">About</a></li><li class="nav-item dropdown" id="gearDropdown" style="white-space: nowrap"> <a href="" class="nav-link" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <i style="width: 1em" class='fa fa-lg fa-cog'></i> </a><ul class="dropdown-menu dropdown-menu-right" aria-labelledby="gearDropdown"><h6 class="dropdown-header">Other versions of this site</h6><li> <a href="https://istio.io">Current Release</a></li><li> <a href="https://preliminary.istio.io">Next Release</a></li><li> <a href="https://archive.istio.io">Older Releases</a></li><li class="dropdown-divider"></li><li> <i class='fa fa-check light'></i> <a href="" onclick="setActiveStyleSheet('light');return false;">Light Theme</a></li><li> <i class='fa fa-check dark'></i> <a href="" onclick="setActiveStyleSheet('dark');return false;">Dark Theme</a></li><li class="dropdown-divider"></li><li><a href="https://github.com/istio/istio.github.io/issues/new?title=Issue with _blog/2018/egress-tcp.md">Report Site Bugs</a></li><li><a href="https://github.com/istio/istio.github.io/edit/master/_blog/2018/egress-tcp.md">Edit this Page on GitHub</a></li></ul></li><li class="nav-item"> <a id="search_show" class="nav-link" href=""><i style="width: 1em" class="fa fa-lg fa-search"></i></a></li></ul><form name="cse" id="search_form" class="form-inline mr-sm-2" role="search"> <input type="hidden" name="cx" value="013699703217164175118:iwwf17ikgf4" /> <input type="hidden" name="ie" value="utf-8" /> <input type="hidden" name="hl" value="en" /> <input id="search_textbox" class="form-control" name="q" type="text" /> <button id="search_close" type="reset"><i class="far fa-lg fa-times-circle"></i></button> </form></div></nav></header><div class="container-fluid blog"><div class="row row-offcanvas row-offcanvas-left"><div class="col-6 col-md-3 col-xl-2 sidebar-offcanvas"><nav class="sidebar"><div class="spacer"></div><div class="directory" role="tablist"><div class="card"><div class="card-header" role="tab" id="header1"> <a data-toggle="collapse" href="#collapse1" title="Blog posts for 2018" role="button" aria-controls="collapse1"><div> 2018 Posts</div></a></div><div id="collapse1" class="collapse show" data-parent="#sidebar" role="tabpanel" aria-labelledby="header1"><div class="card-body"><ul class="tree"><li> <a title="An introduction to safer, lower-risk deployments and release to production" href="/v0.7/blog/2018/traffic-mirroring.html">Traffic Mirroring with Istio for Testing in Production</a></li><li> <span class="current" title="Describes a simple scenario based on Istio Bookinfo sample">Consuming External TCP Services</span></li><li> <a title="Describes a simple scenario based on Istio Bookinfo sample" href="/v0.7/blog/2018/egress-https.html">Consuming External Web Services</a></li></ul></div></div></div><div class="card"><div class="card-header" role="tab" id="header5"> <a data-toggle="collapse" href="#collapse5" title="Blog posts for 2017" role="button" aria-controls="collapse5"><div> 2017 Posts</div></a></div><div id="collapse5" class="collapse" data-parent="#sidebar" role="tabpanel" aria-labelledby="header5"><div class="card-body"><ul class="tree"><li> <a title="Improving availability and reducing latency" href="/v0.7/blog/2017/mixer-spof-myth.html">Mixer and the SPOF Myth</a></li><li> <a title="Provides an overview of the Mixer plug-in architecture" href="/v0.7/blog/2017/adapter-model.html">Mixer Adapter Model</a></li><li> <a title="Istio 0.2 announcement" href="/v0.7/blog/2017/0.2-announcement.html">Announcing Istio 0.2</a></li><li> <a title="How Kubernetes Network Policy relates to Istio policy" href="/v0.7/blog/2017/0.1-using-network-policy.html">Using Network Policy with Istio</a></li><li> <a title="Using Istio to create autoscaled canary deployments" href="/v0.7/blog/2017/0.1-canary.html">Canary Deployments using Istio</a></li><li> <a title="Istio Auth 0.1 announcement" href="/v0.7/blog/2017/0.1-auth.html">Using Istio to Improve End-to-End Security</a></li><li> <a title="Istio 0.1 announcement" href="/v0.7/blog/2017/0.1-announcement.html">Introducing Istio</a></li></ul></div></div></div><div class="text-center" style="margin-top: 1em; font-size: 1.2em;" > <a href="/v0.7/feed.xml"> <img style="width: 1.4em;" src="/v0.7/img/rss.svg" alt="RSS"/> Subscribe </a></div></div></nav></div><div class="col-12 col-md-9 col-lg-7 col-xl-8"><p class="d-md-none"> <label class="sidebar-toggler" data-toggle="offcanvas"> <i class="fa fa-chevron-right"></i> </label></p><main role="main"><h1>Consuming External TCP Services</h1><p class="subtitle">Egress rules for TCP traffic</p><p class="byline"> By <span class="attribution">Vadim Eisenberg</span> / <span class="publish_date">February 6, 2018</span></p><p>In my previous blog post, <a href="/v0.7/blog/2018/egress-https.html">Consuming External Web Services</a>, I described how external services can be consumed by in-mesh Istio applications via HTTPS. In this post, I demonstrate consuming external services over TCP. I use the <a href="/v0.7/docs/guides/bookinfo.html">Istio Bookinfo sample application</a>, the version in which the book ratings data is persisted in a MySQL database. I deploy this database outside the cluster and configure the <em>ratings</em> microservice to use it. I define an <a href="/v0.7/docs/reference/config/istio.routing.v1alpha1.html#EgressRule">egress rule</a> to allow the in-mesh applications to access the external database.</p><h2 id="bookinfo-sample-application-with-external-ratings-database">Bookinfo sample application with external ratings database</h2><p>First, I set up a MySQL database instance to hold book ratings data, outside my Kubernetes cluster. Then I modify the <a href="/v0.7/docs/guides/bookinfo.html">Bookinfo sample application</a> to use my database.</p><h3 id="setting-up-the-database-for-ratings-data">Setting up the database for ratings data</h3><p>For this task I set up an instance of <a href="https://www.mysql.com">MySQL</a>. You can use any MySQL instance; I use <a href="https://www.ibm.com/cloud/compose/mysql">Compose for MySQL</a>. I use <code class="highlighter-rouge">mysqlsh</code> (<a href="https://dev.mysql.com/doc/refman/5.7/en/mysqlsh.html">MySQL Shell</a>) as a MySQL client to feed the ratings data.</p><ol><li>To initialize the database, I run the following command entering the password when prompted. The command is performed with the credentials of the <code class="highlighter-rouge">admin</code> user, created by default by <a href="https://www.ibm.com/cloud/compose/mysql">Compose for MySQL</a>.<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> https://raw.githubusercontent.com/istio/istio/master/samples/bookinfo/src/mysql/mysqldb-init.sql |
mysqlsh <span class="nt">--sql</span> <span class="nt">--ssl-mode</span><span class="o">=</span>REQUIRED <span class="nt">-u</span> admin <span class="nt">-p</span> <span class="nt">--host</span> &lt;the database host&gt; <span class="nt">--port</span> &lt;the database port&gt;
</code></pre></div></div><p><em><strong>OR</strong></em></p><p>When using the <code class="highlighter-rouge">mysql</code> client and a local MySQL database, I would run:</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> https://raw.githubusercontent.com/istio/istio/master/samples/bookinfo/src/mysql/mysqldb-init.sql |
mysql <span class="nt">-u</span> root <span class="nt">-p</span>
</code></pre></div></div></li><li>I then create a user with the name <em>bookinfo</em> and grant it <em>SELECT</em> privilege on the <code class="highlighter-rouge">test.ratings</code> table:<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysqlsh <span class="nt">--sql</span> <span class="nt">--ssl-mode</span><span class="o">=</span>REQUIRED <span class="nt">-u</span> admin <span class="nt">-p</span> <span class="nt">--host</span> &lt;the database host&gt; <span class="nt">--port</span> &lt;the database port&gt; <span class="se">\</span>
<span class="nt">-e</span> <span class="s2">"CREATE USER 'bookinfo' IDENTIFIED BY '&lt;password you choose&gt;'; GRANT SELECT ON test.ratings to 'bookinfo';"</span>
</code></pre></div></div><p><em><strong>OR</strong></em></p><p>For <code class="highlighter-rouge">mysql</code> and the local database, the command would be:</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql <span class="nt">-u</span> root <span class="nt">-p</span> <span class="nt">-e</span> <span class="se">\</span>
<span class="s2">"CREATE USER 'bookinfo' IDENTIFIED BY '&lt;password you choose&gt;'; GRANT SELECT ON test.ratings to 'bookinfo';"</span>
</code></pre></div></div><p>Here I apply the <a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege">principle of least privilege</a>. This means that I do not use my <em>admin</em> user in the Bookinfo application. Instead, I create a special user for the Bookinfo application , <em>bookinfo</em>, with minimal privileges. In this case, the <em>bookinfo</em> user only has the <code class="highlighter-rouge">SELECT</code> privilege on a single table.</p><p>After running the command to create the user, I will clean my bash history by checking the number of the last command and running <code class="highlighter-rouge">history -d &lt;the number of the command that created the user&gt;</code>. I dont want the password of the new user to be stored in the bash history. If Im using <code class="highlighter-rouge">mysql</code>, Ill remove the last command from <code class="highlighter-rouge">~/.mysql_history</code> file as well. Read more about password protection of the newly created user in <a href="https://dev.mysql.com/doc/refman/5.5/en/create-user.html">MySQL documentation</a>.</p></li><li>I inspect the created ratings to see that everything worked as expected:<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysqlsh <span class="nt">--sql</span> <span class="nt">--ssl-mode</span><span class="o">=</span>REQUIRED <span class="nt">-u</span> bookinfo <span class="nt">-p</span> <span class="nt">--host</span> &lt;the database host&gt; <span class="nt">--port</span> &lt;the database port&gt; <span class="se">\</span>
<span class="nt">-e</span> <span class="s2">"select * from test.ratings;"</span>
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Enter password:
+----------+--------+
| ReviewID | Rating |
+----------+--------+
| 1 | 5 |
| 2 | 4 |
+----------+--------+
</code></pre></div></div><p><em><strong>OR</strong></em></p><p>For <code class="highlighter-rouge">mysql</code> and the local database:</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql <span class="nt">-u</span> bookinfo <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"select * from test.ratings;"</span>
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Enter password:
+----------+--------+
| ReviewID | Rating |
+----------+--------+
| 1 | 5 |
| 2 | 4 |
+----------+--------+
</code></pre></div></div></li><li>I set the ratings temporarily to 1 to provide a visual clue when our database is used by the Bookinfo <em>ratings</em> service:<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysqlsh <span class="nt">--sql</span> <span class="nt">--ssl-mode</span><span class="o">=</span>REQUIRED <span class="nt">-u</span> admin <span class="nt">-p</span> <span class="nt">--host</span> &lt;the database host&gt; <span class="nt">--port</span> &lt;the database port&gt; <span class="se">\</span>
<span class="nt">-e</span> <span class="s2">"update test.ratings set rating=1; select * from test.ratings;"</span>
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Enter password:
+----------+--------+
| ReviewID | Rating |
+----------+--------+
| 1 | 1 |
| 2 | 1 |
+----------+--------+
</code></pre></div></div><p><em><strong>OR</strong></em></p><p>For <code class="highlighter-rouge">mysql</code> and the local database:</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql <span class="nt">-u</span> root <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"update test.ratings set rating=1; select * from test.ratings;"</span>
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Enter password:
+----------+--------+
| ReviewID | Rating |
+----------+--------+
| 1 | 1 |
| 2 | 1 |
+----------+--------+
</code></pre></div></div><p>I used the <em>admin</em> user (and <em>root</em> for the local database) in the last command since the <em>bookinfo</em> user does not have the <em>UPDATE</em> privilege on the <code class="highlighter-rouge">test.ratings</code> table.</p></li></ol><p>Now I am ready to deploy a version of the Bookinfo application that will use my database.</p><h3 id="initial-setting-of-bookinfo-application">Initial setting of Bookinfo application</h3><p>To demonstrate the scenario of using an external database, I start with a Kubernetes cluster with <a href="/v0.7/docs/setup/kubernetes/quick-start.html#installation-steps">Istio installed</a>. Then I deploy the <a href="/v0.7/docs/guides/bookinfo.html">Istio Bookinfo sample application</a>. This application uses the <em>ratings</em> microservice to fetch book ratings, a number between 1 and 5. The ratings are displayed as stars for each review. There are several versions of the <em>ratings</em> microservice. Some use <a href="https://www.mongodb.com">MongoDB</a>, others use <a href="https://www.mysql.com">MySQL</a> as their database.</p><p>The example commands in this blog post work with Istio version 0.3+, with or without <a href="/v0.7/docs/concepts/security/mutual-tls.html">Mutual TLS</a> enabled.</p><p>As a reminder, here is the end-to-end architecture of the application from the <a href="/v0.7/docs/guides/bookinfo.html">Bookinfo Guide</a>.</p><div class="figure" style="width: 80%;"><div class="wrapper-with-intrinsic-ratio" style="padding-bottom: 59.08%"><figure> <a href="/v0.7/docs/guides/img/bookinfo/withistio.svg"> <img class="element-to-stretch" src="/v0.7/docs/guides/img/bookinfo/withistio.svg" alt="The original Bookinfo application" title="The original Bookinfo application" /> </a></figure></div><p>The original Bookinfo application</p></div><h3 id="use-the-database-for-ratings-data-in-bookinfo-application">Use the database for ratings data in Bookinfo application</h3><ol><li><p>I modify the deployment spec of a version of the <em>ratings</em> microservice that uses a MySQL database, to use my database instance. The spec is in <code class="highlighter-rouge">samples/bookinfo/kube/bookinfo-ratings-v2-mysql.yaml</code> of an Istio release archive. I edit the following lines:</p><div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MYSQL_DB_HOST</span>
<span class="na">value</span><span class="pi">:</span> <span class="s">mysqldb</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MYSQL_DB_PORT</span>
<span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3306"</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MYSQL_DB_USER</span>
<span class="na">value</span><span class="pi">:</span> <span class="s">root</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MYSQL_DB_PASSWORD</span>
<span class="na">value</span><span class="pi">:</span> <span class="s">password</span>
</code></pre></div></div><p>I replace the values in the snippet above, specifying the database host, port, user, and password. Note that the correct way to work with passwords in containers environment variables in Kubernetes is <a href="https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-environment-variables">to use secrets</a>. For this example task only, I write the password directly in the deployment spec. <strong>Do not do it</strong> in a real environment! I also assume everyone realizes that <code class="highlighter-rouge">"password"</code> should not be used as a password…</p></li><li><p>I apply the modified spec to deploy the version of the <em>ratings</em> microservice, <em>v2-mysql</em>, that will use my database.</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl apply <span class="nt">-f</span> &lt;<span class="o">(</span>istioctl kube-inject <span class="nt">-f</span> samples/bookinfo/kube/bookinfo-ratings-v2-mysql.yaml<span class="o">)</span>
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deployment <span class="s2">"ratings-v2-mysql"</span> created
</code></pre></div></div></li><li><p>I route all the traffic destined to the <em>reviews</em> service to its <em>v3</em> version. I do this to ensure that the <em>reviews</em> service always calls the <em>ratings</em> service. In addition, I route all the traffic destined to the <em>ratings</em> service to <em>ratings v2-mysql</em> that uses my database. I add routing for both services above by adding two <a href="/v0.7/docs/reference/config/istio.routing.v1alpha1.html">route rules</a>. These rules are specified in <code class="highlighter-rouge">samples/bookinfo/kube/route-rule-ratings-mysql.yaml</code> of an Istio release archive.</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>istioctl create <span class="nt">-f</span> samples/bookinfo/kube/route-rule-ratings-mysql.yaml
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Created config route-rule/default/ratings-test-v2-mysql at revision 1918799
Created config route-rule/default/reviews-test-ratings-v2 at revision 1918800
</code></pre></div></div></li></ol><p>The updated architecture appears below. Note that the blue arrows inside the mesh mark the traffic configured according to the route rules we added. According to the route rules, the traffic is sent to <em>reviews v3</em> and <em>ratings v2-mysql</em>.</p><div class="figure" style="width: 80%;"><div class="wrapper-with-intrinsic-ratio" style="padding-bottom: 59.31%"><figure> <a href="./img/bookinfo-ratings-v2-mysql-external.svg"> <img class="element-to-stretch" src="./img/bookinfo-ratings-v2-mysql-external.svg" alt="The Bookinfo application with ratings v2-mysql and an external MySQL database" title="The Bookinfo application with ratings v2-mysql and an external MySQL database" /> </a></figure></div><p>The Bookinfo application with ratings v2-mysql and an external MySQL database</p></div><p>Note that the MySQL database is outside the Istio service mesh, or more precisely outside the Kubernetes cluster. The boundary of the service mesh is marked by a dashed line.</p><h3 id="access-the-webpage">Access the webpage</h3><p>Lets access the webpage of the application, after <a href="/v0.7/docs/guides/bookinfo.html#determining-the-ingress-ip-and-port">determining the ingress IP and port</a>.</p><p>We have a problem… Instead of the rating stars, the message <em>“Ratings service is currently unavailable”</em> is currently displayed below each review:</p><div class="figure" style="width: 80%;"><div class="wrapper-with-intrinsic-ratio" style="padding-bottom: 36.19%"><figure> <a href="./img/errorFetchingBookRating.png"> <img class="element-to-stretch" src="./img/errorFetchingBookRating.png" alt="The Ratings service error messages" title="The Ratings service error messages" /> </a></figure></div><p>The Ratings service error messages</p></div><p>As in <a href="/v0.7/blog/2018/egress-https.html">Consuming External Web Services</a>, we experience <strong>graceful service degradation</strong>, which is good. The application did not crash due to the error in the <em>ratings</em> microservice. The webpage of the application correctly displayed the book information, the details, and the reviews, just without the rating stars.</p><p>We have the same problem as in <a href="/v0.7/blog/2018/egress-https.html">Consuming External Web Services</a>, namely all the traffic outside the Kubernetes cluster, both TCP and HTTP, is blocked by default by the sidecar proxies. To enable such traffic for TCP, an egress rule for TCP must be defined.</p><h3 id="egress-rule-for-an-external-mysql-instance">Egress rule for an external MySQL instance</h3><p>TCP egress rules come to our rescue. I copy the following YAML spec to a text file (lets call it <code class="highlighter-rouge">egress-rule-mysql.yaml</code>) and edit it to specify the IP of my database instance and its port.</p><div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">config.istio.io/v1alpha2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">EgressRule</span>
<span class="na">metadata</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">mysql</span>
<span class="na">namespace</span><span class="pi">:</span> <span class="s">default</span>
<span class="na">spec</span><span class="pi">:</span>
<span class="na">destination</span><span class="pi">:</span>
<span class="na">service</span><span class="pi">:</span> <span class="s">&lt;MySQL instance IP&gt;</span>
<span class="na">ports</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="s">&lt;MySQL instance port&gt;</span>
<span class="na">protocol</span><span class="pi">:</span> <span class="s">tcp</span>
</code></pre></div></div><p>Then I run <code class="highlighter-rouge">istioctl</code> to add the egress rule to the service mesh:</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>istioctl create <span class="nt">-f</span> egress-rule-mysql.yaml
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Created config egress-rule/default/mysql at revision 1954425
</code></pre></div></div><p>Note that for a TCP egress rule, we specify <code class="highlighter-rouge">tcp</code> as the protocol of a port of the rule. Also note that we use an IP of the external service instead of its domain name. I will talk more about TCP egress rules <a href="#egress-rules-for-tcp-traffic">below</a>. For now, lets verify that the egress rule we added fixed the problem. Lets access the webpage and see if the stars are back.</p><p>It worked! Accessing the web page of the application displays the ratings without error:</p><div class="figure" style="width: 80%;"><div class="wrapper-with-intrinsic-ratio" style="padding-bottom: 36.69%"><figure> <a href="./img/externalMySQLRatings.png"> <img class="element-to-stretch" src="./img/externalMySQLRatings.png" alt="Book Ratings Displayed Correctly" title="Book Ratings Displayed Correctly" /> </a></figure></div><p>Book Ratings Displayed Correctly</p></div><p>Note that we see a one-star rating for both displayed reviews, as expected. I changed the ratings to be one star to provide us with a visual clue that our external database is indeed being used.</p><p>As with egress rules for HTTP/HTTPS, we can delete and create egress rules for TCP using <code class="highlighter-rouge">istioctl</code>, dynamically.</p><h2 id="motivation-for-egress-tcp-traffic-control">Motivation for egress TCP traffic control</h2><p>Some in-mesh Istio applications must access external services, for example legacy systems. In many cases, the access is not performed over HTTP or HTTPS protocols. Other TCP protocols are used, such as database-specific protocols like <a href="https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/">MongoDB Wire Protocol</a> and <a href="https://dev.mysql.com/doc/internals/en/client-server-protocol.html">MySQL Client/Server Protocol</a> to communicate with external databases.</p><p>Note that in case of access to external HTTPS services, as described in the <a href="/v0.7/docs/tasks/traffic-management/egress.html">Control Egress TCP Traffic</a> task, an application must issue HTTP requests to the external service. The Envoy sidecar proxy attached to the pod or the VM, will intercept the requests and open an HTTPS connection to the external service. The traffic will be unencrypted inside the pod or the VM, but it will leave the pod or the VM encrypted.</p><p>However, sometimes this approach cannot work due to the following reasons:</p><ul><li>The code of the application is configured to use an HTTPS URL and cannot be changed</li><li>The code of the application uses some library to access the external service and that library uses HTTPS only</li><li>There are compliance requirements that do not allow unencrypted traffic, even if the traffic is unencrypted only inside the pod or the VM</li></ul><p>In this case, HTTPS can be treated by Istio as <em>opaque TCP</em> and can be handled in the same way as other TCP non-HTTP protocols.</p><p>Next lets see how we define egress rules for TCP traffic.</p><h2 id="egress-rules-for-tcp-traffic">Egress rules for TCP traffic</h2><p>The egress rules for enabling TCP traffic to a specific port must specify <code class="highlighter-rouge">TCP</code> as the protocol of the port. Additionally, for the <a href="https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/">MongoDB Wire Protocol</a>, the protocol can be specified as <code class="highlighter-rouge">MONGO</code>, instead of <code class="highlighter-rouge">TCP</code>.</p><p>For the <code class="highlighter-rouge">destination.service</code> field of the rule, an IP or a block of IPs in <a href="https://tools.ietf.org/html/rfc2317">CIDR</a> notation must be used.</p><p>To enable TCP traffic to an external service by its hostname, all the IPs of the hostname must be specified. Each IP must be specified by a CIDR block or as a single IP, with each block or IP in a separate egress rule.</p><p>Note that all the IPs of an external service are not always known. To enable TCP traffic by IPs, as opposed to the traffic by a hostname, only the IPs that are used by the applications must be specified.</p><p>Also note that the IPs of an external service are not always static, for example in the case of <a href="https://en.wikipedia.org/wiki/Content_delivery_network">CDNs</a>. Sometimes the IPs are static most of the time, but can be changed from time to time, for example due to infrastructure changes. In these cases, if the range of the possible IPs is known, you should specify the range by CIDR blocks (even by multiple egress rules if needed). As an example, see the approach we used in the case of <code class="highlighter-rouge">wikipedia.org</code>, described in <a href="/v0.7/docs/tasks/traffic-management/egress-tcp.html">Control Egress TCP Traffic Task</a>. If the range of the possible IPs is not known, egress rules for TCP cannot be used and <a href="/v0.7/docs/tasks/traffic-management/egress.html#calling-external-services-directly">the external services must be called directly</a>, circumventing the sidecar proxies.</p><h2 id="relation-to-mesh-expansion">Relation to mesh expansion</h2><p>Note that the scenario described in this post is different from the mesh expansion scenario, described in the <a href="/v0.7/docs/guides/integrating-vms.html">Integrating Virtual Machines</a> guide. In that scenario, a MySQL instance runs on an external (outside the cluster) machine (a bare metal or a VM), integrated with the Istio service mesh. The MySQL service becomes a first-class citizen of the mesh with all the beneficial features of Istio applicable. Among other things, the service becomes addressable by a local cluster domain name, for example by <code class="highlighter-rouge">mysqldb.vm.svc.cluster.local</code>, and the communication to it can be secured by <a href="/v0.7/docs/concepts/security/mutual-tls.html">mutual TLS authentication</a>. There is no need to create an egress rule to access this service; however, the service must be registered with Istio. To enable such integration, Istio components (<em>Envoy proxy</em>, <em>node-agent</em>, <em>istio-agent</em>) must be installed on the machine and the Istio control plane (<em>Pilot</em>, <em>Mixer</em>, <em>CA</em>) must be accessible from it. See the <a href="/v0.7/docs/setup/kubernetes/mesh-expansion.html">Istio Mesh Expansion</a> instructions for more details.</p><p>In our case, the MySQL instance can run on any machine or can be provisioned as a service by a cloud provider. There is no requirement to integrate the machine with Istio. The Istio contol plane does not have to be accessible from the machine. In the case of MySQL as a service, the machine which MySQL runs on may be not accessible and installing on it the required components may be impossible. In our case, the MySQL instance is addressable by its global domain name, which could be beneficial if the consuming applications expect to use that domain name. This is especially relevant when that expected domain name cannot be changed in the deployment configuration of the consuming applications.</p><h2 id="cleanup">Cleanup</h2><ol><li>Drop the <em>test</em> database and the <em>bookinfo</em> user:<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysqlsh <span class="nt">--sql</span> <span class="nt">--ssl-mode</span><span class="o">=</span>REQUIRED <span class="nt">-u</span> admin <span class="nt">-p</span> <span class="nt">--host</span> &lt;the database host&gt; <span class="nt">--port</span> &lt;the database port&gt; <span class="se">\</span>
<span class="nt">-e</span> <span class="s2">"drop database test; drop user bookinfo;"</span>
</code></pre></div></div><p><em><strong>OR</strong></em></p><p>For <code class="highlighter-rouge">mysql</code> and the local database:</p><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql <span class="nt">-u</span> root <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"drop database test; drop user bookinfo;"</span>
</code></pre></div></div></li><li>Remove the route rules:<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>istioctl delete <span class="nt">-f</span> samples/bookinfo/kube/route-rule-ratings-mysql.yaml
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Deleted config: route-rule/default/ratings-test-v2-mysql
Deleted config: route-rule/default/reviews-test-ratings-v2
</code></pre></div></div></li><li>Undeploy <em>ratings v2-mysql</em>:<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl delete <span class="nt">-f</span> &lt;<span class="o">(</span>istioctl kube-inject <span class="nt">-f</span> samples/bookinfo/kube/bookinfo-ratings-v2-mysql.yaml<span class="o">)</span>
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deployment <span class="s2">"ratings-v2-mysql"</span> deleted
</code></pre></div></div></li><li>Delete the egress rule:<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>istioctl delete egressrule mysql <span class="nt">-n</span> default
</code></pre></div></div><div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Deleted config: egressrule mysql
</code></pre></div></div></li></ol><h2 id="future-work">Future work</h2><p>In my next blog posts, I will show examples of combining route rules and egress rules, and also examples of accessing external services via Kubernetes <em>ExternalName</em> services.</p><h2 id="conclusion">Conclusion</h2><p>In this blog post, I demonstrated how the microservices in an Istio service mesh can consume external services via TCP. By default, Istio blocks all the traffic, TCP and HTTP, to the hosts outside the cluster. To enable such traffic for TCP, TCP egress rules must be created for the service mesh.</p><h2 id="whats-next">Whats next</h2><p>To read more about Istio egress traffic control:</p><ul><li>for TCP, see <a href="/v0.7/docs/tasks/traffic-management/egress-tcp.html">Control Egress TCP Traffic Task</a></li><li>for HTTP/HTTPS, see <a href="/v0.7/docs/tasks/traffic-management/egress.html">Control Egress Traffic Task</a></li></ul></main><br/><hr/><br/><div class="container-fluid"><div class="row"><div class="col-6"> <a href="/v0.7/blog/2018/egress-https.html"><i class="fa fa-arrow-left"></i> Consuming External Web Services</a></div><div class="col-6" style="text-align: right"> <a href="/v0.7/blog/2018/traffic-mirroring.html">Traffic Mirroring with Istio for Testing in Production <i class="fa fa-arrow-right"></i></a></div></div></div></div><div class="col-12 col-md-2 d-none d-lg-block"><nav class="toc"><div class="spacer"></div><div class="directory" role="directory"><ul><li><a href="#bookinfo-sample-application-with-external-ratings-database">Bookinfo sample application with external ratings database</a><ul><li><a href="#setting-up-the-database-for-ratings-data">Setting up the database for ratings data</a></li><li><a href="#initial-setting-of-bookinfo-application">Initial setting of Bookinfo application</a></li><li><a href="#use-the-database-for-ratings-data-in-bookinfo-application">Use the database for ratings data in Bookinfo application</a></li><li><a href="#access-the-webpage">Access the webpage</a></li><li><a href="#egress-rule-for-an-external-mysql-instance">Egress rule for an external MySQL instance</a></li></ul></li><li><a href="#motivation-for-egress-tcp-traffic-control">Motivation for egress TCP traffic control</a></li><li><a href="#egress-rules-for-tcp-traffic">Egress rules for TCP traffic</a></li><li><a href="#relation-to-mesh-expansion">Relation to mesh expansion</a></li><li><a href="#cleanup">Cleanup</a></li><li><a href="#future-work">Future work</a></li><li><a href="#conclusion">Conclusion</a></li><li><a href="#whats-next">Whats next</a></li></ul></div></nav></div></div></div><div class="footer"><footer><div class="container-fluid"><div class="row"><div class="col-6 col-lg-4" role="navigation"><div class="container-fluid"><div class="row justify-content-start"><div class="icon"> <a title="Join the istio-users@ mailing list to participate in discussions and get help troubleshooting problems" href="https://groups.google.com/forum/#!forum/istio-users"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490 490"><path d="M480,410.248H10c-5.523,0-10-4.477-10-10V89.752c0-5.523,4.477-10,10-10h470c5.522,0,10,4.477,10,10v310.495 C490,405.771,485.522,410.248,480,410.248z M20,390.248h450V99.752H20V390.248z"/><path d="M245,286.131c-2.083,0-4.167-0.649-5.931-1.948L48.64,143.929c-4.446-3.275-5.396-9.535-2.121-13.982 c3.275-4.447,9.535-5.396,13.982-2.121L245,263.712l184.5-135.886c4.447-3.274,10.709-2.326,13.982,2.121 c3.275,4.447,2.325,10.707-2.121,13.982L250.931,284.183C249.167,285.482,247.083,286.131,245,286.131z"/> </svg> </a></div><div class="icon"> <a title="Follow us on Twitter to get the latest news" href="https://twitter.com/IstioMesh"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 310 310"><path d="M302.973,57.388c-4.87,2.16-9.877,3.983-14.993,5.463c6.057-6.85,10.675-14.91,13.494-23.73 c0.632-1.977-0.023-4.141-1.648-5.434c-1.623-1.294-3.878-1.449-5.665-0.39c-10.865,6.444-22.587,11.075-34.878,13.783 c-12.381-12.098-29.197-18.983-46.581-18.983c-36.695,0-66.549,29.853-66.549,66.547c0,2.89,0.183,5.764,0.545,8.598 C101.163,99.244,58.83,76.863,29.76,41.204c-1.036-1.271-2.632-1.956-4.266-1.825c-1.635,0.128-3.104,1.05-3.93,2.467 c-5.896,10.117-9.013,21.688-9.013,33.461c0,16.035,5.725,31.249,15.838,43.137c-3.075-1.065-6.059-2.396-8.907-3.977 c-1.529-0.851-3.395-0.838-4.914,0.033c-1.52,0.871-2.473,2.473-2.513,4.224c-0.007,0.295-0.007,0.59-0.007,0.889 c0,23.935,12.882,45.484,32.577,57.229c-1.692-0.169-3.383-0.414-5.063-0.735c-1.732-0.331-3.513,0.276-4.681,1.597 c-1.17,1.32-1.557,3.16-1.018,4.84c7.29,22.76,26.059,39.501,48.749,44.605c-18.819,11.787-40.34,17.961-62.932,17.961 c-4.714,0-9.455-0.277-14.095-0.826c-2.305-0.274-4.509,1.087-5.294,3.279c-0.785,2.193,0.047,4.638,2.008,5.895 c29.023,18.609,62.582,28.445,97.047,28.445c67.754,0,110.139-31.95,133.764-58.753c29.46-33.421,46.356-77.658,46.356-121.367 c0-1.826-0.028-3.67-0.084-5.508c11.623-8.757,21.63-19.355,29.773-31.536c1.237-1.85,1.103-4.295-0.33-5.998 C307.394,57.037,305.009,56.486,302.973,57.388z"/> </svg> </a></div><div class="icon"> <a title="Stack Overflow is where you can ask questions and find curated answers on deploying, configuring, and using Istio" href="https://stackoverflow.com/questions/tagged/istio"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120"><polygon points="84.4,93.8 84.4,70.6 92.1,70.6 92.1,101.5 22.6,101.5 22.6,70.6 30.3,70.6 30.3,93.8 "/><path d="M38.8,68.4l37.8,7.9l1.6-7.6l-37.8-7.9L38.8,68.4z M43.8,50.4l35,16.3l3.2-7l-35-16.4L43.8,50.4z M53.5,33.2 l29.7,24.7l4.9-5.9L58.4,27.3L53.5,33.2z M72.7,14.9l-6.2,4.6l23,31l6.2-4.6L72.7,14.9z M38,86h38.6v-7.7H38V86z"/> </svg> </a></div></div><div class="row justify-content-start d-none d-lg-flex"><p class="tag">for users</p></div></div></div><div class="col-6 col-lg-4"><p class="text-center copyright" role="contentinfo"> Istio Archive 0.7, Copyright &copy; 2018 Istio Authors<br> Archived on 05-May-2018</p></div><div class="col-6 col-lg-4 d-none d-lg-flex" role="navigation"><div class="container-fluid"><div class="row justify-content-end"><div class="icon"> <a title="Join the istio-dev@ mailing list to discuss development issues around the Istio project" href="https://groups.google.com/forum/#!forum/istio-dev"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490 490"><path d="M480,410.248H10c-5.523,0-10-4.477-10-10V89.752c0-5.523,4.477-10,10-10h470c5.522,0,10,4.477,10,10v310.495 C490,405.771,485.522,410.248,480,410.248z M20,390.248h450V99.752H20V390.248z"/><path d="M245,286.131c-2.083,0-4.167-0.649-5.931-1.948L48.64,143.929c-4.446-3.275-5.396-9.535-2.121-13.982 c3.275-4.447,9.535-5.396,13.982-2.121L245,263.712l184.5-135.886c4.447-3.274,10.709-2.326,13.982,2.121 c3.275,4.447,2.325,10.707-2.121,13.982L250.931,284.183C249.167,285.482,247.083,286.131,245,286.131z"/> </svg> </a></div><div class="icon"> <a title="GitHub is where development takes place on Istio code" href="https://github.com/istio/community"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 478.165 478.165"><path d="M349.22,55.768c6.136,14.046,10.241,37.556,4.224,54.69 c24.426,20.999,33.073,71.904,21.079,113.704c35.006,2.73,76.666-1.235,103.642,9.484c-25.183-3.248-59.651-9.563-91.987-7.431 c-6.136,0.458-15.361-0.239-14.903,8.408c37.735,3.008,75.092,6.117,105.894,15.779c-30.702-4.981-67.74-12.552-105.894-13.668 c-15.54,30.921-47.239,46.262-90.991,49.49c4.682,10.261,13.847,14.066,15.879,30.702c3.267,24.406-4.881,60.328,3.208,76.686 c4.064,7.89,10.579,8.009,14.863,14.604c-10.699,12.871-37.257-1.395-40.186-14.604c-5.14-22.852,7.89-58.256-6.415-73.737 c0.996,24.865-5.718,59.85,0.996,82.145c2.789,8.806,10.659,12.113,8.647,20.063c-49.809,5.08-28.989-64.373-37.177-105.356 c-7.471,0.697-4.204,11.197-4.224,15.76c-0.199,40.106,8.189,94.836-34.846,89.556c-1.315-8.348,5.838-11.217,8.467-19.007 c7.91-22.434-1.454-56.045,2.112-83.161c-16.417,12.512,1.793,55.666-8.428,77.961c-5.838,12.671-24.785,18.27-39.19,12.651 c1.873-9.464,11.695-7.989,15.879-16.875c5.818-12.452,0.02-30.244,2.092-48.494c-30.423,6.097-53.993-0.877-65.608-20.023 c-5.12-8.507-6.356-18.708-12.632-26.219c-6.117-7.551-16.098-8.507-19.087-18.808c37.755-9.185,39.17,38.771,73.06,39.807 c10.44,0.418,15.799-2.909,25.402-5.16c2.749-12.113,8.428-21.039,16.875-27.494c-42.078-5.658-76.865-18.788-93.023-50.466 c-38.293,1.893-73.339,7.013-105.894,14.843c29.547-10.679,65.807-14.604,104.778-15.819c-2.351-13.807-22.434-10.022-34.866-9.543 C47.677,227.17,18.449,230.138,0,233.645c26.817-9.543,64.233-8.348,100.454-8.428c-11.038-34.767-7.232-90.014,17.015-110.615 c-6.854-17.254-4.722-45.346,4.184-58.834c27.036,1.175,43.374,12.891,60.388,24.247c21.019-6.017,43.035-9.045,71.904-7.451 c12.133,0.677,24.705,6.097,33.731,5.32c8.906-0.877,18.728-10.898,27.534-14.843C326.507,58.099,336.17,56.206,349.22,55.768z"/> </svg> </a></div><div class="icon"> <a title="Access our team drive if you'd like to take a look at the Istio technical design documents" href="https://groups.google.com/forum/#!forum/istio-team-drive-access"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 207.027 207.027"><path d="M69.866,15.557L0,138.919l28.732,52.552l143.288-0.029l35.008-59.588L136.39,15.735L69.866,15.557z M17.166,139.046 L74.268,38.205L91.21,67.783L33.24,168.447L17.166,139.046z M99.841,82.851l23.805,41.558l-47.732-0.006L99.841,82.851z M163.434,176.443l-117.332,0.024l21.53-37.065l64.606,0.008l0.067,0.119l52.865-0.085L163.434,176.443z M140.932,124.411 L90.157,35.767l-2.966-5.178l40.751,0.121l57.003,93.706L140.932,124.411z"/> </svg> </a></div><div class="icon"> <a title="If you'd like to contribute to the Istio project, consider participating in our working groups" href="https://github.com/istio/community/blob/master/WORKING-GROUPS.md"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -45 439.833 439.833"><polygon points="246.048,195.833 299.966,235.085 319.497,227.296 276.278,195.833"/><polygon points="193.786,195.833 163.556,195.833 120.33,227.3 139.862,235.089"/><path d="M219.927,11.558c-23.854,0-37.057,12.362-36.814,36.182c0.348,32.623,14.211,52.414,36.814,52.068 c0,0,36.802,1.492,36.802-52.068C256.729,23.918,244.294,11.558,219.927,11.558z"/><path d="M285.017,124.567l-36.77-14.659l-8.608-7.256c-2.274-1.922-5.636-1.78-7.741,0.317l-11.973,11.904l-12.008-11.907 c-2.109-2.094-5.465-2.229-7.736-0.313l-8.611,7.256l-36.77,14.661c-11.842,4.715-11.83,46.647-12.848,50.497h155.93 C296.866,171.228,296.862,129.28,285.017,124.567z"/><path d="M77.976,228.568c0,0,36.801,1.492,36.801-52.068c0-23.82-12.434-36.182-36.801-36.182 c-23.854,0-37.057,12.362-36.814,36.182C41.509,209.124,55.372,228.915,77.976,228.568z"/><path d="M143.065,253.329l-36.77-14.658l-8.609-7.256c-2.275-1.923-5.635-1.781-7.742,0.315l-11.971,11.904l-12.008-11.908 c-2.109-2.094-5.465-2.229-7.736-0.312l-8.611,7.256l-36.77,14.66C1.006,258.045,1.018,299.977,0,303.827h155.93 C154.915,299.988,154.911,258.042,143.065,253.329z"/><path d="M361.878,228.568c0,0,36.801,1.492,36.801-52.068c0-23.82-12.434-36.182-36.801-36.182 c-23.854,0-37.057,12.362-36.812,36.182C325.411,209.124,339.274,228.915,361.878,228.568z"/><path d="M426.968,253.329l-36.77-14.658l-8.609-7.256c-2.273-1.923-5.635-1.781-7.742,0.315l-11.971,11.904l-12.008-11.908 c-2.109-2.094-5.465-2.229-7.736-0.312l-8.61,7.256l-36.771,14.66c-11.842,4.715-11.83,46.646-12.848,50.497h155.93 C438.817,299.988,438.812,258.042,426.968,253.329z"/> </svg> </a></div><div class="icon"> <a title="Interactively discuss development issues with the Istio community on Slack (invitation-only)" href="https://istio.slack.com"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.444 31.443"><path d="M31.202,16.369c-0.62-1.388-2.249-2.011-3.637-1.391l-1.325,0.594l-3.396-7.591l1.325-0.592 c1.388-0.622,2.01-2.25,1.389-3.637c-0.62-1.389-2.248-2.012-3.637-1.39l-1.324,0.593l-0.593-1.326 c-0.621-1.388-2.249-2.009-3.637-1.388c-1.388,0.62-2.009,2.247-1.389,3.637l0.593,1.325L7.98,8.598L7.388,7.273 c-0.621-1.39-2.249-2.009-3.637-1.39C2.363,6.504,1.742,8.132,2.362,9.52l0.592,1.324L1.63,11.438 c-1.388,0.621-2.01,2.247-1.389,3.636c0.62,1.388,2.249,2.01,3.637,1.39l1.325-0.594l3.394,7.592l-1.325,0.592 c-1.388,0.621-2.009,2.25-1.389,3.637c0.621,1.389,2.249,2.011,3.637,1.391l1.324-0.593l0.593,1.325 c0.621,1.389,2.249,2.01,3.637,1.389c1.387-0.62,2.009-2.248,1.388-3.636l-0.591-1.326l7.591-3.394l0.592,1.321 c0.621,1.391,2.248,2.013,3.637,1.392c1.388-0.619,2.01-2.248,1.389-3.637l-0.592-1.324l1.323-0.594 C31.201,19.384,31.823,17.757,31.202,16.369z M13.623,21.215l-3.395-7.593l7.591-3.394l3.395,7.591L13.623,21.215z"/> </svg> </a></div></div><div class="row justify-content-end text-right"><p class="text-right tag">for developers</p></div></div></div></div></div></footer></div><script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script> <script src="https://www.google.com/cse/brand?form=search_form"></script> <script src="/v0.7/js/misc.min.js"></script></body></html>