<?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 Cagri Ersen on Medium]]></title>
        <description><![CDATA[Stories by Cagri Ersen on Medium]]></description>
        <link>https://medium.com/@cagri.ersen?source=rss-c815b7843f78------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*nT7AWZm8m135KYfrOFiRRw.jpeg</url>
            <title>Stories by Cagri Ersen on Medium</title>
            <link>https://medium.com/@cagri.ersen?source=rss-c815b7843f78------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 09:11:29 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@cagri.ersen/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[Update: Zabbix v6.0 and Grafana Automated Installation Script]]></title>
            <link>https://medium.com/@cagri.ersen/update-zabbix-v6-0-and-grafana-automated-installation-script-a40241ea38c6?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/a40241ea38c6</guid>
            <category><![CDATA[zabbix]]></category>
            <category><![CDATA[grafana]]></category>
            <category><![CDATA[monitoring]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Tue, 03 Oct 2023 17:14:38 GMT</pubDate>
            <atom:updated>2023-10-04T07:42:54.361Z</atom:updated>
            <content:encoded><![CDATA[<p>I‘ve updated automated Zabbix and Grafana Server installation script to support Zabbix 6.0 LTS and Grafana 10.1.4.</p><p>As mentioned the original post at <a href="https://medium.com/faun/zabbix-v5-0-and-grafana-automated-installation-script-6cd1a87cc36a">https://medium.com/faun/zabbix-v5-0-and-grafana-automated-installation-script-6cd1a87cc36a</a>, you can use it to install the latest LTS version of the Zabbix on Ubuntu 20.04, 22.04 and CentOS 7</p><p>For more information and installation instructions, check out the repositoy: <a href="https://bitbucket.org/secopstech/zabbix-server/src/master/">https://bitbucket.org/secopstech/zabbix-server/src/master/</a></p><p>Any bug reports, ideas and feature requests are welcome.<br><a href="https://bitbucket.org/secopstech/zabbix-server/issues?status=open">https://bitbucket.org/secopstech/zabbix-server/issues</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a40241ea38c6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Zabbix v5.0 and Grafana Automated Installation Script]]></title>
            <link>https://faun.pub/zabbix-v5-0-and-grafana-automated-installation-script-6cd1a87cc36a?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/6cd1a87cc36a</guid>
            <category><![CDATA[monitoring]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[zabbix]]></category>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[installation]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Sun, 18 Oct 2020 14:52:58 GMT</pubDate>
            <atom:updated>2020-10-21T19:16:28.211Z</atom:updated>
            <cc:license>http://creativecommons.org/publicdomain/zero/1.0/</cc:license>
            <content:encoded><![CDATA[<p>Since it’s one of my favorite monitoring solution, I have decided to write a shell script to install a dockerized <a href="https://www.zabbix.com/">Zabbix</a> server from scratch in a fully automated manner. And now the first version is published on <a href="https://bitbucket.org/secopstech/zabbix-server/">https://bitbucket.org/secopstech/zabbix-server/</a></p><p>It’s a simple shell (<strong>bash</strong>) script that installs <strong>docker</strong> (ce), docker-compose, and <strong>jq</strong> packages and deploys the Zabbix containers from <strong>the official Zabbix Docker images</strong> by using <strong>docker-compose</strong>.</p><p>Also, in order to bring better data visualisation support to Zabbix, the script deploys a grafana (7.2.1) service from the official docker image and imports some useful dashboards in it.</p><p>Zabbix server uses a MySQL container as a DB backend. Docker-compose creates two separate host volumes, for MySQL and grafana containers in order to hold their data on the local host permanently. So, as long as you do not delete the docker volumes, Zabbix and grafana data will be persistent.</p><p>The installation script invokes Zabbix and grafana <strong>API</strong>s via curl to do all customizations/configurations like creating additional Zabbix items/triggers/host groups and grafana integration etc.</p><p>Here is a complete list of actions that performed by the script:</p><ul><li>Installs docker (ce), docker-compose, and jq.</li><li>Creates self-signed SSL certificates for Zabbix and grafana frontends.</li><li>Deploys Zabbix services via docker-compose.</li><li>Adds a bunch of hosts group to Zabbix.</li><li>Creates auto registration actions for Linux and Windows hosts.</li><li>Creates an item to show free usage in percentage for Linux and Windows hosts.</li><li>Adds a free memory in percent check for Windows hosts.</li><li>Changes filesystem and netif LLD rules intervals to 30minutes for Linux and Windows hosts.</li><li>Disables annoying Windows service items LLD rules. (If you want to get a lot of meaningless Windows service alert, just enable it.)</li><li>Adds alexanderzobnin-zabbix-app datasource plugin to grafana.</li><li>Adds briangann-gauge-panel, btplc-trend-box-panel and jdbranham-diagram-panel plugins to grafana.</li><li>Invokes the grafana API to add custom dashboards for Linux and Windows hosts. (an overall system status dashboard will be also imported.)</li><li>Sets email notification for admin user. (Optional)</li><li>Sets slack notification for admin user. (Optional)</li></ul><h3>Usage</h3><p>To invoke the Zabbix server installation, execute the “<strong>zabbix-server</strong>” script with “<strong>init</strong>” parameter, and follow the instructions.</p><pre># bash scripts/zabbix-server init</pre><p><strong>Note</strong>: You can find out a complete output of an example execution at the project README file.</p><h3>Accessing Zabbix and Grafana UIs</h3><p>Zabbix UI is accessible via <a href="https://host-ip:8443/">https://host-ip:8443</a> and the default username and password is <strong>Admin/Zabbix</strong></p><p>Also, grafana UI is on <a href="https://host-ip:3000/">https://host-ip:3000</a> with <strong>admin/zabbix</strong></p><p>Both services use https with a self-signed certificate.</p><h3>Screenshots</h3><p>These three graphs listed below are from the “<strong>Linux Server Dashboard</strong>” that imported by the script. (There is also another dashboard for Windows hosts with the same layout.) This is a ready-to-go dashboard so all metrics are collected automatically. For example, any network interfaces and disk devices will be discovered and showed up on the dashboard.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vaKWn3xcvoJXqz6dwwbIOQ.png" /><figcaption>Insight Panel</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nuccBP9KIGsFHVLq7Y4MCw.png" /><figcaption>Cpu and Memory Histograms</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nJx_rAZPndbL_gHgvLTZQA.png" /><figcaption>Filesystems and Network Histograms</figcaption></figure><p>Another dashboard which also imported is “<strong>Zabbix System Status</strong>” that shows the overall status of your infrastructure.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gEhod48L1IoyEnuPZ8Yd-Q.png" /><figcaption>Overall status</figcaption></figure><p>And this is the custom alert messages layout to send Zabbix admins’ notification channels.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JdG8qJrvQO02ycOTasqRow.png" /><figcaption>Slack Notifications</figcaption></figure><p>Any improvement ideas or feature requests are welcome.<br><a href="https://bitbucket.org/secopstech/zabbix-server/issues?status=new&amp;status=open">https://bitbucket.org/secopstech/zabbix-server/issues</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/727/0*Piks8Tu6xUYpF4DU" /></figure><p>👋 <a href="https://faun.dev/join"><strong>Join FAUN today and receive similar stories each week in your inbox!</strong></a><strong> </strong>️ <strong>Get your weekly dose of the must-read tech stories, news, and tutorials.</strong></p><p><strong>Follow us on </strong><a href="https://twitter.com/joinfaun"><strong>Twitter</strong></a><strong> </strong>🐦<strong> and </strong><a href="https://www.facebook.com/faun.dev/"><strong>Facebook</strong></a><strong> </strong>👥<strong> and </strong><a href="https://instagram.com/fauncommunity/"><strong>Instagram</strong></a><strong> </strong>📷 <strong>and join our </strong><a href="https://www.facebook.com/groups/364904580892967/"><strong>Facebook</strong></a><strong> and </strong><a href="https://www.linkedin.com/company/faundev"><strong>Linkedin</strong></a><strong> Groups </strong>💬</p><figure><a href="https://www.faun.dev/join?utm_source=medium.com/faun&amp;utm_medium=medium&amp;utm_campaign=faunmediumbanner"><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_cT0_laE4iPcqW1qrbstAg.gif" /></a></figure><h4>If this post was helpful, please click the clap 👏 button below a few times to show your support for the author! ⬇</h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6cd1a87cc36a" width="1" height="1" alt=""><hr><p><a href="https://faun.pub/zabbix-v5-0-and-grafana-automated-installation-script-6cd1a87cc36a">Zabbix v5.0 and Grafana Automated Installation Script</a> was originally published in <a href="https://faun.pub/">FAUN.dev() 🐾</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rook: A Storage Orchestrator to Run Stateful Workloads on Kubernetes with Ceph]]></title>
            <link>https://medium.com/devopsturkiye/rook-a-storage-orchestrator-to-run-stateful-workloads-on-kubernetes-with-ceph-500882ecf005?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/500882ecf005</guid>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[rook]]></category>
            <category><![CDATA[orchestration]]></category>
            <category><![CDATA[ceph]]></category>
            <category><![CDATA[storage]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Mon, 08 Jun 2020 15:05:36 GMT</pubDate>
            <atom:updated>2020-06-22T22:01:04.201Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong>TL;DR</strong> <br>This article contains detailed informations about <strong>Rook</strong> and explains how to integrate a high available <strong>Ceph Storage Cluster</strong> to an existing kubernetes cluster by using it. If you know what rook is and just looking for a ceph implementation tutorial, skip the next section and jump to the “<strong>Deploying a Ceph Cluster with Rook</strong>” section.</blockquote><h3>Kubernetes and Data Persistence</h3><p>In the early versions of <strong><em>Kubernetes</em></strong>, we intended to use it for our stateless services as it was designed for these kind of workloads at the beginning. For those days, data persistence wasn’t a concern, which running stateful services that tightly coupled to data on Kubernetes wasn’t a common approach. (There was no durable local storage support in late 2015 yet. <a href="https://github.com/kubernetes/kubernetes/pull/1311">https://github.com/kubernetes/kubernetes/pull/1311</a>)</p><p>However, as Kubernetes becomes more popular platform, it started to provide more reliable features that changed our mindsets about scheduling stateful services on it. Currently, we are able to use many type of storage volumes including <strong>GCE Persistent Disk</strong>, <strong>AWS Elastic Block Store</strong>, <strong>vSphere Volume</strong>, <strong>Glusterfs</strong>, <strong>NFS</strong> and <strong>Ceph</strong>. So we‘re now more comfortable to run services that need robust storage backends.</p><p>Especially, if your Kubernetes environments are placed in a cloud provider such as <strong>Google Cloud Platform</strong> or <strong>Amazon Web Services</strong>, it will be easy to use a storage backend for any data persistence requirements, since all cloud providers offer their own reliable storage backends built-in. For instance if your k8s cluster is based on <strong>GKE</strong>, then your stateful services can be backed by <strong>GCE persistent disks</strong>.</p><p>But when it comes to on-premise Kubernetes environments, there are still some challenges as you know.</p><h3>On-premises Challenges</h3><p>For sure, it’s a bit more difficult to implement a storage backend in on-premise Kubernetes environments unlike the cloud providers. In such situations, you need to choose a production grade software defined storage solution, which should be seamlessly integratable to your existing environment and more it should be a robust, scalable, easy to manage platform.</p><p>Fortunately, there are many good solution that can be used as storage backend on Kubernetes environments. You can find out many of them, by googling “software defined storage for Kubernetes” keywords. However, integrating a software-defined-storage (SDS) solution to a Kubernetes environment is not an easy peasy task at all. As the storage backends are one of the most important components, they should be reliable; thus you need to implement them carefully for production use. In other words, you need to know what you’re doing; otherwise, it will be inevitable to be challenged by the day 2 problems.</p><p>Well, at this point, projects like <a href="https://rook.io/"><strong>Rook</strong></a>, that designed to seamlessly implement these kind of important components, become a very important aspects for cloud-native environments. As a storage orchestrator, Rook aims to handle all storage related tasks on Kubernetes environments for you. This is why it is hosted by <strong>Cloud Native Computing Foundation</strong> as an incubation-level project.</p><p>So, in this article, I’ll mention Rook’s features and this will also be a “how to” guide about building and integrating a <strong>Ceph Storage Cluster</strong> on top of a <strong>Kubernetes</strong>.</p><h3>What is Rook ?</h3><p>As it defines itself:</p><blockquote>“… an open source <strong>cloud-native storage orchestrator</strong>, providing the platform, framework, and support for a diverse set of storage solutions to natively integrate with cloud-native environments.</blockquote><blockquote>Rook turns storage software into self-managing, self-scaling, and self-healing storage services. It does this by automating deployment, bootstrapping, configuration, provisioning, scaling, upgrading, migration, disaster recovery, monitoring, and resource management. Rook uses the facilities provided by the underlying cloud-native container management, scheduling and orchestration platform to perform its duties.”</blockquote><p>So, you can use Rook to implement software defined storage building blocks for your Kubernetes environments in an automated manner. It runs as an operator in Kubernetes cluster and enables selected storage system to run on the same Kubernetes cluster. In this way, rook automatically configures a CSI (<a href="https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/">Container Storage Interface</a>) driver to mount storage spaces to your pods.</p><p>While, in this article I’ll explain that how to implement a high available Ceph storage system to a Kubernetes cluster for providing <strong>block storage</strong>, <strong>object storage</strong>, and <strong>shared filesystems</strong> mounts to pods, you can to take a look for other Storage provider options by visiting <a href="https://github.com/rook/rook/blob/master/README.md#project-status">https://github.com/rook/rook/blob/master/README.md#project-status</a></p><h3>Ceph Cluster on top of Kubernetes</h3><p>As you know, <a href="https://ceph.io/">Ceph</a> is a highly scalable distributed storage solution that has a very extensive usage on production infrastructures just as it defines itself:</p><blockquote>Ceph is for you! Ceph’s foundation is the Reliable Autonomic Distributed Object Store (RADOS), which provides your applications with <a href="http://ceph.com/ceph-storage/object-storage/"><strong>object</strong></a>, <a href="http://ceph.com/ceph-storage/block-storage/"><strong>block</strong></a>, and <a href="http://ceph.com/ceph-storage/file-system/"><strong>file system</strong></a> storage in a single unified storage cluster — making Ceph flexible, highly reliable and easy for you to manage.</blockquote><p>It can be implemented Kubernetes to provide storage for applications, however -as I mention above-, to achieve this, you need to know all aspects of Ceph’s dynamics to build a production ready cluster and you should make a reliable Kubernetes integration.</p><p>This is where Rook comes in… It’ll handle all Ceph installation and management tasks for us to provide <a href="https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/">Dynamic volume provisioning</a> for our applications.</p><h4>How it works ?</h4><p>Before we go further, it’s worth to mention how Rook integrates Ceph to Kubernetes clusters.</p><p>As you can see in the image below, it uses Kubernetes primitives to run on Kubernetes.</p><figure><img alt="Ceph rook architecture" src="https://cdn-images-1.medium.com/max/1024/1*L_Pah_PYuyKwV9UGVYSx_w.png" /></figure><p>So, you basically deploy Rook on your Kubernetes and it takes care of the rest to build, manage and monitor a Ceph cluster.</p><p>Typically, Rook uses <strong>custom resource definitions</strong> (<strong>CRD</strong>s) to create and customize storage clusters and these CRDs are implemented to Kubernetes during its deployment process. Also it has an operator within Kubernetes named <strong>Rook Operator for Ceph </strong>which automates configuration of storage components and monitors the cluster to ensure the storage remains available and healthy.</p><p>Rook, also runs a daemonset named <strong>rook-discover </strong>that start a <strong>discovery agent pod</strong> on every nodes of your Kubernetes cluster to discover any <strong>raw disk devices</strong> (no partitions or formatted filesystems) or <strong>raw partitions</strong> (no formatted filesystem) that can be used as <a href="https://rook.io/docs/rook/v1.3/ceph-osd-mgmt.html"><strong>Ceph OSD</strong></a> disk. If minimum required number of disks (for redundancy purposes should be at least 3) are discovered, rook operator uses these disks to build the Ceph Cluster. If you add more disks to your nodes, the operator discovers them via its agents and scales the ceph cluster automatically. As well Rook configures Ceph-CSI drivers to provide Block Storage and Shared Filesystem mount support for pods.</p><p>Also, for monitoring requirements, Rook enables Ceph Dashboard and each Rook Ceph cluster has some built in metrics collectors/exporters for that can be scraped by <a href="https://prometheus.io/">Prometheus</a>.</p><h3>Deploying a Ceph Cluster with Rook</h3><p>A Ceph cluster deployment with rook is a quite straightforward process that we only need to clone a git repo and run a few Kubernetes manifests via kubectl. That’s all.</p><p>But first it will be good to clarify requirements and deployment options.</p><h4><strong>Requirements</strong></h4><p><strong>OSD Disks and Rook Modes<br></strong>Rook needs to use some storage areas to create Ceph OSDs which will be base the cluster. In order to provide these areas, there are two different mode that named “<strong><em>Host-Based</em></strong>” and “<strong><em>PVC-based</em></strong>”.</p><ul><li><strong>Host-based Cluster: </strong><br>In host based cluster mode, rook detects all raw (unformatted) devices or raw partitions on all (or selected) Kubernetes nodes and creates the ceph cluster. In this mode, all you need is to attach some raw disks to your nodes. (Note that, minimum three disks on three different nodes are required to build a high-available ceph cluster)</li><li>Note that, minimum 3 disks are required to build a high-available ceph cluster</li><li><strong>PVC-based Cluster:</strong> <br>On the other hand, PVC-based cluster requires a bit more things, like an existing StorageClass. Because in this mode rook dynamically claims number of PV from the StorageClass that you define, and uses them to create OSD disks. Also this mode requires, Kubernetes version =&gt;1.13.0.</li></ul><p>Since, <em>Host-based cluster</em> mode is the most simple way to build a ceph cluster, we’re gonna use this mode in this article. So on my setup, I have three raw disks are attached to three k8s nodes each.</p><h4><strong>Deployment</strong></h4><blockquote>At the time of writing this article, the latest release of rook was 1.3.5 which we’re gonna deploy this version, but please check its <a href="https://github.com/rook/rook/releases">releases page</a> to be sure you’re on the latest version.</blockquote><p>First step is cloning rook’s 1.3 branch.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/703cfbea02a4a4530d015d97d1fddf5f/href">https://medium.com/media/703cfbea02a4a4530d015d97d1fddf5f/href</a></iframe><p>At this point, we will apply three k8s manifests which reside in <strong><em>rook/cluster/examples/kubernetes/ceph</em></strong> directory to build the cluster.</p><p>So let’s apply them one by one:</p><p><strong>common.yaml<br></strong>This manifest, basicaly creates some “<strong><em>CustomResourceDefinitions</em></strong>”, “<strong><em>ServiceAccounts</em></strong>”, “<strong><em>ClusterRoles</em></strong>” that requires by cluster formation process.</p><p>So apply this yaml.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c831dc60ddfcea5541ea7adc8a1fb0a5/href">https://medium.com/media/c831dc60ddfcea5541ea7adc8a1fb0a5/href</a></iframe><p>As you see that a bunch of resources are created.</p><p>Now we’re ready to deploy Rook system components by applying operator.yaml manifest.</p><p><strong>operator.yaml<br></strong>This manifest simply creates a deployment that deploys a rook-ceph-operator pod and its configmap. This operator is the main component of Rook that responsible to automatically create and manage Ceph clusters on top of our Kubernetes cluster.</p><p>So, we just create it:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7a53c4f333c5ad4aa5432b789617dda6/href">https://medium.com/media/7a53c4f333c5ad4aa5432b789617dda6/href</a></iframe><p>After few seconds, the components should be running like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e8b0112fb1ac8305e42adf62fc9d68f9/href">https://medium.com/media/e8b0112fb1ac8305e42adf62fc9d68f9/href</a></iframe><p>You can see that there is a “<strong><em>Deployment</em></strong>” named <strong>rook-ceph-operator</strong> which responsible to manage ceph clusters as I mentioned before.</p><p>Also, there are a daemon set named <strong>rook-discover</strong> which runs a three pods (as my test k8s cluster has three nodes). This service is regularly checks for any raw device or partitions on k8s nodes to use them as a member of Ceph cluster.</p><blockquote>Note that, any rook related resources is placed in <strong>rook-ceph</strong> namespace.</blockquote><p><strong>cluster.yaml<br></strong>Ok, rook is ready and we can deploy the ceph cluster by running <strong>cluster.yaml</strong> manifest. This manifest simply tells rook that how we wanted to create a ceph cluster.</p><p>By default it’ll use all nodes and all (raw) devices or partitions to create Ceph cluster as follows.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b78a9382fddcdda63904d18d817c9071/href">https://medium.com/media/b78a9382fddcdda63904d18d817c9071/href</a></iframe><p>As I mentioned before, I have three raw disks attached to my three k8s nodes and with this setup, rook will automatically detect and use all of those disks to create Ceph OSDs.</p><blockquote>Also note that, rook will use <strong>/var/lib/rook</strong> directory on each nodes to cache ceph mon ad osd configuration data. If you have previous rook managed ceph cluster, you need to delete the data on this directory.</blockquote><p>So, we apply the manifest:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f421d2d5bee4b9ee4c53dbf0115f456e/href">https://medium.com/media/f421d2d5bee4b9ee4c53dbf0115f456e/href</a></iframe><p>After a while, rook will create all necessary resources to form up a ceph cluster. When you check the status of the deployment, you should see something like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8f2a2fe0d20c5bad5fde82d9873d58ac/href">https://medium.com/media/8f2a2fe0d20c5bad5fde82d9873d58ac/href</a></iframe><p>What we see in this output is, the ceph cluster is successfully deployed and there are a bunch of new deployment.apps are in placed. For example, <strong>rook-ceph-mgr, rook-ceph-mon </strong>and<strong> rook-ceph-osd </strong>deployments, manages pods that related to ceph internals (which I don’t mention them in detail as this is not an entry level Ceph documentation. ) Also, rook has deployed some CSI plugins named “<strong><em>cephfs</em></strong>” (for mounting ceph volumes to pods) and “<strong><em>rbd</em></strong>” (for mounting raw blocks to pods).</p><h4>Checking Ceph Cluster Health</h4><p>As it seems our cluster has deployed, we can check its status by either using its dashboard or using ceph commands via ceph toolbox pod.</p><p><strong>Accessing Ceph Dashboard</strong></p><p>By default, rook enables ceph dashboard and makes it accessible within cluster via “<strong><em>rook-ceph-mgr-dashboard</em></strong>“ service. So we use kubectl’s port-forward option to access the dashboard from our local computer.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/acb3bfc0a7221acb41f9b8779bdceddb/href">https://medium.com/media/acb3bfc0a7221acb41f9b8779bdceddb/href</a></iframe><p>Now, should be accessible over <a href="https://locallhost:8443/">https://locallhost:8443</a></p><p>Default username is <strong>admin </strong>, and its password can be collected from <strong><em>rook-ceph-dashboard-password</em></strong> secret like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e861ab61795fe8c89e3aa379554305f7/href">https://medium.com/media/e861ab61795fe8c89e3aa379554305f7/href</a></iframe><p>So, it looks like everything is OK as it says on Cluster Status section below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hP0psRDL1tunLFoy8snJIA.png" /><figcaption>Rook ceph dashboard</figcaption></figure><p>Since ceph is high-available storage system, there are three <strong><em>Monitors</em></strong> is available in order to avoid create a SPOF (single point of failure). Also, we have three <strong><em>OSDs</em></strong> which uses the raw disk devices that attached to k8s nodes each. You can scale your OSDs by adding more raw devices to your nodes. As I mention before, Rook will automatically watch for new nodes and devices being added to your cluster to scale OSDs.</p><blockquote>As you may notice, there is only one <strong><em>Manager</em></strong> daemon currently. Although you can think that it would be nice if it has some standby daemons, rook leverages Kubernetes auto healing feature on this. As manager service is about to provide additional monitoring and interfaces to external monitoring and management systems, Rook maintainers have decided to improve its eviction process and not implement standby manager daemons as they discussed it at (<a href="https://github.com/rook/rook/issues/1796">https://github.com/rook/rook/issues/1796</a>)</blockquote><p><strong>Accessing Ceph Cluster via CLI</strong></p><p>Another options to interact rook ceph cluster is using rook toolbox. It’s a container that contains common tools used for rook debugging and testing ceph; also the toolbox is based on CentOS, so you can install any other ceph related tools on it via yum.</p><p>In order to create this container, we can use “<strong><em>toolbox.yaml</em></strong>” manifest which resides in <strong>/rook/cluster/examples/kubernetes/ceph</strong>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c32174606f3736b5213ec2e45d02e3c2/href">https://medium.com/media/c32174606f3736b5213ec2e45d02e3c2/href</a></iframe><p>At this point, we can attach the toolbox.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/83399342f77d5a21c80c65b77b345b59/href">https://medium.com/media/83399342f77d5a21c80c65b77b345b59/href</a></iframe><p>Now, we’re ready to use ceph commands. For example, to get cluster status you can use “<strong><em>ceph status”</em></strong><em> </em>command:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7e5d2a637628a8b70d3b632401694361/href">https://medium.com/media/7e5d2a637628a8b70d3b632401694361/href</a></iframe><p>To see osds status:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/154ce6c9d522ef92c7c1c18fe6861c9e/href">https://medium.com/media/154ce6c9d522ef92c7c1c18fe6861c9e/href</a></iframe><p>For other ceph control commands, you can check its documentation at <a href="https://docs.ceph.com/docs/giant/rados/operations/control/">https://docs.ceph.com/docs/giant/rados/operations/control/</a></p><h3>Ceph Cluster Storage Modes</h3><p>OK, we have a ceph cluster is in place and now it can be utilized.</p><p>Typically rook ceph cluster provides three different storage mounting mode for pods. While, the mostly used ones is “<strong><em>block storage</em></strong>”, “<strong><em>shared filesystem</em></strong>” and “<strong><em>object storage</em></strong>” are also supported.</p><p><em>Block storage</em>, allows a single pod to mount storage that typically used for databases, nosqls etc. (in this article we’ll use this mode as an example)</p><p>On the other hand a <em>shared filesystem</em> can be mounted with read/write permission from multiple pods. It’s suitable if your application requires to access to the same set of data.</p><p>And <em>object storage</em> exposes an S3 API to the storage cluster for applications to put and get data over http.</p><h3>Creating a StorageClass for Rook Managed Ceph</h3><p>In order to provision block storages a “<strong><em>StorageClass</em></strong>” and a “<strong><em>CephBlockPool</em></strong>” should be created first.</p><p>To do this we’ll apply a “<strong><em>storageclass.yaml</em></strong>” manifest which placed in “<strong>cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml</strong>”</p><p>But first, let’s take a look at the manifest:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3023c3bed61fc75cdf7801cc1e5ed836/href">https://medium.com/media/3023c3bed61fc75cdf7801cc1e5ed836/href</a></iframe><p>As you see, it declares two Kubernetes resource that I mentioned.</p><p>CephBlockPool definition will create a ceph pool named “<strong><em>replicapool</em></strong>” by using three OSDs to set three replica; so our example will require at least 1 OSD per node, with each OSD located on 3 different nodes; because the “<strong><em>failureDomain</em></strong>” is set to “<strong><em>host</em></strong>” and the “<strong><em>replicated.size</em></strong>” is set to “<strong><em>3</em></strong>” in the manifest.</p><p>And StorageClass definition will create a SC which will utilize the “<strong><em>replicapool</em></strong><em>”</em> CephBlockPool. So whenever you want to dynamically provision a storage space for your pods, you can request it from this storageclass to get the space from ceph cluster.</p><p>OK, we create the resources:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d6844b2fdfa8f3cdde956c531fc8a6a9/href">https://medium.com/media/d6844b2fdfa8f3cdde956c531fc8a6a9/href</a></iframe><p>Now, you should see a storageclass named “<strong><em>rook-ceph-block</em></strong>”:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a4ddbd8fa267dc7bd0f40720d7b05fd5/href">https://medium.com/media/a4ddbd8fa267dc7bd0f40720d7b05fd5/href</a></iframe><p>And also, “<strong><em>replicapool</em></strong>” ceph pool should be in place:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8f60f6c2513411544309ffed888aa489/href">https://medium.com/media/8f60f6c2513411544309ffed888aa489/href</a></iframe><blockquote><strong>Note</strong>: If you didn’t run toolbox deployment, the command above won’t work obviously. In this situation you can check the pool from ceph dashboard.</blockquote><p>Right now, we’re ready to dynamically provision storage spaces for pods.</p><h3>Dynamic Provisioning Example</h3><p>In this section, we’re gonna create a sample wordpress app by using example “<strong><em>mysql.yaml</em></strong>” and “<strong><em>wordpress.yaml</em></strong>” manifests reside in <strong>rook/cluster/examples/kubernetes/</strong> directory. These manifests will dynamically provision two persistent volume which will be created by Rook for mysql and wordpress data placement.</p><p>By default, example wordpress manifest creates a service for port 80 by using <strong>LoadBalancer</strong> service type, however my test Kuberbetes hasn’t a cloud controller to interact any cloud based external load balancer; so I’ll change the type as <strong>ClusterIP</strong> which the service will be available from inside the Kubernetes cluster as this example is for demonstration purpose.</p><p>So let’s create the app:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6fa6868bb2ecd92172606f09b9c05bad/href">https://medium.com/media/6fa6868bb2ecd92172606f09b9c05bad/href</a></iframe><p>As you see from the output above, two “<strong><em>PersistentVolumeClaim</em></strong>” has created for mysql and wordpress. Let’s see the <em>PersistentVolumes</em> and <em>PersistentVolumeClaims:</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f409c0458807275711a77cde669383d2/href">https://medium.com/media/f409c0458807275711a77cde669383d2/href</a></iframe><p>It looks like, they are created and successfully bounded.</p><p>And here is the output of the deployment, pods and services related to wordpress app.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/70c56c8b74de46ee90ff11aec3eafeae/href">https://medium.com/media/70c56c8b74de46ee90ff11aec3eafeae/href</a></iframe><p>Now, we can fetch wordpress enrty page from one of our Kubernetes nodes by using <em>wordpress</em> service’s ClusterIP:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/28fc14979500319be395d2ebbd444c87/href">https://medium.com/media/28fc14979500319be395d2ebbd444c87/href</a></iframe><p>It seems, our wordpress app is running and writes its data (both mysql and wordpress http server) to the Rook managed ceph persistent volumes that created dynamically.</p><h3>Wrapping Up</h3><p>Just we’ve seen it in action, rook is a very promising project that simplify ceph cluster deployment and integration processes.</p><p>As, it’s encourages people to run stateful applications -that requires reliable storage systems within on-premises environments-, I’ve always liked such kind of good quality projects, which expands Kubernetes’ use cases and boost the ecosystem.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=500882ecf005" width="1" height="1" alt=""><hr><p><a href="https://medium.com/devopsturkiye/rook-a-storage-orchestrator-to-run-stateful-workloads-on-kubernetes-with-ceph-500882ecf005">Rook: A Storage Orchestrator to Run Stateful Workloads on Kubernetes with Ceph</a> was originally published in <a href="https://medium.com/devopsturkiye">Devops &amp; AI Türkiye☁️ 🐧 🐳 ☸️</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kong Api Gateway: Installing, Configuring and Securing.]]></title>
            <link>https://medium.com/devopsturkiye/kong-api-gateway-installing-configuring-and-securing-dfea423ee53c?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/dfea423ee53c</guid>
            <category><![CDATA[microservices-application]]></category>
            <category><![CDATA[api-gateway]]></category>
            <category><![CDATA[microservices]]></category>
            <category><![CDATA[clustering]]></category>
            <category><![CDATA[kong-api-gateway]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Wed, 13 May 2020 11:35:57 GMT</pubDate>
            <atom:updated>2020-06-04T19:38:21.534Z</atom:updated>
            <content:encoded><![CDATA[<p>When it comes to choose a reliable api gateway (especially for microservice based applications), we have many options indeed and if you examined these options, you probably came across with Kong as well. As an open source enthusiast and a believer in the “keep it simple” philosophy, I’ve always liked projects such as Kong that addresses a serious problem in a simple and clean way.</p><p>Though, there are many good articles related to Kong, I wanted to share my opinions and write a “yet another tutorial” about <strong>installing</strong>, <strong>configuring</strong> and <strong>securing</strong> Kong for production environments. So, this article will be such a “quick start guide” that includes bit more information for production use.</p><blockquote>If you already know what Kong is and its features, just jump to <strong>Installation &amp; Configuration</strong> section.</blockquote><h3>What is Kong!</h3><p>Simply, it’s an API gateway, that sits in front of your backend services and forwards client requests to those services. Since Kong is the first point that client requests hit, it can transform and/or routes any requests or responses based on your configuration. So you can manipulate any requests or responses by adding, appending or removing headers on the fly and route them to wherever you want.</p><p>What’s more, as an abstraction layer it provides additional features with its plugins such as <strong>authentication</strong>, <strong>rate limiting</strong>, <strong>caching</strong>, <strong>logging</strong>, <strong>bot detection</strong>, <strong>CORS, IP restriction </strong>and many more. This means, that developers don’t need to implement these features in each micro services, thus they can be more focused on business logic which improves productivity.</p><p>Kong is a kind of “platform agnostic” solution that runs on almost every platform. You can run it as a docker container or install on a <strong>GNU/Linux</strong> distribution in on-premise or cloud based environments. Even, it can run as an <strong>kubernetes ingress controller</strong>.</p><h3>Architecture</h3><p>Here is a simple diagram to see how Kong works and how it extends your application ecosystem with its features.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/976/1*ck9u-6iJPw5W8iJrvZokDA.png" /></figure><h3>How It Works</h3><p>Kong is built on top of Nginx, -this is why it’s highly extensible- and by using its own admin api, it configures the nginx server to provide proxying for backend services.</p><blockquote>Kong uses <a href="https://openresty.org/">OpenResty</a> which is a kind of enhanced version of nginx that includes lua-nginx-module. Thus Kong is distributed along with OpenResty to leverage its lua scripting capability to configure and extend nginx.</blockquote><p>In other and simple words, Kong accepts your reverse proxying directives via its own <strong>Admin API</strong> over http/s, translates them to nginx configuration using lua scripting language and builds a gateway platform for micro service backends.</p><h4>Admin API</h4><p>When you install Kong on a host, the Admin API will be accessible on tcp/8001(http) and tcp/8444(https) ports. By interacting the Admin Api , you can configure Kong dynamically.</p><blockquote>As a security best practice, Admin API ports are for internal use and should be firewalled.</blockquote><h4>Proxying</h4><p>On the other hand Kong listens tcp/8001(http) and tcp/8443(https) ports to receive end-user requests and according the configuration you define, forwards to your endpoints. Those ports are publicly-facing services that must allow external connections.</p><h4><strong>Datastore</strong></h4><p>Kong uses an external database to store configurations data. You passes your configuration directives over admin API and Kong stores them in its datastore. And multiple kong servers can use a central database. (Which makes the gateway platform high-available and scalable. I’ll mention this in detail on <strong>Clustering</strong> section.)</p><p>Supported datastores are Cassandra and PostgreSQL. Though Cassandra is preferred datastore to build a multi-regional Kong environment, PostgreSQL can also be used for production without any issues. (In this post, we’ll use a Postgres replication for the sake of simplicity.)</p><h4>Cache</h4><p>As you know, when it comes to performance, databases can be easily become a main bottleneck. In order to avoid this and improve performance, Kong maintains a cache to hold the configuration data in the node’s memory.</p><p>No matter which datastore engine you use, when you call admin API to configure Kong, configuration data is written to the external datastore, then pulled from the external db and written to the node’s memory as cache; finally, configuration is applied.</p><p>At this point there is no need for a database roundtrip anymore while proxying requests. If you do any configuration change, Kong invalidates cache and pulls the new configuration to cache it again. This approach boost Kong’s performance obviously.</p><blockquote>In order to avoid to break Kong’s cache invalidation mechanism, you should not manipulate the datastore db directly.</blockquote><h4>DB-less Mode and Declarative Configuration</h4><p>As I mentioned in <strong>Datastore</strong> section, kong requires a database for the configuration data, however there is another option called DB-less mode. In this mode the configuration directives is declared as a JSON or YAML formatted configuration file, thus Kong reads this file to get configuration directives and write it to node’s memory.</p><p>With this mode, you can manage Kong by using a CI/CD tool. Since, configuration directives are stored in a static file, you can keep it in a git repository and distribute to multiple kong nodes. However in this mode Admin API is read-only, because the only way to configure Kong is using a declarative configuration file. In other words, you can’t pass any configuration directives to the admin api over RESTFull api.</p><p>Moreover, some useful plugins are not compatible with this mode, since they require a database by design. For example, rate-limiting plugin requires to a database to hold limiting values.</p><p>This mode may seem useless for production use however it is also a part of another mode called <strong><em>Hybrid mode</em></strong> that can be used create a Kong cluster. Next section I’ll explain this mode as well.</p><h3>Clustering</h3><p>Probably one of the most reliable service within the infrastructure should be API gateways and we don’t want to create a SPOF on this layer. Since they are act as the entry point to your backend services, any outages or performance issues couldn’t be tolerated on this layer. Needless to say, if the gateway dies, practically the application dies too. So it should be redundant and scalable.</p><p>As I mentioned before, Kong uses a datastore to holds its configurations and multiple kong node can use this central datastore to form a cluster.</p><p>A typical kong cluster looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/402/1*1emPsHB5sDGZUnJhBYy-Ww.png" /></figure><blockquote>Note that, Kong is not a load balancer, you need a load-balancer in front of your Kong cluster to distribute your traffic.</blockquote><p>So, if you need to handle more incoming requests, you can easily add more nodes horizontally by installing Kong on your new nodes and pointing them to the central datastore.</p><p>This simplicity and flexibility makes Kong to an automation suitable platform as well. For example, you can track your Kong cluster resources utilization (by collecting metrics via its prometheus plugin) and decide to scale up or down automatically. Kong installation and initial configuration is a quite straightforward process which can be easily automated.</p><h4>Hybrid Mode Clustering</h4><p>Classically, Kong nodes depend to the central datastore, but this is not the only option, because there is another way called Hybrid mode to build a Kong cluster.</p><p>In this mode, Kong nodes are separated into two roles called <em>Control Plane Nodes(CP)</em> and <em>Data Plane Nodes(DP)</em></p><p>While, CP nodes -which provides Admin API-, are the only components that interact with the central datastore, DP nodes -which handle incoming requests- fetch their configuration from a CP node.</p><p>Here’s a good demonstration of two Kong cluster modes.</p><figure><img alt="Kong hybrid mode" src="https://cdn-images-1.medium.com/max/1024/1*1dETiHQnsT-SzQlz5XbRAw.png" /></figure><p>As you noticed, this mode is used db and db-less mode both, this is why it’s called <em>hybrid.</em></p><p>It has some benefits<em> </em>such as reducing datastore load, improving security and easiness of management.</p><blockquote>Though this mode is very suitable for distributed environments, I’m gonna configure Kong cluster in its classical way on this article for the sake of simplicity.</blockquote><blockquote>For further information about hybrid mode, please check its documentation <a href="https://docs.konghq.com/2.0.x/hybrid-mode/">https://docs.konghq.com/2.0.x/hybrid-mode/</a></blockquote><h3>Features</h3><p>Kong has many useful features (known as plugin). These plugins either comes with Kong as bundled or provided by the community. As I said briefly, it’s using OpenResty’s ngx_http_lua_module to provide its core configuration management and plugin management infrastructure and this makes possible to write new plugins for Kong to extend it further. Thanks to its wise approach, there are many plugins on the <a href="https://docs.konghq.com/hub/">Kong Plugin Hub</a>!</p><p>Here are some useful plugins to worth mention:</p><h4><strong>Authentication Mechanism</strong></h4><p>Authentication plugins, provide bunch of authentication mechanisms to the backend services; and it’s possible to use different mechanism for each service. For example, while service-A is using basic authentication, service-B only accepts request with JWT token.</p><blockquote>One of the well-known benefit to use an API gateways, makes it possible to offload the authentication overhead to the gateway. Thus, developers can be better focused on business logic of their backends without concerning about authentication implementation to each backend service. (Of course you need to be sure that the services are well isolated to unrelated connections nonetheless.)</blockquote><p>Some of the mechanisms that supported by Kong is:</p><ul><li>Basic Auth</li><li>LDAP integration</li><li>OAuth 2 integration</li><li>Key based auth</li><li>HMAC auth</li><li>JWT tokens</li></ul><h4><strong>Security</strong></h4><p>Kong has some goodies to add security related capabilities to the endpoints in an effortless way. For example, by using bot detection plugin, you can detect and block any kind of requests that initiated by specific bots or custom clients. Or with its IP restriction plugin, you can easily restrict access to your services by either whitelisting or blacklisting IP addresses.</p><p>Here is built-in plugins come with Kong open source edition. (It has a commercial version named Kong Enterprise which includes more features.)</p><ul><li>ACME (Let’s encrypt and ACMEv2 integration)</li><li>CORS</li><li>IP Restriction</li><li>Bot Detection</li></ul><h4><strong>Traffic Control</strong></h4><p>Traffic control plugins are very useful features that everyone might be needed. For instance, Proxy cache plugin, caches mostly responded request in Kong nodes’ memory (we will mention that what a kong node is later).</p><blockquote>As you know caching mechanisms are important things to reduces the load on the backends especially related to the data layers. If you cache frequently requested responses, that runs a bunch of heavy SQL queries on the backends, then you can reduce load on your db and related services.</blockquote><p>Other plugins are:</p><ul><li>Rate limiting</li><li>ACL (defines, who can access the services)</li><li>Request body size limiting</li><li>Request terminating (Terminates certain request with a specific status code.)</li></ul><h4><strong>Analytics &amp; Monitoring</strong></h4><p>When it comes to troubleshooting, (especially for performance related issues) one of the most important thing is “metrics” for sure! Particularly, if we’re talking about microservice environments, metrics become an imperative situation.</p><blockquote>Though microservices offer a lot of benefits (which I won’t mention them, since this is not a post about it), also brings its challenges too. Unlike old school monolith applications, decoupled nature of microservices increase the complications of the visibility. So, on a typical microservice environment (since there are bunch of independent services that talk to others), you need to collect good quality metrics and correlate them in an understandably approach in order to understand how performant your environment is!</blockquote><p>Well, Kong is an api gateway and it sits in between clients and services. (also you can place it in between your microservices as well.) therefor you can grab a lot of meaningful metrics from it such as latency values of your endpoints or you can collect tracing spans for any api calls. This is why Kong has these plugins below:</p><ul><li>Prometheus</li><li>Zipkin</li><li>Datadog</li></ul><h4><strong>Logging</strong></h4><p>Needless to say, another “must have” feature for distributed applications is logging.</p><blockquote>Logs say a lot! Based on their importance level, they should be collected, parsed, stored and visualized. Nowadays, we use logs to produce metrics that processed on the modern monitoring systems, then we use these metrics to fire alerts on serious events. They are became important resources more than ever.</blockquote><p>So, these plugins ship logs to remote logging systems in various ways to make them to be processed!</p><ul><li>Syslog</li><li>HTTP Log</li><li>StatsD</li><li>TCP/UDP Log</li></ul><h4><strong>Transformation</strong></h4><p>Another useful subset of plugins are transformation related ones. These plugins can be used for manipulate any requests or responses on the fly. For example you can add, remove or replace header, body or querystrings of the requests or responses. Also there is a good plugin called Correlation ID, which injects unique ID to requests or responses in order to track http traffic flow.</p><ul><li>Request &amp; Response Transformer</li><li>Correlation ID</li><li>gRPC Web</li><li>Inspur Request &amp; Response Transformer</li></ul><h3>Installation &amp; Configuration</h3><p>In this section, three separate CentOS 7 instances will be used to build a kong cluster. And in order to keep it simple, every components will be run on top of Docker.</p><p>Here are the components:</p><ul><li><strong>Datastore</strong><br>As datastore, we’ll use a PostgreSQL docker container on one of the CentOS instance.</li></ul><blockquote>Note that your database should be redundant for production environments. So if you gonna deploy Kong for production use, you need to use a PostgreSQL replica cluster or Cassandra Cluster in order to avoid a single point of failure.</blockquote><ul><li><strong>Kong Nodes<br></strong>Two other CentOS instances will be the Kong nodes.</li></ul><blockquote><strong>Docker and Kong Nodes:</strong> It’s OK to run kong nodes as docker containers in production, but they should also be redundant. So don’t run multiple kong nodes in a single instance.</blockquote><p>Finally, my example environment ip addresses looks below:</p><p><em>kong-datastore: 10.10.10.11<br>kong-node-01: 10.10.10.21<br>kong-node-02: 10.10.10.22</em></p><h4>Step 1 — Prepare Nodes</h4><p>If you don’t have docker in your nodes, install its community edition as following steps:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/69a4ee6bea1b85eb7cfdedf9ffd91a05/href">https://medium.com/media/69a4ee6bea1b85eb7cfdedf9ffd91a05/href</a></iframe><h4>Step 2 — Deploy PostgreSQL Container on Datastore Node</h4><p>Connect your kong-datastore instance and run:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/89eee1c5266bd2b9b9d02ebead8d493d/href">https://medium.com/media/89eee1c5266bd2b9b9d02ebead8d493d/href</a></iframe><p>Now, we started a PostgreSQL 9.6 container on datastore node. As we passed the environment variables, a database named <strong><em>kong</em></strong> is created. So we can connect it with <strong><em>kong</em></strong><em>/</em><strong><em>kong</em></strong> credentials.</p><blockquote><strong>Important note: </strong>Again, this PostgreSQL deployment is just for demonstration purpose, you need to use an external PostgreSQL replica or Cassandra cluster for production use.</blockquote><h4>Step 3 — Prepare Kong Database</h4><p>Next, switch to one of your kong node instance and run bootstrap command to make initial configuration on the db, such as creating schema etc:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/16bcd92c16ae63979331de32fa595fff/href">https://medium.com/media/16bcd92c16ae63979331de32fa595fff/href</a></iframe><p>As you noticed, we’ve pointed it to our postgres host via <em>KONG_PG_HOST</em> parameter.</p><h4>Step 4 — Starting Kong Nodes</h4><p>Finally, start kong container on your every kong node instances:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/58623f107baa0e5479a409e1f0866111/href">https://medium.com/media/58623f107baa0e5479a409e1f0866111/href</a></iframe><p>With this command set, we first tweaked the kernel to get better performance and then started kong node with some configuration parameters which we passed them as environment variables.</p><p>Here’s variable explanations:</p><p><strong>KONG_DATABASE and KONG_PG_*<br></strong>Defines which datastore that kong node will connect.</p><p><strong>KONG_{PROXY|ADMIN}_{ACCESS|ERROR}_LOG<br></strong>As a common containerized application approach, this parameters redirect admin api and proxy http logs to stdout and stderr by its category. (There are some good logging plugins to ship the logs remote, that you can enable regardless of these directives.)</p><p><strong>KONG_{ADMIN|PROXY}_LISTEN</strong></p><p>These environment variables, configure addresses and ports on which the proxy and admin api should listen for HTTP/HTTPS traffic.</p><p>According to our configuration, the admin api will listen to tcp/8001 for http and <em>tcp/8444 </em>for https traffic and proxy server will listen to <em>tcp/8000</em> for http and <em>tcp/8443 </em>for https.</p><blockquote>Note that, backlog=N parameter, sets the maximum length for the queue of pending TCP connections. This number should not be too small in order to prevent clients seeing “Connection refused” error connecting to a busy Kong node.</blockquote><blockquote>And this is related to net.core.somaxconn parameter, so you need to increase this parameter at the same time to match or exceed the backlog number. This is why we’ve tweaked kernel parameters and used “ — sysctl net.core.somaxconn=16384” line in our docker run command above.</blockquote><p><strong>KONG_ANONYMOUS_REPORTS</strong></p><p>By default, anonymous reporting feature that sends anonymous usage data such as error stack traces to help improve Kong, is enabled. But I think it’s not good for any production environments. So we disabled it.</p><h4><strong>Step 5 — Check if Kong Nodes are running</strong></h4><p>Now, kong node should be run and provides its admin api via tcp/8001 or tcp/8444 on localhost.</p><p>So you can call it from any of kong node’s localhost address with curl like:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fcd486852088d683253e059db3bd4141/href">https://medium.com/media/fcd486852088d683253e059db3bd4141/href</a></iframe><p>If everything is OK, it should return a JSON output that shows current configuration.</p><h3>Securing Kong Admin API</h3><p>As you noticed, when we run node kong container, we exposed admin api port on localhost with “-p 127.0.0.1:8001:8001 and -p 127.0.0.1:8444:84444“ parameters which means the admin api wouldn’t be accessible other than localhost. Since the admin api is the most important component that provides a RESTful interface for administration and configuration purposes, it should be well protected. So it’s a good habit to not expose it to network by default as we did.</p><p>But at the end of the day you will need to use the admin api remotely. In order to expose the admin api securely, adding fine-grained access control measures such as authentication and authorization is a good idea.</p><p>In this section, we will leverage kong itself to provide proxy to its own admin api. This will be also a good practice to learn how to use Kong.</p><h4>Step — 1 Create a service</h4><p><strong>Services</strong> are used to refer to the upstreams which the incoming requests will be passed.</p><p>So with this command below, we’ll create a service called <strong>admin-api</strong> which refers the localhost’s port 8001 as an upstream.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/65a38315c88548ff2ae0272fb4ba270a/href">https://medium.com/media/65a38315c88548ff2ae0272fb4ba270a/href</a></iframe><h4>Step — 2 Create a route to pass requests to the admin-api service</h4><p><strong>Routes</strong> specify how (and if) requests are sent to their Services after they reach the API gateway.</p><p>So we’re gonna create a route under the admin-api service to tell Kong that if it receives any incoming requests for the <strong><em>/admin-api</em></strong> endpoint, pass it to the <strong>admin-api</strong> service.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9b6ca6a6a50e8e62bfeed8cb54089987/href">https://medium.com/media/9b6ca6a6a50e8e62bfeed8cb54089987/href</a></iframe><p>As our admin-api service is ready, we can call it via curl from a remote host.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/48c739000e66a3a3bb58524834620ae3/href">https://medium.com/media/48c739000e66a3a3bb58524834620ae3/href</a></iframe><p>As you can see, this time we access the Admin API by sending our request to Kong’s proxy port on its LAN address. So our request hits to Kong proxy and it returned response by fetching from the admin-api upstream (<a href="http://localhost:8001/">http://localhost:8001</a>)</p><h4>Step 3 — Apply Authentication Control to admin-api Service</h4><p>At this point, we will apply an authentication control to our service, in order to deny any unauthorized accesses.</p><blockquote>Though, Kong has a bunch of security related plugins that we mentioned above, I’ll use basic authentication for demonstration purpose. To see other options please take a look at <a href="https://docs.konghq.com/hub/#security">https://docs.konghq.com/hub/#security</a></blockquote><p><strong>Step 3.1 — Enable basic-auth Plugin</strong></p><p>This plugin adds basic authentication to a <em>service</em> or a <em>route</em> with username and password protection. You can enable it for each services or routes individually. As we want to protect the Admin API, we enable it for admin-api service:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/59fed4f5b1fa774b1b061db76306b9f2/href">https://medium.com/media/59fed4f5b1fa774b1b061db76306b9f2/href</a></iframe><p>Notice that we define our service name (admin-api) in the URL and this will enable basic-auth plugin for service called admin-api.</p><p><strong>Step 3.2 — Create a consumer</strong></p><p>Consumers are associated to individuals that uses services. You can think them as users. Now, we create a consumer called <strong><em>my-user</em></strong> for our admin-api service:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ac6593f2f1682ee716962be7fc39d4f0/href">https://medium.com/media/ac6593f2f1682ee716962be7fc39d4f0/href</a></iframe><blockquote><strong>Note</strong>: consumer_id is a field for storing an existing unique ID for the consumer.</blockquote><p>Next we create s credential for our consumer:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/071b58defba37b6e56addba0ab09531b/href">https://medium.com/media/071b58defba37b6e56addba0ab09531b/href</a></iframe><p>As you can see, our username is <strong>my-user</strong> and password is <strong>my-password</strong></p><p><strong>Step 3.3 — Using the credential</strong></p><p>OK we got a consumer and its credentials, so we can call admin-api service with our username and password pair.</p><p>To do this, we need to use <strong><em>Authorization</em></strong> (or <strong><em>Proxy-Authorization</em></strong>) header in our request. This authorization header must be base64 encoded version of <strong><em>username:password</em></strong></p><p>So, we can first encode our credentials, and then send request with the proper header to kong node.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/43773280e7ef20df6757267d29745e8a/href">https://medium.com/media/43773280e7ef20df6757267d29745e8a/href</a></iframe><p>As we enable basic-auth plugin on admin-api service, any unauthorized access will be denied; for example if you don’t define the auth header on your request, you’ll be rejected with the message below :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/125b26c1c54b6e6fcbc41130ea8b3e8d/href">https://medium.com/media/125b26c1c54b6e6fcbc41130ea8b3e8d/href</a></iframe><p>Well, at this point we added basic-auth for the Admin Api. Though, it can be still accessible via localhost:8001(or :8444) on Kong nodes, you need to provide your credentials to interact from a remote location.</p><h3>Wrapping Up</h3><p>There are many other things that we can talk about, however this quick start guide will be useful at the basics I think. If you want to get more information, you can check the docs at <a href="https://docs.konghq.com/">https://docs.konghq.com/</a></p><p>Also, in my next article, I’ll write about Konga -which is a nice user interface for Kong-. So stay tuned.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dfea423ee53c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/devopsturkiye/kong-api-gateway-installing-configuring-and-securing-dfea423ee53c">Kong Api Gateway: Installing, Configuring and Securing.</a> was originally published in <a href="https://medium.com/devopsturkiye">Devops &amp; AI Türkiye☁️ 🐧 🐳 ☸️</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kubernetes Nginx Ingress Controller for On-Premise Environments]]></title>
            <link>https://medium.com/swlh/kubernetes-nginx-ingress-controller-for-on-premise-environments-e64ee3aa04e?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/e64ee3aa04e</guid>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[nginx]]></category>
            <category><![CDATA[ingresscontroller]]></category>
            <category><![CDATA[ingress]]></category>
            <category><![CDATA[nginx-ingress]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Tue, 29 Oct 2019 18:32:42 GMT</pubDate>
            <atom:updated>2020-06-27T19:52:08.607Z</atom:updated>
            <content:encoded><![CDATA[<p>In my <a href="https://medium.com/@cagri.ersen/production-ready-kubernetes-installation-with-kubespray-97063e8a846c">previous post</a>, I’ve mentioned that I would write about nginx ingress controller integration for on-premise kubernetes clusters. If you have such a cluster, you probably wants to run an ingress controller in order to be able expose your apps over http(s), since ingress resources are one of the most clever way to achieve such a thing.</p><p>So here’s the details:</p><h4>1. What is Ingress ?</h4><p>As it explained in kubernetes documentation, Ingress exposes HTTP and HTTPS routes from outside the cluster to <a href="https://kubernetes.io/docs/concepts/services-networking/service/">services</a> within the cluster. Traffic routing is controlled by rules defined on the Ingress resource.</p><p>In other words when you want to expose your applications that reside in the k8s cluster, you configure an ingress resource that defines which inbound traffic will be go to which application.</p><p>A typical flow of an ingress is as below:</p><figure><img alt="ingress" src="https://cdn-images-1.medium.com/max/514/1*cJt6Ix6arRskkteFbtJqxA.png" /></figure><p>As you can see, it’s a load balancer that routes specific traffics to the specific targets in an automated fashion. You just create an ingress resource with desired definitions, it does its magic for you.</p><p>In this way you can expose as many services as you want with the same IP address. Since, this is a L7 load balancer, it supports <strong>host</strong> based (ie: app1.domain.com) or <strong>path</strong> based (i.e: /basket) redirections and <strong>SSL termination </strong>etc.</p><blockquote><strong>Note:</strong> Ingress is not the only option to expose applications to outside of the cluster. But since, this is not a kubernetes networking post, I won’t cover everything in here. If you want to learn more about this topic, reading <a href="https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0">https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0</a> will be a good start.</blockquote><p>A form of an ingress resource manifest is something like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c2ad239e992f63afd4278084bb7a9c1d/href">https://medium.com/media/c2ad239e992f63afd4278084bb7a9c1d/href</a></iframe><p>In this example, any http requests that contain cafe.example.com as hostname will be redirected two separated service, based on their paths. While, <a href="http://cafe.example.com`/">http://cafe.example.co</a>m/tea will be routed to tea-svc service, <a href="http://cafe.example.com/coffe%60">http://cafe.example.com/coffe</a> will be hit to coffe-svc.</p><h4>2. What is Ingress Controller ?</h4><p>As its name implies, ingress controller is a component that responsible to manage ingress resources. In other word, if you want your ingress resources to work, you need to have an ingress controller on your cluster.</p><p>The most cloud provider kubernetes services, have an implementation of an ingress-controller based on their load balancer solution default. For example when you run your cluster on Google Kubernetes Engine (GKE), the default ingress controller manages Google HTTP/S Load Balancers. Whenever you create an ingress resource, GKE ingress controller spins up a GLB to handle the routes that defined in the ingress resource. So if you are on a cloud provider, you probably don’t need to think about it; but if your environment is on-premise, then you have to implement an ingress controller for your cluster.</p><p>Though, there are many <a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/">ingress controllers</a> out there, one of the most popular ones is nginx ingress controller imho. So we’ll integrate it in this post.</p><h4>3. Nginx Ingress Controller</h4><p>Basically, nginx ingress controller manages a bunch of nginx pods in order to do automatic nginx configurations that defined by ingress resources. When you create an ingress, the controller translates the ingress definitions as nginx configuration parameters and applies them to the nginx pods. So it actually does automatic nginx configurations based on your ingress resources.</p><p>There are two different versions of the controller. One is maintained by the kubernetes community and published from a <a href="https://github.com/kubernetes/ingress-nginx">github repo</a>. (its documentation sits on <a href="https://kubernetes.github.io/ingress-nginx/">https://kubernetes.github.io/ingress-nginx/</a>). The other one is maintained by the Nginx community that can be reached from <a href="https://github.com/nginxinc/kubernetes-ingress">https://github.com/nginxinc/kubernetes-ingress</a>.</p><blockquote>Although, we’ll use kubernetes version of the controller in this story, you can use any of them based on your requirements. If you want to take a look at their differences, just check the <a href="https://github.com/nginxinc/kubernetes-ingress/blob/master/docs/nginx-ingress-controllers.md">comparison document</a>.</blockquote><h4>3.1. How it works ?</h4><p>To setup nginx ingress controller on your cluster, all you need is to run a k8s manifest provided by the <a href="https://kubernetes.github.io/ingress-nginx/deploy/">project</a>. So, for a classic integration with very default configurations you can run the manifest published at <a href="https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml">https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml</a></p><p>This manifest creates a namespace for ingress controller resources, then creates some configmaps to hold default nginx configurations, a service account and related RBAC resources and a deployment to run nginx pods, also it creates a service for nginx deployment to route traffic vianodePort.</p><p>Actually, this setup is OK and there is nothing wrong with it. But if we use a DaemonSet instead, then we don’t have to use an additional nodePort typed service, since we can use hostPort parameter with a DaemonSet resource. In order to do this, I’ve just cloned the official manifests to <a href="https://bitbucket.org/secopstech/nginx-ingress-controller/src/master/configs/">my bitbucket</a> repository and converted it to use DaemonSet.</p><p>If you check the <a href="https://bitbucket.org/secopstech/nginx-ingress-controller/raw/c1639687f3fafb5e3c068bcdd2dd566494b64c07/configs/05-nginx-ingress-controller-ds.yaml">related manifest</a>, you can see our definition:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ebddb7ad2cf73b71334526851a284c44/href">https://medium.com/media/ebddb7ad2cf73b71334526851a284c44/href</a></iframe><p>As you know, a <em>DaemonSet</em> ensures that all (or some) <em>Nodes</em> run a copy of a <em>Pod</em>. As nodes are added to the cluster, Pods are added to them. As nodes are removed from the cluster, those Pods are garbage collected.</p><p>Also in the <strong><em>ports</em></strong> section we define that we want to use <strong><em>hostPort</em></strong>, so that the pods will be reachable via the node’s IPs. In other words, our nginx pods will be accessible via <strong><em>nodeIP:80</em></strong> or <strong><em>nodeIP:443</em></strong> notation.</p><blockquote><strong>Note:</strong> Again, if you don’t know much about <strong>nodePort</strong> and <strong>hostPort</strong> differences, please check <a href="https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0">https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0</a></blockquote><h4>3.2 Integration</h4><p>OK lets create the necessary resources to integrate the controller in our cluster.</p><p>First, clone the repo:</p><pre>git clone <a href="https://cagriersen@bitbucket.org/secopstech/nginx-ingress-controller.git">https://bitbucket.org/secopstech/nginx-ingress-controller.git</a></pre><p>Then, apply the manifests that sit in the <em>configs</em> directory:</p><pre>kubectl apply -f nginx-ingress-controller/configs/</pre><p>After few seconds, all resources needed by ingress-controller should be created in ingress-nginx namespace.</p><p>To check them, run:</p><pre>kubectl get all -n ingress-nginx</pre><p>Output should be something like:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/12099ee4d25c73257bd7ba91a018ce3e/href">https://medium.com/media/12099ee4d25c73257bd7ba91a018ce3e/href</a></iframe><p>As you see, we got 6 nginx ingress controller pods (because my test environment has 6 nodes in total). Also there is another pod to be used as default backend which is a service that handles all URL paths and hosts the nginx controller doesn’t understand (i.e., all the requests that are not mapped with an Ingress).</p><h4>3.3 Test the controller</h4><p>In order to check the controller functionality, we create a test application which resides in <em>test-app</em> directory of the repo. This folder has some manifests to deploy a simple web service with an ingress definition.</p><p>Lets look at this example ingress manifest:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/073782b53c6c09c01dfc8cc50a05e691/href">https://medium.com/media/073782b53c6c09c01dfc8cc50a05e691/href</a></iframe><p>This will create two ingress resources to handle routes for <em>web.domain.com</em> and <em>api.domain.com</em> requests. It also provides SSL termination support for our services (with a self signed certificate for *.domain.com)</p><p>Now, create the resource by using kubectl:</p><pre>kubectl apply -f nginx-ingress-controller/test-app/</pre><p>And check its status:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/14b7be5119e120f90a2d7db37ccc7edf/href">https://medium.com/media/14b7be5119e120f90a2d7db37ccc7edf/href</a></iframe><p>So we got two deployments, services and other related resources for our test-app. If your nginx ingress controller is up and running, you can now access your web and api pods via <a href="https://api.domain.com/"><em>https://api.domain.com</em></a> and <a href="https://api.domain.com/"><em>https://web.domain.com</em></a> URLs. (Note that, our SSL terminations use a self signed certificate, so your browser gonna complain about it which you can ignore.</p><p>So, ingress-controller setup is finished. If you want to delete test-app deployment, just delete the ingress-test namespace with kubectl delete ns test-ingress</p><h3>4. Considerations</h3><p>In this section, I want to point out some production considerations.</p><h4>4.1 DaemonSet with nodeSelector</h4><p>In our example, we use a DaemonSet for nginx-ingress-controller, thus our all nodes run a nginx-ingress-controller pod. As I said before, with this setup, our ingress resources would be accessible from all node’s IP addresses.</p><p>For example my k8s node’s ip addresses are 10.10.10.1{1,2,3,4,5,6} and <em>api.domain.com</em> or <em>web.domain.com</em> services are reachable from all of these IPs. So, I can use an external load balancer for this domains and distribute the traffic to all of these 6 nodes.</p><p>If you have few k8s nodes, it won’t be a problem, but as your cluster grows over time, load balancer management and compute resource planning might become more difficult. (You probably don’t want to run dozens of nginx ingress controller pods in total.) So, at this point using DaemonSet with “one pod per node” logic might not be a suitable way to run an ingress-controller.</p><p>In this situation, you can consider to run your pods on specific nodes by using nodeSelector parameter in your DaemonSet. For example, if we want to run our nginx pods on our three nodes only; we can label these nodes and tell DaemonSet to run the pods on the nodes which have the label.</p><p>To do this, add a label to your nodes:</p><pre>kubectl label nodes node1 ingress-controller-node=true<br>kubectl label nodes node2 ingress-controller-node=true<br>kubectl label nodes node3 ingress-controller-node=true</pre><p>Then change the DaemonSet manifest spec something like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b5ba6d65edc32f8703e944c8e7c78fa5/href">https://medium.com/media/b5ba6d65edc32f8703e944c8e7c78fa5/href</a></iframe><p>As you noticed, we add the label as <strong><em>nodeSelector</em></strong> to container spec section. After applying this manifest, nginx pods run on our first three nodes.</p><h4>4.2 Nginx Custom Configurations</h4><p>When a nginx pod is starting, its configuration file is injected through a configmap; and the default configmap provided by the project is something like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7adcd04765a11870884b73c1364a3797/href">https://medium.com/media/7adcd04765a11870884b73c1364a3797/href</a></iframe><p>As you can see, there is no any nginx configuration parameters in it. While, nginx pods have a default nginx configuration that comes from the image, sometimes you might want to adjust the configuration level by your needs. To do this, you can add your specific parameters within the data section:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b5f58885b8c5baa82b3c62369e5feca5/href">https://medium.com/media/b5f58885b8c5baa82b3c62369e5feca5/href</a></iframe><p>In this example, we set some proxy related config parameters our ConfigMap. So whenever a nginx pod starts, it will consider these parameters as well.</p><blockquote><strong>Note:</strong> The above parameters are published as an example and they are not for production use.</blockquote><p>All supported parameters are documented on <a href="https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/">https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/</a></p><p>Also, if you want to configure an ingress resource individually, you can use <strong><em>Annotations</em></strong> in ingress resource manifests as explained on <a href="https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/">https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e64ee3aa04e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/kubernetes-nginx-ingress-controller-for-on-premise-environments-e64ee3aa04e">Kubernetes Nginx Ingress Controller for On-Premise Environments</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ansible AWX Installation]]></title>
            <link>https://medium.com/swlh/ansible-awx-installation-5861b115455a?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/5861b115455a</guid>
            <category><![CDATA[installation]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[tower]]></category>
            <category><![CDATA[awx]]></category>
            <category><![CDATA[ansible]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Tue, 03 Sep 2019 20:12:30 GMT</pubDate>
            <atom:updated>2020-04-30T21:27:54.294Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/521/1*g-L5zDf10Hitj9XMlNNFug.png" /></figure><p>As you know, automation is one of the most important aspect of the DevOPS culture. The time goes by, IT environments converted to more and more complex things and now we have more servers and network equipments that we need to manage. More importantly we need to orchestrate them without using much manpower. This is a “must have” approach to adopt DevOPS culture!</p><p>IMHO, DevOPS culture is a way to embrace “work smart, not hard” philosophy. It’s a mindset to design robust, agile and simple IT environments that produce much output with less input and a way to gaining full control over them with minimal effort!</p><p>This is why I like the clever tools that can be used to achieve to build or manage such an environment. No doubt, one of them is ansible. It’s a simple and agentless automation tool that comes with a comfortable UI like Ansible AWX (or Tower). It also has, less learning curve than others.</p><p>Anyway, Let’s focus on how to install Ansible AWX via docker-compose.</p><h3>Ansible AWX</h3><p>Actually AWX is an open-source version of Ansible Tower which provide a web interface to manage environments via ansible.</p><p>Here’s a quick explanation from <a href="https://www.ansible.com/products/awx-project/faq">AWX project FAQ</a>:</p><blockquote>The AWX Project — AWX for short — is an open source community project, sponsored by Red Hat, that enables users to better control their Ansible project use in IT environments. AWX is the upstream project from which the Red Hat Ansible Tower offering is ultimately derived.</blockquote><p>So, if you’re following ansible’s path to manage your environments, using such a UI might be useful. Because it has some good features which mentioned in the <a href="https://www.ansible.com/products/tower">Tower</a> website:</p><blockquote>Red Hat® Ansible® Tower helps you scale IT automation, manage complex deployments and speed productivity. Centralize and control your IT infrastructure with a visual dashboard, role-based access control, job scheduling, integrated notifications and graphical inventory management. And Ansible Tower’s REST API and CLI make it easy to embed Ansible Tower into existing tools and processes.</blockquote><p>In particular, <strong>job scheduling,</strong> <strong>role-based access controlling</strong> and its <strong>REST-API</strong>, make it worth using.</p><h3>AWX Installation</h3><p>Basically, there are some options for deployment. It can be installed on docker (via docker-compose), kubernetes or openshift. But for the sake of the simplicity I’ll document the installation based on docker on a latest CentOS 7 host.</p><h4>Prerequisites:</h4><p>In order to deploy AWX on a CentOS box, you need to have at least 4GB memory and 20GB disk space. If you decided to use AWX on production, you need to take a look <a href="https://docs.ansible.com/ansible-tower/2.2.2/html/installandreference/requirements_refguide.html">Tower’s requirements page</a> for more information.</p><p>First of all let’s install docker engine and docker-compose from docker repo.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/439955d09f90e79741990e295e79c03e/href">https://medium.com/media/439955d09f90e79741990e295e79c03e/href</a></iframe><h4>Download</h4><p>Now, download the latest AWX release tarball to the home directory and extract it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/08d47227cd563c9a4592576dec47c3f6/href">https://medium.com/media/08d47227cd563c9a4592576dec47c3f6/href</a></iframe><h4>Initial Configurations</h4><p>The tarball includes bunch of ansible playbooks and roles to install AWX with an inventory file that holds default configuration variables. We are going to change the inventory file (<em>located at </em><strong><em>installer/invetory</em></strong>) in order to deploy AWX with additional configurations like https support, official logos etc.</p><p>By default, installer deploys AWX via docker-compose and uses official awx docker images which published on dockerhub to creates postgresql, rabbitmq, memcached, and a nginx based frontend container that required by AWX.</p><p>But if you want to do some additional configurations like https support; you need to build your local awx images by comment out <strong><em>dockerhub_base=ansible</em></strong> line as it says in the official documentation:</p><blockquote>Remove these lines if you want to run a local image build<br>Otherwise the setup playbook will install the official Ansible images. Versions may be selected based on: latest, 1, 1.0, 1.0.0, 1.0.0.123<br>by default the base will be used to search for ansible/awx_web and ansible/awx_task</blockquote><p>So let’s comment out that line:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/becd5bb58a9fd5232a6cb26d6a47df0f/href">https://medium.com/media/becd5bb58a9fd5232a6cb26d6a47df0f/href</a></iframe><blockquote><strong>Note</strong>: As of AWX version 6.1.0, the only way to enable https support is building local images. So, if you want to use official awx images from dockerhub, you SSL setup that we cover later in this post, cannot be done!</blockquote><p>As mentioned above, AWX requires a postgreSQL database and installer will automatically create a psql container for it. But in order to keep data in a persistent location, we need to create a folder to hold the db files in the host system and tell its path to the installer via <strong><em>postgres_data_dir</em></strong> variable.</p><p>So I’ll create a folder named <strong><em>awx-psql-data</em></strong> in /opt directory and define it in the inventory.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4b35eab7ddff69ee61002ffe57c56625/href">https://medium.com/media/4b35eab7ddff69ee61002ffe57c56625/href</a></iframe><blockquote><strong>Note</strong>: If you wish to use an external database, in the inventory file, set the value of <strong>pg_hostname</strong>, and update <strong>pg_username</strong>, <strong>pg_password</strong>, <strong>pg_database</strong>, and <strong>pg_port</strong> with the connection information.</blockquote><p>Next parameters we’ll change is <strong><em>ssl_certificate</em></strong> which tells the installer enable https support for the frontend with the provided ssl key pair. (By default it only supports http.). With this parameter, we need to provide “a file” that should includes both a SSL certificate and its private key.</p><p>In this step, I’ll create a self-signed SSL located at /etc/awx-ssl/ folder and merge the .key and .crt files in another file at <em>/etc/awx-ssl/awx-bundled-key.crt.</em><strong><em> </em></strong>Then I’ll pass the file path to <strong><em>ssl_certificate</em></strong> variable:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a7ae926d634ca96f565e153aaddf8b45/href">https://medium.com/media/a7ae926d634ca96f565e153aaddf8b45/href</a></iframe><p>Next changes is use of eye-candy AWX logos from <a href="https://github.com/ansible/awx-logos">awx-logos</a> repository. I think the default logo that comes with AWX itself is horrible, so changing it might be good choice (believe me :)</p><p>In order to replace the default logo, we need to get awx-logos repository and place it to next to your awx folder (awx-6.1.0 in our example):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f1c307665aa86ad5f7f6b413a716863c/href">https://medium.com/media/f1c307665aa86ad5f7f6b413a716863c/href</a></iframe><p>So our, working directory should be look something like below:</p><pre>drwxrwxr-x 9 root root 4096 Jul 18 17:08 awx-6.1.0<br>drwxrwxr-x 3 root root 55 Sep 1 2017 awx-logos</pre><blockquote><strong>Note</strong>: AWX installer which resides at <strong>installer/install.yml</strong>, searches for “<strong>awx-logos</strong>” directory as “<strong>../../awx-logos</strong>”. So, your awx-logos folder should be located at where awx installer’s parent directory is placed.</blockquote><p>After put the logo directory the correct location, we need to set <strong><em>awx_official</em></strong> parameter as true:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d6018eb4e3f3588db534f482fd3fab6f/href">https://medium.com/media/d6018eb4e3f3588db534f482fd3fab6f/href</a></iframe><p>Next, we need to change <strong><em>admin_user</em></strong>and<em> </em><strong><em>admin_password</em></strong>parameters. By default, installer creates a super user and you can access the AWX ui with “<em>admin/password</em>” credentials. However changing the defaults is a good habit.</p><p>So let’s change them as user: <strong><em>awx-admin</em></strong> and pass: <strong><em>CHANGE_ME</em></strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c68c8a3864641427ecb124ee1eff3f26/href">https://medium.com/media/c68c8a3864641427ecb124ee1eff3f26/href</a></iframe><blockquote><strong>Note</strong>: Set the credentials as your standards. Also there are some other credential parameters like <strong>secret_key</strong> and <strong><em>rabbitmq_password</em></strong> etc. But I don’t cover all of them in this post. If you want to change them as well, you can take a look the inventory file since the comment lines explain everything nicely.</blockquote><h4>Installation</h4><p>As I said before, AWX comes with a installer ansible playbooks/roles which makes the whole installation process simple. When you done with your configuration changes in the inventory file, you can just run the installer playbook like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d157556b2932a5eadd8c997c79c3c93d/href">https://medium.com/media/d157556b2932a5eadd8c997c79c3c93d/href</a></iframe><p>Based on compute power of your host, installation process takes a while (Since, we build local docker images for awx). But if everything goes well it should be completed successfully and you can access to AWX UI by via <a href="https://your-hosts-ip/">https://your-hosts-ip/</a></p><p>After initial setup, AWX upgrades itself; and when you visit the UI, you see that notice.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vPG4kGzc13y5mz6_WPC8pA.png" /><figcaption>AWX is upgrading!</figcaption></figure><p>And after a while, login screen appears.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2HAVdEU8PHo_nyU2_X0CZw.png" /><figcaption>AWX login page</figcaption></figure><p>And AWX is ready to use:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XVoUR1glFfQP_jPNLLMxMg.png" /></figure><p>As you noticed, there is a demo project already deployed and you can examine it as a good usage reference. But, in the next post, I’m gonna publish a document about possible usage scenarios and best practices too.</p><p>Stay tuned.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5861b115455a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/ansible-awx-installation-5861b115455a">Ansible AWX Installation</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Zabbix v4 and Grafana Installation Script for CentOS 7]]></title>
            <link>https://medium.com/swlh/zabbix-v4-and-granfana-installation-script-for-centos-7-ad4179758a1f?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/ad4179758a1f</guid>
            <category><![CDATA[grafana]]></category>
            <category><![CDATA[script]]></category>
            <category><![CDATA[installation]]></category>
            <category><![CDATA[zabbix]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Tue, 03 Sep 2019 07:46:09 GMT</pubDate>
            <atom:updated>2020-10-18T14:55:24.432Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>UPDATE</strong>: Since, I have just updated to project to bump zabbix version 5.0 and grafana 7.2.1, this page is moved to <a href="https://medium.com/@cagri.ersen/zabbix-v5-0-and-grafana-automated-installation-script-6cd1a87cc36a">https://medium.com/@cagri.ersen/zabbix-v5-0-and-grafana-automated-installation-script-6cd1a87cc36a</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ad4179758a1f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/zabbix-v4-and-granfana-installation-script-for-centos-7-ad4179758a1f">Zabbix v4 and Grafana Installation Script for CentOS 7</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Production Ready Kubernetes Installation with kubespray.]]></title>
            <link>https://medium.com/swlh/production-ready-kubernetes-installation-with-kubespray-97063e8a846c?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/97063e8a846c</guid>
            <category><![CDATA[k8s]]></category>
            <category><![CDATA[kubespray]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[installation]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Mon, 01 Apr 2019 09:14:11 GMT</pubDate>
            <atom:updated>2020-06-08T07:51:39.362Z</atom:updated>
            <content:encoded><![CDATA[<p>Kubernetes (k8s) is one of the most popular container orchestration platform for the last few years and almost all cloud providers have native support for it. For instance, Google Cloud Platform offers GKE (Google Kubernetes Engine) which you can deploy a k8s cluster with a few clicks. Also AWS EKS and DigitalOcean Kubernetes Service introduced last year.</p><p>So you can easily build a kubernetes cluster on many cloud platforms; but what if you need a production ready, fully redundant cluster in your on-premise environment ?</p><p>In this situation, there are <a href="https://kubernetes.io/docs/setup/pick-right-solution/">bunch of solutions</a> that can be used to build such a cluster and I personally prefer to use <a href="https://github.com/kubernetes-sigs/kubespray">kubespray</a>.</p><p>Kubespray is a project that contains a bunch of ansible playbooks which deploy kubernetes clusters in an automated way. It is very suitable installation, configuration and maintaining method for on-premise environments since you can use it to deploy kubernetes clusters on different linux distros like, Ubuntu, Coreos and Centos.</p><p>So, in this document I want to explain how to install, configure and maintain a CentOS7 based kubernetes cluster with kubespray.</p><h3>Requirements</h3><p>In order to deploy a fully redundant k8s cluster, you should ideally have at least nine hosts, which three hosts for masters, three for workers and three for etcd cluster. However, in the sake of the simplicity we are going to use only three nodes which all components will be installed on these three nodes, so each node will have master, worker and etcd roles.</p><blockquote><em>But again, don’t forger that, if your cluster will be used as a production environment that run a critical workload, all k8s components should be installed on a separated host groups.</em></blockquote><p>Also, as we now kubespray uses ansible, there should be a host with ansible installation that holds the playbooks and configurations which responsible for running playbooks against k8s-nodes over SSH to deploy the cluster. This host can be a separated or you can use one of the k8s-nodes for this purpose. In our example we’ll use three hosts named <strong><em>k8s-host-0{1,2,3}</em></strong> and ansible host will be <strong><em>k8s-host-01</em></strong></p><p>There are two main preparation part of this document:</p><ul><li>Ansible host preperations</li><li>k8s nodes preperations.</li></ul><p>After preperation steps we’ll initiate kubespray cluster.yml playbook to deploy our first cluster.</p><p>So let’s get started with ansible preperation tasks.</p><h3>Preparing ansible</h3><p>As we’ve mentioned above our ansible host will be <strong>k8s-host-01</strong> and in this section all necessary action steps will be done in this host.</p><p>First of all disable selinux and firewalld:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/95d3d679df53fd48e99e66c4641fd39d/href">https://medium.com/media/95d3d679df53fd48e99e66c4641fd39d/href</a></iframe><p>Then install required packages by kubespray:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1d0ee575f28f492dae7e4d76a5894ca5/href">https://medium.com/media/1d0ee575f28f492dae7e4d76a5894ca5/href</a></iframe><p>Now we’ll create an user named kubespray and add to sudo (as NOPASSWD). Actually this is not a “must have” situation but I think isolating the kubespray from the root useris a good practise.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f6bfe6aa4ebaedee9ac5f8ae96b6ba28/href">https://medium.com/media/f6bfe6aa4ebaedee9ac5f8ae96b6ba28/href</a></iframe><p>Switch to kubespray and create an SSH key pair. We’ll use this keypair to establish passwordless connections to k8s nodes.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a0b8bcb094093ba496b43d5fc509931b/href">https://medium.com/media/a0b8bcb094093ba496b43d5fc509931b/href</a></iframe><p>Get the latest kubespray release from <a href="https://github.com/kubernetes-incubator/kubespray/releases">https://github.com/kubernetes-incubator/kubespray/releases</a> and extract it. (As the date of this document has written/updated the latest version was 2.13.1)</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c70f0602d70380ed6a1f0c7762fff4a8/href">https://medium.com/media/c70f0602d70380ed6a1f0c7762fff4a8/href</a></iframe><p>Now we install required python packages by using pip</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0e15e74c1084885aa2a84a409cfc4b9a/href">https://medium.com/media/0e15e74c1084885aa2a84a409cfc4b9a/href</a></iframe><p>Next steps are related to cluster configurations and there are some variables which shoud be defined correctly in you environment.</p><p>By default, when you initiate a cluster deployment via kubespray, it sets the cluster name as <strong>cluster.local</strong>, we’ll change it via environment variables. So please change $CLUSTER_NAME and $CLUSTER_DOMAIN variables as your needs.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b3763462ecf70514f526f31b2e3c6e8c/href">https://medium.com/media/b3763462ecf70514f526f31b2e3c6e8c/href</a></iframe><p>Create a directory that presents your cluster name. This directory will contains cluster specific ansible inventory and variable files and we get those files by copying them from the sample directory.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d31832195fe56d683ff10fff5fea9b94/href">https://medium.com/media/d31832195fe56d683ff10fff5fea9b94/href</a></iframe><p>Now our ansible environment is ready to be configured as our specifis needs. There are two main configuration files named <strong>all.yml</strong> and <strong>k8s-cluster.yml</strong> and both files is placed in “<em>inventory/$CLUSTER_NAME/group_vars/</em>” directory.</p><p>Let’s change the cluster name first.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b3506dbf70a30004dfc400e502be17ff/href">https://medium.com/media/b3506dbf70a30004dfc400e502be17ff/href</a></iframe><p>And set token auth modes true:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b163a5ac6a85ca9ab7e1b393ee14745d/href">https://medium.com/media/b163a5ac6a85ca9ab7e1b393ee14745d/href</a></iframe><p>I prefer to use weave as CNI (for further reading, here is a good comparison <a href="https://chrislovecnm.com/kubernetes/cni/choosing-a-cni-provider/">https://chrislovecnm.com/kubernetes/cni/choosing-a-cni-provider/</a>) so we set the <strong><em>kube_network_plugin</em></strong> variable as weave and define a password for encryption.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/78b3e4bac613b729cbfdbe7b34192603/href">https://medium.com/media/78b3e4bac613b729cbfdbe7b34192603/href</a></iframe><p>also <strong><em>kube_read_only_port</em></strong> variable should be enabled in order to metrics-server work.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5f5679a8b7ca1ed2e5228ca67bb397ea/href">https://medium.com/media/5f5679a8b7ca1ed2e5228ca67bb397ea/href</a></iframe><p>rename the <em>hosts.ini</em> file as <em>inventory.cfg</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/be9314c7d253c33404ef4d7525cf640a/href">https://medium.com/media/be9314c7d253c33404ef4d7525cf640a/href</a></iframe><p><em>inventory.cfg</em> file is main configuration file that defines the k8s host roles. So you need to be set as properly. In our example we have three nodes named k8s-host-0{1,2,3} so our inventory.cfg should look like below:</p><p><strong>Note:</strong> You need to also set the bastion and ip variable options if your network requires it. And there is an another section related to nginx ingress controller. But we’ll deploy ingress controller separately on the next post (in a more customized way) we don’t use the ingress section of inventory so simply delete it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a289c3d4b3057a29a1d68d3cfd3e3475/href">https://medium.com/media/a289c3d4b3057a29a1d68d3cfd3e3475/href</a></iframe><blockquote><strong>A few notes about connectivity:<br>- </strong>Your nodes should be resolve each other hostname. So create proper DNS records or add them to the hosts file.</blockquote><blockquote>- Since we use a custom user for SSH connections, you need to set kubespray as remote_user in ansible.cfg file’s [default] section as below:</blockquote><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a7c63b2961b4ea5ed20cc224866ec2b3/href">https://medium.com/media/a7c63b2961b4ea5ed20cc224866ec2b3/href</a></iframe><blockquote><strong>Not: </strong>If you use a different port for sshd, you also need to change remote_port definition.</blockquote><p>Ansible host preparation is done and now we will configure our k8s nodes.</p><h3>Preparing k8s nodes</h3><p>These steps should be done on all k8s nodes.</p><p>Disable selinux and firewalld:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/95d3d679df53fd48e99e66c4641fd39d/href">https://medium.com/media/95d3d679df53fd48e99e66c4641fd39d/href</a></iframe><p>Swap should be disabled on all nodes otherwise kubespray will fail. So let’s disable it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ba6d0ed6869d883db854e61ffae54a5/href">https://medium.com/media/4ba6d0ed6869d883db854e61ffae54a5/href</a></iframe><p>And add kubespray user and setup sudoers for it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2ed63a7553118cd906c9c8fafeae21b4/href">https://medium.com/media/2ed63a7553118cd906c9c8fafeae21b4/href</a></iframe><p>Now you need to distribute the ansible host’s (k8s-host-01) pubkey to the k8s nodes’ kubespray user as authorized key. And you need to also put the pubkey to host-01’s authorized_keys file, since ansible will make a ssh connection for itself too by using its primary ip address.</p><h3>Deploy kubernetes cluster from the ansible instance</h3><p>OK, we are ready to deploy cluster. It’s a very simple step, only run <em>cluster.yml</em> playbook as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4b52df98f2c5c9d80ca0b5315a2f3fbf/href">https://medium.com/media/4b52df98f2c5c9d80ca0b5315a2f3fbf/href</a></iframe><p>After deployment process the cluster should be installed properly.</p><h3>Accessing kubernetes API from k8s-node with kubectl</h3><p>By default kubernetes API can be accessible via kubectl from any k8s host.</p><p>Let’s check it on host-01:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/98b9379695baf726e10783e2d5b5dbdd/href">https://medium.com/media/98b9379695baf726e10783e2d5b5dbdd/href</a></iframe><p>As you can see our k8s cluster is ready and all nodes act as master and worker (also etcd host).</p><h3>Accessing kubernetes API from a remote kubectl</h3><p>When you initiate cluster deployment, by default kubespray adds an admin user to the cluster named “<em>kube</em>”. And its password written down to “<em>inventory/$CLUSTER_NAME/credentials/kube_user.creds</em>” file.</p><p>Although we’ll create a user and join it to a new admin group in order to do more granular access control (also this will interact to the kubernetes API by using its PKI key pair instead of basic auth.) on the next section, if you still want to use the default user to connect kubernetes you can follow the steps below:</p><p>Create a kubeconfig file on your local machine.</p><p><strong>Note:</strong> Your local machine should meets the requirements listed below:</p><ul><li>Able to access to kubernetes API on <a href="https://kubernetes.default.svc.$cluster_fqdn:6443/"><em>https://kubernetes.default.svc.$CLUSTER_FQDN:6443</em></a></li><li>Masters tcp port 6443 needs to be accessible from your client.</li><li><em>$CLUSTER_FQDN</em> resolves to one of the master’s primary IP addresses (or you can load balance them).</li></ul><p>And paste the snippet:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6fbd40af0119b20ffe0fdd1260b542e0/href">https://medium.com/media/6fbd40af0119b20ffe0fdd1260b542e0/href</a></iframe><p>And change the variables listed below:</p><ul><li><strong><em>$CLUSTER_FQDN:</em></strong> should be changed as your cluster <em>FQDN</em></li><li><strong><em>certificate-authority-data:</em></strong> It has to contains <em>/etc/kubernetes/ssl/ca.crt</em> information as one-line base64 encoded format. To grab it as required format, you can run something like this in one of your master node: <br>cat /etc/kubernetes/ssl/ca.crt |base64 -w 0 then copy the output and paste it to <em>$CLUSTER_CA_CRT_BASE64_IN_ONE_LINE</em> section.</li><li><strong>password:</strong> This needs to be changed with the password that stored in<strong><em> “</em></strong><em>inventory/$CLUSTER_NAME/credentials/kube_user.creds”</em> file.</li></ul><p>If you prepare the kubeconfig properly, you can set this file as kubectl config via KUBECONFIG variable:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/859ffb6433ad339dce123c2d3b439aa4/href">https://medium.com/media/859ffb6433ad339dce123c2d3b439aa4/href</a></iframe><p>Now you can test your connectivity like below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/54e3c243759d812c5f80edfe5b72b4c4/href">https://medium.com/media/54e3c243759d812c5f80edfe5b72b4c4/href</a></iframe><p>Well, our cluster is formed and we have connected it by using the basic authentication method. Now we’ll do some customization like creating a new admin user which will use PKI instead of basic auth as mentioned above.</p><h3>Additional Configurations</h3><p>In this chapter we’ll cover the additional configuration steps listed below:</p><ul><li>Create an admin group and an user</li><li>Dashboard Settings</li><li>Metrics Server Deployment</li><li>Nginx Ingress Controller Deployment</li></ul><h4>Create an user with administrator privileges</h4><p>This section consists of four phases:</p><ol><li>Create a “ClusterRole” for the custom admin group</li><li>Create a “ClusterRoleBinding” for our newly created “ClusterRole”</li><li>Create an user certificate for kubectl</li><li>kubeconfig configuration</li></ol><p><strong>1. Create a ClusterRole</strong></p><p>First of all we’re going to create a cluster wide role with full permission to any api group. In our example the ClusterRole name is <strong><em>cluster-admins-cr</em></strong>. So each binding member to this ClusterRole, can be able to access any kubernetes API group.</p><p>On one of your master hosts, run:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/31dc5e48d0278e593302d4004db6fc99/href">https://medium.com/media/31dc5e48d0278e593302d4004db6fc99/href</a></iframe><p><strong>2. Create a ClusterRoleBinding</strong></p><p>Next, we create a “<strong><em>ClusterRoleBinding</em></strong>” to give access “<strong><em>ClusterRole</em></strong>” named <strong><em>cluster-admins-cr</em></strong> for members of <strong><em>cluster-admins</em></strong> group.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a4491d141f69056bd381267eec1c8fc6/href">https://medium.com/media/a4491d141f69056bd381267eec1c8fc6/href</a></iframe><p>This creates a ClusterRoleBinding named <strong><em>cluster-admins-crb</em></strong>, binds it to <strong><em>cluster-admins-cr</em></strong><em> </em>ClusterRole<em> </em>and permits ClusterRole’s privileges (which is permitted for all API group calls) for each users who provide a certificate that issued by this kubernetes cluster CA. Also as we’ll see the next section, any user certificate organization (O) should be defined as the group name to access the cluster with admin privileges.</p><p><strong>3. Crate an user certificate</strong></p><p>When you create a user certificate, there are two important variable that you need to define before:</p><ul><li><strong><em>$USERNAME:</em></strong> This is the username to be created</li><li><strong><em>$GROUP_NAME</em>:</strong> This is the group name which we’ll be bind our admin “ClusterRole” that we’ve created the last section. Our example is “<strong>cluster-admins</strong>”</li></ul><p>So let’s define them first as environment variables:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3364d50c2c7f04cf7824fd2836b4890a/href">https://medium.com/media/3364d50c2c7f04cf7824fd2836b4890a/href</a></iframe><p>Now we ready to issue for our user by using our cluster’s CA. We’ll run our commands as root and create a hidden folder in root’s home directory to place the generated certificates which should be keep as secure as possible.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/83db6a59af6464723bcc845bd39c7519/href">https://medium.com/media/83db6a59af6464723bcc845bd39c7519/href</a></iframe><p>Our user certificate should be issued, let’s check it:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b146ec0262ac4a447c348c32f2c2621d/href">https://medium.com/media/b146ec0262ac4a447c348c32f2c2621d/href</a></iframe><p>Now we can configure our kubeconfig by using these PKI files.</p><p><strong>4. kubeconfig Configuration</strong></p><p>Create a kubeconfig file on your local machine and populate it as follows and change the variables as your setup.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6417411c02f8ba1530f210ba72d46019/href">https://medium.com/media/6417411c02f8ba1530f210ba72d46019/href</a></iframe><p>In order to set kubeconfig properly these variables should be defined:</p><ul><li><strong>$CLUSTER_CA_CRT_CONTENT:</strong> <br>This is your kubernetes cluster CA public key file which places in /etc/kubernetes/ssl/ca.crt on any master hosts. It have to be presented in one-line base64 encoded format in kubeconfig. To grab it in correct format run: # cat /etc/kubernetes/ssl/ca.crt |base64 -w 0 and copy the output to paste it as <strong><em>certificate-authority-data</em></strong><em> </em>value.</li><li><strong>CLUSTER_FQDN:</strong> <br>This is our clusters name variable which we’ve defined in installation section.</li><li><strong>USERNAME:</strong> In our example this is <em>my-admins-username</em></li><li><strong>USERNAME.CRT_CONTENT:</strong> This is our user’s certificate content and its should be as one line base64 encoded format too. You can convert it as follow: # cat ~/.k8s-user-certs/my-admins-username.crt |base64 -w 0 and use it as <strong><em>client-certificate-data</em></strong></li><li><strong>USERNAME.KEY_CONTENT:</strong> This is our user’s private key content and also its must be as one line base64 encoded format: <br># cat ~/.k8s-user-certs/my-admin-user.key |base64 -w 0 copy and paste it as <strong><em>client-key-data</em></strong></li></ul><p>If you populate the kubeconfig you can set this file as kubectl config via KUBECONFIG variable:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1c3090ea2606cbee9361fcd451363dd8/href">https://medium.com/media/1c3090ea2606cbee9361fcd451363dd8/href</a></iframe><p>Now test your connectivity:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e32601579ac6d850ecdab9f4bb40d9c1/href">https://medium.com/media/e32601579ac6d850ecdab9f4bb40d9c1/href</a></iframe><h4><strong>Kubernetes Dashboard Settings</strong></h4><p>Kubespray installs the dashboard by default (since k8s 1.7) and you can authenticate by using the default kube admin user. However we will create a service account with admin privileges and use its token to access the dashboard.</p><p>Create an Service account named dashboard-admin:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6adaad8013d31c733b3ce1a334d09dcf/href">https://medium.com/media/6adaad8013d31c733b3ce1a334d09dcf/href</a></iframe><p>Then create an ClusterRoleBinding for the dashboard-admin ServiceAccount and assign it to cluster-admin ClusterRole.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/50cc1ded4e936697d3db91e714a35c30/href">https://medium.com/media/50cc1ded4e936697d3db91e714a35c30/href</a></iframe><p>Now you can extract the bearer token from the dashboard-admin ServiceAccount to use it on the dashboard authentication.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/532907bc3c591c08b9449a716e9c951c/href">https://medium.com/media/532907bc3c591c08b9449a716e9c951c/href</a></iframe><p>Now run <strong><em>kubectl proxy</em></strong> command then visit <a href="http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/login">http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/login</a></p><p>In the login section you can select token based authentication option and provide your bearer-token to login.</p><h4>Metrics Server Deployment</h4><p>Please see:</p><p><a href="https://medium.com/@cagri.ersen/kubernetes-metrics-server-installation-d93380de008">Kubernetes metrics-server Installation</a></p><h4>Nginx Ingress Controller Deployment</h4><p>Nginx controller deployment post will be published soon.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=97063e8a846c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/production-ready-kubernetes-installation-with-kubespray-97063e8a846c">Production Ready Kubernetes Installation with kubespray.</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kubernetes metrics-server Installation]]></title>
            <link>https://medium.com/@cagri.ersen/kubernetes-metrics-server-installation-d93380de008?source=rss-c815b7843f78------2</link>
            <guid isPermaLink="false">https://medium.com/p/d93380de008</guid>
            <category><![CDATA[metrics-server]]></category>
            <category><![CDATA[kubespray]]></category>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[Cagri Ersen]]></dc:creator>
            <pubDate>Sun, 17 Mar 2019 20:08:44 GMT</pubDate>
            <atom:updated>2020-02-24T12:46:21.147Z</atom:updated>
            <content:encoded><![CDATA[<p>As you know (or not) Heapster was marked as deprecated with Kubernetes version 1.11 and as you can see in its <a href="https://github.com/kubernetes-retired/heapster/blob/master/docs/deprecation.md">documentation</a> it’s totally retired now (with the latest version 1.13)</p><p>So, if you want to use k8s’ features like <strong><em>horizontal pod autoscaler</em></strong> which was depended on heapster (or even to be able to use <strong><em>kubectl top</em></strong> command) you need to use metrics-server instead.</p><h3>What is metrics-server ?</h3><p><a href="https://github.com/kubernetes-incubator/metrics-server">Metrics Server</a> is a cluster-wide aggregator of resource usage data. It collects metrics like CPU or memory consumption for containers or nodes, from the Summary API, exposed by <a href="https://kubernetes.io/docs/admin/kubelet/">Kubelet</a> on each node. If your kubernetes is formed by kube-up.sh script then you probably have a metrics-server by default. But if you use kubespray or kops to build a production ready k8s cluster, then you need to deploy separately.</p><h3>How to deploy ?</h3><p>Though, I prefer to use kubespray and this document is based on k8s clusters which deployed by kubespray, installation steps are same all other solutions like kops etc.</p><h4>Requirements</h4><p>In order to deploy metrics-server, <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/">aggregation layer</a> should be enabled in your k8s cluster. As of kubespray version 2.8.3, aggregation layer is already up by default. If you need to enable it especially see the link below. <a href="https://kubernetes.io/docs/tasks/access-kubernetes-api/configure-aggregation-layer/">https://kubernetes.io/docs/tasks/access-kubernetes-api/configure-aggregation-layer/</a></p><h4>Deployment</h4><p>Clone metrics-server git repo from <a href="https://github.com/kubernetes-incubator/metrics-server">https://github.com/kubernetes-incubator/metrics-server</a> and deploy it as follow:</p><pre>git clone https://github.com/kubernetes-incubator/metrics-server.git<br>cd metrics-server<br>kubectl apply -f deploy/kubernetes/</pre><p>kubectl apply step sets an apiservice named <strong><em>v1beta1.metrics.k8s.io</em></strong>, creates a deployment named <strong><em>metrics-server</em></strong> and configures a <strong><em>service</em></strong> for the deployment.</p><p>If everything gone well, you can check the apiservice like below:</p><pre>$ <strong>kubectl get apiservices |egrep metrics</strong><br><em>v1beta1.metrics.k8s.io                  kube-system/metrics-server   True        11h</em></pre><p>Also, there should be a deployment and a service named metrics-server:</p><pre>$ <strong>kubectl get deploy,svc -n kube-system |egrep metrics-server</strong></pre><pre><em>deployment.extensions/metrics-server 1 1 1 1 11h<br>service/metrics-server ClusterIP 10.233.31.135 &lt;none&gt; 443/TCP 11h</em></pre><p>And if you run kubectl top nodecommand you should see node utilizations:</p><pre>$ <strong>kubectl top node<br></strong>NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%<br>k8s012-node001 667m 8% 2683Mi 17%<br>k8s012-node002 828m 10% 2489Mi 16%<br>k8s012-node003 431m 5% 2424Mi 15%</pre><p>Lastly you can call the apiservice via kubectl; for example this should return basic node metrics:</p><pre>$ <strong>kubectl get --raw &quot;/apis/metrics.k8s.io/v1beta1/nodes&quot; |jq .<br></strong>{<br>  &quot;kind&quot;: &quot;NodeMetricsList&quot;,<br>  &quot;apiVersion&quot;: &quot;metrics.k8s.io/v1beta1&quot;,<br>  &quot;metadata&quot;: {<br>    &quot;selfLink&quot;: &quot;/apis/metrics.k8s.io/v1beta1/nodes&quot;<br>  },<br>  &quot;items&quot;: [<br>    {<br>      &quot;metadata&quot;: {<br>        &quot;name&quot;: &quot;k8s012-node001&quot;,<br>        &quot;selfLink&quot;: &quot;/apis/metrics.k8s.io/v1beta1/nodes/k8s002-node001&quot;,<br>        &quot;creationTimestamp&quot;: &quot;2019-03-17T20:01:12Z&quot;<br>      },<br>      &quot;timestamp&quot;: &quot;2019-03-17T20:01:10Z&quot;,<br>      &quot;window&quot;: &quot;30s&quot;,<br>      &quot;usage&quot;: {<br>        &quot;cpu&quot;: &quot;1118046599n&quot;,<br>        &quot;memory&quot;: &quot;2748964Ki&quot;<br>      }<br>    },<br>    {<br>      &quot;metadata&quot;: {<br>        &quot;name&quot;: &quot;k8s012-node002&quot;,<br>        &quot;selfLink&quot;: &quot;/apis/metrics.k8s.io/v1beta1/nodes/k8s002-node002&quot;,<br>        &quot;creationTimestamp&quot;: &quot;2019-03-17T20:01:12Z&quot;<br>      },<br>      &quot;timestamp&quot;: &quot;2019-03-17T20:01:02Z&quot;,<br>      &quot;window&quot;: &quot;30s&quot;,<br>      &quot;usage&quot;: {<br>        &quot;cpu&quot;: &quot;483888352n&quot;,<br>        &quot;memory&quot;: &quot;2545964Ki&quot;<br>      }<br>    },<br>    {<br>      &quot;metadata&quot;: {<br>        &quot;name&quot;: &quot;k8s012-node003&quot;,<br>        &quot;selfLink&quot;: &quot;/apis/metrics.k8s.io/v1beta1/nodes/k8s002-node003&quot;,<br>        &quot;creationTimestamp&quot;: &quot;2019-03-17T20:01:12Z&quot;<br>      },<br>      &quot;timestamp&quot;: &quot;2019-03-17T20:01:02Z&quot;,<br>      &quot;window&quot;: &quot;30s&quot;,<br>      &quot;usage&quot;: {<br>        &quot;cpu&quot;: &quot;454428677n&quot;,<br>        &quot;memory&quot;: &quot;2481852Ki&quot;<br>      }<br>    }<br>  ]<br>}</pre><p>Not: <strong>jq</strong> is for good looking json output.</p><h4>Troubleshooting</h4><p>If kubectl get --raw &quot;/apis/metrics.k8s.io/v1beta1/nodes&quot; command returns empty response and metrics-server pod throw an error like</p><pre>E0903  1 manager.go:102] unable to fully collect metrics: [unable to fully scrape metrics from source kubelet_summary:&lt;hostname&gt;: unable to fetch metrics from Kubelet &lt;hostname&gt; (&lt;hostname&gt;): Get https://&lt;hostname&gt;:10250/stats/summary/: dial tcp: lookup &lt;hostname&gt; on 10.96.0.10:53: no such host</pre><p>This error is related to a known issue as of metrics-server v0.3.1 which reported here <a href="https://github.com/kubernetes-incubator/metrics-server/issues/131">https://github.com/kubernetes-incubator/metrics-server/issues/131</a></p><p>To fix the issue, you need to edit metrics-server-deployment.yaml and add the parameters below right after image: k8s.gcr.io/metrics-server-amd64:v0.3.1 line:</p><pre>command:<br>        - /metrics-server<br>        - --kubelet-insecure-tls<br>        - --kubelet-preferred-address-types=InternalIP</pre><p>then re-apply it as $ kubectl apply -f metrics-server-deployment.yaml</p><p>After few seconds, you can get metrics via kubectl get --raw &quot;/apis/metrics.k8s.io/v1beta1/nodes&quot; or kubectl top nodecommands.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d93380de008" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>