<?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"><channel><title><![CDATA[DevOps with Vic]]></title><description><![CDATA[DevOps with Vic]]></description><link>https://blog.nvmodeberesume.link</link><generator>RSS for Node</generator><lastBuildDate>Tue, 15 Sep 2026 00:45:45 GMT</lastBuildDate><atom:link href="https://blog.nvmodeberesume.link/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[🔧 Complete Guide: Setting Up a Kubernetes v1.36 Cluster with Kubeadm and Containerd on AWS]]></title><description><![CDATA[📑 Table of Contents

Introduction

Why Use kubeadm

System Requirements

Cluster Architecture Overview

Networking Requirements

Why Disable Swap

Understanding Cgroups and Drivers

Step-by-Step Setu]]></description><link>https://blog.nvmodeberesume.link/complete-guide-setting-up-a-kubernetes-v1-36-cluster-with-kubeadm-and-containerd-on-aws</link><guid isPermaLink="true">https://blog.nvmodeberesume.link/complete-guide-setting-up-a-kubernetes-v1-36-cluster-with-kubeadm-and-containerd-on-aws</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[kubeadm]]></category><category><![CDATA[AWS]]></category><category><![CDATA[containers]]></category><dc:creator><![CDATA[Victor Modebe]]></dc:creator><pubDate>Thu, 18 Jun 2026 01:15:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752076435936/1e590a6e-ae6f-4766-8764-a78d7edb88ab.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>📑 Table of Contents</strong></h2>
<ol>
<li><p><a href="#introduction">Introduction</a></p>
</li>
<li><p><a href="#introduction">Wh</a><a href="#why-use-kubeadm">y Use kube</a><a href="#introduction">adm</a></p>
</li>
<li><p><a href="#introduction">System Re</a><a href="#why-use-kubeadm">quir</a><a href="#introduction">ements</a></p>
</li>
<li><p><a href="#introduction">Cl</a><a href="#why-use-kubeadm">us</a><a href="#system-requirements">t</a><a href="#why-use-kubeadm">er A</a><a href="#introduction">rchitecture</a> <a href="#system-requirements">Ove</a><a href="#why-use-kubeadm">rview</a></p>
</li>
<li><p><a href="#introduction">Networking</a> <a href="#system-requirements">Req</a><a href="#why-use-kubeadm">uirements</a></p>
</li>
<li><p><a href="#why-use-kubeadm">Wh</a><a href="#cluster-topology-for-this-setup">y D</a><a href="#introduction">isable Swap</a></p>
</li>
<li><p><a href="#why-use-kubeadm">Understandin</a><a href="#introduction">g Cgroups an</a><a href="#system-requirements">d D</a><a href="#why-use-kubeadm">river</a><a href="#introduction">s</a></p>
</li>
<li><p><a href="#introduction">Step-by</a><a href="#networking-between-nodes">-S</a><a href="#system-requirements">t</a><a href="#why-use-kubeadm">ep Setup</a></p>
<ul>
<li><p>a<a href="#system-requirements">. L</a><a href="#cluster-topology-for-this-setup">a</a><a href="#system-requirements">unc</a><a href="#introduction">h EC2 Instan</a><a href="#system-requirements">ces</a></p>
</li>
<li><p>b<a href="#introduction">. Prepare All No</a><a href="#why-use-kubeadm">des</a></p>
</li>
<li><p>c. <a href="#why-use-kubeadm">Insta</a><a href="#introduction">ll and Confi</a><a href="#system-requirements">gur</a><a href="#why-use-kubeadm">e Contain</a><a href="#introduction">erd</a></p>
</li>
<li><p>d. <a href="#introduction">In</a><a href="#system-requirements">sta</a><a href="#why-use-kubeadm">ll Kubernetes B</a><a href="#cluster-topology-for-this-setup">inar</a><a href="#system-requirements">ies</a></p>
</li>
<li><p>e. <a href="#introduction">Initial</a><a href="#system-requirements">iz</a><a href="#cluster-topology-for-this-setup">e</a> <a href="#why-use-kubeadm">Control Plane</a></p>
</li>
<li><p>f<a href="#introduction">. Join Worke</a><a href="#system-requirements">r N</a><a href="#why-use-kubeadm">odes</a></p>
</li>
<li><p>g. <a href="#why-use-kubeadm">Inst</a><a href="#cluster-topology-for-this-setup">a</a><a href="#introduction">ll Network A</a><a href="#system-requirements">dd-</a><a href="#why-use-kubeadm">On</a></p>
</li>
</ul>
</li>
<li><p><a href="#introduction">Validation</a> <a href="#system-requirements">and</a> <a href="#why-use-kubeadm">Testing</a></p>
</li>
<li><p><a href="#why-use-kubeadm">Cl</a><a href="#introduction">eanup Reminder</a></p>
</li>
<li><p><a href="#why-use-kubeadm">Conclusion</a></p>
</li>
</ol>
<hr />
<h2><strong>📌 Introduction</strong></h2>
<p>Managed Kubernetes services such as EKS are convenient, but they hide many of the components that make Kubernetes work. In this guide you'll build a Kubernetes v1.36 cluster from scratch on AWS using kubeadm and containerd, giving you hands-on experience with the control plane, worker nodes, networking, and cluster bootstrapping.</p>
<hr />
<h2><strong>🛠️ Why Use kubeadm ?</strong></h2>
<p>kubeadm is a tool designed to <strong>simplify Kubernetes cluster setup</strong>.</p>
<p>It automates:</p>
<ul>
<li><p>Control plane component setup</p>
</li>
<li><p>Certificate generation</p>
</li>
<li><p>Worker node joining</p>
</li>
<li><p>Bootstrapping configs</p>
</li>
</ul>
<p>Without kubeadm, you’d need to manually configure:</p>
<ul>
<li><p>kube-apiserver</p>
</li>
<li><p>etcd</p>
</li>
<li><p>kube-scheduler</p>
</li>
<li><p>controller-manager</p>
</li>
<li><p>kubelet</p>
</li>
<li><p>kube-proxy</p>
</li>
<li><p>A container runtime (e.g., containerd)</p>
</li>
</ul>
<p>That’s a heavy lift—and error-prone. So let’s do it the smart way.</p>
<hr />
<h2><strong>🧱 System Requirements</strong></h2>
<p>Each node should have:</p>
<table>
<thead>
<tr>
<th><strong>Requirement</strong></th>
<th><strong>Value</strong></th>
</tr>
</thead>
<tbody><tr>
<td>OS</td>
<td>Ubuntu 24.04 LTS</td>
</tr>
<tr>
<td>RAM</td>
<td>≥ 4GB</td>
</tr>
<tr>
<td>CPU</td>
<td>≥ 2 vCPUs</td>
</tr>
<tr>
<td>Swap</td>
<td>Disabled</td>
</tr>
<tr>
<td>Ports Open</td>
<td>6443, 10250, 10256, 10259, 2379-2380</td>
</tr>
<tr>
<td>IPv4 Forwarding</td>
<td>Enabled</td>
</tr>
<tr>
<td>Hostnames</td>
<td>Unique per node</td>
</tr>
</tbody></table>
<blockquote>
<p>⚠️ Kubernetes cannot install the control plane on Windows. Worker nodes can be Windows, but only Linux supports the control plane.</p>
</blockquote>
<p>Note: For learning environments you may allow all TCP within the security group. For production environments restrict traffic to only required Kubernetes ports.</p>
<p>This rule <strong>only allows internal traffic</strong> — for example:</p>
<ul>
<li><p>Pods talking to each other across nodes</p>
</li>
<li><p>kubelet or containerd communicating</p>
</li>
<li><p>Control plane → worker communication</p>
</li>
</ul>
<p>✅ <strong>Good</strong> for Kubernetes <strong>node-to-node communication</strong></p>
<p>❌ <strong>NOT enough</strong> for traffic from <strong>outside AWS</strong> (like your laptop)</p>
<hr />
<h2><strong>🌐 Cluster Topology (for this setup)</strong></h2>
<ul>
<li><p><strong>Control Plane Node:</strong> t2.medium (2 vCPU, 4GB RAM)</p>
</li>
<li><p><strong>Worker Node 1:</strong> t2.medium (2 vCPU, 4GB RAM)</p>
</li>
<li><p><strong>Worker Node 2:</strong> t2.medium (2 vCPU, 4GB RAM)</p>
</li>
</ul>
<p>This is a <strong>cost-optimized test cluster</strong> for learning—not for heavy workloads.</p>
<hr />
<h2><strong>🔌 Networking Between Nodes</strong></h2>
<p>Kubernetes requires:</p>
<ul>
<li><p><strong>Full network connectivity</strong> between all nodes (ping from any to any)</p>
</li>
<li><p><strong>Open ports</strong> for components to talk (API server, kubelet, etc.)</p>
</li>
<li><p><strong>Seamless Pod-to-Pod communication</strong>, even across nodes</p>
</li>
</ul>
<p>Without this, your cluster may initialize but won’t function correctly.</p>
<hr />
<h2><strong>🚫 Why Disable Swap?</strong></h2>
<p>Kubernetes uses real-time memory metrics to schedule pods. If swap is enabled:</p>
<ul>
<li><p>The OS may “fake” available memory</p>
</li>
<li><p>Scheduler makes bad decisions</p>
</li>
<li><p>Pods crash unexpectedly</p>
</li>
</ul>
<p>So we <strong>must disable swap</strong> to ensure scheduling is predictable.</p>
<hr />
<h2><strong>🧠 Cgroups and Why They Matter</strong></h2>
<p><strong>Cgroups (Control Groups)</strong> are a Linux kernel feature that:</p>
<ul>
<li><p>Isolate and limit CPU/RAM for processes (like pods)</p>
</li>
<li><p>Help enforce resource requests/limits in Kubernetes</p>
</li>
</ul>
<p>Cgroup Drivers:</p>
<ul>
<li><p>systemd: Recommended for modern Ubuntu</p>
</li>
<li><p>cgroupfs: Older or alternate driver</p>
</li>
</ul>
<blockquote>
<p>⚠️ Both the container runtime and kubelet must use the <strong>same cgroup driver</strong>—or you’ll get errors. Thankfully, kubeadm sets kubelet to use systemd by default on modern distros.</p>
</blockquote>
<hr />
<h2><strong>⚙️ Step-by-Step Setup</strong></h2>
<h3><strong>a. Launch EC2 Instances</strong></h3>
<p>In AWS:</p>
<ul>
<li><p>Launch 3 EC2 instances</p>
</li>
<li><p>Ubuntu 24.04 LTS AMI or latest</p>
</li>
<li><p>SG allows <strong>internal communication</strong> (use custom SG, allow all TCP from self)</p>
</li>
</ul>
<p>Label your instances clearly: control-node, worker1, worker2.</p>
<hr />
<h3><strong>b. Prepare All Nodes</strong></h3>
<p>SSH into each node and run:</p>
<h4><strong>Set Hostname</strong></h4>
<pre><code class="language-plaintext">hostnamectl set-hostname control-node # or worker1 / worker2
</code></pre>
<h4><strong>Enable IPv4 Forwarding</strong></h4>
<pre><code class="language-plaintext">cat &lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
</code></pre>
<h4><strong>Disable Swap</strong></h4>
<pre><code class="language-plaintext">sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab
</code></pre>
<hr />
<h3><strong>c. Install and Configure Containerd</strong></h3>
<h4><strong>Install containerd</strong></h4>
<pre><code class="language-plaintext">sudo apt-get update &amp;&amp; sudo apt-get install -y containerd

cat &lt;&lt;EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter
</code></pre>
<h4><strong>Generate Default Config</strong></h4>
<pre><code class="language-plaintext">sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
</code></pre>
<h4><strong>Enable SystemdCgroup</strong></h4>
<p>Update SystemdCgroup to True:</p>
<pre><code class="language-yaml">nano /etc/containerd/config.toml

# search for below then update to true
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true
</code></pre>
<h4><strong>Restart containerd</strong></h4>
<pre><code class="language-bash">sudo systemctl restart containerd
sudo systemctl enable containerd
</code></pre>
<hr />
<h3><strong>d. Install Kubernetes Binaries</strong></h3>
<p>Run on all 3 nodes:</p>
<pre><code class="language-bash"># Install prerequisites.Update apt and install the packages needed for repository setup 
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

# Download and save the new signing key.The same key is used for all versions
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key \
  | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# Add the new Kubernetes repository. This overwrites any previous Kubernetes list file and points apt to the new repo
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] \
https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /" \
| sudo tee /etc/apt/sources.list.d/kubernetes.list

# Update apt and install the Kubernetes binaries
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
</code></pre>
<p>Note: Executing apt-mark hold command on kubernetes components (like kubeadm, kubelet, kubectl) will stop the components from automatically upgrading so it will always be in sync with the cluster’s version.</p>
<p>If they upgrade unexpectedly:</p>
<ul>
<li><p>You may break compatibility.</p>
</li>
<li><p>Your nodes could fail to join the cluster.</p>
</li>
<li><p>Upgrades must be coordinated carefully using kubeadm upgrade.</p>
</li>
</ul>
<ol>
<li><em>(You can substitute</em> <code>v1.36</code> <em>with another minor release, e.g.</em> <code>v1.30</code><em>, if you plan to install a different Kubernetes version.)</em></li>
</ol>
<hr />
<h3><strong>e. Initialize Control Plane</strong></h3>
<p>On <strong>control-node</strong> only:</p>
<pre><code class="language-bash"># replace &lt;PRIVATE-IP&gt; the the actual Private-IP of your server
sudo kubeadm init \
  --apiserver-advertise-address=&lt;PRIVATE-IP&gt; \
  --pod-network-cidr=10.244.0.0/16 \
  --cri-socket=unix:///run/containerd/containerd.sock
</code></pre>
<h4>Once your Kubernetes control-plane has initialized successfully</h4>
<pre><code class="language-bash"># start Cluster
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown \((id -u):\)(id -g) $HOME/.kube/config

# Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
</code></pre>
<hr />
<h3><strong>f. Join Worker Nodes</strong></h3>
<p>Copy the kubeadm join command shown in the output and run it on <strong>worker1</strong> and <strong>worker2</strong>:</p>
<pre><code class="language-bash">sudo kubeadm join &lt;CONTROL-PLANE-IP&gt;:6443 --token &lt;TOKEN&gt; \
  --discovery-token-ca-cert-hash sha256:&lt;HASH&gt; \
  --cri-socket unix:///run/containerd/containerd.sock
</code></pre>
<hr />
<h3><strong>g. Install Network Add-On</strong></h3>
<p>Use Flannel (compatible with CIDR above):</p>
<pre><code class="language-bash">kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
</code></pre>
<hr />
<h2><strong>🔍 Validate Setup</strong></h2>
<h4><strong>View Nodes</strong></h4>
<pre><code class="language-bash">kubectl get nodes

# you should see if you setup correctly 
NAME           STATUS   ROLES           AGE   VERSION
control-node   Ready    control-plane   28m   v1.36.2
worker-01      Ready    &lt;none&gt;          20m   v1.36.2
worker-02      Ready    &lt;none&gt;          20m   v1.36.2

# You can label the workers so it reads worker(purely cosmetic)
kubectl label node worker-01 node-role.kubernetes.io/worker=worker
kubectl label node worker-02 node-role.kubernetes.io/worker=worker

NAME           STATUS   ROLES           AGE   VERSION
control-node   Ready    control-plane   38m   v1.36.2
worker-01      Ready    worker          30m   v1.36.2
worker-02      Ready    worker          30m   v1.36.2
</code></pre>
<h4><strong>Create Namespace &amp; Pod</strong></h4>
<pre><code class="language-bash">kubectl create ns test
kubectl run nginx --image=nginx -n test
kubectl get pods -n test
</code></pre>
<p>Your Kubernetes cluster is now <strong>fully up and running</strong> with:</p>
<ul>
<li><p>✅ control-node: Ready</p>
</li>
<li><p>✅ worker1: Ready</p>
</li>
<li><p>✅ worker2: Ready</p>
</li>
</ul>
<p>You’re all set to start deploying workloads!</p>
<hr />
<h2><strong>🧹 Cleanup Reminder</strong></h2>
<p>Terminate the EC2 instances if you’re not using them to avoid AWS charges.</p>
<hr />
<h2><strong>📌 Conclusion</strong></h2>
<p>You now have:</p>
<ul>
<li><p>Full control plane setup</p>
</li>
<li><p>Worker nodes registered</p>
</li>
<li><p>Networking configured</p>
</li>
<li><p>A working kubectl setup</p>
</li>
</ul>
<p>This setup is <strong>learning-grade in structure</strong>, even if you’re using smaller instances for testing.</p>
<p>In my next blog post I will setup our kubernetes cluster created with kubeadm to be production grade with the following;</p>
<ul>
<li><p>HA control plane</p>
</li>
<li><p>Load balancer</p>
</li>
<li><p>External etcd</p>
</li>
<li><p>Backup strategy</p>
</li>
<li><p>Monitoring</p>
</li>
<li><p>Logging</p>
</li>
<li><p>Pod security standards</p>
</li>
<li><p>RBAC hardening</p>
</li>
<li><p>Ingress controller</p>
</li>
<li><p>Certificate management</p>
</li>
</ul>
<p>After which we’re all set to start deploying workloads.</p>
<ul>
<li><p>Deploy a voter application</p>
</li>
<li><p>Set up monitoring (Prometheus, Grafana)</p>
</li>
<li><p>Configure persistent storage</p>
</li>
<li><p>Automate with GitOps or CI/CD</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to Securely Access Jenkins Over HTTPS Using DuckDNS & Nginx]]></title><description><![CDATA[Jenkins is an essential tool for automating software builds, deployments, and CI/CD pipelines. If you're running Jenkins on an AWS EC2 instance, exposing it securely over the internet is crucial. By default, Jenkins runs on port 8080 over HTTP, which...]]></description><link>https://blog.nvmodeberesume.link/how-to-securely-access-jenkins-over-https-using-duckdns-and-nginx</link><guid isPermaLink="true">https://blog.nvmodeberesume.link/how-to-securely-access-jenkins-over-https-using-duckdns-and-nginx</guid><category><![CDATA[nginx]]></category><category><![CDATA[dns]]></category><category><![CDATA[Jenkins]]></category><dc:creator><![CDATA[Victor Modebe]]></dc:creator><pubDate>Mon, 10 Mar 2025 21:38:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741642500961/016fb1ff-03f2-4ed9-8651-aa80cc45937b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Jenkins is an essential tool for automating software builds, deployments, and CI/CD pipelines. If you're running Jenkins on an AWS EC2 instance, exposing it securely over the internet is crucial. By default, Jenkins runs on <strong>port 8080</strong> over HTTP, which isn't ideal for security.</p>
<p>In this guide, we'll walkthrough how to set up <strong>DuckDNS</strong> with <strong>Nginx</strong> as a reverse proxy and secure it with <strong>Let's Encrypt SSL certificates</strong>.</p>
<p>Let's get started!!!</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before we begin, ensure you have the following:</p>
<p>✅ An <strong>AWS EC2 instance</strong> running Linux (Amazon Linux, Ubuntu, or CentOS) with Jenkins installed.<br />✅ <strong>Nginx</strong> installed as a reverse proxy.<br />✅ A <strong>DuckDNS subdomain</strong> (e.g., <strong>jenkins-projectlab1.duckdns.org</strong>).<br />✅ A <strong>security group</strong> that allows inbound traffic on ports <strong>8080 (Jenkins), 80 (HTTP)</strong> and <strong>443 (HTTPS)</strong>.</p>
<p>If you haven’t set up Jenkins yet. You will have to install OpenJDK 17 or newer which is required by Jenkins. For this tutorial I’ll launch an Amazon linux 2023 EC2 instance (t2 medium). You can use any instance you like by make sure you there is enough storage and memory (2 CPU minimum).</p>
<p>First install java:</p>
<pre><code class="lang-bash">sudo dnf install -y java-17-amazon-corretto
<span class="hljs-comment">#verify installation</span>
java -version
</code></pre>
<h2 id="heading-now-proceed-with-jenkins-installation"><strong>Now Proceed with Jenkins Installation</strong></h2>
<p>After installing Java, continue with these steps:</p>
<p><strong>a.</strong> Add the Jenkins repository:</p>
<pre><code class="lang-bash">sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
</code></pre>
<p><strong>b.</strong> Install Jenkins:</p>
<pre><code class="lang-bash">sudo dnf install -y jenkins
</code></pre>
<p><strong>c.</strong> Enable and start Jenkins:</p>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> --now jenkins

<span class="hljs-comment">#Check if Jenkins is running</span>
sudo systemctl status jenkins
</code></pre>
<p>By default, Jenkins runs on <strong>http://your-ec2-ip:8080</strong>. Let’s make it accessible via <strong>https://jenkins-projectlab1.duckdns.org.</strong></p>
<h2 id="heading-step-1-set-up-duckdns-for-your-domain"><strong>Step 1: Set Up DuckDNS for Your Domain</strong></h2>
<p><strong>a.</strong> Go to <a target="_blank" href="https://www.duckdns.org/">DuckDNS</a> and create an account if you don’t have one then sign in using your <strong>GitHub</strong> or <strong>Google</strong> account.<br /><strong>b.</strong> Under “subdomains” enter a name (e.g., jenkins-projectlab1) and click add domain.<br /><strong>c.</strong> Your full domain will be: jenkins-projectlab1.duckdns.org<br /><strong>d.</strong> Copy your <strong>EC2 Public IP</strong> from AWS then go to DuckDNS and update the IP field with your EC2 public IP.</p>
<h3 id="heading-set-up-automatic-updates-recommended"><strong>Set Up Automatic Updates (Recommended)</strong></h3>
<p>Since EC2 instances often get dynamic IPs, use a script to update DuckDNS automatically, so you don’t have to manually copy the EC2 instance IP and paste in DuckDNS when the IP changes.</p>
<p>To write a script to automatically update DuckDNS with your EC2 instance IP follow the steps below:</p>
<p><strong>a</strong>. Connect to your EC2 instance via SSH.</p>
<p><strong>b</strong>. Create and cd into the directory**:**</p>
<pre><code class="lang-bash">mkdir -p ~/duckdns &amp;&amp; <span class="hljs-built_in">cd</span> ~/duckdns
</code></pre>
<p><strong>c.</strong> Create an executable script to automatically update a new IP in DuckDNS when it changes:</p>
<pre><code class="lang-bash">nano duck.sh
</code></pre>
<p><strong>d</strong>. Login into DuckDNS to get your token and domain name. Add the following lines (replace <strong>&lt;YOUR-DUCKDNS-TOKEN&gt;</strong> and <strong>&lt;YOUR-DOMAIN</strong>\&gt;) :</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> url=<span class="hljs-string">"https://www.duckdns.org/update?domains=&lt;YOUR-DOMAIN&gt;&amp;token=&lt;YOUR-DUCKDNS-TOKEN&gt;&amp;ip="</span> | curl -k -o ~/duckdns/duck.log -K -
</code></pre>
<p><strong>e.</strong> Save and exit.</p>
<p><strong>f.</strong> Make the <code>duck.sh</code> file executable:</p>
<pre><code class="lang-bash">chmod +x duck.sh
</code></pre>
<p><strong>g.</strong> Test the script:</p>
<pre><code class="lang-bash">./duck.sh

<span class="hljs-comment">#Check duck.log to confirm the update:</span>
cat duck.log
</code></pre>
<h3 id="heading-automate-updates-with-cron"><strong>Automate Updates with Cron</strong></h3>
<p><strong>Cron</strong> is a time-based job scheduler in Unix/Linux systems used to automate tasks at specified intervals. It runs scheduled commands or scripts without manual intervention.</p>
<p>First install Cron on your EC2 Instance:</p>
<pre><code class="lang-bash"><span class="hljs-comment">#For Amazon Linux / Amazon Linux 2</span>
sudo yum install cronie -y

<span class="hljs-comment">#For Ubuntu/Debian:</span>
sudo apt update &amp;&amp; sudo apt install cron -y

<span class="hljs-comment">#Start and Enable the Cron Service</span>
sudo systemctl start crond
sudo systemctl <span class="hljs-built_in">enable</span> crond

<span class="hljs-comment">#Verify Cron is Running</span>
systemctl status crond
</code></pre>
<p>Next, Open the cron editor:</p>
<pre><code class="lang-bash">crontab -e
</code></pre>
<p>Add your cron job (e.g., updating DuckDNS every 5 minutes or however you choose):</p>
<pre><code class="lang-bash"><span class="hljs-comment">#This updates your IP every 5 minutes.</span>
*/5 * * * * ~/duckdns/duck.sh &gt;/dev/null 2&gt;&amp;1
</code></pre>
<h3 id="heading-verify-your-duckdns-domain"><strong>Verify Your DuckDNS Domain</strong></h3>
<p>Open your browser and go to: <strong>http://jenkins-projectlab1.duckdns.org:8080</strong></p>
<p><strong>Note:</strong> Ensure your EC2 security group allows traffic on port <strong>8080</strong>, <strong>80</strong> &amp; <strong>443</strong> is for <strong>Jenkins</strong>, <strong>HTTP</strong> &amp; <strong>HTTPS</strong> respectively.</p>
<h2 id="heading-step-2-install-amp-configure-nginx-as-a-reverse-proxy"><strong>Step 2: Install &amp; Configure Nginx as a Reverse Proxy</strong></h2>
<p>A <strong>reverse proxy</strong> is a server that sits between client devices and backend servers, forwarding client requests to the appropriate backend service.</p>
<p>For a better understanding, a <strong>reverse proxy</strong> is like a <strong>middleman</strong> between users and a website or app. When you visit a website, instead of connecting directly to the main server, your request first goes through the reverse proxy. The proxy then <strong>forwards your request</strong> to the correct backend server and sends the response back to you.</p>
<p><strong>Why Use a Reverse Proxy?</strong></p>
<p><strong>Security</strong> – Hides the real server, protecting it from attacks. <strong>Faster Loading</strong> – Stores and delivers frequently used content quickly. <strong>Load Balancing</strong> – Spreads traffic across multiple servers to prevent overload. <strong>HTTPS Support</strong> – Handles encryption to secure connections.</p>
<p>To access Jenkins over HTTPS using Nginx as a reserve proxy follow these steps.</p>
<p>First install Nginx:</p>
<pre><code class="lang-bash">sudo yum install -y nginx  <span class="hljs-comment"># For Amazon Linux/CentOS</span>
sudo apt install -y nginx  <span class="hljs-comment"># For Ubuntu/Debian</span>

<span class="hljs-comment">#Start and enable Nginx</span>
sudo systemctl <span class="hljs-built_in">enable</span> --now nginx
</code></pre>
<h3 id="heading-configure-nginx-for-jenkins"><strong>Configure Nginx for Jenkins</strong></h3>
<p>Create a new Nginx configuration file:</p>
<pre><code class="lang-bash">sudo nano /etc/nginx/conf.d/jenkins.conf
</code></pre>
<p>Add the following content:</p>
<pre><code class="lang-bash">server {
    listen 80;
    server_name jenkins-projectlab1.duckdns.org;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host <span class="hljs-variable">$host</span>;
        proxy_set_header X-Real-IP <span class="hljs-variable">$remote_addr</span>;
        proxy_set_header X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
        proxy_set_header X-Forwarded-Proto <span class="hljs-variable">$scheme</span>;
    }
}
</code></pre>
<p>Save and exit (‘:wq’ if using vim editor).</p>
<p>Test and restart Nginx:</p>
<pre><code class="lang-bash">sudo nginx -t
sudo systemctl restart nginx
</code></pre>
<p>After execution of <code>sudo nginx -t</code> command, you should get “<strong>syntax is ok</strong>” &amp; <strong>“test is successful”</strong> confirmation on your terminal.</p>
<p>At this point, your Jenkins should be accessible via <strong>http://jenkins-projectlab1.duckdns.org</strong>.</p>
<hr />
<h2 id="heading-step-3-secure-jenkins-with-lets-encrypt-ssl"><strong>Step 3: Secure Jenkins with ‘Let's Encrypt’ SSL</strong></h2>
<p>Now, let’s install <strong>Certbot</strong> to get a free SSL certificate so we can access Jenkins via HTTPS:</p>
<p>First, install Certbot:</p>
<pre><code class="lang-bash"><span class="hljs-comment">#For Amazon Linux/CentOS:</span>
sudo yum install -y certbot python3-certbot-nginx

<span class="hljs-comment">#For Ubuntu/Debian</span>
sudo apt install -y certbot python3-certbot-nginx
</code></pre>
<h3 id="heading-generate-an-ssl-certificate"><strong>Generate an SSL Certificate</strong></h3>
<p>Next step, run the codes below to generate an SSL Certificate for our DuckDNS domain:</p>
<pre><code class="lang-bash">sudo certbot --nginx -d jenkins-projectlab1.duckdns.org
</code></pre>
<p>Follow the prompts:</p>
<ul>
<li><p>Enter your <strong>email</strong> address.</p>
</li>
<li><p>Agree to the <strong>terms of service</strong>.</p>
</li>
<li><p>Certbot will configure Nginx for HTTPS automatically.</p>
</li>
</ul>
<p>If it doesn’t, manually update your Nginx config:</p>
<pre><code class="lang-bash">sudo nano /etc/nginx/conf.d/jenkins.conf
</code></pre>
<p>Replace with:</p>
<pre><code class="lang-bash">server {
    listen 80;
    server_name your-subdomain.duckdns.org;
    <span class="hljs-built_in">return</span> 301 https://<span class="hljs-variable">$host</span><span class="hljs-variable">$request_uri</span>;
}

server {
    listen 443 ssl;
    server_name jenkins-projectlab1.duckdns.org;

    ssl_certificate /etc/letsencrypt/live/jenkins-projectlab1.duckdns.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jenkins-projectlab1.duckdns.org/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host <span class="hljs-variable">$host</span>;
        proxy_set_header X-Real-IP <span class="hljs-variable">$remote_addr</span>;
        proxy_set_header X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
        proxy_set_header X-Forwarded-Proto <span class="hljs-variable">$scheme</span>;
    }
}
</code></pre>
<p>Save and restart Nginx:</p>
<pre><code class="lang-bash">sudo nginx -t
sudo systemctl restart nginx
</code></pre>
<p><strong>Note:</strong> Certbot will most likely configure Nginx for HTTPS automatically hence you may not need to update manually.</p>
<h2 id="heading-step-4-enable-auto-renewal-for-ssl"><strong>Step 4: Enable Auto-Renewal for SSL</strong></h2>
<p>Let's Encrypt certificates expire every <strong>90 days</strong>. To renew them automatically, set up a cron job:</p>
<pre><code class="lang-bash">sudo crontab -e
</code></pre>
<p>Add this line at the bottom:</p>
<pre><code class="lang-bash">0 0 * * * certbot renew --quiet &amp;&amp; systemctl reload nginx
</code></pre>
<p>This will renew the SSL certificate <strong>daily at midnight</strong>.</p>
<hr />
<h2 id="heading-final-test"><strong>Final Test</strong> 🎉</h2>
<p>Now, open your browser and visit <strong>https://jenkins-projectlab1.duckdns.org</strong></p>
<p>Your Jenkins is now running securely over <strong>HTTPS</strong>! 🚀</p>
<hr />
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>By following this guide, you:</p>
<p>✅ Configured <strong>DuckDNS</strong> for dynamic DNS.<br />✅ Set up <strong>Nginx</strong> as a reverse proxy for Jenkins.<br />✅ Secured Jenkins with <strong>Let's Encrypt SSL</strong>.<br />✅ Enabled <strong>auto-renewal</strong> for SSL certificates.</p>
<p>Your Jenkins instance is now secure and accessible over HTTPS from anywhere, and you can use this process to assign a free domain to any IP or resource accessed via HTTPS.</p>
<p>Have questions? Let me know in the comments! 😊</p>
]]></content:encoded></item><item><title><![CDATA[Effortlessly Connect to Your AWS EC2 Instance with AWS Session Manager: A Step-by-Step Guide]]></title><description><![CDATA[Introduction
Cloud computing is being adopted vastly by more than 90 percent of the fortune 500 companies with Amazon Web Services (AWS) getting a large slice of the pie while it continues on it's quest for world domination in the cloud computing spa...]]></description><link>https://blog.nvmodeberesume.link/effortlessly-connect-to-your-aws-ec2-instance-with-aws-session-manager-a-step-by-step-guide</link><guid isPermaLink="true">https://blog.nvmodeberesume.link/effortlessly-connect-to-your-aws-ec2-instance-with-aws-session-manager-a-step-by-step-guide</guid><category><![CDATA[Cloud Computing]]></category><category><![CDATA[cloud native]]></category><category><![CDATA[AWS]]></category><category><![CDATA[AWS Session Manager]]></category><category><![CDATA[EC2 instance]]></category><dc:creator><![CDATA[Victor Modebe]]></dc:creator><pubDate>Fri, 05 Apr 2024 16:24:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1712334122683/f2756f37-d146-4478-8559-d19b0acba0ed.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction"><strong>Introduction</strong></h3>
<p>Cloud computing is being adopted vastly by more than 90 percent of the fortune 500 companies with Amazon Web Services (AWS) getting a large slice of the pie while it continues on it's quest for world domination in the cloud computing space by a very wide margin. Amazon Web Services (AWS) offers a vast array of services that empowers developers to configure, build, deploy and manage applications seamlessly with second to none flexibility and scale. Among these services Amazon EC2 (Elastic Cloud Compute) is a frontrunner, providing resizable compute capacity in the cloud, which has revolutionised the way companies approach IT infrastructure. However, with great power comes the need for secure, efficient management tools, especially when it comes to accessing these virtual servers. Traditional methods, like SSH (Secure Shell), while powerful, come with their own set of challenges, including the management of SSH keys and the security risks associated with open ports.</p>
<p>Enter AWS Session Manager, a game-changer for developers and IT administrators looking for a more secure, streamlined way to access and manage their EC2 instances. This blog post will dive into the world of AWS Session Manager, exploring its benefits over traditional SSH access methods, such as eliminating the need for SSH keys, reducing the attack surface by keeping ports closed, and providing an audit trail of access and command execution. Whether you're a seasoned AWS user or new to cloud computing, this guide will walk you through everything you need to know about connecting to your AWS EC2 instance using AWS Session Manager, ensuring that you can leverage this powerful tool to enhance your cloud management strategy.</p>
<h3 id="heading-what-is-aws-session-manager"><strong>What is AWS Session Manager?</strong></h3>
<p>AWS Session Manager is a fully managed service provided by Amazon Web Services (AWS) that allows users to manage their Amazon Elastic Compute Cloud (EC2) instances, containers, and virtual machines (VMs) securely and efficiently. It's part of the AWS Systems Manager suite, which offers a unified interface for automating tasks across AWS resources.</p>
<p>The key functionality of Session Manager is to provide a secure browser-based interactive shell and command-line interface (CLI) access to your instances without the need to open inbound ports, manage SSH keys, or use bastion hosts. By eliminating the need for these traditional access mechanisms, Session Manager enhances security and simplifies the management of your compute resources.</p>
<p>Here are some of the notable features and benefits of using AWS Session Manager:</p>
<ul>
<li><p><strong>Secure Access</strong>: It uses IAM (Identity and Access Management) roles and policies to control access to instances, ensuring that only authorized users can initiate sessions. All session data is encrypted, providing an additional layer of security.</p>
</li>
<li><p><strong>Simplified Management</strong>: With Session Manager, there's no need to manage SSH keys or SSL certificates. This simplification reduces the operational burden on teams and minimizes the risk of misconfiguration or unauthorized access.</p>
</li>
<li><p><strong>Audit and Compliance</strong>: All sessions can be logged and audited. Session Manager integrates with AWS CloudTrail and Amazon S3 to log session activity and store session output, aiding in compliance and audit processes.</p>
</li>
<li><p><strong>Seamless Integration</strong>: It integrates with other AWS services and features, like Amazon CloudWatch for logs, AWS S3 for session output storage, and AWS CloudTrail for audit trails. This integration allows for a cohesive and robust management ecosystem.</p>
</li>
<li><p><strong>Cost-Effective</strong>: As a feature of AWS Systems Manager, there are no additional costs for using Session Manager beyond the standard costs for Systems Manager and any associated resources, such as S3 for log storage or CloudWatch logs.</p>
</li>
</ul>
<p>By leveraging AWS Session Manager, organisations can streamline their operations, enhance security, and comply with audit requirements, making it an essential tool for managing cloud resources efficiently and securely.</p>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>To effectively use AWS Session Manager for managing your EC2 instances, you'll need to ensure that several prerequisites are in place. These prerequisites encompass AWS account setup, EC2 instance configuration, and necessary IAM role configurations. Here’s a comprehensive list to get you started:</p>
<ol>
<li><p><strong>AWS Account</strong>: You will need an active AWS account, if you don't one, you can create it by signing up on the AWS website. You'll need to have a beginner level knowledge of AWS.</p>
</li>
<li><p><strong>AWS Systems Manager Agent (SSM Agent):</strong> The AWS Systems Manager Agent (SSM Agent) must be installed and running on the EC2 instances you want to manage. The SSM Agent facilitates communication between your instances and the Systems Manager, enabling features like Session Manager. Most Amazon Machine Images (AMIs) come with the SSM Agent pre-installed, but you should verify and update it to the latest version for the best performance and security. More details can be found here <a target="_blank" href="https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html">https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html</a></p>
</li>
<li><p><strong>IAM Role Configurations:</strong> Create an IAM role with permissions that allow the instance to communicate with the Systems Manager service. Attach the <code>AmazonSSMManagedInstanceCore</code> managed policy to this role. This step is crucial for the SSM Agent on your EC2 instances to function correctly. Ensure your IAM user account has the necessary permissions to access Systems Manager and initiate sessions. You can use managed policies like <code>AmazonSSMFullAccess</code> or create a custom policy that suits your security requirements.</p>
</li>
<li><p><strong>Network Configuration:</strong> Your EC2 instances must have internet access to communicate with the AWS Systems Manager endpoint. This can be achieved through a public subnet with an internet gateway or through a private subnet with NAT gateway or VPC endpoint configuration. To configure, secure and facilitate access to your EC2 instance, it's essential to configure your instance's security group settings correctly. This involves adjusting the inbound traffic rules to allow connections through TCP protocol on three key ports: 443 for HTTPS, 80 for HTTP, and 8080 for custom TCP traffic. To ensure universal accessibility, set the source IP range to 0.0.0.0/0, which effectively permits any IP address across the globe to interact with your instance via these specified ports or set it to your IP for enhance security. This setup is crucial for enabling secure web access and supporting any specialised applications running on port 8080.</p>
</li>
<li><p><strong>Operating System Compatibility</strong>: Ensure that your EC2 instances are running an operating system that is compatible with the SSM Agent. AWS provides a list of supported operating systems on their official documentation.</p>
</li>
</ol>
<p>By ensuring these prerequisites are met, you'll be in a strong position to leverage AWS Session Manager for secure, efficient, and hassle-free management of your EC2 instances.</p>
<h3 id="heading-setting-up-your-ec2-instance-for-session-manager"><strong>Setting up your EC2 Instance for Session Manager</strong></h3>
<p>The simple solution to configure session manager on your EC2 Instance starts with creating an IAM role for the Instance and attach the <code>AmazonSSMManagedInstanceCore</code> managed policy to this role.</p>
<p>Session manager tends to have some advantages that makes it highly encouraged and it's also a security best practice. With session manager agent installed on your EC2 instance there is no need for SSH keys which can be compromised. As a cloud computing expert, It's always best practice to apply the principles of least privileged when building/designing or configuring cloud infrastructure.</p>
<ol>
<li>Launch an EC2 instance, for this blog post I will use Amazon Linux 2023 AMI &amp; t2.micro instance type to eliminate instance cost which has SSM Agent preinstalled. Make sure to configure your network components correctly most especially the inbound traffic on your security group.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712329008992/51542ca4-340c-4f5e-8564-310088ad4b3c.png" alt class="image--center mx-auto" /></p>
<p>For testing purposes I assigned IPv4 on launch of my Instance and opened SSH port 22 to connect to my instance via instance connect and programmatically confirmed if SSM Agent is installed and running on Amazon Linux 2023 operating system.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712325470986/436f5506-d3d3-4575-8e93-5c6c13b7f984.png" alt class="image--center mx-auto" /></p>
<p>We can confidently confirm that the SSM Agent is pre-installed and actively running as you can clearly see from above.</p>
<pre><code class="lang-bash">sudo systemctl status amazon-ssm-agent
</code></pre>
<p>The code above is used to verify the installation of the SSM Agent on Amazon Linux 2023. Refer to Amazon's official documentation to get a list of commands to verify the installation status of the SSM Agent of your instance's operating system type <a target="_blank" href="https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html">https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html</a>. Delete SSH inbound rule after confirmation before proceeding to the next step.</p>
<p>NOTE: There is no requirement to allocate an IPv4 address to your instance upon initialisation, and you can effectively establish a connection to your EC2 instance using Session Manager while operating within a private subnet.</p>
<h3 id="heading-creating-an-iam-role-for-the-ec2-instance"><strong>Creating an IAM Role for the EC2 Instance</strong></h3>
<p>To utilize AWS Session Manager for securely connecting to your EC2 instances without requiring SSH keys, it's essential to have the appropriate IAM role attached to your instances. This guide will walk you through creating an IAM role with the <code>AmazonSSMManagedInstanceCore</code> managed policy, which grants the necessary permissions for your instances to communicate with AWS Systems Manager.</p>
<h4 id="heading-step-1-navigate-to-the-iam-console">Step 1: Navigate to the IAM Console</h4>
<ul>
<li><p>Log in to your AWS Management Console.</p>
</li>
<li><p>Open the IAM console by finding the IAM (Identity and Access Management) service in the "Services" menu or by using the search bar.</p>
</li>
</ul>
<h4 id="heading-step-2-create-a-new-role">Step 2: Create a New Role</h4>
<ul>
<li><p>In the IAM dashboard, click on "Roles" on the left sidebar.</p>
</li>
<li><p>Click the "Create role" button.</p>
</li>
<li><p>Choose "AWS service" as the type of trusted entity.</p>
</li>
<li><p>Select "EC2" under the “Common use cases” section, as this role will be used by EC2 instances. Then, click "Next: Permissions".</p>
</li>
</ul>
<h4 id="heading-step-3-attach-the-amazonssmmanagedinstancecore-policy">Step 3: Attach the <code>AmazonSSMManagedInstanceCore</code> Policy</h4>
<ul>
<li><p>In the "Attach permissions policies" screen, use the search bar to find the <code>AmazonSSMManagedInstanceCore</code> policy.</p>
</li>
<li><p>Check the box next to <code>AmazonSSMManagedInstanceCore</code> to select it.</p>
</li>
<li><p>Click "Next: Tags" (optional step, you can add tags as per your organizational needs).</p>
</li>
<li><p>Click "Next: Review".</p>
</li>
</ul>
<h4 id="heading-step-4-review-and-create-the-role">Step 4: Review and Create the Role</h4>
<ul>
<li><p>Assign a name to your role, such as "EC2SessionManagerRole". You can also add a description for clarity.</p>
</li>
<li><p>Review the details to ensure the <code>AmazonSSMManagedInstanceCore</code> policy is attached.</p>
</li>
<li><p>Click "Create role" to finalize the creation process.</p>
</li>
</ul>
<h3 id="heading-attach-the-role-to-your-ec2-instance"><strong>Attach the Role to Your EC2 Instance</strong></h3>
<ul>
<li><p>Navigate to the EC2 dashboard from the AWS Management Console.</p>
</li>
<li><p>Select the "Instances" link on the left sidebar to view your list of instances.</p>
</li>
<li><p>Choose the instance you wish to attach the role to and select "Actions".</p>
</li>
<li><p>Navigate to "Security" and select "Modify IAM role".</p>
</li>
<li><p>In the "IAM role" dropdown menu, select the role you created earlier ("EC2-SSM-ACCESS-ROLE").</p>
</li>
<li><p>Click to apply the changes.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712330888880/df6b0e06-e9f2-4852-83a7-e52968c09abd.png" alt class="image--center mx-auto" /></p>
<p>Return to the EC2 dashboard and select the recently modified instance. Right-click on it, then choose the "Connect" option. If you attempt to initiate a session immediately, an error message may appear. This is normal, as it typically takes between 10 to 15 minutes for the newly attached IAM role to become active on your EC2 instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712330521928/30c1468e-c173-4dcb-9f74-71d1552b54e6.png" alt class="image--center mx-auto" /></p>
<p>After successfully attaching the IAM role to the EC2 Instance, you will observe the following on your screen within the management console:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712330531603/921f340d-a5a5-493c-9821-f8b8b28ff020.png" alt class="image--center mx-auto" /></p>
<p>Upon clicking "Connect," you will have successfully established a connection with your EC2 instance via Session Manager. The display on your screen will then confirm that you've executed the steps correctly, leading you into a session where you can directly interact with your EC2 instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712330542179/0432ccb5-422c-4057-9979-1ae7eca57363.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-conclusion">Conclusion</h4>
<p>Your EC2 instance is now equipped with the necessary IAM role to facilitate secure connections via AWS Session Manager. This setup enhances your instance's security by eliminating the need for SSH keys and simplifies access management.</p>
<p>Remember to regularly review and update your IAM policies and roles to adhere to the principle of least privilege, ensuring that your AWS resources are accessible only as necessary.</p>
<p>Thank you for taking the time to read through this guide. I hope it has been informative and helpful in your journey to harness the power of AWS Session Manager for your EC2 instances. Stay tuned for more content like this, designed to empower you on your cloud learning journey.</p>
]]></content:encoded></item><item><title><![CDATA[How to Deploy a Cloud Native Monitoring Application on Kubernetes]]></title><description><![CDATA[Project Overview
Here's a brief overview of the process of building the monitoring app and what the app does:
In this blog post, we will be building a monitoring application using Python and Flask that can monitor the CPU and memory utilization of yo...]]></description><link>https://blog.nvmodeberesume.link/how-to-deploy-a-cloud-native-monitoring-application-on-kubernetes</link><guid isPermaLink="true">https://blog.nvmodeberesume.link/how-to-deploy-a-cloud-native-monitoring-application-on-kubernetes</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Flask Framework]]></category><category><![CDATA[Python]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Victor Modebe]]></dc:creator><pubDate>Sat, 22 Apr 2023 11:39:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GSiEeoHcNTQ/upload/f97b0d9dc32aa4f4c232dcd61833ba81.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-project-overview">Project Overview</h2>
<p>Here's a brief overview of the process of building the monitoring app and what the app does:</p>
<p>In this blog post, we will be building a monitoring application using Python and Flask that can monitor the CPU and memory utilization of your computer. The application makes use of the psutil library to retrieve the CPU and memory utilization data.</p>
<p>We start by creating a basic Flask application that renders an HTML page with two gauges showing the CPU and memory utilization using <a target="_blank" href="http://Plotly.com">Plotly.com</a>. We use the psutil library to retrieve the CPU and memory utilization data and display it on the gauges. We also add a message to be displayed on the page if the CPU or memory utilization goes above 80%.</p>
<p>Next, we containerize the application using Docker. We create a Dockerfile that installs the required Python libraries and copies the application code to the container. We then build a Docker image from the Dockerfile and run a Docker container from the image.</p>
<p>We then deploy the application on Kubernetes using Amazon EKS. We create an ECR repository and push the Docker image to the repository. We then create an Amazon EKS cluster and nodes, and deploy the application on the cluster using a python boto3 library writing codes for kubernetes Deployment and Service.</p>
<p>Finally, we port forward and expose the Kubernetes application to our local machine, allowing us to access the monitoring application from a web browser.</p>
<p>Overall, the monitoring application allows us to monitor the CPU and memory utilization of our computer in a scalable and efficient manner, and with the use of <a target="_blank" href="http://Plotly.com">Plotly.com</a>, we can display the utilization data in a visually rich format.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before we begin, make sure you have the following:</p>
<ul>
<li><p>Python 3.10 or higher installed on your machine</p>
</li>
<li><p>Flask web framework</p>
</li>
<li><p>Docker installed on your machine</p>
</li>
<li><p>AWS CLI installed on your machine</p>
</li>
<li><p>You should have eksctl command-line tool installed on your machine</p>
</li>
<li><p>Access to an AWS account</p>
</li>
<li><p>You should have kubectl command-line tool installed on your machine</p>
</li>
<li><p>Access to an EKS cluster</p>
</li>
<li><p>VSCode installed</p>
</li>
<li><p>Basic Docker and Kubernetes knowledge</p>
</li>
</ul>
<h2 id="heading-project-architecture">Project Architecture</h2>
<p>I have used LucidChart to design a sketch architectural diagram that represents what we intend to build. The diagram provides an overview of the system's components, their relationships, and how they fit together.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682150258256/e0785e2e-a902-41b4-9943-225fed61ea05.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-1-create-the-flask-application"><strong>Step 1: Create the Flask application</strong></h2>
<p>Create a new Python file and name it <a target="_blank" href="http://app.py"><code>app.py</code></a>. Copy and paste the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> psutil
<span class="hljs-comment"># render - add style in flask application</span>
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template

app = Flask(__name__)


<span class="hljs-meta">@app.route("/")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    cpuGauge = psutil.cpu_percent()
    memGauge = psutil.virtual_memory().percent
    Message = <span class="hljs-literal">None</span>
    <span class="hljs-keyword">if</span> cpuGauge &gt; <span class="hljs-number">80</span> <span class="hljs-keyword">or</span> memGauge &gt; <span class="hljs-number">80</span>:
        Message = <span class="hljs-string">" High CPU or Memory utilization detected. Please scale up"</span>
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"index.html"</span>, cpu_metric=cpuGauge, mem_metric=memGauge, message=Message)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(debug=<span class="hljs-literal">True</span>, host=<span class="hljs-string">'0.0.0.0'</span>)
</code></pre>
<p>This code defines a Flask application that listens on the root URL <code>/</code> and returns the CPU and memory utilization of your computer in HTML rich format using <code>render_template</code>.</p>
<p>The <code>psutil</code> library is used to retrieve the CPU and memory utilization data. The CPU utilization is measured as a percentage using the <code>cpu_percent</code> method, and the memory utilization is also measured as a percentage using the <code>virtual_memory().percent</code> method.</p>
<p>If the CPU or memory utilization goes above 80%, a message is displayed on the page asking you to scale up.</p>
<h3 id="heading-running-the-application-locally"><strong>Running the Application Locally</strong></h3>
<p>To run the application locally, save the code in a file called <a target="_blank" href="http://app.py"><code>app.py</code></a> and run it using the following command:</p>
<pre><code class="lang-python">python3 app.py
</code></pre>
<p>This will start the Flask development server and you can access the application by opening a web browser and navigating to <a target="_blank" href="http://localhost:5000/"><code>http://localhost:5000/</code></a></p>
<p>If you are using a MacBook you will encounter an error "<strong>Port 5000 already in use</strong>". This happens because the port 5000 we are using for localserver is being used by MacOS.</p>
<p>The solution to resolving this: Goto system preferences --&gt; sharing --&gt; uncheck Airplay Receiver. Airplay Receiver is using port 5000.</p>
<p>Run the server again you should be able to open <a target="_blank" href="http://localhost:5000/"><code>http://localhost:5000/</code></a> on your browser:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682154915385/54aba84e-cefc-41e0-bf83-00f243e8b6dc.png" alt class="image--center mx-auto" /></p>
<p>Next: Create Folder 'templates' and a file inside the folder 'index.html' with the codes below:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>System Monitoring<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.plot.ly/plotly-latest.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-class">.plotly-graph-div</span> {
            <span class="hljs-attribute">margin</span>: auto;
            <span class="hljs-attribute">width</span>: <span class="hljs-number">50%</span>;
            <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">151</span>, <span class="hljs-number">128</span>, <span class="hljs-number">128</span>, <span class="hljs-number">0.688</span>);
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
        }

        <span class="hljs-selector-class">.alert</span> {
            <span class="hljs-attribute">color</span>: red;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>System Monitoring<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"cpu-gauge"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"mem-gauge"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {% if message %}
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert"</span>&gt;</span>{{ message }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {% endif %}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
        <span class="hljs-keyword">var</span> cpu_metric = {{ cpu_metric }}; <span class="hljs-comment">// Render the dynamic value here</span>
        <span class="hljs-keyword">var</span> mem_metric = {{ mem_metric }}; <span class="hljs-comment">// Render the dynamic value here</span>

        <span class="hljs-keyword">var</span> cpuGauge = {
            <span class="hljs-attr">type</span>: <span class="hljs-string">"indicator"</span>,
            <span class="hljs-attr">mode</span>: <span class="hljs-string">"gauge+number"</span>,
            <span class="hljs-attr">value</span>: cpu_metric,
            <span class="hljs-attr">gauge</span>: {
                <span class="hljs-attr">axis</span>: { <span class="hljs-attr">range</span>: [<span class="hljs-literal">null</span>, <span class="hljs-number">100</span>] },
                <span class="hljs-attr">bar</span>: { <span class="hljs-attr">color</span>: <span class="hljs-string">"#1f77b4"</span> },
                <span class="hljs-attr">bgcolor</span>: <span class="hljs-string">"white"</span>,
                <span class="hljs-attr">borderwidth</span>: <span class="hljs-number">2</span>,
                <span class="hljs-attr">bordercolor</span>: <span class="hljs-string">"#ccc"</span>,
                <span class="hljs-attr">steps</span>: [
                    { <span class="hljs-attr">range</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">50</span>], <span class="hljs-attr">color</span>: <span class="hljs-string">"#d9f0a3"</span> },
                    { <span class="hljs-attr">range</span>: [<span class="hljs-number">50</span>, <span class="hljs-number">85</span>], <span class="hljs-attr">color</span>: <span class="hljs-string">"#ffeb84"</span> },
                    { <span class="hljs-attr">range</span>: [<span class="hljs-number">85</span>, <span class="hljs-number">100</span>], <span class="hljs-attr">color</span>: <span class="hljs-string">"#ff5f5f"</span> }
                ],
                <span class="hljs-attr">threshold</span>: {
                    <span class="hljs-attr">line</span>: { <span class="hljs-attr">color</span>: <span class="hljs-string">"red"</span>, <span class="hljs-attr">width</span>: <span class="hljs-number">4</span> },
                    <span class="hljs-attr">thickness</span>: <span class="hljs-number">0.75</span>,
                    <span class="hljs-attr">value</span>: cpu_metric,
                }
            }
        };

        <span class="hljs-keyword">var</span> memGauge = {
            <span class="hljs-attr">type</span>: <span class="hljs-string">"indicator"</span>,
            <span class="hljs-attr">mode</span>: <span class="hljs-string">"gauge+number"</span>,
            <span class="hljs-attr">value</span>: mem_metric,
            <span class="hljs-attr">gauge</span>: {
                <span class="hljs-attr">axis</span>: { <span class="hljs-attr">range</span>: [<span class="hljs-literal">null</span>, <span class="hljs-number">100</span>] },
                <span class="hljs-attr">bar</span>: { <span class="hljs-attr">color</span>: <span class="hljs-string">"#1f77b4"</span> },
                <span class="hljs-attr">bgcolor</span>: <span class="hljs-string">"white"</span>,
                <span class="hljs-attr">borderwidth</span>: <span class="hljs-number">2</span>,
                <span class="hljs-attr">bordercolor</span>: <span class="hljs-string">"#ccc"</span>,
                <span class="hljs-attr">steps</span>: [
                    { <span class="hljs-attr">range</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">50</span>], <span class="hljs-attr">color</span>: <span class="hljs-string">"#d9f0a3"</span> },
                    { <span class="hljs-attr">range</span>: [<span class="hljs-number">50</span>, <span class="hljs-number">85</span>], <span class="hljs-attr">color</span>: <span class="hljs-string">"#ffeb84"</span> },
                    { <span class="hljs-attr">range</span>: [<span class="hljs-number">85</span>, <span class="hljs-number">100</span>], <span class="hljs-attr">color</span>: <span class="hljs-string">"#ff5f5f"</span> }
                ],
                <span class="hljs-attr">threshold</span>: {
                    <span class="hljs-attr">line</span>: { <span class="hljs-attr">color</span>: <span class="hljs-string">"red"</span>, <span class="hljs-attr">width</span>: <span class="hljs-number">4</span> },
                    <span class="hljs-attr">thickness</span>: <span class="hljs-number">0.75</span>,
                    <span class="hljs-attr">value</span>: mem_metric
                }
            }
        };

        <span class="hljs-keyword">var</span> cpuGaugeLayout = { <span class="hljs-attr">title</span>: <span class="hljs-string">"CPU Utilization"</span> };
        <span class="hljs-keyword">var</span> memGaugeLayout = { <span class="hljs-attr">title</span>: <span class="hljs-string">"Memory Utilization"</span> };

        Plotly.newPlot(<span class="hljs-string">'cpu-gauge'</span>, [cpuGauge], cpuGaugeLayout);
        Plotly.newPlot(<span class="hljs-string">'mem-gauge'</span>, [memGauge], memGaugeLayout);
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>After creating the file 'index.html' we should have a rich HTML visual of the CPU and Memory Utilization Metrics.</p>
<p>Run <code>python3 app.py</code> Open your browser you should see below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682155793006/ebada9a9-029c-4bb2-97a6-185d7b7565a2.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-2-containerize-the-application-using-docker"><strong>Step 2: Containerize the application using Docker</strong></h2>
<p>Create a new file in the same directory as <a target="_blank" href="http://app.py"><code>app.py</code></a> name it <code>requirements.txt</code>. These are the dependency libraries and appropriate versions that are required to be used for the project. Copy and paste the following:</p>
<pre><code class="lang-markdown">Flask==2.2.3
MarkupSafe==2.1.2
Werkzeug==2.2.3
itsdangerous==2.1.2
psutil==5.8.0
plotly==5.5.0
tenacity==8.0.1
boto3==1.9.148
kubernetes==10.0.1
</code></pre>
<p>Create a new file in the same directory as <a target="_blank" href="http://app.py"><code>app.py</code></a> and name it <code>Dockerfile</code>. Copy and paste the following code:</p>
<pre><code class="lang-yaml"><span class="hljs-string">FROM</span> <span class="hljs-string">python:3.9-slim-buster</span>

<span class="hljs-string">WORKDIR</span> <span class="hljs-string">/app</span>

<span class="hljs-string">COPY</span> <span class="hljs-string">requirements.txt</span> <span class="hljs-string">.</span>

<span class="hljs-comment"># Update package manager and install required dependencies</span>
<span class="hljs-string">RUN</span> <span class="hljs-string">apt-get</span> <span class="hljs-string">update</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">apt-get</span> <span class="hljs-string">install</span> <span class="hljs-string">-y</span> <span class="hljs-string">gcc</span> <span class="hljs-string">python3-dev</span>

<span class="hljs-comment"># Update pip</span>
<span class="hljs-string">RUN</span> <span class="hljs-string">python3</span> <span class="hljs-string">-m</span> <span class="hljs-string">pip</span> <span class="hljs-string">install</span> <span class="hljs-string">--upgrade</span> <span class="hljs-string">pip</span>

<span class="hljs-comment">#install the required python packages</span>
<span class="hljs-string">RUN</span> <span class="hljs-string">pip3</span> <span class="hljs-string">install</span> <span class="hljs-string">--no-cache-dir</span> <span class="hljs-string">-r</span> <span class="hljs-string">requirements.txt</span>

<span class="hljs-comment">#copy the application code to the working directory</span>
<span class="hljs-string">COPY</span> <span class="hljs-string">.</span> <span class="hljs-string">.</span>

<span class="hljs-comment">#set the environment variables for the flask app</span>
<span class="hljs-string">ENV</span> <span class="hljs-string">FLASK_RUN_HOST=0.0.0.0</span>

<span class="hljs-comment"># Expose the port on which the flask app wil run</span>
<span class="hljs-string">EXPOSE</span> <span class="hljs-number">5000</span>

<span class="hljs-comment"># start the Flask app when the conainer is run</span>
<span class="hljs-string">CMD</span> [ <span class="hljs-string">"flask"</span>, <span class="hljs-string">"run"</span>]
</code></pre>
<p>This Dockerfile defines the steps to build a Docker image for a Python application that uses Flask web framework. Here's what each instruction does:</p>
<ul>
<li><p><code>FROM python:3.9-slim-buster</code>: This specifies the base image for the container. In this case, the base image is <code>python:3.9-slim-buster</code>.</p>
</li>
<li><p><code>WORKDIR /app</code>: This sets the working directory to <code>/app</code> in the container.</p>
</li>
<li><p><code>COPY requirements.txt .</code>: This copies the <code>requirements.txt</code> file from the local directory to the container's working directory.</p>
</li>
<li><p><code>RUN apt-get update &amp;&amp; apt-get install -y gcc python3-dev</code>: This updates the package manager and installs required dependencies such as <code>gcc</code> and <code>python3-dev</code>.</p>
</li>
<li><p><code>RUN python3 -m pip install --upgrade pip</code>: This updates <code>pip</code> to the latest version.</p>
</li>
<li><p><code>RUN pip3 install --no-cache-dir -r requirements.txt</code>: This installs the Python packages specified in <code>requirements.txt</code>.</p>
</li>
<li><p><code>COPY . .</code>: This copies the application code from the local directory to the container's working directory.</p>
</li>
<li><p><code>ENV FLASK_RUN_HOST=0.0.0.0</code>: This sets the environment variable <code>FLASK_RUN_HOST</code> to <code>0.0.0.0</code>, which allows the Flask app to listen on all network interfaces.</p>
</li>
<li><p><code>EXPOSE 5000</code>: This exposes port <code>5000</code> on the container, which is the port on which the Flask app will run.</p>
</li>
<li><p><code>CMD [ "flask", "run"]</code>: This specifies the command that will be executed when the container starts. In this case, it starts the Flask app using the command <code>flask run</code>.</p>
</li>
</ul>
<p>To build the Docker image, run the following command in the same directory as the <code>Dockerfile</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">build</span> <span class="hljs-string">-t</span> <span class="hljs-string">my-flask-app</span> <span class="hljs-string">.</span>
</code></pre>
<p>This will build a Docker image with the tag <code>my-flask-app</code>.</p>
<p>To run the Docker container from the image, run the following command:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">run</span> <span class="hljs-string">-d</span> <span class="hljs-string">-p</span> <span class="hljs-number">5000</span><span class="hljs-string">:5000</span> <span class="hljs-string">my-flask-app</span>
</code></pre>
<p>This command starts a Docker container and maps port 5000 on the host to port 5000 on the container. The command '-d' also runs the container in a detached mode.</p>
<p>Open a web browser and navigate to <a target="_blank" href="http://localhost:5000"><code>http://localhost:5000</code></a> to see the render_template HTML output:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682157382653/b242371c-82a5-4080-8b52-d01e0f76e046.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-3-create-ecr-and-push-image-to-the-repo"><strong>Step 3: Create ECR and push image to the Repo</strong></h2>
<p>Assuming you have an AWS account, you can use the AWS CLI to create an Amazon ECR repository and push the Docker image to the repository.</p>
<p>First, create an ECR repository by running the following command:</p>
<pre><code class="lang-yaml"><span class="hljs-string">aws</span> <span class="hljs-string">ecr</span> <span class="hljs-string">create-repository</span> <span class="hljs-string">--repository-name</span> <span class="hljs-string">cloud-native-repo</span>
</code></pre>
<p>The next step is to access the AWS management console and search for "ECR" in the search bar to navigate to the Elastic Container Registry. Once there, you can view the recently created repository.</p>
<p>To push the Docker image that was created on your local machine to the AWS ECR Repository, click on "View Push Commands" to get instructions on how to proceed. Follow the instructions to complete the image push.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682157894376/8335bee3-7b7a-41db-94f5-678aab6cb0c8.png" alt class="image--center mx-auto" /></p>
<p>OR Use the following steps to authenticate and push an image to your repository.</p>
<ol>
<li><p>Retrieve an authentication token and authenticate your Docker client to your registry.</p>
<p> Use the AWS CLI:</p>
</li>
<li><pre><code class="lang-yaml">   <span class="hljs-string">aws</span> <span class="hljs-string">ecr</span> <span class="hljs-string">get-login-password</span> <span class="hljs-string">--region</span> <span class="hljs-string">us-east-1</span> <span class="hljs-string">|</span> <span class="hljs-string">docker</span> <span class="hljs-string">login</span> <span class="hljs-string">--username</span> <span class="hljs-string">AWS</span> <span class="hljs-string">--password-stdin</span> <span class="hljs-string">&lt;your-aws-account-id&gt;.dkr.ecr.us-east-1.amazonaws.com</span>
</code></pre>
<p> Note: if you receive an error using the AWS CLI, make sure that you have the latest version of the AWS CLI and Docker installed.</p>
</li>
<li><p>After the build is completed, tag your image so you can push the image to this repository:</p>
<pre><code class="lang-yaml"> <span class="hljs-string">docker</span> <span class="hljs-string">tag</span> <span class="hljs-string">my-cloud-native-repo:latest</span> <span class="hljs-string">&lt;your-aws-account-id&gt;.dkr.ecr.us-east-1.amazonaws.com/my-cloud-native-repo:latest</span>
</code></pre>
</li>
<li><p>Run the following command to push this image to your newly created AWS repository:</p>
<pre><code class="lang-yaml"> <span class="hljs-string">docker</span> <span class="hljs-string">push</span> <span class="hljs-string">&lt;your-aws-account-id&gt;.dkr.ecr.us-east-1.amazonaws.com/my-cloud-native-repo:latest</span>
</code></pre>
</li>
</ol>
<p>This command pushes the <code>my-flask-app</code> image to the ECR repository.</p>
<h2 id="heading-step-4-create-eks-cluster-and-nodes"><strong>Step 4: Create EKS cluster and nodes</strong></h2>
<p>You can use the AWS CLI to create an Amazon EKS cluster and nodes.</p>
<p>First, create an Amazon EKS cluster by running the following command:</p>
<pre><code class="lang-yaml"><span class="hljs-string">eksctl</span> <span class="hljs-string">create</span> <span class="hljs-string">cluster</span> <span class="hljs-string">--name</span> <span class="hljs-string">my-flask-app</span> <span class="hljs-string">--region</span> <span class="hljs-string">&lt;your-aws-region&gt;</span> <span class="hljs-string">--nodegroup-name</span> <span class="hljs-string">standard-workers</span> <span class="hljs-string">--node-type</span> <span class="hljs-string">t2.micro</span> <span class="hljs-string">--nodes</span> <span class="hljs-number">2</span> <span class="hljs-string">--nodes-min</span> <span class="hljs-number">1</span> <span class="hljs-string">--nodes-max</span> <span class="hljs-number">3</span> <span class="hljs-string">--ssh-access</span> <span class="hljs-string">--ssh-public-key</span> <span class="hljs-string">&lt;your-public-ssh-key&gt;</span>
</code></pre>
<p>Replace <code>&lt;your-aws-region&gt;</code> and <code>&lt;your-public-ssh-key&gt;</code> with the appropriate values for your AWS account.</p>
<p>This command creates a new EKS cluster named <code>monitoring-app</code> and launches two worker nodes of type <code>t2.micro</code> with minimum one node and maximum three nodes.</p>
<p>I have chosen to use an AWS free tier t2.micro instance type to minimize expenses. However, this instance type is equipped with only 1 CPU and 1GB memory, which may be inadequate for certain Kubernetes deployments to operate efficiently. If you encounter performance issues or errors, it is recommended to upgrade to an instance type with at least 2 CPUs and 2GB memory, such as the t3.small instance type. Please note that the specific instance type required for your deployment may vary depending on its requirements.</p>
<p>Overall, <code>eksctl</code> automates the process of creating and configuring the necessary AWS resources for an EKS cluster and node groups, including the required IAM roles and policies.</p>
<h2 id="heading-step-5-create-kubernetes-deployment-and-service-using-python"><strong>Step 5: Create Kubernetes Deployment and Service using Python</strong></h2>
<p>Now, create a Kubernetes Deployment and Service that use the <code>my-flask-app</code> Docker image.</p>
<p>Create a new file in the same directory as <a target="_blank" href="http://app.py"><code>app.py</code></a> and name it <code>eck.py</code>. Copy and paste the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> kubernetes <span class="hljs-keyword">import</span> client, config

<span class="hljs-comment"># Load kubernetes configuration</span>
config.load_kube_config()

<span class="hljs-comment"># create a kubernetes API Client</span>
api_client = client.ApiClient()

<span class="hljs-comment"># Define the deployment</span>
deployment = client.V1Deployment(
    metadata=client.V1ObjectMeta(name=<span class="hljs-string">"my-flask-app"</span>),
    spec=client.V1DeploymentSpec(
        replicas=<span class="hljs-number">3</span>,  <span class="hljs-comment"># set the desired number of replicas</span>
        min_ready_seconds=<span class="hljs-number">30</span>,  <span class="hljs-comment"># set the minimum number of seconds for a pod to become ready</span>
        strategy=client.V1DeploymentStrategy(
            type=<span class="hljs-string">"RollingUpdate"</span>,
            rolling_update=client.V1RollingUpdateDeployment(
                max_unavailable=<span class="hljs-number">1</span>,  <span class="hljs-comment"># set the maximum number of unavailable pods during a rolling update</span>
                max_surge=<span class="hljs-number">1</span>  <span class="hljs-comment"># set the maximum number of pods that can be created above the desired number of replicas during a rolling update</span>
            )
        ),
        selector=client.V1LabelSelector(
            match_labels={<span class="hljs-string">"app"</span>: <span class="hljs-string">"my-flask-app"</span>}
        ),
        template=client.V1PodTemplateSpec(
            metadata=client.V1ObjectMeta(
                labels={<span class="hljs-string">"app"</span>: <span class="hljs-string">"my-flask-app"</span>}
            ),
            spec=client.V1PodSpec(
                containers=[
                    client.V1Container(
                        name=<span class="hljs-string">"my-flask-container"</span>,
                        image=<span class="hljs-string">"&lt;your-aws-account-id&gt;.dkr.ecr.us-east-1.amazonaws.com/my-cloud-native-repo:latest"</span>,
                        ports=[client.V1ContainerPort(container_port=<span class="hljs-number">5000</span>)]
                    )
                ]
            )
        )
    )
)

<span class="hljs-comment"># Create the deployment</span>
api_instance = client.AppsV1Api(api_client)

api_instance.create_namespaced_deployment(
    namespace=<span class="hljs-string">"default"</span>,
    body=deployment
)

<span class="hljs-comment"># Define the service</span>
service = client.V1Service(
    metadata=client.V1ObjectMeta(name=<span class="hljs-string">"my-flask-service"</span>),
    spec=client.V1ServiceSpec(
        selector={<span class="hljs-string">"app"</span>: <span class="hljs-string">"my-flask-app"</span>},
        ports=[client.V1ServicePort(port=<span class="hljs-number">5000</span>)]
    )
)

<span class="hljs-comment"># Create the service</span>
api_instance = client.CoreV1Api(api_client)
api_instance.create_namespaced_service(
    namespace=<span class="hljs-string">"default"</span>,
    body=service
)
</code></pre>
<p>This Python code is for deploying a Flask application on a Kubernetes cluster.</p>
<p>Firstly, the code imports the required Kubernetes client and configuration modules from the Kubernetes Python library.</p>
<p>Then it loads the Kubernetes configuration using <code>config.load_kube_config()</code>. This step ensures that the code is using the correct Kubernetes cluster for deployment.</p>
<p>Next, the code defines a <code>V1Deployment</code> object that describes the deployment of the Flask application. It includes the desired number of replicas, minimum number of seconds for a pod to become ready, and the rolling update strategy. The deployment object also specifies the label selector, pod template, and container image for the application.</p>
<p>After defining the deployment object, the code creates the deployment by calling <code>api_instance.create_namespaced_deployment()</code> method of the <code>AppsV1Api</code> class, passing in the deployment object and the namespace where it should be created.</p>
<p>Finally, the code defines a <code>V1Service</code> object that describes how to access the Flask application. The service object specifies the label selector and the port where the Flask application is listening. The code creates the service object by calling the <code>api_instance.create_namespaced_service()</code> method of the <code>CoreV1Api</code> class, passing in the service object and the namespace where it should be created.</p>
<p>To apply these Kubernetes manifests, run the following command:</p>
<pre><code class="lang-python">python3 eks.py
</code></pre>
<p>confirm if the kubernetes deployment and service was created</p>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">get</span> <span class="hljs-string">deploy,svc</span>
</code></pre>
<h2 id="heading-step-6-port-forward-and-expose-the-kubernetes-application"><strong>Step 6: Port forward and expose the Kubernetes application</strong></h2>
<p>To access the Kubernetes application from your local machine, you need to forward the Kubernetes Service port to your local machine.</p>
<p>To do this, run the following command:</p>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">port-forward</span> <span class="hljs-string">service/my-flask-service</span> <span class="hljs-number">5000</span><span class="hljs-string">:5000</span>
</code></pre>
<p>This command forwards port 5000 of the Kubernetes Service named <code>my-flask-service</code> to port 5000 on your local machine.</p>
<p>You can now access the <code>my-flask-app</code> application by opening a web browser and navigating to <a target="_blank" href="http://localhost:8080/"><code>http://localhost:5000/</code></a>.</p>
<h2 id="heading-cleanup">Cleanup</h2>
<p>To delete the EKS cluster and associated resources created by <code>eksctl</code> to avoid incurring charges, you can use the following command:</p>
<pre><code class="lang-yaml"><span class="hljs-string">eksctl</span> <span class="hljs-string">delete</span> <span class="hljs-string">cluster</span> <span class="hljs-string">--name</span> <span class="hljs-string">&lt;cluster-name&gt;</span> <span class="hljs-string">--region</span> <span class="hljs-string">&lt;your-aws-region&gt;</span>
</code></pre>
<p>Replace <code>&lt;cluster-name&gt;</code> with the name of the EKS cluster you want to delete, and <code>&lt;your-aws-region&gt;</code> with the AWS region where the cluster is located.</p>
<p>This command will delete the CloudFormation stack that was created by <code>eksctl</code>, including the control plane, worker nodes, and networking resources. It will also delete the IAM roles and policies created for the EKS cluster and worker nodes.</p>
<p>Note that deleting an EKS cluster and its associated resources is an irreversible operation, and you will lose all data stored on the worker nodes. Before running the <code>eksctl delete cluster</code> command, make sure you have backed up any data that you want to keep and that you have terminated any services or applications running on the cluster.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this blog post, you learned how to build a cloud-native monitoring application using Python and Flask, monitor CPU and memory utilization of your computer, containerize the application using Docker, deploy the application on Kubernetes using Amazon EKS, and port forward and expose the Kubernetes application to your local machine.</p>
<p>By following these steps, you can create a scalable and efficient monitoring application that can be run in the cloud. You can also extend the application to monitor other aspects of your system, such as network usage, disk usage, and more.</p>
<p>I hope this blog post was helpful to you in building your own cloud-native monitoring application.</p>
]]></content:encoded></item><item><title><![CDATA[WebScrapping using BeautifulSoup library, then storing the data in MongoDB]]></title><description><![CDATA[Web scraping is the process of extracting data from websites, which can be a very useful technique for obtaining large amounts of data quickly and efficiently. In this blog, we will explore how to write a web scraper in Python using the requests and ...]]></description><link>https://blog.nvmodeberesume.link/webscrapping-using-beautifulsoup-library-then-storing-the-data-in-mongodb</link><guid isPermaLink="true">https://blog.nvmodeberesume.link/webscrapping-using-beautifulsoup-library-then-storing-the-data-in-mongodb</guid><category><![CDATA[Devops]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Python]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[pymongo]]></category><dc:creator><![CDATA[Victor Modebe]]></dc:creator><pubDate>Mon, 13 Mar 2023 15:06:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678719618546/ac6645b6-0e83-4990-b688-e2d560c4c5c7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Web scraping is the process of extracting data from websites, which can be a very useful technique for obtaining large amounts of data quickly and efficiently. In this blog, we will explore how to write a web scraper in Python using the requests and BeautifulSoup libraries, and how to save the results in MongoDB using the pymongo library. Mongo Express will also be connected to MongoDB to have visuals of the database. MongoDB and Mongo Express will be set up using Docker.</p>
<p>Before we begin, it's important to understand the legal and ethical considerations when web scraping. Be sure to check the website's terms of service to ensure that web scraping is allowed, and be respectful of the website's resources and bandwidth by not overloading their server with requests. Additionally, some websites may have anti-scraping measures in place, so proceed with caution and avoid any actions that may be seen as malicious or harmful.</p>
<p>You can check by inspecting the robots.txt file of the website 'https://example.com/robots.txt'</p>
<p>Now, let's dive into the code.</p>
<p>Let's walk through the process of setting up MongoDB and Mongo Express using Docker.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before we begin, you will need to have Docker installed on your machine. You can download and install Docker from the official website: <a target="_blank" href="https://www.docker.com/products/docker-desktop"><strong>https://www.docker.com/products/docker-desktop</strong></a>.</p>
<p>First, let's create a Docker Network which will be used by both MongoDB and Mongo Express to enable communication between them. Then we test to see if the network has been created successfully.</p>
<pre><code class="lang-bash">docker network create mongo-network
docker network ls
</code></pre>
<p>Next, let's create a Docker container for MongoDB. To do this, we can run the following command:</p>
<pre><code class="lang-bash">docker run -d --name mongodb --network mongo-network -p 27017:27017 mongo
</code></pre>
<p>This command will download the latest MongoDB image from Docker Hub and start a container named 'mongo' while a network is connected to it. We are also mapping port 27017 from the container to port 27017 on our host machine.</p>
<p>You can verify that the container is running by running the command:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>This should list all running containers, and you should see the 'mongo' container listed.</p>
<pre><code class="lang-bash">docker run -d \
    --network mongo-network \
    --name mongo-express \
    -p 8081:8081 \
    -e ME_CONFIG_MONGODB_SERVER=<span class="hljs-string">"mongodb"</span> \
    -e ME_CONFIG_BASICAUTH_USERNAME=<span class="hljs-string">"admin"</span> \
    -e ME_CONFIG_BASICAUTH_PASSWORD=<span class="hljs-string">"1234567890"</span> \
    mongo-express
</code></pre>
<p>This command will download the latest Mongo Express image from Docker Hub and start a container named 'mongo-express'. We are also mapping port 8081 from the container to port 8081 on our host machine.</p>
<p>The environment variables ME_CONFIG_MONGODB_SERVER, ME_CONFIG_BASICAUTH_USERNAME, and ME_CONFIG_BASICAUTH_PASSWORD are used to configure Mongo Express to connect to our MongoDB container and set up basic authentication.</p>
<h2 id="heading-accessing-mongo-express"><strong>Accessing Mongo Express</strong></h2>
<p>Once the container is running, you can access Mongo Express by opening a web browser and navigating to <a target="_blank" href="http://localhost:8081"><strong>http://localhost:8081</strong></a>. You should see a login screen where you can enter the username and password you set up in the previous step.</p>
<p>After logging in, you should see a dashboard where you can view and manage your MongoDB databases.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678707974250/2801827a-acf6-4eff-a5f4-58a467dde066.png" alt class="image--center mx-auto" /></p>
<p>We have three options to create a database. First, you can create it from the mongo-express UI by simply clicking 'create database'. Secondly, you can write python code. Thirdly, you can create it on the command line or terminal with the following commands:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -it mongodb mongosh
</code></pre>
<p>The above command is used to interactively login into the MongoDB container. The command <code>mongosh</code> is the mongo interactive shell.</p>
<p>Next, we will create a DataBase and a Collection where our data will be stored, using the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-comment">#create a database named web_scraper_db.</span>
use web_scraper_db 

<span class="hljs-comment">#create new user</span>
db.createUser({ user: <span class="hljs-string">"User1"</span>, <span class="hljs-built_in">pwd</span>: <span class="hljs-string">"1234567890"</span>, roles: [{ role: <span class="hljs-string">"readWrite"</span>, db: <span class="hljs-string">"web_scraper_db"</span> }] })   

<span class="hljs-comment">#create a collection named web_scraper_collection.</span>
db.createCollection(<span class="hljs-string">'web_scraper_collection'</span>) 

<span class="hljs-comment">#add a document in the web_scraper_collection colection.</span>
db.web_scraper_collection.insert({item: <span class="hljs-string">"apple"</span>, cost: 3}) 

<span class="hljs-comment">#add a document in the web_scraper_collection colection.</span>
db.web_scraper_collection.insert({item: <span class="hljs-string">"pear"</span>, qty: 5}) 

<span class="hljs-comment">#select documents in the web_scraper_collection collection. It should display the two created documents with the _id property.</span>
db.web_scraper_collection.find(): 

show users <span class="hljs-comment">#list all users.</span>
<span class="hljs-built_in">exit</span> <span class="hljs-comment">#exit.</span>

<span class="hljs-comment">#log back into mongo container with a specific user</span>
docker <span class="hljs-built_in">exec</span> -it mongodb mongosh --username User1 --password 1234567890 --authenticationDatabase web_scraper_db 

<span class="hljs-comment">#check the current user.</span>
db.runCommand({connectionStatus : 1}) 

<span class="hljs-comment">#confirm if User1 can query documents.</span>
db.web_scraper_collection.find() 

<span class="hljs-built_in">exit</span> <span class="hljs-comment">#exit the mongodb shell.</span>
</code></pre>
<p>You can check out <a target="_blank" href="https://www.mongodb.com/docs/v5.0/tutorial/manage-users-and-roles/">mongodb.com</a> for additional info on more MongoDB commands.</p>
<p>Next step we write some python code to bring our idea to life.</p>
<h3 id="heading-importing-libraries"><strong>Importing Libraries</strong></h3>
<p>The first step is to import the necessary libraries. In this case, we will need requests, BeautifulSoup, and <code>pymongo</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup
<span class="hljs-keyword">from</span> pymongo <span class="hljs-keyword">import</span> MongoClient
</code></pre>
<h3 id="heading-setting-up-the-mongodb-connection"><strong>Setting up the MongoDB Connection</strong></h3>
<p>Next, we need to set up a connection to the MongoDB database where we will be storing our scraped data. We will use the MongoClient class to connect to the database and create a collection to store our data.</p>
<pre><code class="lang-python">MONGO_DB_URL = <span class="hljs-string">'mongodb://User1:1234567890@localhost:27017/web_scraper_db'</span>
client = MongoClient(MONGO_DB_URL)
db = client[<span class="hljs-string">'web_scraper_db'</span>]
collection = db[<span class="hljs-string">'web_scraper_collection'</span>]
</code></pre>
<p>Here, we are connecting to a local MongoDB instance with the URL <code>'mongodb://User1:1234567890@localhost:27017/web_scraper_db'</code> You will need to modify this URL to match your own MongoDB instance. We are then creating a database called <code>web_scraper_db</code> and a collection called <code>web_scraper_collection</code></p>
<h3 id="heading-scraping-the-website"><strong>Scraping the Website</strong></h3>
<p>Now that we have our MongoDB connection set up, we can begin scraping the website. In this example, we will be scraping the website <a target="_blank" href="https://books.toscrape.com/"><strong>https://books.toscrape.com/</strong></a> to extract information about books.</p>
<pre><code class="lang-python">URL = <span class="hljs-string">'https://books.toscrape.com/'</span>
response = requests.get(URL)
soup = BeautifulSoup(response.content, <span class="hljs-string">'html.parser'</span>)
</code></pre>
<p>Here, we are using the requests library to send a GET request to the website and retrieve the HTML content. We then use BeautifulSoup to parse the HTML content and create a BeautifulSoup object that we can use to extract information.</p>
<pre><code class="lang-python">books = soup.find_all(<span class="hljs-string">'article'</span>, class_=<span class="hljs-string">'product_pod'</span>)
book_list = []
<span class="hljs-keyword">for</span> book <span class="hljs-keyword">in</span> books:
    title = book.h3.a.attrs[<span class="hljs-string">'title'</span>]
    link = book.h3.a.attrs[<span class="hljs-string">'href'</span>]
    price = book.select_one(<span class="hljs-string">'p.price_color'</span>).get_text()
    book_list.append({<span class="hljs-string">'title'</span>: title, <span class="hljs-string">'link'</span>: link, <span class="hljs-string">'price'</span>: price})
</code></pre>
<p>In this section of the code, we are using BeautifulSoup to extract information about the books on the website. We are searching for all HTML elements with the tag 'article' and the class 'product_pod', which represents the individual books on the website. We then iterate over each book and extract the title, link, and price information. We store this information in a list of dictionaries called book_list.</p>
<h3 id="heading-inserting-the-data-into-mongodb"><strong>Inserting the Data into MongoDB</strong></h3>
<p>Now that we have our data in the book_list variable, we can insert it into our MongoDB collection.</p>
<pre><code class="lang-python">collection.insert_many(book_list)
</code></pre>
<p>Here, we are using the insert_many method to insert all the books in the book_list variable into the MongoDB collection.</p>
<pre><code class="lang-python">num_books = collection.count_documents({})
print(<span class="hljs-string">f"Total number of books in the collection: <span class="hljs-subst">{num_books}</span>"</span>)
</code></pre>
<p>Save the python codes in a file <code>web_scrapper.py</code> You can run the python script to execute the commands. All things being equal you should get the results below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678715441100/28c13ad3-974f-43c0-8120-d9b403cd76c3.png" alt class="image--center mx-auto" /></p>
<p>Next, let's log in to mongo express using the URL <code>http://localhost:8081</code> Once logged in click the database created earlier, then click the collection we created which will lead to a page where we can visualize the data scrapped from 'https://books.toscrape.com/'</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678716010309/d4e3dd3c-9e87-4c81-9e6a-13a3a07f23d8.png" alt class="image--center mx-auto" /></p>
<p>You can log into the MongoDB container created earlier to query the database with the commands I provided earlier to see the data we saved in it.</p>
<pre><code class="lang-python">docker <span class="hljs-keyword">exec</span> -it mongodb mongosh

<span class="hljs-comment">#mongodb commands to view web scrape data </span>
show dbs
use web_scraper_db
show collections
db.web_scraper_collection.find()
</code></pre>
<p>You should get the results below</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678718697879/e6d61571-5d34-425d-a3cc-7dd3d2e9b2ba.png" alt class="image--center mx-auto" /></p>
<p><strong>Conclusion</strong></p>
<p>In this blog, we have covered how to set up MongoDB and Mongo Express using Docker, allowing us to easily create and manage MongoDB databases and access a visual interface for managing our data. Then we wrote a python code to run the command to web scrape the website. With these tools, we can easily create web scrapers like the one we discussed earlier and store the data in our MongoDB databases.</p>
<p>Hope you enjoyed this TUTORIAL !!! Good Luck and Happy coding !!!</p>
]]></content:encoded></item><item><title><![CDATA[Build a CRUD RESTful Microservice with AWS Lambda, API Gateway and DynamoDB using AWS SAM and VSCode]]></title><description><![CDATA[Serverless architectures have become increasingly popular in recent years, as they offer several benefits over traditional server-based architectures. One of the key advantages of serverless architectures is that they enable you to focus on writing c...]]></description><link>https://blog.nvmodeberesume.link/build-a-crud-restful-microservice-with-aws-lambda-api-gateway-and-dynamodb-using-aws-sam-and-vscode</link><guid isPermaLink="true">https://blog.nvmodeberesume.link/build-a-crud-restful-microservice-with-aws-lambda-api-gateway-and-dynamodb-using-aws-sam-and-vscode</guid><category><![CDATA[aws lambda]]></category><category><![CDATA[crud api]]></category><category><![CDATA[serverless]]></category><category><![CDATA[DynamoDB]]></category><category><![CDATA[vscode]]></category><dc:creator><![CDATA[Victor Modebe]]></dc:creator><pubDate>Sat, 04 Feb 2023 01:21:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680566139108/3906f9a9-cee0-4cc7-8273-118dd794cae0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Serverless architectures have become increasingly popular in recent years, as they offer several benefits over traditional server-based architectures. One of the key advantages of serverless architectures is that they enable you to focus on writing code instead of managing servers. In this tutorial, I'll show you how to build a serverless CRUD RESTful microservice using AWS Lambda, API Gateway, and DynamoDB using the AWS Serverless Application Model (SAM) and VSCode.</p>
<h3 id="heading-serverless-architecture"><strong>Serverless Architecture</strong></h3>
<p>Just in case you are a beginner in cloud, I will explain the term 'SERVERLESS' so you can have a brief understanding of what it means.</p>
<p>Serverless architecture is a cloud computing model where a third-party provider manages the infrastructure and handles server operations automatically. This means that developers do not have to manage servers or infrastructure themselves, and they can focus on writing code and building applications.</p>
<p>In a serverless architecture, the cloud provider automatically provisions and manages the server resources needed to run the code, and bills the developer based on the actual usage of resources, rather than a fixed amount. This can lead to cost savings, as the developer only pays for what is used.</p>
<p>Serverless architectures typically involve writing small, single-purpose functions that are triggered by events, such as a user uploading a file or a message being sent to a queue. These functions are run on demand and scaled automatically based on the number of requests received.</p>
<p>Serverless from the name doesn't mean that servers are not involved, it means the cloud developer shouldn't worry about provisioning, deploying and maintaining servers. Just focus on the codes that will be used for deployment. The best part is that, in a serverless architecture, you are billed based on the actual usage of resources rather than a fixed amount. This means that you are only charged for the number of computing resources and time that your code uses.</p>
<p>For example, if you create a function that runs for 100 milliseconds and is triggered 100 times in a month, you will only be billed for the total time that your function runs, which would be 10 seconds (100 x 0.1 seconds). The exact pricing model varies by cloud provider, but generally, you will be charged based on the number of requests, the duration of each request, and the amount of memory used.</p>
<p>I will be utilizing Infrastructure as Code tools, such as Serverless Framework and AWS Serverless Application Model (SAM), throughout this article. By doing so, deploying the entire architecture on AWS will only require a few simple commands. Before we dive in, let's take a moment to familiarize ourselves with Infrastructure as Code (IaC), AWS SAM (Serverless Application Model) and CRUD REST API.</p>
<h3 id="heading-infrastructure-as-code-iac"><strong>Infrastructure as code (IaC)</strong></h3>
<p>Infrastructure as Code (IaC) is the practice of managing and provisioning technology infrastructure using machine-readable definition files, rather than manual configuration. This approach enables teams to automate the process of deploying and managing infrastructure, resulting in faster and more consistent deployments.</p>
<p>At a low level, IaC involves writing code to define the resources and configurations needed for a particular infrastructure, such as servers, databases, load balancers, and other components. The code is typically written using a domain-specific language (DSL) that is specific to the infrastructure provider, such as AWS CloudFormation, Terraform, or Azure Resource Manager.</p>
<h3 id="heading-aws-sam-serverless-application-model"><strong>AWS SAM (Serverless Application Model)</strong></h3>
<p>AWS Serverless Application Model (SAM) is an open-source framework for building serverless applications using AWS services. SAM extends AWS CloudFormation to provide a simplified way of defining the Amazon API Gateway APIs, AWS Lambda functions, and Amazon DynamoDB tables needed by your serverless application. SAM also provides tools for locally testing and debugging serverless applications, as well as deploying them to AWS. With SAM, developers can quickly and easily build and deploy serverless applications on AWS.</p>
<h3 id="heading-crud-rest-api"><strong>CRUD REST API</strong></h3>
<p>A CRUD REST API is a RESTful API that allows for Create, Read, Update, and Delete operations on resources through HTTP methods. REST stands for Representational State Transfer, and is a set of architectural principles for designing web applications.</p>
<p>In a CRUD REST API, each resource is identified by a unique URL, and the HTTP methods are used to perform operations on the resource. The HTTP methods used are:</p>
<ul>
<li><p>POST: used to create a new resource</p>
</li>
<li><p>GET: used to retrieve an existing resource or a collection of resources</p>
</li>
<li><p>PUT: used to update an existing resource</p>
</li>
<li><p>DELETE: used to delete an existing resource</p>
</li>
</ul>
<p>Now we have had some brief explanations of the technologies we will use. It's about time we use Serverless Framework and AWS SAM (Serverless Application Model) to deploy our application on AWS using VSCode as our cloud development environment (CDE).</p>
<p>AWS SAM will be creating a Lambda function, DynamoDB table and API Gateway (REST API) using our CDE VSCode. We will be issuing some commands on our terminal to deploy mentioned resources.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before we get started, you'll need the following:</p>
<ul>
<li><p>An AWS account</p>
</li>
<li><p>VSCode and the AWS Toolkit extension installed</p>
</li>
<li><p>The AWS SAM CLI installed</p>
</li>
<li><p>Basic knowledge of Node.js and the AWS SDK</p>
</li>
</ul>
<p>Confirm installation of AWS CLI and AWS SAM using commands:</p>
<pre><code class="lang-bash">$ aws --version
-&gt; aws-cli/1.27.32 Python/3.10.10 Darwin/21.6.0 botocore/1.29.32
$ sam --version
-&gt;SAM CLI, version 1.66.0
</code></pre>
<h2 id="heading-creating-an-aws-sam-project"><strong>Creating an AWS SAM project</strong></h2>
<p>First, we'll create a new AWS SAM project using the <code>sam init</code> command. Open a terminal window on VSCode and navigate to a directory where you want to create the project. You will get a prompt like below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680561548693/e9191d27-bab8-425f-94be-323cc63c5515.png" alt class="image--center mx-auto" /></p>
<p>Choose the options from the prompt as below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680561554565/8fe8ad46-a712-4227-81b4-7bcb5688425d.png" alt class="image--center mx-auto" /></p>
<p>You should get these files automatically added by AWS SAM. You can inspect the files to have a better understanding of what SAM is building.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680562001762/1df5a788-89d2-4bcb-adb0-a80074991315.png" alt class="image--center mx-auto" /></p>
<p>Next Step: Input <code>sam build</code> in your terminal which will build and package serverless applications before they are deployed to AWS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680562515634/6de1c105-ecbc-4140-9b44-337fc06fd723.png" alt class="image--center mx-auto" /></p>
<p>Next command: <code>sam deploy --guided</code></p>
<p>"Sam deploy --guided" is a command used in the AWS Serverless Application Model (SAM) that initiates a guided deployment process for your serverless application. When you run this command, SAM will prompt you to answer a series of questions about your deployment, such as the AWS Region to deploy to, the name of the stack to create, and any input parameters required by the stack.</p>
<p>This command <code>sam deploy --guided</code> provides a step-by-step guided experience that helps to ensure that your deployment is configured correctly and follows best practices. It also helps to identify and resolve any potential issues or errors before the deployment process begins.</p>
<p>After SAM has deployed all the resources required for the CRUD REST API, you will see the endpoint URL below. Copy and put in a text file as we will be testing it later.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680563032370/ea65d604-397b-48c4-b5cd-04aa47be40ce.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-confirmation-of-sam-deployment">Confirmation of SAM deployment</h3>
<p>Log into the AWS Management Console, and search for DynamoDB, API Gateway, CloudFormation and Lambda one after the other to investigate if it was deployed successfully.</p>
<p>The YAML template file generated by the <code>sam init</code> command in AWS Serverless Application Model (SAM) is used to deploy your serverless application using AWS CloudFormation. if you inspect CloudFormation you will see the template.yaml SAM generated was used to deploy our resources in AWS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680563930493/8d2c81f0-259b-41da-8179-776d687e33a6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-testing-the-crud-api">TESTING THE CRUD API</h3>
<p>There are several API testers, but for this tutorial, the Talend API tester will be used.</p>
<p>Talend API Tester is a tool that is designed to test the API of web services and RESTful services. With this tool, developers and testers can simulate API calls, generate test cases, and perform functional and performance testing of APIs.</p>
<p>Talend API tester is a Chrome extension that can be added to your Chrome browser. You can get it from the Chrome store.</p>
<p>Open the Talend extension and input the endpoint URL you saved in a text file earlier, select the 'GET' method, then click send. You should get an empty string ' [ ] ' the simple reason being that DynamoDB is empty.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680564741231/671a96d6-feff-4fa8-82e6-2693acdfac22.png" alt class="image--center mx-auto" /></p>
<p>To populate DynamoDB we will use the 'POST' method, then input some code in BODY. You should get a 200 response confirmation. After you click send, you can change the values in the body to add more items to DynamoDB.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680565071396/1b79445a-79ac-4a4d-88ea-98b4d3218346.png" alt class="image--center mx-auto" /></p>
<p>If the 'POST' Method worked perfectly, change to the 'GET' Method to confirm if we successfully added items to DynamoDB. You should get a 200 response.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680565499440/f6bcc8f7-76c9-4dc3-9a3f-d8ea967f41dd.png" alt class="image--center mx-auto" /></p>
<p>Goto DynamoDB to confirm</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680565748281/6a27708d-a5e2-44f1-9196-9f7877c0d592.png" alt class="image--center mx-auto" /></p>
<p>Now we can confirm that we have successfully built a CRUD RESTful Microservice API using AWS SAM. This method saves a lot of time, code debugging and troubleshooting. To understand this fully, you have to configure or build it manually which I will do in my next blog post.</p>
<h3 id="heading-adding-api-keys"><strong>ADDING API KEYS</strong></h3>
<p>Add API Keys that will restrict and grant access to selected users.</p>
<p>To add API keys to your CRUD API on AWS API Gateway, you can follow these steps:</p>
<ol>
<li><p>Create an API key: In the AWS Management Console, navigate to the API Gateway service and select "API Keys" from the left-hand menu. Click on "Create API Key" and follow the prompts to create a new API key.</p>
</li>
<li><p>Enable API key usage: In the same "API Keys" menu, select "Usage Plans" and create a new usage plan if you haven't already. Select the usage plan and click on "Add API Key" to add the API key you just created to the plan.</p>
</li>
<li><p>Deploy the API: To deploy your API, first click on 'Resources' in the left-hand menu. Then, click on 'GET' and select 'Method Request'. Set 'API Key Required' to 'True'. To deploy the API, click on 'Actions' and select 'Deploy API'. Make sure to deploy the API whenever you make any changes for the changes to take effect.</p>
</li>
<li><p>Test the API: Test the URL on your browser to confirm that it requires API Key for access. If you Test on Talend API tester you will get a 403 error response.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680567395203/4e5c0726-5cdd-49d9-8fa5-ced5c8752451.png" alt class="image--center mx-auto" /></p>
<p>To add our API keys and enable access, click on 'API Keys' in the left-hand menu of the API created by SAM. Then, click on the previously created key name and click on 'Show' next to 'API Key'. Copy the key into a text file and save it for later.</p>
<p>Next, go to Talend API Tester, select the 'GET' method and input the endpoint URL. Scroll down one step to the headers section and input the default header for API Keys, <code>X-Api-Key</code> and paste in the API key as the value. Finally, click 'Send'. You should receive a 200 response.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680568175802/99410e9a-4e79-4e5a-a001-a790ef5d892b.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-monitoring-api-calls"><strong>MONITORING API CALLS</strong></h3>
<p>Monitoring API calls using CloudWatch involves configuring CloudWatch to capture and log relevant API calls data, such as API Gateway requests, Lambda function invocations, and other metrics.</p>
<p>To view CloudWatch logs, open the Lambda function created by SAM, click "Monitor", then "Logs". Select the logstream of the recent invocations, which will take you to CloudWatch to view logs.</p>
<p>From here, you can use CloudWatch to analyze and visualize your API call data. For example, you can create CloudWatch dashboards to track key metrics, set up CloudWatch alarms to alert you when specific metrics cross a threshold, and use CloudWatch Insights to perform ad-hoc queries and analysis on your log data.</p>
<p>In addition to CloudWatch, AWS provides other tools for monitoring and analyzing your API calls, such as AWS X-Ray and Amazon CloudTrail. AWS X-Ray is a service that lets you trace requests made to your API and identify performance bottlenecks or issues. Amazon CloudTrail is a service that records AWS API calls and events and provides visibility into user and resource activity.</p>
<p>By using CloudWatch, AWS X-Ray, and Amazon CloudTrail together, you can gain comprehensive visibility into your API calls and ensure that your application is running optimally and securely.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680570899619/38841ceb-da77-48db-bb89-3da4a8d6f73a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-cleanup"><strong>CLEANUP</strong></h3>
<p>You need to clean up your AWS account to avoid incurring unnecessary fees.</p>
<p>You can clean up in VSCode using either of the codes below:</p>
<pre><code class="lang-bash">$ sam destroy

$ aws cloudformation delete-stack --stack-name &lt;stack-name&gt; --region &lt;aws-region&gt;
</code></pre>
<p>'sam destroy' is a command in the AWS SAM CLI that removes the AWS CloudFormation stack associated with your SAM application. This command deletes all resources that were created when the stack was created, including the API Gateway, Lambda functions, DynamoDB tables, and any other resources defined in the CloudFormation template.</p>
<p>The command "AWS CloudFormation delete-stack" is used to delete an existing stack. The "&lt;stack-name&gt;" parameter should be replaced with the name of the stack you want to delete, and the "&lt;aws-region&gt;" parameter should be replaced with the AWS region where the stack is located.</p>
<p>By running this command, the AWS CloudFormation service will remove all resources that were created by the stack, and the stack will be deleted. Note that this is an irreversible action, and once a stack is deleted, all associated resources will be terminated and cannot be recovered. Therefore, it is important to use caution when using this command and ensure that you are deleting the correct stack before proceeding.</p>
<h3 id="heading-summary"><strong>SUMMARY</strong></h3>
<p>In conclusion, deploying a CRUD RESTful API using AWS SAM is a straightforward process that allows you to quickly and easily build and deploy your API. With AWS SAM, you can easily manage your serverless applications and scale your infrastructure as your needs grow.</p>
<p>The process involves designing and creating the API using an API design tool and AWS SAM, developing and testing the API locally with the AWS SAM CLI, packaging and deploying the application using AWS CloudFormation, testing the deployed API using a tool like Postman or Talend API Tester, and monitoring and troubleshooting the API using AWS CloudWatch.</p>
<p>By following the steps outlined in this post, you can get your API up and running in no time. So, whether you're building a new API or migrating an existing one, AWS SAM is a powerful tool that can help you get the job done quickly and efficiently.</p>
<p>Hope you enjoyed the tutorial. Good luck and happy coding...</p>
]]></content:encoded></item></channel></rss>