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

80 lines
40 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="title" content="Consuming External TCP Services"><meta name="og:title" content="Consuming External TCP Services"><meta name="og:image" content="/v0.5/img/logo.png"/><meta name="theme-color" content="#466BB0"/><meta name="description" content="Describes a simple scenario based on Istio Bookinfo sample"><meta name="og:description" content="Describes a simple scenario based on Istio Bookinfo sample"><title>Istioldie 0.5 / 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.5/feed.xml"><link rel="shortcut icon" href="/v0.5/favicons/favicon.ico" ><link rel="apple-touch-icon" href="/v0.5/favicons/apple-touch-icon-180x180.png" sizes="180x180"><link rel="icon" type="image/png" href="/v0.5/favicons/favicon-16x16.png" sizes="16x16"><link rel="icon" type="image/png" href="/v0.5/favicons/favicon-32x32.png" sizes="32x32"><link rel="icon" type="image/png" href="/v0.5/favicons/android-36x36.png" sizes="36x36"><link rel="icon" type="image/png" href="/v0.5/favicons/android-48x48.png" sizes="48x48"><link rel="icon" type="image/png" href="/v0.5/favicons/android-72x72.png" sizes="72x72"><link rel="icon" type="image/png" href="/v0.5/favicons/android-96x196.png" sizes="96x196"><link rel="icon" type="image/png" href="/v0.5/favicons/android-144x144.png" sizes="144x144"><link rel="icon" type="image/png" href="/v0.5/favicons/android-192x192.png" sizes="192x192"><link rel="manifest" href="/v0.5/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.5/css/light_theme.css" title="light"><link rel="alternate stylesheet" href="/v0.5/css/dark_theme.css" title="dark"> <script src="/v0.5/js/styleswitcher.js"></script></head><body class="language-unknown theme-unknown"><header role="banner"><nav class="navbar navbar-expand-sm navbar-dark fixed-top bg-dark"> <a class="navbar-brand d-flex w-50 mr-auto" href="/v0.5/" style="visibility: visible"> <img class="logo" src="/v0.5/img/istio-logo.svg" alt="Istio Logo"/> <span class="brand-name">Istioldie 0.5</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" id="navbarCollapse"><div class="navbar-nav justify-content-end"> <a class="nav-item nav-link " href="/v0.5/about/intro.html">About</a> <a class="nav-item nav-link active" href="/v0.5/blog/2018/traffic-mirroring.html">Blog</a> <a class="nav-item nav-link " href="/v0.5/docs/">Docs</a> <a class="nav-item nav-link " href="/v0.5/help">Help</a> <a class="nav-item nav-link " href="/v0.5/community">Community</a></div><div class="dropdown"> <a href="" class="nav-link nav-item dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <i class='fa fa-lg fa-cog'></i> </a><div class="dropdown-menu"><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></div></div><form name="cse" id="searchbox" class="form-inline justify-content-end" 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" /><div class="input-group"> <input name="q" class="form-control search-box" type="text" size="30" /> <button class="btn btn-search input-group-addon my-2 my-sm-0 fa fa-search" type="submit"></button></div></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.5/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.5/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.5/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.5/blog/2017/adapter-model.html">Mixer Adapter Model</a></li><li> <a title="Istio 0.2 announcement" href="/v0.5/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.5/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.5/blog/2017/0.1-canary.html">Canary Deployments using Istio</a></li><li> <a title="Istio Auth 0.1 announcement" href="/v0.5/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.5/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.5/feed.xml"> <img style="width: 1.4em;" src="/v0.5/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.5/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.5/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 will configure the <em>ratings</em> microservice to use it. I define an <a href="/v0.5/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.5/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>. Any MySQL instance would do, I use <a href="https://www.ibm.com/cloud/compose/mysql">Compose for MySQL</a>. As a MySQL client to feed the ratings data, I use <code class="highlighter-rouge">mysqlsh</code> (<a href="https://dev.mysql.com/doc/refman/5.7/en/mysqlsh.html">MySQL Shell</a>).</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>Then I 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>. It means that I do not use my <em>admin</em> user in the Bookinfo application. Instead, for Bookinfo application I create a special user, <em>bookinfo</em>, with minimal privileges, in this case only the <code class="highlighter-rouge">SELECT</code> privilege and only 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 do not want the password of the new user to be stored in the bash history. If I would use mysql, I would 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.5/docs/setup/kubernetes/quick-start.html#installation-steps">Istio installed</a>. Then I deploy <a href="/v0.5/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 per 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.5/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.5/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.5/docs/guides/img/bookinfo/withistio.svg"> <img class="element-to-stretch" src="/v0.5/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! No need to mention 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.5/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 dotted line.</p><h3 id="access-the-webpage">Access the webpage</h3><p>Lets access the webpage of the application, after <a href="/v0.5/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 we have the <em>Ratings service is currently unavailable</em> message displayed per 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.5/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.5/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 the both displayed reviews, as expected. I changed the ratings to be one star to provide us a visual clue that our external database is indeed 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, for example database specific protocols <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.5/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 will 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, 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, by multiple egress rules if needed, as in the case of <code class="highlighter-rouge">wikipedia.org</code>, described in <a href="/v0.5/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.5/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.5/docs/guides/integrating-vms.html">Integrating Virtual Machines</a> guide. In that scenario, a MySQL instance runs on a 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.5/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 has to 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.5/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. Especially, when this expectation cannot be changed by 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.5/docs/tasks/traffic-management/egress-tcp.html">Control Egress TCP Traffic Task</a></li><li>for HTTP/HTTPS, see <a href="/v0.5/docs/tasks/traffic-management/egress.html">Control Egress Traffic Task</a></li></ul></main></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-sm-2"></div><nav class=" col-12 col-sm-3" role="navigation"><ul class="first"><li><a class="header" href="/v0.5/docs">Docs</a></li><li><a href="/v0.5/docs/concepts">Concepts</a></li><li><a href="/v0.5/docs/setup">Setup</a></li><li><a href="/v0.5/docs/tasks">Tasks</a></li><li><a href="/v0.5/docs/guides">Guides</a></li><li><a href="/v0.5/docs/reference">Reference</a></li></ul></nav><nav class="col-12 col-sm-3" role="navigation"><ul><li><a class="header" href="/v0.5/help">Help</a></li><li><a href="/v0.5/help/faq.html">FAQ</a></li><li><a href="/v0.5/help/glossary.html">Glossary</a></li><li><a href="/v0.5/help/troubleshooting.html">Troubleshooting</a></li><li><a href="/v0.5/help/bugs.html">Report Bugs</a></li><li><a href="https://github.com/istio/istio.github.io/issues/new?title=Issue with _blog/2018/egress-tcp.md" target="_blank" rel="noopener">Doc Bugs & Gaps</a></li><li><a href="https://github.com/istio/istio.github.io/edit/master/_blog/2018/egress-tcp.md" target="_blank" rel="noopener">Edit This Page</a></li></ul></nav><nav class="col-12 col-sm-3" role="navigation"><ul><li><a class="header" href="/v0.5/community">Community</a></li><li> <a href="https://groups.google.com/forum/#!forum/istio-users" target="_blank" rel="noopener">User</a> | <a href="https://groups.google.com/forum/#!forum/istio-dev" target="_blank" rel="noopener">Dev Mailing Lists</a></li><li><a href="https://twitter.com/IstioMesh" target="_blank" rel="noopener">Twitter</a></li><li><a href="https://stackoverflow.com/questions/tagged/istio" target="_blank" rel="noopener">Stack Overflow</a></li><li><a href="https://github.com/istio/community" target="_blank" rel="noopener">GitHub</a></li><li><a href="https://github.com/istio/community/blob/master/WORKING-GROUPS.md" target="_blank" rel="noopener">Working Groups</a></li></ul></nav></div><div class="row"><div class="col-12"><p class="description text-center" role="contentinfo"> Istio 0.5, Copyright &copy; 2018 Istio Authors<br> Archived on 14-Feb-2018</p></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://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" 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=searchbox"></script> <script src="/v0.5/js/misc.js"></script></body></html>