<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Phuong Hau</title>
    <description>The latest articles on Forem by Phuong Hau (@phuonghau98).</description>
    <link>https://forem.com/phuonghau98</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F208382%2F37ca58c0-2884-4a2d-8847-b26e9c519090.jpeg</url>
      <title>Forem: Phuong Hau</title>
      <link>https://forem.com/phuonghau98</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/phuonghau98"/>
    <language>en</language>
    <item>
      <title>How I expose my applications on Kubernetes with Ingress and a domain</title>
      <dc:creator>Phuong Hau</dc:creator>
      <pubDate>Sat, 28 Dec 2019 03:20:07 +0000</pubDate>
      <link>https://forem.com/phuonghau98/how-i-expose-my-applications-on-kubernetes-with-ingress-and-a-domain-3e33</link>
      <guid>https://forem.com/phuonghau98/how-i-expose-my-applications-on-kubernetes-with-ingress-and-a-domain-3e33</guid>
      <description>&lt;p&gt;My intention of writing this post is to share with you things that I learned today and hope that might help some guy on the Internet just like me searching for a quick way to do something rather than reading a lot of dry text.&lt;/p&gt;

&lt;h1&gt;
  
  
  Motivation
&lt;/h1&gt;

&lt;p&gt;The demand emerges when there is a fact that I have multiple applications running on my Kubernetes cluster, I have only one domain though. That is where Kubernetes Ingress resource comes into play. &lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;p&gt;For the sake of simplicity and focusing on Ingress resource, I will assume that you are familiar with Kubernetes resource objects like: Deployment, Service,... and having control over your cluster with &lt;strong&gt;kubectl&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's do it
&lt;/h1&gt;

&lt;p&gt;For demonstration purpose, I am going to spin up two applications,  &lt;strong&gt;portfolio&lt;/strong&gt; and &lt;strong&gt;healthinsight&lt;/strong&gt; respectively. To achieve that, I simply create two deployments with one replica each, and also two services in order to keep track of IP of the pods created and destroy frequently.&lt;br&gt;
If not mentioned otherwise, all resources are deployed on &lt;strong&gt;default&lt;/strong&gt; namespace.&lt;/p&gt;

&lt;p&gt;Yaml file for &lt;strong&gt;portfolio&lt;/strong&gt; app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;extensions/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portfolio&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portfolio&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portfolio&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/phuong-devops/portfolio:v2&lt;/span&gt;
        &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portfolio&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr-key&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portfolio&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portfolio&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;YAML file for &lt;strong&gt;healthinsight&lt;/strong&gt; app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;extensions/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthinsight-app&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthinsight-app&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthinsight-app&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/phuong-devops/healthinsight-frontend:v1&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthinsight-app&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr-key&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthinsight-app&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3001&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthinsight-app&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Deploying these two applications is easy as a piece of cake.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D4rfoXCP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/89pwc6m1i7zsipn78uhl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D4rfoXCP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/89pwc6m1i7zsipn78uhl.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem is our application is unreachable from the external network (Internet), so I deploy an Ingress resource on our Kubernetes cluster so as to it routes the traffic from Internet to our pods (applications). &lt;br&gt;
&lt;em&gt;Keep in mind that you have to setup an Ingress Controller to satisfy Ingress Resource. Only creating Ingress Resource below has no effect.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are a bunch of Controller that may fit your preferences, take a look at &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/"&gt;here&lt;/a&gt;. For demonstration purpose, I will pick &lt;strong&gt;Nginx Ingress Controller&lt;/strong&gt;, you could easily have your Nginx controller installed on your cluster after following the official documentation &lt;a href="https://kubernetes.github.io/ingress-nginx/"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  So what exactly Ingress do?
&lt;/h3&gt;

&lt;p&gt;Ingress exposed HTTP and HTTPS routes from outside the cluster to services within cluster. Traffic routing is controlled by rules defined on the Ingress resource.&lt;/p&gt;

&lt;p&gt;An Ingress can be configured to give Services externally-reachable URL, load balance, terminate SSL/TLS, and offer name based virtual hosting.&lt;/p&gt;

&lt;p&gt;The below diagram illustrates our implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lJnqxCpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9r2o9vqlakzmwyh0dwqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lJnqxCpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9r2o9vqlakzmwyh0dwqr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am going to deploy our Ingress resource configured to satisfy above demand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;extensions/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx"&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hi.phuonghau.com&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthinsight-app&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3001&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;phuonghau.com&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portfolio&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Check out if our Ingress resource deployment works fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2mT_r1Qa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/45emom63d9fcr0p43h79.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2mT_r1Qa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/45emom63d9fcr0p43h79.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  DNS
&lt;/h3&gt;

&lt;p&gt;It is just not enough, we need to explicitly configure our domain DNS to navigate to our server cluster IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;:&lt;br&gt;
When we expose our applications on cluster on a domain name, we need the external IP address of an application to be static that does not change. So that the domain can correctly navigate to our clusters.&lt;/p&gt;

&lt;p&gt;I am using GoDaddy for example, the steps are not the same if you are using other domain broker services, but the terms is applicable.&lt;/p&gt;

&lt;p&gt;To reiterate, I want &lt;strong&gt;phuonghau.com&lt;/strong&gt; to points to &lt;strong&gt;portfolio&lt;/strong&gt; service and &lt;strong&gt;hi.phuonghau.com&lt;/strong&gt; to point to &lt;strong&gt;health insight&lt;/strong&gt; service.&lt;/p&gt;

&lt;p&gt;Simply add an A record pointing to cluster IP (phuonghau.com points to clusterIP), and a CName &lt;em&gt;hi&lt;/em&gt; points to phuonghau.com. Check the brief differences between an A record and Cname at &lt;a href="https://support.dnsimple.com/articles/differences-a-cname-records/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YR7VoWMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/im747lqn1otr8hnynlxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YR7VoWMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/im747lqn1otr8hnynlxb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
It could take couple of minutes for your DNS configuration propagate over global.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tadaaa
&lt;/h3&gt;

&lt;p&gt;Now we can access multiple services on our Kubernetes cluster with only one domain.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sfoxi3Qs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u6t8d1e7eunq0wxfz2st.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sfoxi3Qs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u6t8d1e7eunq0wxfz2st.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;With Kubernetes Ingress we are able to expose many applications to outside  on the Internet with ease, those above steps is basically describe practical steps. The official document still the best place which we should take a glance at. Any feedback on this article is more than welcome.Thanks for reading.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>gcp</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How the Unix Shell sees the world (Expansion, Quoting)</title>
      <dc:creator>Phuong Hau</dc:creator>
      <pubDate>Thu, 19 Dec 2019 16:17:09 +0000</pubDate>
      <link>https://forem.com/phuonghau98/how-the-unix-shell-sees-the-world-expansion-quoting-42ng</link>
      <guid>https://forem.com/phuonghau98/how-the-unix-shell-sees-the-world-expansion-quoting-42ng</guid>
      <description>&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;It was me using Unix terminal every single day, typing hundred lines of terminal command, but sometimes, I encounter some unexpected behaviors at the moment I press Enter button after a command. It's pretty frustrated at some points, it turns out that the way the Shell sees the world is different than mine. I read some books (at least some chapters) about how the Shell acts upon a command, I find out it's really interesting so that I share with you today. Hope this helpful.&lt;/p&gt;

&lt;p&gt;Because this is pretty straightforward. With only &lt;strong&gt;echo&lt;/strong&gt; command, we can examine several interesting features, the "magic" of the Shell&lt;/p&gt;

&lt;h1&gt;
  
  
  The Expansion
&lt;/h1&gt;

&lt;p&gt;Every time we press enter after a command, it get transformed by the Shell under the hood before get carried out. The process that makes it happen is call &lt;strong&gt;expansion&lt;/strong&gt;. The expansion is that when you type in something, it get expanded to another thing before the shell acts upon it. For example: The * means a lot to the Shell. To illustrate this, I will use the &lt;strong&gt;echo&lt;/strong&gt; command, as you recall that &lt;strong&gt;echo&lt;/strong&gt; is a simple Shell built-in function that directs our typing to standard output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;Hello world
Hello world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the &lt;strong&gt;*&lt;/strong&gt; wildcard means: Match any characters in file-name. The fact  that the * get transformed into other characters before being carried out by the shell so that the echo command never see the *&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/kitchen&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
bash  device  devops  gateway  go  hook  iuh  js  linux  ml  python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Filename expansion
&lt;/h3&gt;

&lt;p&gt;In fact, this behavior carried out by a mechanism, which is popularly called &lt;em&gt;path name expansion&lt;/em&gt;. As we know, file names begin with period character (ex: .git) is hidden. The path name extension respect this rule. An expansion does not reveal hidden files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;D&lt;span class="k"&gt;*&lt;/span&gt;
Desktop Documents Downloads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we can show all hidden files by following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; .&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt; .. .3T .ansible .anydesk .bash_history .bash_logout .bash_profile .bashrc .bashrc.backup .boto .cache .config .dbshell .designer .docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Arithmetic expansion
&lt;/h3&gt;

&lt;p&gt;We are also able to use our terminal command as a calculator thanks to arithmetic expansion, but this it only works with simple integers (We need tools like bc,... for float numbers). In spite of its limitation, it might useful in some scenarios, it also supports many popular operators. Let's say: &lt;em&gt;, -, *&lt;/em&gt;, %, /, +,...&lt;br&gt;
Arithmetic expansion follows below pattern:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;$((expression))&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;This month has &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; days
This month has 28 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As an alternative to double parentheses, we can also use single parentheses to nest sub expansion. Such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;This year has &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)*&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="k"&gt;))))&lt;/span&gt; days
This year has 363 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tilde expansion
&lt;/h3&gt;

&lt;p&gt;When we using &lt;em&gt;~&lt;/em&gt; as the beginning of the string in terminal, it expands to home directory of named user.&lt;br&gt;
Let's say, I am using an account on the cloud where there are many users there and I don't known where their home directory are, I just know usernames of them. That is where it come into play&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@devcloud1:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; ~wingzero
/home/wingzero
phuong@devcloud1:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; ~phuong
/home/phuong
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Brace expansion
&lt;/h3&gt;

&lt;p&gt;The brace expression itself may contain either a comma-separated list of strings or a range of integers or single characters.&lt;/p&gt;

&lt;p&gt;For the comma-separated example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/kitchen/ips&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;A&lt;span class="o"&gt;{&lt;/span&gt;A,B&lt;span class="o"&gt;}&lt;/span&gt;
AA AB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, the range of single characters&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;A..Z&lt;span class="o"&gt;}&lt;/span&gt;
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From my point of view, this kind of expansion is one of the most interesting expansion mechanisms as it is pretty useful in specified situations. Suppose that I want to create each folder for each network IP in the subnet range. Pretending that &lt;strong&gt;192.168.43.1 -&amp;gt; 192.168.43.13&lt;/strong&gt;. So  I confess the way that I would do 4 months a go is that: &lt;strong&gt;mkdir ...&lt;/strong&gt;. But this approach is prone to typo errors. So that is where &lt;em&gt;the brace expansion&lt;/em&gt; comes and saves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/kitchen/ips&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;192.168.43.&lt;span class="o"&gt;{&lt;/span&gt;1..13&lt;span class="o"&gt;}&lt;/span&gt;
phuong@Arch ~/kitchen/ips&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;192.168.43.1   192.168.43.12  192.168.43.3  192.168.43.6  192.168.43.9
192.168.43.10  192.168.43.13  192.168.43.4  192.168.43.7
192.168.43.11  192.168.43.2   192.168.43.5  192.168.43.8

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is true as you may guest that the brace expansion can also be nested.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/kitchen/ips&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;A&lt;span class="o"&gt;{&lt;/span&gt;a&lt;span class="o"&gt;{&lt;/span&gt;1,2&lt;span class="o"&gt;}&lt;/span&gt;,b&lt;span class="o"&gt;{&lt;/span&gt;9,10&lt;span class="o"&gt;}}&lt;/span&gt;
Aa1 Aa2 Ab9 Ab10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command substitution
&lt;/h3&gt;

&lt;p&gt;Beside above expansion mechanisms, the &lt;em&gt;command substitution&lt;/em&gt; maybe the one that you might feel familiar with because its widely used in scripting. The &lt;em&gt;command substitution&lt;/em&gt; allows us to use the output of a command as an expansion. For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/kitchen&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;which go&lt;span class="si"&gt;)&lt;/span&gt;
lrwxrwxrwx 1 root root 18 Nov  2 17:25 /usr/bin/go -&amp;gt; /usr/lib/go/bin/go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is another example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;Hello &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;world&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$w&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
Hello world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parameter substitution
&lt;/h3&gt;

&lt;p&gt;This kind of substitution is intensively used in scripting than directly in command line. For simple example, you want to print USER variable, you just add dollar symbol($) at the beginning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;
phuong
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that if you mistyping the name of a variable, the expansion will still take place, but it results into empty string.&lt;/p&gt;

&lt;p&gt;For the sake of simplicity, I will not go into detail. Because this powerful mechanism deserve another post for it. The scope of this post is to point out some popular expansion mechanisms and how to control them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Quoting
&lt;/h1&gt;

&lt;p&gt;So we have seen how powerful the expansion in the Shell is, that is why it could lead to some unexpected behaviors unless we know how to control it.&lt;br&gt;
Now we come to interesting part.&lt;/p&gt;

&lt;p&gt;Consider two samples below:&lt;br&gt;
Example 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;Hello I am a          string
Hello I am a string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;It costs me &lt;span class="nv"&gt;$200&lt;/span&gt;
It costs me 00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel familiar ah?&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7etomm2e1wz9s0a6us46.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7etomm2e1wz9s0a6us46.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the first example, the &lt;em&gt;word splitting&lt;/em&gt; ditches extra white spaces from &lt;em&gt;the echo command&lt;/em&gt;'s list of arguments. In the second example, the &lt;em&gt;$2&lt;/em&gt; is expanded by parameter expansion mechanism, that is on purpose, the problem is $2 variable is undefined so the Shell suppress unexpected expansion which finally leads to how it behaves. Let's figure out how to fix this.&lt;/p&gt;
&lt;h3&gt;
  
  
  Double Quotes
&lt;/h3&gt;

&lt;p&gt;If you put special characters into &lt;em&gt;double quotes&lt;/em&gt; will lost their special meanings to the Shell, but there are 3 exceptions for that, namely &lt;em&gt;dollar sign, backslash and back tick&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is pretty useful in specified scenario. To show you what I mean, imagine that one of your friends send you a homework file named "&lt;em&gt;homework part1.doc&lt;/em&gt;". Well, that might drive you crazy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fj4nhsnpvepdbfjqxm553.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fj4nhsnpvepdbfjqxm553.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which leads you to below situation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;homework part1.doc 
&lt;span class="nb"&gt;cat&lt;/span&gt;: homework: No such file or directory
&lt;span class="nb"&gt;cat&lt;/span&gt;: part1.doc: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The double quotes comes to save you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"homework part1.doc"&lt;/span&gt; homework_part1.doc 
phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;homework_part1.doc 
This is my stupid homework part, now is your turn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once double quotes are added , our command contains a command followed by a single argument. That is how we preserve our extra white spaces, because &lt;em&gt;world splitting&lt;/em&gt; mentioned above only take effect for multiple parameters. With everything putted in double quotes, it behave like only one parameter following the echo command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;Hello I am a          string
Hello I am a string
phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello I am a          string"&lt;/span&gt;
Hello I am a          string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Single quote
&lt;/h3&gt;

&lt;p&gt;If you simply need to suppress all special characters, just use single quote&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'cat ~/Download/.doc {a,b} $(echo bar) $((3+2)) $USER'&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/Download/.doc &lt;span class="o"&gt;{&lt;/span&gt;a,b&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;bar&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Escaping characters
&lt;/h3&gt;

&lt;p&gt;Sometimes we want to quote only a single character, to do this, just preceding that character with a &lt;em&gt;backslash&lt;/em&gt; \&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You own me &lt;/span&gt;&lt;span class="nv"&gt;$30&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
You own me 0
phuong@Arch ~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You own me &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;30"&lt;/span&gt;
You own me &lt;span class="nv"&gt;$30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's common to selectively prevent the expansion of a filename.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;To summarize, the expansion mechanisms in Unix Shell is great and powerful. If we know how to control it, I believe those features surely enhance your productivity. If you have any question, feel free to drop a comment below, that would be nice to hear from you.&lt;/p&gt;

&lt;p&gt;Thanks for your time.&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;The Linux Command Line - William E. Shotts JR.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>computerscience</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Yet another way to containerize your React app with Docker multi-stage build</title>
      <dc:creator>Phuong Hau</dc:creator>
      <pubDate>Sat, 14 Dec 2019 15:20:37 +0000</pubDate>
      <link>https://forem.com/phuonghau98/yet-another-way-to-containerize-your-react-app-with-docker-multi-stage-build-3n6</link>
      <guid>https://forem.com/phuonghau98/yet-another-way-to-containerize-your-react-app-with-docker-multi-stage-build-3n6</guid>
      <description>&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;In cloud computing world, an containerized application promotes the decoupling principle, which offers a logical packaging mechanism. It allows container-based applications to be easily deployed and ensures the consistency. As a React enthusiast, I am going to share with you yet another way how I package my React application.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preparation
&lt;/h1&gt;

&lt;p&gt;For the following steps, i assume that you have some basic knowledge of &lt;strong&gt;Docker&lt;/strong&gt;, &lt;strong&gt;React&lt;/strong&gt;, &lt;strong&gt;Linux-based folder structure&lt;/strong&gt;...&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's start
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Init our React application
&lt;/h2&gt;

&lt;p&gt;For convenience's sake, I init a blank React application with create-react-app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;phuong@Arch ~/kitchen/js&lt;span class="nv"&gt;$ &lt;/span&gt;npx create-react-app fooapp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Build our application using node:alpine image
&lt;/h2&gt;

&lt;p&gt;We will use a NodeJS image in order to build application to guarantee complete isolation. After application to our application folder (in this case it is &lt;strong&gt;cd fooapp&lt;/strong&gt;). Create a file named Dockerfile like below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:alpine as builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Line 1: We declare the image that we use to build our React app and attach &lt;em&gt;builder&lt;/em&gt; label to it.&lt;br&gt;
Line 2: We use WORKDIR directive to indicate that we are currently in /app in container&lt;br&gt;
Line 3: Copy our application into Docker container&lt;br&gt;
Line 4: Install dependencies for our React application in container&lt;br&gt;
Line 5: Execute command to build our application, our application will be built into chunk and saved in a directory named &lt;em&gt;build&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Serving our application using Nginx
&lt;/h2&gt;

&lt;p&gt;But wait, it is true that our built application obviously cannot serve itself, we need a server to serve application as static resource. I recommend &lt;strong&gt;nginx&lt;/strong&gt; image as our server because of its popularity for low resource consumption, simple configuration and high performance.&lt;br&gt;
We need a configuration file for nginx server, let's create nginx.conf in your root of application folder:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;server &lt;span class="o"&gt;{&lt;/span&gt;
    listen       80&lt;span class="p"&gt;;&lt;/span&gt;
    server_name  localhost&lt;span class="p"&gt;;&lt;/span&gt;
    location / &lt;span class="o"&gt;{&lt;/span&gt;
        root   /usr/share/nginx/html&lt;span class="p"&gt;;&lt;/span&gt;
        index  index.html index.htm&lt;span class="p"&gt;;&lt;/span&gt;
        try_files &lt;span class="nv"&gt;$uri&lt;/span&gt; /index.html&lt;span class="p"&gt;;&lt;/span&gt;                 
    &lt;span class="o"&gt;}&lt;/span&gt;
    error_page   500 502 503 504  /50x.html&lt;span class="p"&gt;;&lt;/span&gt;
    location &lt;span class="o"&gt;=&lt;/span&gt; /50x.html &lt;span class="o"&gt;{&lt;/span&gt;
        root   /usr/share/nginx/html&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In order not to copy &lt;em&gt;node_modules&lt;/em&gt; and unwanted folders into our container. We simply list them in &lt;strong&gt;.dockerignore&lt;/strong&gt; file&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;.git
node_modules
build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;em&gt;So our complete Dockerfile will be:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:alpine as builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/build /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Line 7: Directive FROM indicates we use nginx:alpine image (in conjunction with node:alpine image)&lt;br&gt;
Line 8: We copy nginx configuration file into our container&lt;br&gt;
Line 9: &lt;strong&gt;--from=builder&lt;/strong&gt; instructs docker to copy built folder from stage 1 as we labeled it above&lt;br&gt;
Line 10: Expose &lt;strong&gt;port 80&lt;/strong&gt; to outside of the container&lt;br&gt;
Line 11: Directive tell that nginx should stay in the foreground. Because for containers, it is useful as best practice is one process = one container.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrap everything up
&lt;/h2&gt;

&lt;p&gt;Let's check our directory, we should have exact application directory like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--icr2L1-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/s6l8fse4j86nqt39kob1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--icr2L1-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/s6l8fse4j86nqt39kob1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start building our container using command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; fooapp:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Our build process&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ju0cD90d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nba8sbs6ghrykpf2y3qh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ju0cD90d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nba8sbs6ghrykpf2y3qh.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify everything is fine, we run our newly built container using command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -d 8080:80 fooapp:v1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;strong&gt;--rm&lt;/strong&gt; flag tells docker to remove the container after running our application container and &lt;strong&gt;-d&lt;/strong&gt; instructs docker to bind port 80 on our host machine to port 8080 of our application container.&lt;/p&gt;
&lt;h2&gt;
  
  
  Voilà
&lt;/h2&gt;

&lt;p&gt;Now we should be able access our application from browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DsoS-NxW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xc1k7unwf9b02p0ln6p4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DsoS-NxW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xc1k7unwf9b02p0ln6p4.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For conclusion, I would like to thank you for your time reading my first post, above steps and arguments are just my personal thoughts, if there is anything wrong, let me hear from you. Feel free to drop comment below. Thanks. :)&lt;/p&gt;

&lt;p&gt;P.s: I also published my git repo for this post at &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/phuonghau98"&gt;
        phuonghau98
      &lt;/a&gt; / &lt;a href="https://github.com/phuonghau98/dockerized-react-app"&gt;
        dockerized-react-app
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h2&gt;
My dev.to reference docs:&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://dev.to/phuonghau98/yet-another-way-to-containerize-your-react-app-with-docker-multi-stage-build-3n6" rel="nofollow"&gt;https://dev.to/phuonghau98/yet-another-way-to-containerize-your-react-app-with-docker-multi-stage-build-3n6&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/phuonghau98/dockerized-react-app"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/"&gt;Docker multi-stage build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/nginx"&gt;Nginx Docker image&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>docker</category>
      <category>nginx</category>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
