<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Chairat Onyaem (Par) on Medium]]></title>
        <description><![CDATA[Stories by Chairat Onyaem (Par) on Medium]]></description>
        <link>https://medium.com/@pacroy?source=rss-c349bdf2ae99------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*sP-A6cCxQ_-Z5QxHUdmdqg.jpeg</url>
            <title>Stories by Chairat Onyaem (Par) on Medium</title>
            <link>https://medium.com/@pacroy?source=rss-c349bdf2ae99------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 06 Jun 2026 04:59:59 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@pacroy/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[The GitHub Insider Newsletter — February Digest]]></title>
            <link>https://pacroy.medium.com/the-github-insider-newsletter-february-digest-2b0ded4e3ca7?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/2b0ded4e3ca7</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[github-copilot]]></category>
            <category><![CDATA[github]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Sat, 24 Feb 2024 10:43:17 GMT</pubDate>
            <atom:updated>2024-02-24T10:43:17.789Z</atom:updated>
            <content:encoded><![CDATA[<h3>The GitHub Insider Newsletter — February Digest</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gm6glNfI4V-IlXjWiv1AQA.png" /><figcaption>The GitHub Insider Newletter banner — <a href="https://github.blog/">github.blog</a></figcaption></figure><h3>What is prompt engineering?</h3><p>This GitHub blog from June 2023 tells you what prompt and prompt engineering are and shares examples and best practices for communicating your desired results to the GitHub Copilot.</p><p><a href="https://github.blog/2023-06-20-how-to-write-better-prompts-for-github-copilot">How to use GitHub Copilot: Prompts, tips, and use cases</a></p><h3>Tips and Tricks for Effective Prompting</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FImWfIDTxn7E%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DImWfIDTxn7E&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FImWfIDTxn7E%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7bb3b925d618e8bcf85bdd5e37343e46/href">https://medium.com/media/7bb3b925d618e8bcf85bdd5e37343e46/href</a></iframe><p>This video shows tips and tricks on how to use GitHub Copilot in Visual Studio Code with demos and examples in Python, SQLAlchemy, and Django MVC.</p><p>And If it is not enough, you can check out <a href="https://youtu.be/DSHfHT5qnGc?si=EsQ7Hawn59aY-s-H">this video</a> to see how GitHub Copilot helps you turning a command line app into a Flask API app.</p><h3>You can now change ghost text font!</h3><p>As of the latest version of Visual Studio Code, you can now <a href="https://code.visualstudio.com/updates/v1_86#_github-copilot">change your ghost text font</a> (but not the style 😅) by using the setting editor.inlineSuggest.fontFamily.</p><h3>Get to know GitHub Copilot in VS Code</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FjXp5D5ZnxGM%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DjXp5D5ZnxGM&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FjXp5D5ZnxGM%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/65cf64a2929a4ba43fb11bc3dd416650/href">https://medium.com/media/65cf64a2929a4ba43fb11bc3dd416650/href</a></iframe><p>This new great video is recommended to those who is new to GitHub Copilot and want to get up and run with it in just minutes.</p><h3>Copilot in GitHub Support is now available!</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*p9Nao1M2ABgkyq-f" /><figcaption>Chat with Copilot in GitHub Support — <a href="https://github.blog/2024-02-09-copilot-in-github-support-is-now-available">github.blog</a></figcaption></figure><p>You can now chat with AI which trained on the official GitHub documentation and will help you find answers to your GitHub-specific questions.</p><p>Check out this GitHub blog for more information.</p><p><a href="https://github.blog/2024-02-09-copilot-in-github-support-is-now-available">Copilot in GitHub Support is now available!</a></p><h3>GitHub Copilot for Enterprise is coming</h3><p><a href="https://app.github.media/e/er?s=88570519&amp;lid=5888&amp;elqTrackId=f401d27cb88346ec84fedbdac239e61c&amp;elq=bf4ebd07f9a24c3abf3f848010aac654&amp;elqaid=4015&amp;elqat=1">GitHub Copilot for Enterprise</a> launches this week. The <a href="https://github.com/github-copilot/copilot_enterprise_waitlist_signup/join">sign up for the Copilot Enterprise waitlist</a> is now closed.</p><h3>Prompt Engineering 101 Training</h3><p>This introductory training course uncovers techniques to transform your coding comments into precise and actionable code.</p><p><a href="https://learn.microsoft.com/en-us/training/modules/introduction-prompt-engineering-with-github-copilot">Introduction to prompt engineering with GitHub Copilot - Training</a></p><p><em>You can subscribe to the GitHub Insider Newsletter by going to </em><a href="https://github.blog"><em>github.blog</em></a><em>. Scroll down to the bottom, input your email address, click Subscribe. Then it will send an email which you also need to click the link within to complete the subsciption.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2b0ded4e3ca7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Create Your Own Azure Bastion with Guacamole and Save $100+ a month]]></title>
            <link>https://pacroy.medium.com/create-your-own-azure-bastion-with-guacamole-and-save-100-a-month-995c683dbc85?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/995c683dbc85</guid>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[bastion]]></category>
            <category><![CDATA[ubuntu]]></category>
            <category><![CDATA[guacamole]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Fri, 30 Apr 2021 15:18:40 GMT</pubDate>
            <atom:updated>2021-05-01T12:02:28.866Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JJShyVdAGraRHmjBXEzg_g.jpeg" /><figcaption>Photo <em>by </em><a href="https://unsplash.com/@footluz?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Footluz Blog</em></a><em> on </em><a href="https://unsplash.com/s/photos/bastion?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Unsplash</em></a></figcaption></figure><p>Microsoft Azure provides <a href="https://azure.microsoft.com/en-us/services/azure-bastion">Azure Bastion</a> service which is a jump server so you can securely access your virtual machines via its Azure Portal web interface without exposing SSH or RDP port. Here is its architecture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/843/1*-qrDOMMqm2wpqLXtXk43hg.jpeg" /><figcaption>Azure Bastion Architecture from <a href="https://azure.microsoft.com/en-us/services/azure-bastion/">Microsoft</a></figcaption></figure><p>This service is great except <a href="https://azure.microsoft.com/en-us/pricing/details/azure-bastion/">it costs $0.19 per hour</a>. Combining with other components then this may costs you almost $150 a month. Bad news is you can’t stop it unless you delete it.</p><p>This post, I will show you how I create my own jump server on Ubuntu VM to replace Azure Bastion using <a href="https://guacamole.apache.org/">Apache Guacamole</a> which is an open source tool which provide similar functionalities (I wonder that Azure Bastion may be even built on top of it). The VM can be a small one that may costs only a few tens bucks per month.</p><h3>Apache Guacamole</h3><p>From <a href="https://dev.to/pacroy/(https://guacamole.apache.org/)">its website</a>, Apache Guacamole is a clientless remote desktop gateway that supports standard protocols like VNC, RDP, and SSH. Clientless means your clients don’t need to install anything but just use a web browser to remotely access your fleet of VMs.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F116207678%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F116207678&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F502487202_640.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="640" height="428" frameborder="0" scrolling="no"><a href="https://medium.com/media/02662c5c8bd5db7a1906b3094e026c4a/href">https://medium.com/media/02662c5c8bd5db7a1906b3094e026c4a/href</a></iframe><p>The Guacamole comprises of two main components:</p><ul><li>Guacamole Server which provides guacd which is like a proxy server for the client to connect to the remote server.</li><li>Guacamole Client which is a servelet container that user will log in and via web browser.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/320/1*zhmg91B5qqtdgsgiqIjjUw.png" /><figcaption>Guacamole Architecture from <a href="https://guacamole.apache.org/doc/gug/guacamole-architecture.html">Guacamole Documentation</a></figcaption></figure><p>For more information about Guacamole, visit its <a href="https://guacamole.apache.org/doc/gug/guacamole-architecture.html">architecture page</a>.</p><p>As my disclaimer, installation is not simple as there are several components you need to install and configure before it is <em>good enough</em> to use. There may be simpler ways to deploy e.g. using <a href="https://guacamole.apache.org/doc/gug/guacamole-docker.html">Docker image</a> or using <a href="https://github.com/prabhatsharma/apache-guacamole-helm-chart/blob/master/values.yaml">this Helm chart</a> but I haven’t tried them yet. Because of cost is my concern so I’d like to deploy on a small VM that may not run a Kubernetes cluster (and may be I just prefer to learn it in hard way :P).</p><h3>Network Topology</h3><p>You use Azure Bastion or a jump server because you want to secure your VMs behind so having the right network design is needed. Here it mine:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/839/1*ixFcsb94CH9u_4g6pUc12w.png" /><figcaption>Network Topology Modified from <a href="https://portal.azure.com/#blade/Microsoft_Azure_Network/NetworkWatcherMenuBlade/overview">Azure Network Watcher</a> by Author</figcaption></figure><p>In my virtual network (VNet), I split into two subnets:</p><ol><li>snet-gateway where I will deploy Guacamole on a Ubuntu VM which has a public IP so its web interface can be reached from the Internet.</li><li>snet-default where I will deploy my backend pool of VM and enable remote access via Guacamole only.</li></ol><p>For the sake of security, you should configure Network Security Group (NSG) of your snet-gateway to limit inbound and outbound connections to/from Ubuntu VM in the same way as you do for <a href="https://docs.microsoft.com/en-us/azure/bastion/bastion-nsg#apply">AzureBastionSubnet</a>. But in this example, I just associate NSG to the VM directly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G4Knfa8D6qkBPtkdKuaYUg.png" /><figcaption>Network Security Group Inbound Rules from <a href="https://docs.microsoft.com/en-us/azure/bastion/bastion-nsg#apply">Microsoft Docs</a></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*taMkxNCRRHJzHAcnImWsZQ.png" /><figcaption>Network Security Group Outbound Rules from <a href="https://docs.microsoft.com/en-us/azure/bastion/bastion-nsg#apply">Microsoft Docs</a></figcaption></figure><p>Once you setup your network then create the VMs. In this example, I create the following two VMs:</p><ol><li>vm-win10 in the snet-default which is the machine I will remote access to.</li><li>vm-ubuntu1804 in the snet-gateway where I will install Guacamole and use it as a jump server. I choose B2s size which cost around $39 per month but, of course, you can change its size later.</li></ol><h3>Install Guacamole Server</h3><p>Once your Ubuntu Server 18.04 is created then log in via SSH. You may need to expose SSH port publicly for now. You can change SSH port to something than 22 to make it more secure. See how to do it in my previous blog.</p><p><a href="https://pacroy.medium.com/setting-up-your-own-vpn-server-with-just-5-a-month-c934cf073ea1">Setting Up Your Own VPN Server with just $5 a month</a></p><p>From <a href="https://guacamole.apache.org/doc/gug/installing-guacamole.html">its documentation</a>, there’s no executable binary available for Guacamole server and you need to build it from source (unless you deploy from Docker image).</p><p>First you need to install build tools and all required dependencies for the build process. Missing some of them may result in missing features or build failure.</p><pre># Update &amp; upgrade system<br>sudo apt-get update &amp;&amp; sudo apt-get --yes upgrade<br><br># Install build tools<br>sudo apt install --yes build-essential<br><br># Install build dependencies<br>sudo apt install --yes libcairo2-dev libjpeg-turbo8-dev libpng-dev libtool-bin libossp-uuid-dev<br><br># Install optional dependencies<br>sudo apt install --yes \<br>    libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \<br>    freerdp2-dev \<br>    libpango1.0-dev \<br>    libssh2-1-dev \<br>    libtelnet-dev \<br>    libvncserver-dev \<br>    libwebsockets-dev \<br>    libpulse-dev \<br>    libssl-dev \<br>    libvorbis-dev \<br>    libwebp-dev<br><br># Install runtime dependencies<br>sudo apt install --yes --no-install-recommends \<br>    netcat-openbsd                \<br>    ca-certificates               \<br>    ghostscript                   \<br>    fonts-liberation              \<br>    fonts-dejavu                  \<br>    xfonts-terminus</pre><p>Next, download source codes and extract.</p><pre>export GUAC_VERSION=&quot;1.3.0&quot;<br>curl -fLO &quot;https://downloads.apache.org/guacamole/${GUAC_VERSION}/source/guacamole-server-${GUAC_VERSION}.tar.gz&quot;<br>tar -xzf &quot;guacamole-server-${GUAC_VERSION}.tar.gz&quot;</pre><p>Configure the build and start the build.</p><pre>cd &quot;guacamole-server-${GUAC_VERSION}&quot;<br>./configure --with-init-dir=/etc/init.d<br>make</pre><p>Once done, install and start the service.</p><pre>sudo make install<br>sudo ldconfig<br>sudo systemctl daemon-reload<br>sudo systemctl start guacd<br>sudo systemctl enable guacd</pre><p>At this point, the Guacamole server (guacd) service should be up and running. Inspect by executing systemctl status guacd --no-pager.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/0*IkwyP1TSdtKlVzk2.png" /><figcaption>Guacamole server (guacd) service status captured by author</figcaption></figure><h3>Install Guacamole Client</h3><p>Unlike the server, we don’t need to build it (but you can if you want). So we will download the WAR file and install on Tomcat server then expose it through nginx via HTTPS.</p><h4>Install Tomcat</h4><p>First, we need Tomcat to run the WAR file. Use this script to install it.</p><pre>export TOMCAT_VERSION=&quot;8.5.65&quot;<br>TOMCAT_MAJOR_VERSION=$(echo ${TOMCAT_VERSION} | awk -F . &#39;{print $1}&#39;)<br>sudo apt-get install --yes default-jdk<br>sudo groupadd tomcat<br>sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat<br>sudo usermod -a -G tomcat &quot;$USER&quot;<br>curl -LO &quot;https://downloads.apache.org/tomcat/tomcat-${TOMCAT_MAJOR_VERSION}/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz&quot;<br>sudo mkdir -p /opt/tomcat<br>sudo tar -xzf &quot;apache-tomcat-${TOMCAT_VERSION}.tar.gz&quot; -C /opt/tomcat --strip-components=1<br>sudo chgrp -R tomcat /opt/tomcat<br>cd /opt/tomcat<br>sudo chmod -R g+r conf<br>sudo chmod g+x conf<br>sudo chown -R tomcat webapps/ work/ temp/ logs/</pre><p>Configure Tomcat and start the service.</p><pre>JAVA_ALT_TEXT=&quot;$(update-java-alternatives -l || true)&quot;<br>JAVA_HOME=&quot;$(echo &quot;${JAVA_ALT_TEXT}&quot; | awk &#39;{print $3}&#39;)&quot;<br>echo &quot;[Unit]<br>Description=Apache Tomcat Web Application Container<br>After=network.target<br><br>[Service]<br>Type=forking<br><br>Environment=JAVA_HOME=${JAVA_HOME}<br>Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid<br>Environment=CATALINA_HOME=/opt/tomcat<br>Environment=CATALINA_BASE=/opt/tomcat<br>Environment=&#39;CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC&#39;<br>Environment=&#39;JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom&#39;<br><br>ExecStart=/opt/tomcat/bin/startup.sh<br>ExecStop=/opt/tomcat/bin/shutdown.sh<br><br>User=tomcat<br>Group=tomcat<br>UMask=0007<br>RestartSec=10<br>Restart=always<br><br>[Install]<br>WantedBy=multi-user.target&quot; | sudo tee /etc/systemd/system/tomcat.service &gt; /dev/null<br>sudo systemctl daemon-reload<br>sudo systemctl start tomcat<br>sudo systemctl enable tomcat</pre><p>Now the Tomcat service should be up and running. Inspect by executing systemctl status tomcat.service --no-pager.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/765/0*dnx3I5UpxR1pv5t8.png" /><figcaption>Tomcat service status captured by author</figcaption></figure><h3>Add Guacamole Client Servlet</h3><p>Download and add the WAR file to the Tomcat.</p><pre>curl -LO &quot;https://downloads.apache.org/guacamole/${GUAC_VERSION}/binary/guacamole-${GUAC_VERSION}.war&quot;<br>sudo cp &quot;guacamole-${GUAC_VERSION}.war&quot; &quot;/opt/tomcat/webapps/ROOT.war&quot;<br>sudo chown tomcat:tomcat &quot;/opt/tomcat/webapps/ROOT.war&quot;<br>sudo rm -rf /opt/tomcat/webapps/ROOT</pre><p>You may try to access the client at http://&lt;your-server&gt;:8080/ by either open or forward the port and you should see the Guacamole&#39;s login screen (but you can&#39;t login now).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/0*K2XaLZ8a65StuauV.png" /><figcaption>Guacamole login screen captured by author</figcaption></figure><p>In case you see Tomcat instead of Guacamole, restart Tomcat using command sudo systemctl restart tomcat and try again.</p><h3>Install nginx and certbot</h3><p>We will use <a href="https://www.nginx.com/">nginx</a> as a proxy and <a href="https://certbot.eff.org/">certbot</a> to get a certificate from Let’s Encrypt.</p><pre>sudo apt install --yes nginx-core<br>sudo snap install core; sudo snap refresh core<br>sudo snap install --classic certbot</pre><p>Configure certbot with a domain and an email address and integrate with nginx.</p><pre>export DOMAIN_NAME=&quot;&lt;Your VM FQDN&gt;&quot;<br>export EMAIL=&quot;&lt;Your Email Address&gt;&quot;<br>sudo certbot --nginx -d &quot;${DOMAIN_NAME}&quot; -m &quot;${EMAIL}&quot; --agree-tos -n</pre><p>Edit the file /etc/nginx/sites-enabled/default and replace the following section:</p><pre>server_name your.server.fqdn; # managed by Certbot<br><br>        location / {<br>                # First attempt to serve request as file, then<br>                # as directory, then fall back to displaying a 404.<br>                try_files $uri $uri/ =404;<br>        }</pre><p>with this one:</p><pre>server_name your.server.fqdn; # managed by Certbot<br><br>        location / {<br><br><br>                proxy_pass http://localhost:8080/;<br>                proxy_buffering off;<br>                proxy_http_version 1.1;<br>                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br>                proxy_set_header Upgrade $http_upgrade;<br>                proxy_set_header Connection $http_connection;<br>                proxy_cookie_path /guacamole/ /;<br>                access_log off;<br>        }</pre><p>This will set nginx as a proxy to your servlet. You don’t need to configure to redirect HTTP to HTTPS as that is already done by certbot.</p><p>Test the configuration and restart nginx.</p><pre>sudo nginx -t<br>sudo systemctl restart nginx</pre><p>Now you should be able to access Guacamole ay https://&lt;your-server&gt;/</p><h3>Configure User and Connections</h3><p>We will create a user with two connections in the user-mapping.xml file so we can test logging on.</p><p>Create the file /etc/guacamole/user-mapping.xml an put below content. Create the directory /etc/guacamole first if it does not exist.</p><pre>&lt;user-mapping&gt;<br>    &lt;authorize username=&quot;guacadmin&quot; password=&quot;guacadmin&quot;&gt;<br>        &lt;connection name=&quot;this-server-ssh&quot;&gt;<br>             &lt;protocol&gt;ssh&lt;/protocol&gt;<br>             &lt;param name=&quot;hostname&quot;&gt;localhost&lt;/param&gt;<br>             &lt;param name=&quot;port&quot;&gt;22&lt;/param&gt;<br>        &lt;/connection&gt;<br>        &lt;connection name=&quot;some-win10-rdp&quot;&gt;<br>             &lt;protocol&gt;rdp&lt;/protocol&gt;<br>             &lt;param name=&quot;hostname&quot;&gt;vm-win10&lt;/param&gt;<br>             &lt;param name=&quot;port&quot;&gt;3389&lt;/param&gt;<br>             &lt;param name=&quot;username&quot;&gt;username&lt;/param&gt;<br>             &lt;param name=&quot;password&quot;&gt;thisisyourpassword&lt;/param&gt;<br>             &lt;param name=&quot;ignore-cert&quot;&gt;true&lt;/param&gt;<br>        &lt;/connection&gt;<br>    &lt;/authorize&gt;<br>&lt;/user-mapping&gt;</pre><p>The first connection is SSH to local server itself while the second one is RDP to Windows 10 VM. Don’t forget to update the username and password.</p><p>Restart Tomcat and test logging in and making the connections.</p><pre>sudo systemctl restart tomcat</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/0*mvNYseEAyvoj5Fzc.png" /><figcaption>Guacamole home screen captured by author</figcaption></figure><p>If everything is configured properly, you should be able to connect to your VMs via either SSH and RDP. However, you cannot change anything on the web GUI the configuration is static in the file user-mapping.xml</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/0*EIeViNcdQDhqeHI_.png" /><figcaption>Guacamole settings screen captured by author</figcaption></figure><p>To make it editable via the web GUI, we need to install a database. In this example, I will use MySQL but Guacamole also supports other databases e.g. MariaDB, PostgreSQL. You can check for more information in <a href="https://guacamole.apache.org/doc/gug/jdbc-auth.html">this documentation page</a>.</p><h3>Install MySQL</h3><p>Let’s install MySQL.</p><pre>sudo apt install --yes mysql-server</pre><p>Check MySQL service status by executing systemctl status mysql --no-pager.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/621/0*CC2dpq44ISwpeumf.png" /><figcaption>MySQL service status captured by author</figcaption></figure><p>After install the MySQL, we also need to install the Guacamole extension and library so it knows how to talk to the database.</p><pre># Download and install JDBC extensions<br>curl -fLO &quot;https://downloads.apache.org/guacamole/${GUAC_VERSION}/binary/guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz&quot;<br>tar -xzf &quot;guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz&quot;<br>sudo mkdir -p /etc/guacamole/extensions<br>sudo cp &quot;guacamole-auth-jdbc-${GUAC_VERSION}/mysql/guacamole-auth-jdbc-mysql-${GUAC_VERSION}.jar&quot; &quot;/etc/guacamole/extensions/&quot;<br><br># Download and install MySQL Connector/J<br>CONNECTORJ_VERSION=&quot;8.0.24&quot;<br>curl -fLO &quot;https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-${CONNECTORJ_VERSION}.tar.gz&quot;<br>tar -xvf &quot;mysql-connector-java-${CONNECTORJ_VERSION}.tar.gz&quot;<br>sudo mkdir -p /etc/guacamole/lib<br>sudo cp &quot;mysql-connector-java-${CONNECTORJ_VERSION}/mysql-connector-java-${CONNECTORJ_VERSION}.jar&quot; &quot;/etc/guacamole/lib/&quot;</pre><h3>Configure Database</h3><p>Next, we need to create a database and a user. In this example, I will create the database named guacamole_db and the user named guacamole_user. Please note the password must meet complexity criteria.</p><pre>MYSQL_PASSWORD=&quot;&lt;YourPasswordHere&gt;&quot;<br>sudo mysql --execute=&#39;CREATE DATABASE guacamole_db;&#39;<br>sudo mysql --execute=&quot;CREATE USER &#39;guacamole_user&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;${MYSQL_PASSWORD}&#39;;&quot;<br>sudo mysql --execute=&quot;GRANT SELECT,INSERT,UPDATE,DELETE ON guacamole_db.* TO &#39;guacamole_user&#39;@&#39;localhost&#39;;&quot;<br>sudo mysql --execute=&#39;FLUSH PRIVILEGES;&#39;</pre><p>Then run the provides scripts to create schema and the Guacamole default user guacadmin.</p><pre>cat guacamole-auth-jdbc-1.3.0/mysql/schema/*.sql | sudo mysql guacamole_db</pre><p>Lastly, you need to configure Guacamole client so it can connect to the database by creating file /etc/guacamole/guacamole.properties and put the following content.</p><pre># MySQL properties<br>mysql-hostname: localhost<br>mysql-port: 3306<br>mysql-database: guacamole_db<br>mysql-username: guacamole_user<br>mysql-password: &lt;YourPasswordHere&gt;</pre><p>If you configure the user-mapping.xml file before then you may no longer need that so remove it.</p><pre>sudo rm -f /etc/guacamole/user-mapping.xml</pre><p>Restart Tomcat and test logging in again using default username and password i.e. guacadmin.</p><pre>sudo systemctl restart tomcat</pre><p>Now, you should be able to make changes in the Settings. It is recommended you create a new user and then remove the guacadmin as soon as possible.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/0*tF4P31fsQ2mYXfBu.png" /><figcaption>Guacamole settings screen captured by author</figcaption></figure><h3>Enable Two-Factor Authentication</h3><p>If you want you can optionally install TOTP module to enable two-factor authentication to add more security to your VM pool. Just download the extension and restart Tomcat.</p><pre># Download and install TOTP extension<br>curl -fLO &quot;https://downloads.apache.org/guacamole/${GUAC_VERSION}/binary/guacamole-auth-totp-${GUAC_VERSION}.tar.gz&quot;<br>tar -xzf &quot;guacamole-auth-totp-${GUAC_VERSION}.tar.gz&quot;<br>sudo mkdir -p /etc/guacamole/extensions<br>sudo cp &quot;guacamole-auth-totp-${GUAC_VERSION}/guacamole-auth-totp-${GUAC_VERSION}.jar&quot; &quot;/etc/guacamole/extensions/&quot;<br><br># Restart tomcat<br>sudo systemctl restart tomcat</pre><p>Now, when you try logging in again, it will ask you to setup an authenticator and require you to input in every time you log in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/0*HcU_-AdApfArhOBz.png" /><figcaption>Multi-factor authentication setup screen captured by author</figcaption></figure><h3>Scripts</h3><p>If you don’t want to perform all above steps one by one, you may leverage bash scripts I created in this repository.</p><p><a href="https://github.com/pacroy/guacamole-setup-script">pacroy/guacamole-setup-script</a></p><p>Use at your own risk!</p><p><em>Originally published at </em><a href="https://dev.to/pacroy/create-your-own-azure-bastion-with-guacamole-and-save-100-a-month-3fld"><em>https://dev.to</em></a><em> on April 30, 2021.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=995c683dbc85" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Let’s Encrypt ACME CAA Limitation]]></title>
            <link>https://pacroy.medium.com/lets-encrypt-acme-caa-limitation-97411ea6ebf5?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/97411ea6ebf5</guid>
            <category><![CDATA[acme]]></category>
            <category><![CDATA[lets-encrypt]]></category>
            <category><![CDATA[dns]]></category>
            <category><![CDATA[caa]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Tue, 13 Apr 2021 12:02:29 GMT</pubDate>
            <atom:updated>2021-04-13T12:02:29.729Z</atom:updated>
            <content:encoded><![CDATA[<p>Not all the domain name are supported by Let’s Encrypt ACME</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9z5utNSwG1Hpt35XXr42gw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@markuswinkler">Markus Winkler</a> from <a href="https://unsplash.com/photos/SZ98vfIx0pw">Unsplash</a></figcaption></figure><p>In my <a href="https://pacroy.medium.com/single-node-kubernetes-on-home-lab-using-microk8s-metallb-and-traefik-7bb1ea38fcc2">previous blog about setting up Kubernetes with MicroK8s and Traefik</a>, I used DDNS service from <a href="https://www.noip.com/">noip.com</a> to create a domain name microk8s.ddns.net for testing with <a href="https://github.com/pacroy/whoami">whoami application</a> which was working without any problem.</p><p>But when I tried to switch to my other domain which is a DDNS service from my internet provider, the certificate cannot be issues with the error query timed out looking up CAA.</p><p>This blog is to get to know more about ACME and CAA.</p><h3>Introduction</h3><p>To host a website or web application securely, you may want to serve it over HTTPS (HTTP over SSL, or should be over TLS now) only and disable HTTP or, better, to always redirect it to HTTPS.</p><p>To make HTTPS works properly, you need a certificate to prove your web’s identity with your clients that you’re really from the domain you serve, not from a middleman who try to see your transferred data. Of course, the self-signed certificate won’t do the job.</p><p>To validate the certificate that it is belong to whom it claims to, the certificate must be issued by a trusted certificate authorities (CA). Every web browsers or OSs maintain the list of trusted CAs so the validation works and cross-checked.</p><p><a href="https://letsencrypt.org/"><strong>Let’s Encrypt</strong></a> is a trusted CA that provide automated ways to issue certificate for your application for free using <strong>Automatic Certificate Management Environment (ACME) protocol</strong>. Learn more about how Let’s Encrypt and ACME protocol work in <a href="https://letsencrypt.org/how-it-works/">this documentation page</a>.</p><p>For servers or VMs, you can use <a href="https://certbot.eff.org/">certbot</a> to do this job. For Kubernetes clusters, you have <a href="https://cert-manager.io/">cert-manager</a> and <a href="https://traefik.io/">Traefik</a> that can do this job.</p><h3>Traefik Error Log</h3><p>Let’s get back to my situation. When I tried to setup a new IngressRoute with certificate resolver for my whoami application for a specific domain, I got these errors in the Traefik’s log:</p><pre>Unable to obtain ACME certificate for domains &quot;this.isnotwork.com&quot;: unable to generate a certificate for the domains [this.isnotwork.com]: error: one or more domains had a problem:<br>[this.isnotwork.com] acme: error: 400 :: urn:ietf:params:acme:error:dns :: <strong>DNS problem: query timed out looking up CAA for </strong>this.isnotwork.com</pre><p>After several of the same errors, it showed this error as the last one.</p><pre>Unable to obtain ACME certificate for domains &quot;this.isnotwork.com&quot;: unable to generate a certificate for the domains [this.isnotwork.com]: acme: <strong>error: 429</strong> :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-order :: urn:ietf:params:acme:error:<strong>rateLimited</strong> :: Error creating new order :: <strong>too many failed authorizations recently</strong>: see https://letsencrypt.org/docs/rate-limits/</pre><p><em>*For security reason, the domain is substituted.</em></p><p>I searched with the error message and found this post explaining concisely about CAA error.</p><p><a href="https://community.letsencrypt.org/t/lets-encrypt-returns-query-timed-out-looking-up-caa-for-domain-name-error-when-obtaining-a-new-certificate/47047">Let&#39;s encrypt returns &quot;query timed out looking up CAA for &quot; error when obtaining a new certificate</a></p><p>In Let’s Encrypt documentation page, it also explain about CAA.</p><p><a href="https://letsencrypt.org/docs/caa/">Certificate Authority Authorization (CAA)</a></p><h3>Certificate Authority Authorization (CAA)</h3><p>In short, Let’s Encrypt (or any CA) should query for a CAA record from the DNS server to determine if it is authorized to issue a certificate for that specific domain. This is to prevent mis-issue certificates that could potentially affect every domain names.</p><h4>Verify CAA</h4><p>You can verify if your domain name is compatible with Let’s Encrypt ACME by using <a href="https://unboundtest.com/">https://unboundtest.com/</a> which simulate like what Let’s Encrypt will perform when it’s trying to issue a certificate.</p><p>You will get i/o timeout for the domain that does not provide CAA query and hence won’t work with Let’s Encrypt.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2b98baffda426c2c2665e76f72bcbc78/href">https://medium.com/media/2b98baffda426c2c2665e76f72bcbc78/href</a></iframe><p>Here is an example for one that provide CAA query and will work with Let’s Encrypt.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ce05b50054af5d0dd7b90ce3afae0d0/href">https://medium.com/media/4ce05b50054af5d0dd7b90ce3afae0d0/href</a></iframe><h3>Solution</h3><p>Contact your DNS service provider whether they can add CAA record for you or otherwise change the service provider. Most of DNS service providers should support CAA record out of the box. You may check out <a href="https://doc.traefik.io/traefik/https/acme/#providers">this list from Traefik documentation</a> page.</p><h4>Azure DNS</h4><p>Here, I have tried to add a new A record under my Azure DNS zone and CAA query works fine.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/434/1*2P8eRcDqSXeMOOt0pK4FIQ.png" /><figcaption>Azure DNS A Record — screen captured by the author</figcaption></figure><p>Here is Unboundtest log:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8e461461dcabab439736fd7b0980d931/href">https://medium.com/media/8e461461dcabab439736fd7b0980d931/href</a></iframe><p>Then tried to add a CAA record to explicitly authorize letsencrypt.org to issue certificate for this specific domain.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/434/1*jDySKt_05tRn3vQcis3jzA.png" /><figcaption>Azure DNS CAA record — screen captured by the author</figcaption></figure><p>Here is Unboundtest log:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a435f4ff8c2ff821e8fa1bb16cef5e34/href">https://medium.com/media/a435f4ff8c2ff821e8fa1bb16cef5e34/href</a></iframe><h3>CNAME Record</h3><p>The other question still persists. Can I use CNAME record for my domain name?</p><p>If your home’s public IP is dynamic so you cannot use A record as the IP keeps changing but you want to use CNAME record to map your domain name (the pretty domain that you bought) to your DDNS domain name (the uglier domain that provided by DDNS service provider e.g. noip.com).</p><p>The short answer is NO. It seems ACME protocol will resolve IP address for your domain from A (for IPv4) or AAAA (for IPv6) record only. In addition, as I tried, if you create a CNAME record, you cannot create CAA record and vice versa.</p><p>I’m still looking for solution for this scenario. One option is to use dnschallenge resolver but I’m not sure if it will work with CNAME or otherwise I need to develop a <a href="https://github.com/pacroy/azure-ddns-helm">script run by a regular job to manually update DNS record</a>.</p><p>Will share later. Cheers!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=97411ea6ebf5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Single-node Kubernetes on Home Lab using MicroK8s, Metallb, and Traefik]]></title>
            <link>https://pacroy.medium.com/single-node-kubernetes-on-home-lab-using-microk8s-metallb-and-traefik-7bb1ea38fcc2?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/7bb1ea38fcc2</guid>
            <category><![CDATA[microk8s]]></category>
            <category><![CDATA[metallb]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[homelab]]></category>
            <category><![CDATA[traefik]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Mon, 12 Apr 2021 14:21:20 GMT</pubDate>
            <atom:updated>2021-04-13T13:05:44.983Z</atom:updated>
            <content:encoded><![CDATA[<p>MicroK8s: High availability, Low-ops, Minimal Kubernetes for All-Size Clusters</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BkKSXbBohSD8hFmGqbL1Hg.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@good_citizen">Humphrey Muleba</a> from <a href="https://unsplash.com/photos/TejFa7VW5e4">Unsplash</a></figcaption></figure><p>In my recent blog, I have setup a single-node Kubernetes cluster on my home lab server using <a href="https://k0sproject.io/">k0s</a>. It is a good tool that make it easy to setup a cluster in just one or two commands. However, as it may be relatively new, I found a problem when starting the cluster that worker node sometime isn’t up and running and you need to restart the service.</p><p><a href="https://pacroy.medium.com/setup-single-node-kubernetes-cluster-on-a-home-lab-server-using-k0s-594e32624399">Setup Single-node Kubernetes Cluster on a Home Lab server using k0s</a></p><h3>TL;DR</h3><p>In this blog, I will try another tool called <a href="https://microk8s.io/">MicroK8s</a> which is around for sometime and hopefully will be more stable than <a href="https://k0sproject.io/">k0s</a> to setup a single-node Kubernetes cluster on Ubuntu Linux 20.04 on my home lab box.</p><h3>Prerequisites</h3><ul><li>A public IP address from your internet provider, either dynamic or static.</li><li>A domain name mapped to your static IP or a dynamic DNS domain name for dynamic IP which can be configured on your modem router to sync with the Dynamic DNS provider e.g. <a href="https://www.noip.com/">no-ip</a>.</li><li>You have a fresh install of Ubuntu 20.04 on your home lab server — see <a href="https://pacroy.medium.com/setup-single-node-kubernetes-cluster-on-a-home-lab-server-using-k0s-594e32624399">this blog</a> for how-to.</li><li>SSH has been configured for remote access on your home lab server— see my previous blogs on how to setup on <a href="https://pacroy.medium.com/setup-single-node-kubernetes-cluster-on-a-home-lab-server-using-k0s-594e32624399">home lab</a> or <a href="https://pacroy.medium.com/setting-up-your-own-vpn-server-with-just-5-a-month-c934cf073ea1">Linode VM</a>.</li><li><a href="https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/">kubectl</a>, git, and <a href="https://helm.sh/docs/intro/install/">helm</a> are installed on your local machine</li></ul><h3>Setup MicroK8s</h3><p>In this section, we will install MicroK8s on our Ubuntu server.</p><h4>Install MicroK8s</h4><p>On your server, use snap to install the MicroK8s package.</p><pre>sudo snap install microk8s --classic --channel=1.21</pre><p>Add yourself into the group microk8s, gain access to .kube caching directory, and refresh the session so group update takes effect.</p><pre>sudo usermod -a -G microk8s $USER<br>sudo chown -f -R $USER ~/.kube<br>su - $USER</pre><p>Monitor the cluster provisioning status. This may take a few minutes until the cluster is ready.</p><pre>microk8s status --wait-ready</pre><p>Get nodes and services on the cluster.</p><pre>microk8s kubectl get nodes<br>microk8s kubectl get services</pre><p>Enable foundation addons dns and storage</p><pre>microk8s enable dns storage</pre><h4>Remote Access</h4><p>To enable remote access to the api-server using kubectl, edit the file /var/snap/microk8s/current/certs/csr.conf.template on your server and add domain name and/or IP address (if static) of your server under alt_names section that reachable from the internet.</p><pre>[ alt_names ]<br>DNS.1 = kubernetes<br>DNS.2 = kubernetes.default<br>DNS.3 = kubernetes.default.svc<br>DNS.4 = kubernetes.default.svc.cluster<br>DNS.5 = kubernetes.default.svc.cluster.local<br><strong>DNS.6 = your.dnsname.com</strong><br>IP.1 = 127.0.0.1<br>IP.2 = 10.152.183.1<br>IP.3 = 192.168.1.xx<br><strong>IP.4 = 123.456.789.0</strong></pre><p>Export kubeconfig to file.</p><pre>microk8s config &gt; admin.config</pre><p>On your local machine, copy the kubeconfig file from the server to your machine.</p><pre>scp <em>username@your.dnsname.com</em>:~/admin.config .</pre><p>Edit the admin.config file and update clusters.cluster.server by replacing the IP and port with your domain name or public IP (if static). It is recommended to use the port other than 16443 for better security and then configure your router to forward the specified port to your server’s port 16443.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ec48b236a646e7665060041f19b0ce40/href">https://medium.com/media/ec48b236a646e7665060041f19b0ce40/href</a></iframe><p>Test the connection</p><pre>export KUBECONFIG=admin.config<br>kubectl version<br>kubectl get nodes</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/993/1*hbCat3R973jTxtumRKIpgA.png" /><figcaption>kubectl commands — screen captured from Windows Terminal by the author</figcaption></figure><h3>Deploy Application</h3><p>In this section, we will deploy an application named whoami for testing purpose.</p><h4>Install whoami</h4><p>First, clone <a href="https://github.com/pacroy/whoami">this repository</a> and apply deployment and service on the cluster.</p><pre>git clone <a href="https://github.com/pacroy/whoami.git">https://github.com/pacroy/whoami.git</a><br>cd whoami<br>kubectl create ns whoami<br>kubectl apply -f whoami.yml -n whoami</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3af93a6231b24a0f045433414cc4dfe1/href">https://medium.com/media/3af93a6231b24a0f045433414cc4dfe1/href</a></iframe><p>Once the pod is up and running, forward port to its service.</p><pre>kubectl port-forward service/whoami 8080:80 -n whoami</pre><p>Go to <a href="http://localhost:8080/">localhost:8080</a> on your local machine and you should see the response like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1019/1*Z-RC4MRA5BNWGY_zl6KbuA.png" /><figcaption>whoami via port-forward — screen captured from Microsoft Edge by the author</figcaption></figure><h3>Expose Service</h3><p>In this section, we will expose whoami service to the outside world using <a href="https://metallb.universe.tf/">metallb load balancer</a> and <a href="https://traefik.io/">traefik ingress controller</a>.</p><h4>Enable meltallb</h4><p>To expose a service with LoadBalancer, we need to enable metallb which a load balancer for bare metal.</p><pre>microk8s enable metallb</pre><p>It will ask to input IP address range allocated for load balancers. It is good to assign s small IP address pool within your subnet that are outside your DHCP range to avoid collision.</p><p>In this case I choose 192.168.1.85–192.168.1.89.</p><pre>Enabling MetalLB<br>Enter each IP address range delimited by comma (e.g. ‘10.64.140.43–10.64.140.49,192.168.0.105–192.168.0.111’): <strong>192.168.1.85–192.168.1.89</strong></pre><p>Update whoami service type to LoadBalancer.</p><pre>apply -f service-lb.yml -n whoami</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6cae2b3495414ea978ff4219aab0e4a4/href">https://medium.com/media/6cae2b3495414ea978ff4219aab0e4a4/href</a></iframe><p>Get service</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/304191d8f8b29b6613cbb540ee1e090e/href">https://medium.com/media/304191d8f8b29b6613cbb540ee1e090e/href</a></iframe><p>Test the load balancer by taking note the EXTERNAL-IP and opening it in your browser (in this case it is <a href="http://192.168.1.85">http://192.168.1.85</a>) and you should see the same result as before.</p><p>Restore the service type to ClusterIP as we will expose it via Ingress in the next section.</p><pre>kubectl apply -f whoami.yml -n whoami</pre><h4>Install Traefik Ingress</h4><p>On your server, enable <a href="https://traefik.io/">traefik</a> ingress controller for external access.</p><pre>helm repo add traefik <a href="https://helm.traefik.io/traefik">https://helm.traefik.io/traefik</a><br>helm repo update<br>kubectl create namespace traefik<br>helm install traefik traefik/traefik -n traefik</pre><p>Check component and you should see the traefik service is exposed via LoadBalancer on both port 80 and 443, by default.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3de9af966faf12b4695df99140aea020/href">https://medium.com/media/3de9af966faf12b4695df99140aea020/href</a></iframe><p>Restore whoami service type to ClusterIP and create an ingress which route traffics at any host with URI /whoami to our service.</p><pre>kubectl apply -f ingress.yml -n whoami</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3147635ef98f28e9bfa9ddad3d912b07/href">https://medium.com/media/3147635ef98f28e9bfa9ddad3d912b07/href</a></iframe><p>Configure your modem router to forward port 80 and 443 to your LoadBalancer IP address. Instruction varies by router’s brand and model and won’t be covered here.</p><p>Open your browser and go to your domain name or public IP and access URI /whoami. In my case, it is <a href="http://microk8s.ddns.net/whoami">http://microk8s.ddns.net/whoami</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1019/1*J3aYH8ul0B2xEaxs9BvC-w.png" /><figcaption>whoami via ingress — screen captured from Microsoft Edge by the author</figcaption></figure><h4>traefik Dashboard</h4><p>You can access traefik dashboard by forwarding port 9000 from a traefik pod.</p><pre>kubectl port-forward $(kubectl get pods --selector &quot;app.kubernetes.io/name=traefik&quot; --output=name -n traefik) -n traefik 9000:9000</pre><p>Then access the traefik dashboard at <a href="http://localhost:9000/dashboard/#/">http://localhost:9000/dashboard</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iagb8CLB1jQV3YMDxLdZhg.png" /><figcaption>traefik dashboard — screen captured from Microsoft Edge by the author</figcaption></figure><h3>Configure HTTPs</h3><p>In this section, we will enable HTTPS by setup a mechanism to automatically generate SSL certificate for our application using <a href="https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment">ACME</a> provider from <a href="https://letsencrypt.org/">Let’s Encrypt</a> authority (It’s free!).</p><p><em>UPDATE</em>: You may wanna check first if your domain name can work with Let’s Encrypt by follow what I did in <a href="https://pacroy.medium.com/lets-encrypt-acme-caa-limitation-97411ea6ebf5">this blog</a>.</p><h4>Introduction</h4><p>Now, if you try to access <a href="https://microk8s.ddns.net/whoami">https://your.dnsname.com/whoami</a> then you will get this warning page.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1019/1*QMneXq_Qp-yl-N7YUmX8qw.png" /><figcaption>Invalid certificate — screen captured from Microsoft Edge by the author</figcaption></figure><p>This is because the traefik is using a self-signed certificate which is not trusted by the browser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/405/1*JltsmMSMJIFF2VAbb4RYVw.png" /><figcaption>traefik self-signed certificate — screen captured by the author</figcaption></figure><p>To make our whoami website trusted by the browser, we need to tell traefik to get and use a certificate that issued by a trusted certificate authority (CA) e.g. <a href="https://letsencrypt.org/">Let’s Encrypt</a>.</p><p>Delete the ingress as we will use IngressRoute instead in the next section.</p><pre>kubectl delete ingress/whoami -n whoami</pre><h4>IngressRoute</h4><p>Instead of using standard Ingress object with lots of annotations, traefik also provides their own CRD called IngressRoute that make the configuration more readable and structured.</p><p>Let’s try IngressRoute. Edit the file ingressroute.yml and update the hostname microk8s.ddns.net with your domain name then apply.</p><pre>kubectl apply -f ingressroute.yml -n whoami</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9f3dedbca8744d83f5a7f610588b718b/href">https://medium.com/media/9f3dedbca8744d83f5a7f610588b718b/href</a></iframe><p>Try accessing whoami again at <a href="http://microk8s.ddns.net/whoami">http://your.dnsname.com/whoami</a> and you should see the same result.</p><h4>Certificate Resolver</h4><p>Next, we need to upgrade the traefik to include a certificate resolver. Edit the file values.yml and update youremail@domain.com with your email address. This will be used by Let’s Encrypt to send notification email when the certificate nearly expires.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6046af28dc40eb11d3df3c80de1cb862/href">https://medium.com/media/6046af28dc40eb11d3df3c80de1cb862/href</a></iframe><p>Upgrade the traefik release.</p><pre>helm upgrade traefik traefik/traefik -n traefik --values values.yml</pre><p>Check if the arguments are applied correctly by looking the Deployment’s manifest.</p><pre>helm get manifest traefik -n traefik</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/063a9fb57a4e8dffaaff9b33ab329cef/href">https://medium.com/media/063a9fb57a4e8dffaaff9b33ab329cef/href</a></iframe><p>Check traefik’s log and you should see no error about the certificate resolver.</p><pre>kubectl logs $(kubectl get pod -n traefik -o name) -n traefik -f</pre><h4>IngressRoute with TLS</h4><p>Now, let’s apply a new IngressRoute that using the certificate resolver. Don’t forget to update the hostname to yours!</p><pre>kubectl apply ingressroute-tls.yml -n whoami</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/901fa8355ae25ca490ab7c75dc1acaf4/href">https://medium.com/media/901fa8355ae25ca490ab7c75dc1acaf4/href</a></iframe><p>Check if the certificate is created properly.</p><pre>kubectl exec -it $(kubectl get pod -n traefik -o name) -n traefik -- cat /data/letsencrypt.json</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c7e77966d7222d30a9c549379a85a290/href">https://medium.com/media/c7e77966d7222d30a9c549379a85a290/href</a></iframe><p>NOTE: It may take a while before the certificate shows up for the configured domain.</p><p>Test accessing your application at <a href="https://your.dnsname.com/whoami">https://your.dnsname.com/whoami</a> and the browser should show the page with a valid certificate without warning.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yjtSbcyxLfqyIy1n4geqRA.png" /><figcaption>whoami with valid certificate — screen captured from Microsoft Edge by the author</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/405/1*u4RfBFAoMnb2-yhcJcO7rg.png" /><figcaption>valid certificate issued by Let’s Encrypt — screen captured by the author</figcaption></figure><h3>Security</h3><p>This section will show you how to enhance security for our application.</p><h4>TLS version 1.2</h4><p>HTTPS should no longer support SSL and TLS v1.1 and TLS v1.2 as they are weak in term of security. If we scan our endpoint with <a href="https://www.ssllabs.com/ssltest/analyze.html">SSLLabs</a>, you will get rating at B.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nAHxZABpSEHV8y-Ra0nf_g.png" /><figcaption>Rate B HTTPS — screen captured from <a href="https://www.ssllabs.com/ssltest/analyze.html">SSLLabs</a> by the author</figcaption></figure><p>We can make our traefik ingress to accept connection with minimum TLS v1.2 by creating a new TLSOption in <em>default </em>namespace.</p><pre>kubectl apply -f tlsoption.yml</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9ffbc8f304f9bf945bb023bbcd977715/href">https://medium.com/media/9ffbc8f304f9bf945bb023bbcd977715/href</a></iframe><p>This create a default TLSoption that will apply to all traefik routers by default (if not explicitly overridden). Scan the endpoint again with SSLLabs and you will now get rating A.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MH4pqcW5BtqSCzCoQtZbdg.png" /><figcaption>Rate A HTTPS — screen captured from <a href="https://www.ssllabs.com/ssltest/analyze.html">SSLLabs</a> by the author</figcaption></figure><h4>Redirect to HTTPS</h4><p>So far, our whoami application serves HTTP traffic at port 80 and HTTPS traffic at port 443. What if we want to redirect all HTTP traffics to HTTPS. We need to leverage <a href="https://doc.traefik.io/traefik/middlewares/redirectscheme/">RedirectScheme middleware</a>.</p><p>Let’s create a new middleware in <em>default </em>namespace.</p><pre>kubectl apply -f middleware-https.yml</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/60593b1c219232b37ed9ac9225f3ee89/href">https://medium.com/media/60593b1c219232b37ed9ac9225f3ee89/href</a></iframe><p>Then replace IngressRoute with the new one with the middleware.</p><pre>kubectl apply -f ingressroute-http.yml -n whoami</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a98a4c55371779c43c04aa05343b624f/href">https://medium.com/media/a98a4c55371779c43c04aa05343b624f/href</a></iframe><p>Test your HTTP endpoint and it should now redirect to HTTPS.</p><pre>$ curl <a href="http://microk8s.ddns.net/whoami">http://microk8s.ddns.net/whoami</a> -D-<br>HTTP/1.1 <strong>301 Moved Permanently</strong><br>Location: <a href="https://microk8s.ddns.net/whoami"><strong>https://microk8s.ddns.net/whoami</strong></a><br>Date: Mon, 12 Apr 2021 14:14:36 GMT<br>Content-Length: 17<br>Content-Type: text/plain; charset=utf-8</pre><pre>Moved Permanently</pre><p>For more information about the traefik’s concept and configuration, please visit <a href="https://doc.traefik.io/traefik/">its documentation site</a>.</p><p>See all available values for traefik Helm chart <a href="https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml">here</a>.</p><p>Cheers!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7bb1ea38fcc2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setup Single-node Kubernetes Cluster on a Home Lab server using k0s]]></title>
            <link>https://pacroy.medium.com/setup-single-node-kubernetes-cluster-on-a-home-lab-server-using-k0s-594e32624399?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/594e32624399</guid>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[cert-manager]]></category>
            <category><![CDATA[nginx-ingress]]></category>
            <category><![CDATA[k0s]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Sun, 14 Mar 2021 12:02:41 GMT</pubDate>
            <atom:updated>2021-03-14T12:02:41.036Z</atom:updated>
            <content:encoded><![CDATA[<p>Reducing your cloud bill and make use of your PC at home</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vRJgJhPxFJL3UAQaDRNbHQ.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@edoering">Erin Doering</a> from <a href="https://unsplash.com/photos/kmiUkK7vgak">Unsplash</a></figcaption></figure><p>I have been running a small Kubernetes (k8s) cluster on a cloud provider for some time and finding alternative to reduce my bill. I could have run my small workloads on a small server that cost only $5 a month like I did for in <a href="https://pacroy.medium.com/setting-up-your-own-vpn-server-with-just-5-a-month-c934cf073ea1">this blog</a> but I prefer a k8s cluster as all my existing configurations and scripts are built for it and I can keep learning and catch-up this popular ecosystem.</p><p>Recently, I have read <a href="https://betterprogramming.pub/create-a-multi-architecture-k0s-cluster-e265d24937b3">this blog by Luc Juggery</a> which show how easy you can setup a k8s cluster on your home lab server with <a href="https://k0sproject.io/">k0s</a>. Fortunately, I have a miniPC box with CPU Atom 1.44Ghz , 4GB RAM that I don’t use much. So here come this blog.</p><h3>TL;DR</h3><p>This blog guides you how to setup a single-node Kubernetes cluster with <a href="https://k0sproject.io/">k0s</a> on a miniPC running Ubuntu 20.04 OS and install <a href="https://github.com/kubernetes/ingress-nginx">ingress-nginx</a> and <a href="https://cert-manager.io/">cert-manager</a> and a sample whoami application that can be publicly accessed over the Internet.</p><h3>Prerequisites</h3><ul><li>Broadband internet with a good download/upload bandwidth — in my case it is 500/500Mbps</li><li>A home lab PC, <a href="https://docs.k0sproject.io/main/system-requirements/">at least 2 CPU cores, 2GB of RAM</a>, connected to the your home broadband network</li><li>A public IP from your internet service provider — this may incur additional cost, check with your ISP</li></ul><h3>Install Ubuntu Server 20.04 on Home Lab server</h3><p>Download Ubuntu Server 20.04 ISO file from <a href="https://ubuntu.com/download/server">its official site</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8AjbQSmkKQoaj5apSeMPtQ.png" /><figcaption>Screenshot captured by the author from <a href="https://ubuntu.com/download/server">Ubuntu</a></figcaption></figure><p>Make a bootable USB stick from the downloaded ISO file follow <a href="https://ubuntu.com/tutorials/create-a-usb-stick-on-windows#2-requirements">this official instruction</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZdyfybJFHv_R2hqmYFFX7A.png" /><figcaption>Screenshot captured by the author from <a href="https://ubuntu.com/tutorials/create-a-usb-stick-on-windows#2-requirements">Ubuntu</a></figcaption></figure><p>Once you get the bootable USB stick, restart your PC and boot with your USB. Just follow on-screen instruction to install Ubuntu server.</p><h3>Configure Ubuntu</h3><h4>Update System</h4><p>Once the installation complete, log on and update the system.</p><pre>apt-get update &amp;&amp; apt-get -y upgrade</pre><h4>Configure IP Address</h4><p>Configure your DHCP server in your router to issue a fixed IP address to your server. Try to assign an IP address that outside of your DHCP range to avoid collision. For example, your router may have DHCP range from 192.168.1.100 to 192.168.1.255. Then you may assign 192.168.1.10 to your server.</p><p>Configuration steps are vary by router. If not possible, you may need configure IP manually on Ubuntu. The instruction to do so is beyond this blog.</p><p>After DHCP is configured, you may restart the server and check IP address if it is configured properly.</p><pre># Reboot<br>sudo reboot</pre><pre># Show local IP address<br>hostname -I | awk ‘{print $1}’</pre><h4>Setup SSH</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/0*aqYYZXsqMM9M-3wN.png" /><figcaption>Image from <a href="https://saadhost.com/what-is-ssh/">Saddhost</a></figcaption></figure><p>Add your SSH key to the server by executing this command on another computer/laptop.</p><pre>ssh-copy-id yourusername@ip_address</pre><p>Edit the SSH config file</p><pre>sudo nano /etc/ssh/sshd_config</pre><p>Change the following lines for better security</p><pre>PermitRootLogin no<br>PasswordAuthentication no</pre><p>Restart SSH</p><pre>systemctl restart sshd</pre><p>Test SSH from another computer/laptop and make sure you can connect and logon.</p><pre>ssh yourusername@ip_address</pre><h4>Configure SSH for Remote Access</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*Qysdva2Ir9RcGPtA" /><figcaption>Image by <a href="https://unsplash.com/@hostreviews">Stephen Phillips</a> from <a href="https://unsplash.com/photos/tN344soypQM">Unsplash</a></figcaption></figure><p>If you want to SSH to your server from the internet, you need to configure port forwarding on your router. Choose the external port other than 22 for better security. This vary by router so the instruction is beyond this blog.</p><p>Once configured, you may test the connection.</p><pre>ssh your_username@external_ip -p external_port</pre><p>You can see your external IP address by executing this command on your server.</p><pre>curl ipv4.icanhazip.com</pre><p>If your public IP is static then this is fine. You can use the IP address to connect as it is fixed and will never change.</p><p>But most of ISPs won’t usually give you a static one but dynamic one i.e. your external IP keeps changing. In this case, you may need to configure Dynamic DNS (DDNS) settings on your router so you can access your server using a domain name and you don’t need to worry about the IP.</p><p>Again, the steps to configure DDNS are vary by router and will not be included in this blog.</p><p>To save some keys on SSH command, you can create the file ~/.ssh/config with the following content:</p><pre>Host youralias<br>  User your_username<br>  Port external_port<br>  Hostname your_domain_name</pre><p>Then you can easily make SSH connection in short like this</p><pre>ssh youralias</pre><h4>Unattended Upgrade</h4><p>You can also configure the system to automatically upgrade and restart by following steps I wrote in <a href="https://pacroy.medium.com/setting-up-your-own-vpn-server-with-just-5-a-month-c934cf073ea1">this blog</a>. Look for <em>Setup Unattended Upgrades </em>title.</p><h3>Install k0s</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5j0INsnj04CeYqRe" /><figcaption>Image from <a href="https://morioh.com/p/9608ddcf156d">Morioh</a></figcaption></figure><p>Follow <a href="https://docs.k0sproject.io/v0.11.0/install/">its official instruction</a> by executing this command.</p><pre>curl -sSLf <a href="https://get.k0s.sh">https://get.k0s.sh</a> | sudo sh</pre><p>Check if it is install properly</p><pre>$ k0s version<br>v0.11.0</pre><h3>Setup the Cluster</h3><h4>Building Config</h4><p>Generate default config YAML file to a folder that make it accessible by root .</p><pre>sudo k0s default-config &gt; /root/k0s.yaml</pre><p>Edit the file and append the following extension. Make sure you update two things:</p><ol><li>The IP address range load balancer —Use a small range of available and addressable IP addresses in your local networks that outside DHCP scope to avoid collision. In this example, I use 192.168.1.20–192.168.1.25.</li><li>The email address in the cluster-issuer section.</li></ol><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/225e1304fe133a483f50c72a4dbd98ad/href">https://medium.com/media/225e1304fe133a483f50c72a4dbd98ad/href</a></iframe><p>This will install <a href="https://github.com/kubernetes/ingress-nginx">ingress-nginx</a> and <a href="https://cert-manager.io/">cert-manager</a> on your cluster in one shot.</p><h4>Start the Cluster</h4><p>Install the cluster from our configuration file and start the server.</p><pre>sudo k0s install controller -c /root/k0s.yaml — enable-worker<br>sudo systemctl start k0scontroller.service</pre><p>Wait a bit and check server status</p><pre>$ sudo k0s status<br>Version: v0.11.0<br>Process ID: 1472<br>Parent Process ID: 1<br>Role: controller+worker<br>Init System: linux-systemd</pre><p>Check server node</p><pre>$sudo k0s kubectl get nodes<br>NAME        STATUS   ROLES    AGE   VERSION<br>urserver1   Ready    &lt;none&gt;   6d    v1.20.4-k0s1</pre><p><strong>NOTE</strong>: After a few minutes, if you still don’t see the node then try to restart the service.</p><pre>sudo systemctl restart k0scontroller.service</pre><h4>Reset the Cluster</h4><p>In case your cluster go wrongly and you want to resetup the cluster then use this command to reset the cluster and restart the server before repeating the steps.</p><pre>sudo k0s reset<br>sudo reboot</pre><h3>Remote Access to the Cluster</h3><p>To access the cluster with kubectl, you need to copy the kubeconfig file.</p><pre>sudo cp /var/lib/k0s/pki/admin.conf ~/admin.conf</pre><p>And transfer to your other computer.</p><pre>scp your_username@your_server:~/admin.conf ~</pre><p>Edit the file and replace server field with your server IP address:port. If you decide to open this port (6443) to the internet. This could be your server’s external IP or domain name:external port.</p><pre>apiVersion: v1<br>clusters:<br>- cluster:<br> certificate-authority-data: ***<br><strong> server: </strong><a href="https://pacroy.thddns.net:15843"><strong>https://your_server:port</strong></a><br> name: local<br>...</pre><p>Test connection</p><pre>$ export KUBECONFIG=~/admin.conf<br>$ kubectl version<br>Client Version: version.Info{Major:&quot;1&quot;, Minor:&quot;20&quot;, GitVersion:&quot;v1.20.2&quot;, GitCommit:&quot;faecb196815e248d3ecfb03c680a4507229c2a56&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2021-01-13T13:28:09Z&quot;, GoVersion:&quot;go1.15.5&quot;, Compiler:&quot;gc&quot;, Platform:&quot;linux/amd64&quot;}<br><strong>Server Version: version.Info{Major:&quot;1&quot;, Minor:&quot;20+&quot;, GitVersion:&quot;v1.20.4-k0s1&quot;, GitCommit:&quot;e87da0bd6e03ec3fea7933c4b5263d151aafd07c&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2021-03-03T07:31:16Z&quot;, GoVersion:&quot;go1.15.8&quot;, Compiler:&quot;gc&quot;, Platform:&quot;linux/amd64&quot;}</strong></pre><p>If you server’s version information then the connection is successful.</p><h3>Verify Cluster</h3><p>Check if all pod are up and running. You may need to wait for a while until all pods are up.</p><pre>kubectl get pods --all-namespaces</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b46facdfb9df1d674fbdb75c86a1e944/href">https://medium.com/media/b46facdfb9df1d674fbdb75c86a1e944/href</a></iframe><p>Check if all Helm charts are installed as expected</p><pre>helm list --all-namespaces</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9ffd8b9627b5bc052af1ee3bec74e463/href">https://medium.com/media/9ffd8b9627b5bc052af1ee3bec74e463/href</a></iframe><p>Check ingress external IP. It should be your load balancer IP address on your local network that you defined in k0s.yaml file when setting up the server.</p><pre>kubectl get services --namespaces kube-system</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f2eab46daa723f1b255185377165c071/href">https://medium.com/media/f2eab46daa723f1b255185377165c071/href</a></iframe><h3>Forward Ports</h3><p>To make your web application accessible from the internet, you need to forward the port 80 and 443. The configuration steps should be the same as you did for SSH (port 22) and kubectl (port 6443).</p><p>Eventually, you may have these port forwarding configuration on your router (Assume 192.168.1.10 is the server’s IP and 192.168.1.20 is the LB’s IP).</p><pre>Ext.Port   Int.Port   Server IP<br>--------   --------   ---------<br>12322      22         192.168.1.10<br>12343      6443       192.168.1.10<br>80         80         192.168.1.20<br>443        443        192.168.1.20</pre><h3>Test Deploying whoami</h3><h4>Prepare Configuration File</h4><p>Create a new namespace</p><pre>kubectl create namespace whoami</pre><p>Create a YAML file whoami.yml with the following content</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/acf912523b94af652a56e8471503d402/href">https://medium.com/media/acf912523b94af652a56e8471503d402/href</a></iframe><h4>Deploy Application and Test</h4><p>Apply the YAML file to the namespace</p><pre>$ kubectl apply -f whoami.yml --namespace whomai<br>deployment.apps/whoami-deployment created<br>service/whoami-service created<br>ingress.networking.k8s.io/whoami-ingress created</pre><p>Check the status</p><pre>kubectl get all --namespace whoami</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/65389622aa783d482b9bbfa7ecdd09dd/href">https://medium.com/media/65389622aa783d482b9bbfa7ecdd09dd/href</a></iframe><p>Try accessing your application at <a href="http://load_balancer_ip/whoami">http://load_balancer_ip/whoami</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/871/1*rY2YNcIG6JfS8tPn-KiKAg.png" /><figcaption>Screenshot captured by the author</figcaption></figure><h4>Configure HTTPS</h4><p>To have a proper SSL certificate for HTTPS connection, you need a valid domain name. Then create a DNS A record pointing to your public IP address. The instruction is vary by DNS service provider and won’t be included here.</p><p>Check if DNS record can be resolved properly</p><pre>$ nslookup whoami.yourdomain.com 8.8.8.8<br>Server:         8.8.8.8<br>Address:        8.8.8.8#53</pre><pre>Non-authoritative answer:<br>Name:   whoami.yourdomain.com<br>Address: xxx.xxx.xxx.xxx</pre><p>Create a new ingress file ingress.yml with the following context:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f859e8a107163f91d067438aa5802e36/href">https://medium.com/media/f859e8a107163f91d067438aa5802e36/href</a></iframe><p>Reapply the ingress</p><pre>$ kubectl apply -f ingress.yml --namespace whoami<br>ingress.networking.k8s.io/whoami-ingress configured</pre><p>Watch the pods. You will see the a cert-manager pod is created to auto-provision SSL certificate and then get terminated. This process usually takes less than one minute.</p><pre>kubectl get pod --watch --namespace whoami</pre><p>Check the certificate status and you should see <strong><em>True </em></strong>in the <em>Ready </em>column.</p><pre>$ kubectl get certificates --namespace whoami<br>NAME     READY   SECRET   AGE<br>whoami   True    whoami   2m56s</pre><p>Now, test accessing your application at <a href="http://yourdomain.com/whoami">http://yourdomain.com/whoami</a> and it should redirects to HTTPS with valid SSL certificate.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/871/1*S2xQcA72i5GByED9xMa6LQ.png" /><figcaption>Screenshot captured by the author</figcaption></figure><h4>Clean up</h4><p>Once you finished testing, you can delete whoami.</p><pre>kubectl delete all --all --namespace whoami<br>kubectl delete namespace/whoami</pre><h3>Caveats</h3><h4>k0s worker node is sometimes not up and running</h4><p>when you start the cluster first time, sometimes the worker node is not up even after some mount of time. In this case, restarting the service is usually help.</p><pre>sudo systemctl restart k0scontroller.service</pre><h4>Cannot use CNAME record with cert-manager</h4><p>In my environment, my public IP is dynamic and mapped to a DNS using Dynamic DNS (DDNS) service. I have my own custom domain that I try to map to DDNS domain using CNAME record. But that doesn’t seem to work for cert-manager.</p><p>I end up mapping my custom domain using a A record and thinking about to setup an automated task to regularly check and update DNS A record.</p><p>But please let me know if you find a better way to deal with this.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=594e32624399" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting Up Your Own VPN Server with just $5 a month]]></title>
            <link>https://pacroy.medium.com/setting-up-your-own-vpn-server-with-just-5-a-month-c934cf073ea1?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/c934cf073ea1</guid>
            <category><![CDATA[ubuntu]]></category>
            <category><![CDATA[vpn]]></category>
            <category><![CDATA[linode]]></category>
            <category><![CDATA[openvpn]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Sat, 06 Feb 2021 08:47:12 GMT</pubDate>
            <atom:updated>2021-03-13T08:00:30.640Z</atom:updated>
            <content:encoded><![CDATA[<p>Your Own Server, Your Own Privacy, Multiple Users</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tR8VO0vFjL--EyEZiGtWFg.jpeg" /><figcaption>Image by <a href="https://pixabay.com/users/stefancoders-11782809/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=4072715">Stefan Coders</a> from <a href="https://pixabay.com/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=4072715">Pixabay</a></figcaption></figure><p>I have recently stumbled upon a <a href="https://www.youtube.com/watch?v=gxpX_mubz2A">video on YouTube by Wolfgang’s Channel</a> about how to setup your own VPN server using <a href="https://openvpn.net/">OpenVPN</a>. There’s also a <a href="https://notthebe.ee/Creating-your-own-OpenVPN-server.html">transcript version of the video</a> that you can read and follow along. Or you can follow my guide below.</p><h3>TL;DR</h3><p>In this guide, I will show you how to setup OpenVPN server on a Linux VM hosted on Linode. The cost of the VM is $5 per month (1TB traffic inclusive) but you can get free $100 credit for 60 days from <a href="https://www.linode.com/?r=1b5a45d1e5c517cf3f96a708ab132df9025840a1">this link</a>. Since this is your own server, you can create multiple VPN profiles for multiple devices or give them to your friend or family to use.</p><h3>Find a Cloud Server</h3><p>I was recently looking for a cheap virtual machines (VM) or virtual private server (VPS) solution for running my workloads. The cheapest one (1 vCPU, 0.75GB RAM) on Microsoft Azure is ~ $13/month and it only includes 5GB transfer. It could cost you ~$100/month for 1TB transfer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*VvTyz_CunAdcsyZRgN9OOw.png" /><figcaption>Virtual Machine cost from Microsoft Azure <a href="https://azure.microsoft.com/en-us/pricing/calculator/">Pricing Calculator</a></figcaption></figure><p>I’m also looking for local providers in-country and its cheapest plan starts from ~$10/month with similar size VM (1 vCPU, 1GB RAM). But the good thing is they offer unlimited transfer. However, it may not serve the purpose of using VPN as you might not want website to know where you are from.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1Qfg2zcUuDVhf42XBDMWdg.png" /><figcaption>Linux VPS plans from a local provider</figcaption></figure><p>The cheapest one I found is <a href="https://www.linode.com/?r=1b5a45d1e5c517cf3f96a708ab132df9025840a1">Linode</a> that offer a 1vCPU, 1GB RAM for $5/month with 1TB transfer included which should be enough for general VPN usage. It would cost $10 for each additional TB transfer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F-UyU6cY7zUNYneLWvxhqw.png" /><figcaption>VM shared plans on <a href="https://www.linode.com/pricing/">Linode Pricing</a></figcaption></figure><p><a href="https://www.digitalocean.com/pricing/">Digital Ocean</a> is another popular provider that offer similar price. If you see any better alternative, please also let me know. But in this guide I’ll just use Linode to host my VPN server.</p><h3>Create Virtual Machine</h3><p>Click Create a Linode with the following parameters:</p><p>Image: <strong>Ubuntu 20.04 LTS</strong><br>Region: Choose the closest one to you<br>Linode Plan: <strong>Nanode 1GB</strong> ($5 monthly)<br>Linode Label: Name your server<br>Root Password: Set a secure root password (we will disable root login later)<br>Private ID: Select to add a private IP</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*neyycFgr1ZwKH-hE-95cNg.png" /><figcaption>Create a Nanode 1GB VM</figcaption></figure><p>Wait until the provisioning is completed when the status turns to <em>Running</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W2llp5yc4jOOnZVh9maNpg.png" /><figcaption>The VM is up and running</figcaption></figure><p>Try to connect to the server using your favourite SSH client. In my case, I will use SSH client inside Ubuntu distro of <a href="https://aka.ms/wsl2">WSL</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/958/1*hBI3WVHy1cXV9Y0tMIXHeQ.png" /><figcaption>Log in Linode Server via SSH</figcaption></figure><h3>Configure System</h3><h4>Update system</h4><p>Upgrade your system</p><pre>apt-get update &amp;&amp; apt-get -y upgrade</pre><h4>Create User</h4><p>Create a new user as using root is not a good security practice.</p><pre>useradd -G sudo -m yourusername -s /bin/bash</pre><p>Set the password for the new user.</p><pre>passwd yourusername</pre><p>If you don’t have a SSH key yet, open a new terminal and create a new SSH key on your local PC.</p><pre>ssh-keygen -t ed25519</pre><p>You may set a passphase for better security.</p><p>Then, add the SSH key to the new user on the server.</p><pre>ssh-copy-id yourusername@ip_address</pre><h4>Configure SSH</h4><p>On your server, edit the file /etc/ssh/sshd_config</p><pre>nano /etc/ssh/sshd_config</pre><p>Uncomment and change the following lines:</p><pre>Port 12345<br>PermitRootLogin no<br>PasswordAuthentication no</pre><p>For <em>Port</em>, change to other number than 22 to prevent SSH port scanners.</p><p>Restart the SSH service</p><pre>systemctl restart sshd</pre><p>Test loggin in using SSH key in a separated terminal window but don’t close the current root terminal yet in case you cannot log on.</p><pre>ssh yourusername@ip_address -p 12345</pre><p>If you can log on then you can log off the root and try logging in again. You should not be able to to log on.</p><h4>Disable sudo Password</h4><p>If you don’t like to enter password everytime you use <em>sudo</em> command then edit the file <em>/etc/sudoers</em> by executing the following command</p><pre>sudo visudo</pre><p>Append the following line:</p><pre>yourusername ALL=(ALL) NOPASSWD: ALL</pre><p>Please note that this reduces the security.</p><h4>Change Hostname</h4><p>Edit the file <em>/etc/hostname</em></p><pre>sudo nano /etc/hostname</pre><p>Change the hostname from <em>locahost</em> to the name you want e.g. <em>demo-server</em> in my case.</p><p>Edit the file <em>/etc/hosts</em></p><pre>sudo nano /etc/hosts</pre><p>And add your new hostname like below:</p><pre>127.0.0.1 localhost <strong>demo-server</strong></pre><pre># The following lines are desirable for IPv6 capable hosts<br>::1 localhost ip6-localhost ip6-loopback <strong>demo-server</strong><br>ff02::1 ip6-allnodes<br>ff02::2 ip6-allrouters</pre><p>Reboot the server from your Linode console.</p><h4>Create SSH alias</h4><p>To avoid typing long command to login to server like <em>ssh pacroy@123.456.78.90 -p 12345</em>. We can create an alias for it.</p><p>Create the file <em>~/.ssh/config</em> on your local PC and add the following:</p><pre>Host demo-server<br>    User yourusername<br>    Port 12345<br>    Hostname server_domain<br>    IdentityFile ~/.ssh/id_ed25519</pre><p>Name your alias on the first line. In this case, I name it <em>demo-server</em>.</p><p>For <em>Hostname</em>, instead of using IP address which can be changed for some cloud providers, you can use server’s domain name. For Linode, you can get the server’s domain name from the console:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z0_S142MNIU5EuelvNUtyw.png" /><figcaption>Server’s domain name from Linode console</figcaption></figure><p>For <em>IdentityFile</em>, you need to specify if you have multiple SSH keys. If you have only one key then you can omit this line.</p><p>Test your alias with:</p><pre>ssh demo-server</pre><p>And you should be able to log in.</p><h3>Install OpenVPN</h3><h4>Execute the Script</h4><p>In this step, we will use the installation script from <a href="https://github.com/Nyr/openvpn-install">https://github.com/Nyr/openvpn-install</a></p><p>Execute this command to install OpenVPN:</p><pre>wget <a href="https://git.io/vpn">https://git.io/vpn</a> -O openvpn-install.sh &amp;&amp; sudo bash openvpn-install.sh</pre><p>It will ask a few questions which you can accept the defaults by keep hitting enter.</p><pre>Welcome to this OpenVPN road warrior installer!</pre><pre>Which IPv4 address should be used?<br> 1) 139.162.54.189<br> 2) 192.168.169.11<br>IPv4 address [1]:</pre><pre>Which protocol should OpenVPN use?<br> 1) UDP (recommended)<br> 2) TCP<br>Protocol [1]:</pre><pre>What port should OpenVPN listen to?<br>Port [1194]: 443</pre><pre>Select a DNS server for the clients:<br> 1) Current system resolvers<br> 2) Google<br> 3) 1.1.1.1<br> 4) OpenDNS<br> 5) Quad9<br> 6) AdGuard<br>DNS server [1]: 3</pre><pre>Enter a name for the first client:<br>Name [client]: demo-server</pre><pre>OpenVPN installation is ready to begin.<br>Press any key to continue…</pre><p>However, I choose the port to <em>443</em> and <em>1.1.1.1</em> as my DNS server as suggested in <a href="https://notthebe.ee/Creating-your-own-OpenVPN-server.html">the original guide</a>.</p><p>For the client name, I recommend to choose a name that can scale if you plan to create more clients for different devices or for multiple users. For example, demo-1, demo-2, etc.</p><p>Once ready, press anykey to start the installtion. It should take only several seconds to complete.</p><h4>Copy the VPN Profile</h4><p>You need this file to setup the client.</p><p>Copy the file and change owner so you can download it later via SSH.</p><pre>sudo mv /root/demo-server.ovpn ~<br>sudo chown pacroy:pacroy ~/demo-server.ovpn</pre><h4>Disable the log</h4><p>Edit the file <em>/etc/openvpn/server/server.conf</em></p><pre>sudo nano /etc/openvpn/server/server.conf</pre><p>Find and change this line to verb 0</p><pre>verb 0</pre><p>Restart the OpenVPN service</p><pre>systemctl restart openvpn-server@server.service</pre><h3>Setting up Your Client</h3><h4>Download the Profile</h4><p>Use scp command to download the ovpn file to your local PC.</p><pre>scp demo-server:~/demo-server.ovpn ./Downloads</pre><h4>Download OpenVPN client</h4><p>Head to <a href="https://openvpn.net/download-open-vpn/">https://openvpn.net/download-open-vpn/</a> and download client for your device. It supports Windows, MacOS, Linux, iOS, Android.</p><p>In this case, I’ll show how to setup the client on Windows. But the method is similar on all devices. You need to use a secure way to transfer the ovpn file to your device e.g. using cloud storage, IM, etc. Avoid using email.</p><p>Drop the ovpn file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*ciLhEoM1tGOZ6oH0IZPGiw.png" /><figcaption>Drop .ovpn file on OpenVPN client</figcaption></figure><p>Click Add</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*kJVjc9NWj67siwG8pa5JMA.png" /><figcaption>Import an OpenVPN Profile</figcaption></figure><p>Try to connect</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*c9vGCA2ZGdguv2raZZhCcA.png" /><figcaption>OpenVPN client connected</figcaption></figure><p>If you see the above screen then you have setup everything correctly and you’re ready to roll!</p><h3>Adding More Clients</h3><p>If you have multiple devices or you want to create new profile for other users. Do not use the same ovpn profile as the connection will not work well if there are multiple clients connect using the same profile. Instead, create a new client</p><p>Execute the installation script again on the server to add a new client.</p><pre>sudo bash openvpn-install.sh</pre><p>Follow the on-screen instruction to add a new client (or remove).</p><pre>OpenVPN is already installed.</pre><pre>Select an option:<br> 1) Add a new client<br> 2) Revoke an existing client<br> 3) Remove OpenVPN<br> 4) Exit<br>Option: 1</pre><pre>Provide a name for the client:<br>Name: client-2<br>…</pre><p>Use the same method above to download .ovpn file and send to your device to setup the client.</p><h3>Setup Unattended Upgrades (Optional)</h3><p>You can configure the server to automatically upgrade and reboot to apply security patches.</p><p>Install required components:</p><pre>sudo apt install -y unattended-upgrades apt-listchanges</pre><p>Follow on-screen instruction to install Postfix.</p><p>Enable the stable security upgrades</p><pre>sudo dpkg-reconfigure -plow unattended-upgrades</pre><p>Choose <em>Yes</em>.</p><p>Edit the config file.</p><pre>sudo nano /etc/apt/apt.conf.d/50unattended-upgrades</pre><p><strong>Uncomment </strong>and update the following lines as shown</p><pre>Unattended-Upgrade::Remove-Unused-Kernel-Packages “true”;<br>Unattended-Upgrade::Remove-Unused-Dependencies “true”;<br>Unattended-Upgrade::Automatic-Reboot “true”;<br>Unattended-Upgrade::Automatic-Reboot-Time “23:00”;</pre><p>Time should be in your server timezone which is UTC by default.</p><p>Test to see if it works</p><pre>sudo unattended-upgrades — dry-run</pre><p>Now, the system will be automatically upgraded.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c934cf073ea1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Try Podman, Buildah, and Skopeo instead of Docker]]></title>
            <link>https://pacroy.medium.com/try-podman-buildah-and-skopeo-instead-of-docker-c45a0e9394b0?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/c45a0e9394b0</guid>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[buildah]]></category>
            <category><![CDATA[skopeo]]></category>
            <category><![CDATA[podman]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Sun, 10 Jan 2021 15:27:47 GMT</pubDate>
            <atom:updated>2021-01-10T15:27:47.454Z</atom:updated>
            <content:encoded><![CDATA[<p>Docker is not the only tool to work with containers</p><figure><img alt="Docker vs. Podman+Buidah+Skopeo" src="https://cdn-images-1.medium.com/max/1024/1*qf738HmIMaE8fbPiV8Ac8Q.jpeg" /><figcaption>Docker vs. Podman+Buidah+Skopeo —Image by <a href="https://medium.com/u/c349bdf2ae99">Chairat Onyaem (Par)</a></figcaption></figure><p>We often use Docker when we talk about containers, similar to we use ‘Coke’ when we talk about carbonated soft drink. We all know that Coke is not the only brand that sells carbonated soft drink. Same to Docker, it is not the only one that does containers.</p><h3>Why not Docker</h3><p>Docker has been around for quite a while and it is a good tool. It is one of the very first thing I always tell junior developers to study when they start their first job from school.</p><p>Recenly, I feel annoying installing Docker on my different machines that I need to switch back and forth. Due to its big installation file so it is heavy weight. I normally use Docker to just build images and deploy workloads on my Kubernetes clusters and use it for nothing else.</p><p>I knew about podman and skopeo that may do the job I need but never have a chance to try them out until I stumble upon <a href="https://martinheinz.dev/blog/35?utm_source=tds&amp;utm_medium=referral&amp;utm_campaign=blog_post_35">this blog</a> from Martin Heinz.</p><p><a href="https://towardsdatascience.com/its-time-to-say-goodbye-to-docker-5cfec8eff833">You Don’t Have to Use Docker Anymore</a></p><p>It is pretty long one but good to read through. But if you don’t have time, here is my summary:</p><ul><li>Docker is a monolithic tool that tries to do everything. But the functionality can be divided into several components:</li></ul><h4>Container Engines</h4><figure><img alt="podman logo" src="https://cdn-images-1.medium.com/max/696/1*pyC5VB4Vbkv0RT36RpSR2A.png" /><figcaption>Podman logo — from <a href="https://podman.io/">https://podman.io/</a></figcaption></figure><ul><li>Container Engines is a tool providing UI for working with images and containers (excluding running containers)</li><li>The most prominent competitor to Docker is <a href="https://podman.io/">Podman</a>, developed by Red Hat.</li><li>Podman doesn’t need daemon to run and also doesn’t need root privileges which has been long-standing concern with Docker.</li><li>Podman can also run pods which make it easier to later migrate the workloads to Kubernetes.</li><li>Podman provides the exact same CLI commands as Docker as they are implemented using the same standard defined by the <a href="https://opencontainers.org/">Open Container Initiatives (OCI)</a>.</li><li>There are other Container Engines but can be considered as dead-end e.g. <a href="https://linuxcontainers.org/lxd/introduction/">LXD</a>, <a href="https://cri-o.io/">CRI-O</a>, <a href="https://coreos.com/rkt/">rkt</a> (rocket)</li></ul><h4>Image Builder</h4><figure><img alt="buildah logo" src="https://cdn-images-1.medium.com/max/885/0*NyB_DULF2qUipHud" /><figcaption>Buildah logo — from <a href="https://github.com/containers/buildah">https://github.com/containers/buildah</a></figcaption></figure><ul><li><a href="https://buildah.io/">Buildah</a> is another tool developed by Red Hat and it comes along with Podman and is called when you run podman build.</li><li>Buildah is daemonless and rootless and produces OCI compliant images so it’s guaranteed that your images will run the same way as the ones built with Docker.</li><li>Buildah is also able to build images from Dockerfile .</li><li>Buildah are user specific, so you will be able to list only images you built yourself.</li><li>buildah CLI is superset of commands included in podman build . Learn more about the differences between Podman and Buildah from <a href="https://podman.io/blogs/2018/10/31/podman-buildah-relationship.html">this article</a>.</li><li>Another tools for building images are Google’s <a href="https://github.com/GoogleContainerTools/kaniko">Kaniko</a>, Docker’s <a href="https://github.com/moby/buildkit">buildkit</a>, OpenShift’s <a href="https://github.com/openshift/source-to-image">Source-To-Image (S2I)</a>, <a href="https://github.com/GoogleContainerTools/jib">Jib</a>, and <a href="https://github.com/bazelbuild/bazel">Bazel</a>.</li></ul><h4>Container Runtime</h4><figure><img alt="containerd logo" src="https://cdn-images-1.medium.com/max/759/0*UO6itkYp10F7qaaX.png" /><figcaption>Containerd logo — from <a href="https://containerd.io/">https://containerd.io/</a></figcaption></figure><ul><li>Container Runtime is responsible for running containers and is one part of the whole container lifecycle/stack, which you will most likely not going to mess with.</li><li><a href="https://github.com/opencontainers/runc">runc</a> is the most popular container runtime created based on OCI container runtime specification. It’s used by Docker (through <em>containerd</em>), Podman, and CRI-O (default for OpenShift cluster).</li><li>Alternative to runc is <a href="https://github.com/containers/crun">crun</a> which is a tool developed by Red Hat and fully written in C (runc is written in Go). Considering that it’s Red Hat product, we might eventually see as default for Podman or CRI-O.</li><li>Last one to mention is <a href="https://containerd.io/">containerd</a> which is a CNCF graduating project and acts as an API facade for various container runtimes. It’s used by Docker Engine, Google Kubernetes Engine (GKE), IBM Kubernetes Service (IKS).</li></ul><h4>Image Inspection and Distribution</h4><figure><img alt="skopeo logo" src="https://cdn-images-1.medium.com/max/463/0*cDKVGgpW2jPwFdaW" /><figcaption>Skopeo logo — from <a href="https://www.redhat.com/en/blog/rhel-8-enables-containers-tools-software-craftsmanship-0">https://www.redhat.com</a></figcaption></figure><ul><li><a href="https://github.com/containers/skopeo">Skopeo</a> is made by Red Hat and it’s an accompanying tool for Buildah, Podman.</li><li>Skopeo is also able to copy images using skopeo copy which allows you to mirror images between remote registries without first pulling them to local registry.</li><li><a href="https://github.com/wagoodman/dive">Dive</a> is another tool for inspecting, exploring, and analyzing images. It’s little more user friendly and provides more readable output.</li></ul><h3>Install podman, buildah, and skopeo</h3><p>Now, it’s time for hands-on. Let’s install these three tools from Red Hat. Please note that here I tried on Ubuntu 18.04 so the steps may be different on other distros.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/21c2c57d03b3d67dd57001c80e192cdd/href">https://medium.com/media/21c2c57d03b3d67dd57001c80e192cdd/href</a></iframe><p>In order to map container ports to host ports, you also need <a href="https://github.com/rootless-containers/slirp4netns">slirp4netns</a>. It must be found within PATH variable so check it with command which slirp4netns. If not found, install using the script below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ac78302d93954af92da0f1be620ec6f9/href">https://medium.com/media/ac78302d93954af92da0f1be620ec6f9/href</a></iframe><p>Add it to PATH variable, if you haven’t done so.</p><h3>Testing podman</h3><p>Run a new httpd container and forward port 8080 to host.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9c1500241d0af12a01a3509773278cb1/href">https://medium.com/media/9c1500241d0af12a01a3509773278cb1/href</a></iframe><p>If you get the error ERRO[0001] unable to write pod event: &quot;write unixgram @00018-&gt;/run/systemd/journal/socket: sendmsg: no such file or directory&quot;, you seem to run podman in <a href="https://aka.ms/wsl2">WSL2</a>. Then you need to use the flag --events-backend=file to suppress this error.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a0cee962a8deb82d142700707cc67e37/href">https://medium.com/media/a0cee962a8deb82d142700707cc67e37/href</a></iframe><blockquote>Note: Podman will search in default registries if you don’t specify full image name. The default registries are defined in <em>/etc/containers/registries.conf</em>. You can use command <em>podman info</em> to see the list of registries.</blockquote><p>Check container status.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d873b2dd3fc6e9aaa0a4bf0d1bed9909/href">https://medium.com/media/d873b2dd3fc6e9aaa0a4bf0d1bed9909/href</a></iframe><p>Try accessing the application at <a href="http://localhost:8080">http://localhost:8080</a>.</p><figure><img alt="Fedora test page" src="https://cdn-images-1.medium.com/max/1024/1*UJrJwxeE0be6188XMhp7Wg.png" /><figcaption>Fedora Test Page — image by <a href="https://medium.com/u/c349bdf2ae99">Chairat Onyaem (Par)</a></figcaption></figure><h3>Testing buildah</h3><p>Clone repository <a href="https://github.com/pacroy/flask-app.git">https://github.com/pacroy/flask-app</a> and use buildah to build the image from the Dockerfile.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ae0f839dd14f352a8e5d9c9ebd24aa22/href">https://medium.com/media/ae0f839dd14f352a8e5d9c9ebd24aa22/href</a></iframe><p>List local images.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5661e92bf51437925b84022ad4c4bacc/href">https://medium.com/media/5661e92bf51437925b84022ad4c4bacc/href</a></iframe><p>Run the container</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8b8cbc60584882a1a6ddd16aef7820c4/href">https://medium.com/media/8b8cbc60584882a1a6ddd16aef7820c4/href</a></iframe><p>Try accessing the application at <a href="http://localhost:8080">http://localhost:5000</a>.</p><figure><img alt="Simple Flask app (Cat GIF of the day)" src="https://cdn-images-1.medium.com/max/945/1*eRSObJY6EgIdYDO-TGQCBw.png" /><figcaption>Simple Flask app (Cat GIF of the day) — image by <a href="https://medium.com/u/c349bdf2ae99">Chairat Onyaem (Par)</a></figcaption></figure><h3>Testing skopeo</h3><p>You can inspect properties or configuration of an image on a remote repository using skopeo.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6e3309285e26f65ceb284be725ec99ff/href">https://medium.com/media/6e3309285e26f65ceb284be725ec99ff/href</a></iframe><blockquote>Note: If you don’t have jq installed, you can download it from <a href="https://stedolan.github.io/jq/">https://stedolan.github.io/jq/</a> and add it to PATH variable.</blockquote><p>You can also inspect a local image.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7145e63a733123aaa3ee008fc8665a2d/href">https://medium.com/media/7145e63a733123aaa3ee008fc8665a2d/href</a></iframe><p>Next, we will push the built image to Docker.io. Create a new repository on Docker Hub first if you haven’t done so. Login and push the image, just like you use Docker.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0959df60d289fe8a0e48e490b0de1a4c/href">https://medium.com/media/0959df60d289fe8a0e48e490b0de1a4c/href</a></iframe><p>Then, we will copy the image from Docker.io to Quay.io. Create a new repository on quay.io first and then copy the image from docker.io.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9e298498e76c78bac13eaa065959578e/href">https://medium.com/media/9e298498e76c78bac13eaa065959578e/href</a></iframe><blockquote>Note: You may want to use access tokens (aka. robot account in quay.io) instead of password.</blockquote><p>Now, I hope you see that we can use podman, buildah, and skopeo for working with containers and images like we use Docker. There are also some useful features that does not even exist in Docker CLI that you may love.</p><p>Next time, when you feel overwhelm with Docker installation then now you know an alternative option.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c45a0e9394b0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Get Kubernetes+Docker Container Platform on Azure]]></title>
            <link>https://pacroy.medium.com/get-kubernetes-docker-container-platform-on-azure-d47b631f3dca?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/d47b631f3dca</guid>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Sat, 21 Apr 2018 16:18:56 GMT</pubDate>
            <atom:updated>2019-02-17T12:57:20.884Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zglF8TsXYDVeBsaacbWzfw.jpeg" /><figcaption>A man standing on top of a mountain — <a href="https://www.pexels.com/@le-ngoc-tan-342723">Le Ngoc Tan</a></figcaption></figure><blockquote>Updated Jan 2019: Azure Container Service (ACS) has been replaced by Azure Kubernetes Service (AKS). Please refer to <a href="https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal">this guide</a> on how to easily create an AKS cluster.</blockquote><h4>Table of Contents</h4><ul><li><a href="https://medium.com/p/d47b631f3dca#9747">1. Create Application ID</a></li><li><a href="https://medium.com/p/d47b631f3dca#6661">2. Deploy Azure Container Service</a></li><li><a href="https://medium.com/p/d47b631f3dca#7498">3. Connect to the Master</a></li><li><a href="https://medium.com/p/d47b631f3dca#23d0">References</a></li></ul><h3>1. Create Application ID</h3><p>1) Go to Azure Active Directory → App registrations and click New application registration</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/792/1*pySKb7La88o3iJNQxVogCQ.png" /></figure><p>2) Name your app and URL then click Create</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/322/1*dYyKySETSHZ8d5X8EX8BeA.png" /></figure><p>3) Note the Application ID and click Settings</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/589/1*VQIX2B6W9NsnVk8xmltLqw.png" /></figure><p>4) Go to Keys. Fill in key description and expires then click Save</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*CbNmOa-YhE_XMLTvM7CtXw.png" /></figure><p>5) Note the secret key generated</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/701/1*rhTN8k72eWL7xENvyQ894A.png" /></figure><p>6) Go to your Subscription → Access control (IAM) and click Add</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NEArSwG3lYoNsckNnlQmRQ.png" /></figure><p>7) Add your new app with role at least Contributor</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/438/1*RAxWVJLbQdaxcAQC4v49NQ.png" /></figure><h3>2. Deploy Azure Container Service</h3><p>1) Go to Marketplace and search for ACS. Select and click Create.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/585/1*YJBERiMTsqiFO7guI6xI1Q.png" /></figure><p>2) Name your service and resource group</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/629/1*C6pAM62L8a9LyXXuJyB0fA.png" /></figure><p>3) Select Kubernetes as the orchestrator. Complete other fields and fill Application ID in Service principal client ID and Secret key in client secret.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/635/1*qgTyHqerE6ayD34ABjCjbg.png" /></figure><p>4) Select agent size. (This size is only for agents but not the master so you may need to resize the master after deploy)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FREvUpRSpyV-pnFbt5Uzbg.png" /></figure><p>5) On the summary page, click Deploy. This may take ~15–20 minutes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/906/1*z_c-YEPL9nYUj2cLtJRkSw.png" /></figure><p>6) After the deployment is done, here are the resources you get:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/608/1*SwU1DIneG33eNSusmfoppQ.png" /></figure><h3>3. Connect to the Master</h3><p>1) Select the master VM and click Connect to see SSH command.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1007/1*c7NFtIv6vGq6too9Blx3jA.png" /></figure><p>2) Once connected, try to see the kubectl’s version using kubectl version</p><pre>$ kubectl version<br>Client Version: version.Info{Major:&quot;1&quot;, Minor:&quot;7&quot;, GitVersion:&quot;v1.7.7&quot;, GitCommit:&quot;8e1552342355496b62754e61ad5f802a0f3f1fa7&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2017-09-28T23:56:03Z&quot;, GoVersion:&quot;go1.8.3&quot;, Compiler:&quot;gc&quot;, Platform:&quot;linux/amd64&quot;}<br>Server Version: version.Info{Major:&quot;1&quot;, Minor:&quot;7&quot;, GitVersion:&quot;v1.7.7&quot;, GitCommit:&quot;8e1552342355496b62754e61ad5f802a0f3f1fa7&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2017-09-28T23:56:03Z&quot;, GoVersion:&quot;go1.8.3&quot;, Compiler:&quot;gc&quot;, Platform:&quot;linux/amd64&quot;}</pre><h3>References</h3><ul><li><a href="https://docs.microsoft.com/en-us/azure/container-service/kubernetes/container-service-kubernetes-service-principal">Service principal for Azure Kubernetes cluster</a></li><li><a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal">Create identity for Azure app in portal</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d47b631f3dca" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setup Web Terminal using Wetty Docker Image]]></title>
            <link>https://pacroy.medium.com/setup-web-terminal-using-wetty-docker-image-dcb1ea75bfaf?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/dcb1ea75bfaf</guid>
            <category><![CDATA[dockerfiles]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[wetty]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[ubuntu]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Fri, 23 Mar 2018 15:48:37 GMT</pubDate>
            <atom:updated>2018-07-03T06:03:27.138Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0DkXApx5kdi95Cf_CPRoWw.jpeg" /></figure><h3>Introduction</h3><p>SSH is the way to connect and command your Linux machine. Doing this from Windows is even more painful as you need a client like <a href="https://www.putty.org/">Putty</a> installed (Or <a href="https://www.howtogeek.com/336775/how-to-enable-and-use-windows-10s-built-in-ssh-commands/">enable built-in SSH if you’re using Windows 10</a>).</p><p>What if you can do it right from your web browser from any OS without installing anything!?!</p><h3>What is Wetty?</h3><blockquote>Wetty = Web + tty.</blockquote><blockquote>Terminal over HTTP and HTTPS. Wetty uses ChromeOS’ terminal emulator (hterm) which is a full fledged implementation of terminal emulation written entirely in Javascript. Also it uses websockets instead of Ajax and hence better response time.</blockquote><p>Source: <a href="https://github.com/krishnasrinivas/wetty">https://github.com/krishnasrinivas/wetty</a></p><h3>Prerequisite</h3><ul><li>Ubuntu Server with Docker installed (See <a href="https://medium.com/pacroy/setting-up-docker-on-azure-with-ubuntu-server-4c148deb1108">this blog</a> on how to get one on Azure)</li></ul><h3>Installation</h3><ol><li>Clone <a href="https://github.com/krishnasrinivas/wetty">Wetty repository on GitHub</a>.</li></ol><pre>$ git clone <a href="mailto:git@github.com">git@github.com</a>:krishnasrinivas/wetty.git</pre><p>2. Edit Dockerfile</p><pre>$ vim Dockerfile</pre><p>You will see the something like this:</p><pre>FROM node:0.10.38<br>MAINTAINER Nathan LeClaire &lt;<a href="mailto:nathan@docker.com">nathan@docker.com</a>&gt;</pre><pre>ADD . /app<br>WORKDIR /app<br>RUN npm install<br>RUN apt-get update<br>RUN apt-get install -y vim<br>RUN useradd -d /home/term -m -s /bin/bash term<br>RUN echo &#39;term:term&#39; | chpasswd</pre><pre>EXPOSE 3000</pre><pre>ENTRYPOINT [&quot;node&quot;]<br>CMD [&quot;app.js&quot;, &quot;-p&quot;, &quot;3000&quot;]</pre><p>This tells us that:</p><ul><li>Wetty is a Node.js application.</li><li>It creates a new user term which we can use to logon.</li><li>The web interface is exposed at port 3000</li></ul><h4>Setup HTTPS — Always use HTTPS!</h4><p>3. If you don’t have SSL certificates from a CA you can<br>create a self signed certificate using this command:</p><pre>$ openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 30000 -nodes</pre><p>You need to fill in some information. Better to put common name with your domain name you want to publish your web terminal e.g. terminal.yourdomain.com</p><pre>Country Name (2 letter code) [AU]:<strong>TH</strong><br>State or Province Name (full name) [Some-State]:<strong>Bangkok</strong><br>Locality Name (eg, city) []:<br>Organization Name (eg, company) [Internet Widgits Pty Ltd]:D<strong>emo Company</strong><br>Organizational Unit Name (eg, section) []:<br>Common Name (e.g. server FQDN or YOUR name) []:<strong>terminal.yourdomain.com</strong><br>Email Address []:<a href="mailto:admin@yourdomain.com"><strong>admin@yourdomain.com</strong></a></pre><p>4. Update entry point in Dockerfile to include certificates.</p><pre>CMD [&quot;app.js&quot;, &quot;--sslkey&quot;, &quot;key.pem&quot;, &quot;--sslcert&quot;, &quot;cert.pem&quot;, &quot;-p&quot;, &quot;3000&quot;]</pre><p>5. Copy private key id_rsa you normally use to connect to your Ubuntu server and add these lines to Dockerfile.</p><pre>RUN mkdir /home/term/.ssh<br>COPY id_rsa /home/term/.ssh/id_rsa<br>RUN chmod 600 /home/term/.ssh/id_rsa &amp;&amp; chown -Rf term /home/term/.ssh</pre><p>6. Update password as you wish.</p><pre>RUN echo &#39;term:yourpassword&#39; | chpasswd</pre><p>At the end, you Dockerfile may look like this:</p><pre>FROM node:0.10.38<br>MAINTAINER <a href="mailto:pacroy@gmail.com">admin@yourdomain.com</a></pre><pre>ADD . /app<br>WORKDIR /app<br>RUN npm install<br>RUN apt-get update<br>RUN apt-get install -y vim<br>RUN useradd -d /home/term -m -s /bin/bash term<br>RUN echo &#39;term:yourpassword&#39; | chpasswd<br>RUN mkdir /home/term/.ssh<br>COPY id_rsa /home/term/.ssh/id_rsa<br>RUN chmod 600 /home/term/.ssh/id_rsa &amp;&amp; chown -Rf term /home/term/.ssh</pre><pre>EXPOSE 3000</pre><pre>ENTRYPOINT [&quot;node&quot;]<br>CMD [&quot;app.js&quot;, &quot;--sslkey&quot;, &quot;key.pem&quot;, &quot;--sslcert&quot;, &quot;cert.pem&quot;, &quot;-p&quot;, &quot;3000&quot;]</pre><h3>Build Your Docker Image</h3><pre>$ sudo docker build -t yourusername/wetty .</pre><p>Wait until it finishes, like this:</p><pre>$ sudo docker build -t demo/wetty .<br>Sending build context to Docker daemon  1.042MB<br>Step 1/15 : FROM node:0.10.38<br> ---&gt; 82073591bd0c<br>Step 2/15 : MAINTAINER <a href="mailto:admin@yourdomain.com">admin@yourdomain.com</a><br> ---&gt; Using cache<br> ---&gt; 067a8d078ccb<br>Step 3/15 : ADD . /app<br> ---&gt; ccd418e158ee<br>Step 4/15 : WORKDIR /app<br>Removing intermediate container 514c8c3f5511<br> ---&gt; d8611af96ced<br>Step 5/15 : RUN npm install<br> ---&gt; Running in e635ea2d39be<br>.<br>.<br>.<br>Step 13/15 : EXPOSE 3000<br> ---&gt; Running in f246b51e0a0f<br>Removing intermediate container f246b51e0a0f<br> ---&gt; 39b74e2eda59<br>Step 14/15 : ENTRYPOINT [&quot;node&quot;]<br> ---&gt; Running in 4fa38e6c2d19<br>Removing intermediate container 4fa38e6c2d19<br> ---&gt; ee6c549f88ca<br>Step 15/15 : CMD [&quot;app.js&quot;, &quot;--sslkey&quot;, &quot;key.pem&quot;, &quot;--sslcert&quot;, &quot;cert.pem&quot;, &quot;-p&quot;, &quot;3000&quot;]<br> ---&gt; Running in 26c1f3817918<br>Removing intermediate container 26c1f3817918<br> ---&gt; f69d767e6c92<br>Successfully built f69d767e6c92<br>Successfully tagged demo/wetty:latest</pre><h3>Spin Up a Container</h3><pre>$ sudo docker run --name termdemo -p 3000:3000 -dt demo/wetty</pre><h3>Test Your Wetty</h3><p>Go to your domain e.g. <a href="https://terminal.yourdomain.com:3000">https://terminal.yourdomain.com:3000</a> and log in with id term and its password.</p><p>Then, try SSH to your Ubuntu host.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/832/1*-FOPIOyTis8CIvVtmj8ksw.png" /></figure><h3>Tips</h3><p>To change the login timeout, edit file /etc/login.defs and look for LOGIN_TIMEOUT. (<a href="https://github.com/krishnasrinivas/wetty/issues/100">Reference</a>)</p><h3>References</h3><p><a href="https://github.com/krishnasrinivas/wetty">krishnasrinivas/wetty</a></p><p><a href="https://hub.docker.com/r/krishnasrinivas/wetty/">https://hub.docker.com/r/krishnasrinivas/wetty/</a></p><h3>Related</h3><p><a href="https://medium.com/pacroy/setting-up-docker-on-azure-with-ubuntu-server-4c148deb1108">Setting up Docker on Azure with Ubuntu server</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dcb1ea75bfaf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Application Telemetry with Prometheus]]></title>
            <link>https://pacroy.medium.com/application-telemetry-with-prometheus-sap-blogs-c4b5b6239d28?source=rss-c349bdf2ae99------2</link>
            <guid isPermaLink="false">https://medium.com/p/c4b5b6239d28</guid>
            <category><![CDATA[prometheus]]></category>
            <category><![CDATA[abap]]></category>
            <category><![CDATA[monitoring]]></category>
            <category><![CDATA[sap]]></category>
            <category><![CDATA[grafana]]></category>
            <dc:creator><![CDATA[Chairat Onyaem (Par)]]></dc:creator>
            <pubDate>Sun, 07 Jan 2018 00:00:00 GMT</pubDate>
            <atom:updated>2018-01-08T14:17:05.097Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*lTor0aQsn4jy2AMx.jpg" /></figure><p>In <a href="https://blogs.sap.com/2017/11/11/continuous-integration-in-abap-using-jenkins/">my last blog</a>, I’ve explained about deployment pipeline I built for Continuous Integration and Continuous Delivery in ABAP. Another thing that our team has built was the application monitoring.</p><p>For our Java Spring Boot services, it can seamlessly integrate <a href="https://prometheus.io/">Prometheus</a> module in the POM file and then your application is ready to be monitored with built-in metrics.</p><p>I managed to create Prometheus client for ABAP. I will explain how you can setup one for yours below.</p><h3>What is Prometheus?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*NbeGadphf7smctUk.png" /></figure><p>Prometheus is a time series database. It stores your data stream and it also has a web interface so you can query and visualize the data e.g. into a graph.</p><p>Imagine a temperature measured from a thermometer at regular interval. This is a time series data. In IT operations, this can be like CPU usage, memory allocated, etc. But in our case, we just want to monitor our application.</p><p>The concept I like is that the server will poll the data from applications instead of getting applications sending the data to the server. This ensures that monitoring will never break your application. If the monitoring server down, your applications are still running fine.</p><p>Visit <a href="https://prometheus.io/">their website</a> to learn more.</p><h3>Why do we need to monitor our application?</h3><p>Well, have you ever wondered about the feature that you build, how much is it used by the users? How fast or slow is it? What if you want to experiment which button color would attract the customer better?</p><p>To answer these questions, you need some kind of monitoring, some kind of metric that you can measure.</p><h3>Let’s start!</h3><p>In this example, I will show you how we can monitor usage and response time of <a href="https://github.com/pacroy/abap-rest-api">ABAP HelloWorld RESTful APIs</a>.</p><h3>Cloning repository</h3><p>If you have a problem cloning the class and Shared Memory Area ZCL_SHR_PROMETHEUS_AREA then you can create it on your own as this class is automatically generated from the transaction SHMA. Make sure you configure it precisely as shown below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/604/0*8Q4WyxDisIsk0Qfk.png" /></figure><h3>Recording metrics in your application</h3><p>Prepare runtime for duration metric.</p><p>Create a new instance attribute for the timer instance.</p><pre>DATA runtime TYPE REF TO if_abap_runtime.</pre><p>In the constructor, create the timer instance.</p><pre>METHOD constructor. <br>  super-&gt;constructor( ). <br>  me-&gt;runtime = cl_abap_runtime=&gt;create_hr_timer( ). <br>ENDMETHOD.</pre><p>Create a new method in the REST resource class (ZCL_REST_RESOURCE).</p><pre>METHODS record_metric <br>  IMPORTING <br>    i_method TYPE string <br>    i_response TYPE i.</pre><p>The method accepts HTTP method and response status code which we will put in the metrics.</p><pre>METHOD record_metric. <br>  TRY. <br>      zcl_prometheus=&gt;set_instance_from_request( me-&gt;mo_request ).     <br>      zcl_prometheus=&gt;write_multiple( VALUE #( <br>        ( key = |hello_count\{method=&quot;{ i_method }&quot;,status=&quot;{ i_response }&quot;\}| value = &#39;1&#39; command = zif_prometheus=&gt;c_command-increment ) <br>        ( key = |hello_duration\{method=&quot;{ i_method }&quot;,status=&quot;{ i_response }&quot;\}| value = me-&gt;runtime-&gt;get_runtime( ) ) ) ). <br>    CATCH cx_root. <br>  ENDTRY.<br>ENDMETHOD.</pre><p><em>set_instance*</em> methods will set the Shared Memory Area instance that you can see in the transaction SHMM. So you can use one instance for each application and the metrics can be kept and queried separately.</p><p><em>set_instance_from_request</em> will set the instance from the root endpoint by default (i.e. hello) if not explicitly specified by <em>instance</em> attribute or query parameter.</p><p>There are three fields you need to pass when you want to record metrics (via <em>write*</em> methods)</p><ul><li><strong>key</strong> is the metric name and its label according to <a href="https://prometheus.io/docs/concepts/data_model/">Prometheus data model and metric naming convention</a>.</li><li><strong>value</strong> is the value to record</li><li><strong>command</strong> is optional and is ‘overwrite’ by default. Using increment will add <em>value</em> to the current so you don’t need to read and write on your own.</li></ul><p>Surround the method with try…catch… so this codes will (almost) never break your application.</p><p>Then you call this method at the end of each REST handler. Don’t forget to start the timer at the beginning.</p><p>For example:</p><pre>METHOD if_rest_resource~get.<br>  me-&gt;runtime-&gt;get_runtime( ).<br>  .<br>  .<br>  . <br>  record_metric( i_method = mo_request-&gt;get_method( ) i_response = cl_rest_status_code=&gt;gc_success_ok ).<br>ENDMETHOD.</pre><h3>Creating the metrics endpoint</h3><p>Next, you need an endpoint for Prometheus server to call and grab the metrics data.</p><p>In REST handler class (ZCL_REST_HANDLER), insert the following routing string in the method if_rest_application~get_root_handler.</p><pre>lo_router-&gt;attach( iv_template = &#39;/hello/metrics&#39; iv_handler_class = zcl_prometheus_rest_resource=&gt;c_class_name ).</pre><h3>Testing your endpoint</h3><p>Open your browser and open your /hello/metrics endpoint. You should see a blank page.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/0*woModx2WgHVHhJp7.png" /></figure><p>Try using your application so the metrics are recorded.</p><p>In case you’re using <a href="https://www.getpostman.com/">Postman</a> to test the APIs you can import my Postman files from <a href="https://github.com/pacroy/abap-rest-api/tree/master/postman">here</a>.(Please note Postman is a commercial product but there’s a free version available)</p><p>After one GET and one POST request, refresh the metrics page and you should start seeing the data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/591/0*wRAj-DOv7qENsBee.png" /></figure><p>Now, your application is ready to be monitored</p><h3>Setting up Prometheus Server</h3><p>Download Prometheus from <a href="https://prometheus.io/download/">this page</a>. Extract the package and you can run the executable without installing.</p><p>Note: If you want to install as a window service, you can use <a href="https://nssm.cc/download">NSSM</a>.</p><p>Open browser and go to <a href="http://localhost:9090/">http://localhost:9090</a>. and you should see its web UI.</p><h3>Adding a new job to monitor your application</h3><p>Now, your Prometheus server does not yet recognize your application so you need to configure it first.</p><p>Edit the file prometheus.yml and add the following lines:</p><pre> - job_name: npl-test<br>   params:<br>     sap-client: [&#39;001&#39;]<br>     sap-language: [&#39;EN&#39;]<br>   metrics_path: /test/hello/metrics<br>     basic_auth:<br>       username: DEVELOPER<br>       password: yourpassword<br>   static_configs:<br>     - targets:<br>       - vhcalnplci.dummy.nodomain:8000</pre><p>In your secured environment, you may want to use HTTPS like this:</p><pre>  - job_name: npl-test<br>    <br>    scheme: https<br>    params:<br>      sap-client: [&#39;001&#39;]<br>      sap-language: [&#39;EN&#39;]<br>    metrics_path: &#39;/test/hello/metrics&#39;<br>    basic_auth:<br>      username: DEVELOPER<br>      password: yourpassword<br>    tls_config:<br>      insecure_skip_verify: true<br>    static_configs:<br>      - targets: [&#39;vhcalnplci.dummy.nodomain:44300&#39;]</pre><p>Please note that you should add one job for each application server. Don’t use load balance URL as the metrics are bound for each application server. Go to transaction SM51 to see the list of all application servers on the system.</p><p>After saving, restart the service and access the web UI. Query for <em>up</em> metric and click <em>Execute</em>. If your job is setup properly, you should see the value 1.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MXZVuI1cJwJktHYZ.png" /></figure><p>You may try querying your application metric e.g. <em>hello_count</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mPn4Yi1nGKqWcc0L.png" /></figure><h3>Setting up Grafana</h3><p>Prometheus is good at collecting and querying time series data but to have a better a visualization you may need <a href="https://grafana.com/">Grafana</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EHAqAYBz7UwKLuv3.png" /><figcaption>Grafana Sample Dashboard</figcaption></figure><h3>What is Grafana?</h3><p>Grafana is a tool for data visualization &amp; monitoring with support for Graphite, InfluxDB, Prometheus, Elasticsearch and many more databases. In short, Grafana will pull the data from Prometheus and visualize them on their dashboard web interface.</p><p>We setup a desktop PC with two monitoring screens and put it where the team can see easily.</p><h3>Installation</h3><p>Download from <a href="https://grafana.com/get">here</a>. Extract and run it the same way you do for Prometheus.</p><h3>Setting up data source</h3><p>First, we need to setup Grafana to recognize Prometheus server.</p><p>Go to <a href="http://localhost:3000/">http://localhost:3000</a> and log in with default username and password (i.e. admin:admin). Select <em>Data Source</em> from the menu and click <em>Add Data Source</em>. Fill in the connection to your Prometheus server like below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/563/0*aD8ITjlFd9iN3VJY.png" /></figure><h3>Setting up dashboard</h3><p>We’re going to setup 2 graphs to monitor API usage count and response time.</p><p>Select <em>Dashboards</em> from the menu, click <em>Home</em> and click + New Dashboard.</p><p>Select <em>Graph</em>. Click the graph title and select <em>Edit</em>.</p><p>On tab <em>General</em>, name your graph title as you wish.</p><p>On tab Metrics, you will specify which metric data will be displayed on this graph. Fill in hello_count and put {{method}} ({{status}}) in <em>Legend format</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/430/0*cxmMpiKWShsdkLfD.png" /></figure><p>On tab <em>Axes</em>, you can customize the graph axes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/509/0*FVMfmqdPfhrl0Nsb.png" /></figure><p>Once done customizing, click <em>Back to dashboard</em> on the top-right.</p><p>Click + ADD ROW to add a new row and create a new graph.</p><p>Configure tab <em>Metrics</em> with hello_duration with the same Legend format.</p><p>Configure tab Axes like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/0*MPMYUm-PuetjC-sC.png" /></figure><p>Once done, you should see your dashboard like this.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/943/0*tguyv1Rdb1oiC8J-.png" /></figure><p>Don’t forget to set the time range and refresh rate so your monitor screen keeps refreshing with the latest data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/913/0*k6ZOQfVx0YUY61Q9.png" /></figure><p>Try to use your application and see how the graph reflect.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JkjSXdVjtxkp7B5A.png" /></figure><p><em>Originally published at </em><a href="https://blogs.sap.com/2018/01/07/application-telemetry-with-prometheus/"><em>blogs.sap.com</em></a><em> on January 7, 2018.</em></p><h3>Read More</h3><p><a href="https://medium.com/pacroy/continuous-integration-in-abap-using-jenkins-9c45372a90e8">Continuous Integration in ABAP using Jenkins</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c4b5b6239d28" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>