<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Mekuleyi</title>
    <description>The latest articles on DEV Community by Michael Mekuleyi (@monarene).</description>
    <link>https://dev.to/monarene</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F317218%2Fa3b7386b-ce1d-4083-b101-9064ec52552a.jpg</url>
      <title>DEV Community: Michael Mekuleyi</title>
      <link>https://dev.to/monarene</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/monarene"/>
    <language>en</language>
    <item>
      <title>Designing Alerts That Matters using Amazon CloudWatch</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Wed, 15 Apr 2026 16:56:40 +0000</pubDate>
      <link>https://dev.to/monarene/designing-alerts-that-matters-using-amazon-cloudwatch-48fh</link>
      <guid>https://dev.to/monarene/designing-alerts-that-matters-using-amazon-cloudwatch-48fh</guid>
      <description>&lt;h2&gt;
  
  
  The Alert Fatigue Problem
&lt;/h2&gt;

&lt;p&gt;Cloud systems today generate a huge amount of data. Every time a Lambda function runs, an RDS query happens, or an API Gateway is called, it creates information. When teams are just starting out with the cloud, it’s easy to want to set up alerts for everything. If a metric is available, someone usually wants to be notified about it. But this often leads to so many alerts that it ends up overwhelming the team, making it hard to focus on what really matters. Instead of helping, it steals the engineers’ attention.&lt;/p&gt;

&lt;p&gt;Alert fatigue is not a soft problem. It is a direct cause of production incidents being missed or escalated too slowly. When an on-call engineer receives 200 notifications on a quiet night, and 50 of them fire routinely without action, the signal-to-noise ratio collapses. The 201st notification — the one that actually matters — gets lost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wph68ys8l2iewagnsi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wph68ys8l2iewagnsi5.png" alt="Industry Average vs On-call average" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal of this guide is to reframe how you think about alerting. Rather than asking "what should we alarm on?", start with "what conditions require immediate human intervention?" Everything else can wait for a dashboard review, a weekly metric review, or be surfaced as a log insight.&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudWatch Alarms — Fundamentals
&lt;/h2&gt;

&lt;p&gt;Every CloudWatch Alarm is made up of three parts: a Metric, which is the data you’re keeping an eye on; a Condition, which is the rule or threshold that triggers the alarm (this can be a fixed number or based on unusual behavior); and an Action, which is what happens when the alarm changes state—like sending a notification or starting an auto-scaling event. Think of alarms like state machines—you can set them to respond whenever their status changes, not just when something crosses a set limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alarm States Explained
&lt;/h2&gt;

&lt;p&gt;It’s really important to understand the three alarm states because if engineers don’t, the alarms can act in unexpected ways—especially during things like deployments or when there’s missing data. Getting how the alarm’s “state machine” works helps avoid surprises and keeps everything running smoothly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o7wbq7f0rn7om857p77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o7wbq7f0rn7om857p77.png" alt="Alarm States" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Metrics
&lt;/h2&gt;

&lt;p&gt;The four Golden signals;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fcf1ac8l5myow1nkvi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fcf1ac8l5myow1nkvi6.png" alt="Golden Signals" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid Pure Resource Utilization Alarms&lt;/strong&gt;: Having your CPU at 90% isn’t a problem on its own. It only becomes an issue if it’s happening alongside something users notice, like slow response times. To handle this better, you can use Composite Alarms to make sure alerts only go off when multiple signals show there’s a real problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Metrics via EMF&lt;/strong&gt;: You can use the Embedded Metrics Format to send detailed application data from your Lambda functions in a neat, structured JSON format. CloudWatch then automatically understands and processes this data without any extra cost for API calls. Now let us start to review the alert strategies that ensure that alerting is not a nightmare. &lt;/p&gt;

&lt;h2&gt;
  
  
  Thresholds &amp;amp; Evaluation Periods
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The M-of-N Pattern&lt;/strong&gt;: An alarm should go off only when several of the recent data points (M out of N) cross the limit. Don’t trigger an alarm just because 1 out of 1 data point did, unless it’s a clear failure, like having zero healthy hosts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set Thresholds from Baselines&lt;/strong&gt;; Observe 2–4 weeks of normal operation, then set thresholds at a meaningful distance from your p95/p99. Avoid round-number intuition guesses like "80% feels high.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foclfja4rej43s9pt6ni5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foclfja4rej43s9pt6ni5.png" alt=" CloudWatch Alarm — CloudFormation YAML " width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Composite Alarms
&lt;/h2&gt;

&lt;p&gt;Composite Alarms combine several alarms using AND, OR, or NOT logic, so the main alarm only triggers when a specific combination of conditions happens. This way, you avoid unnecessary alerts and focus only on real issues. It’s a powerful way to reduce false alarms and make monitoring more accurate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjbe4eto43w5z5kuz50h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjbe4eto43w5z5kuz50h.png" alt=" Composite Alarms " width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Anomaly Detection Alarms
&lt;/h2&gt;

&lt;p&gt;For metrics that naturally change over time like higher traffic on weekdays and lower at 3 a.m, fixed thresholds can either trigger alarms too often during normal busy times or miss real issues. &lt;/p&gt;

&lt;p&gt;CloudWatch Anomaly Detection utilizes machine learning to recognize these patterns, including time of day and day of week, and alerts you only when the metric exceeds the expected range. It requires at least 14 days of data to create an effective model, so start with static thresholds on new services and then switch to anomaly detection once sufficient data is collected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6erw27srnbrycivp1bty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6erw27srnbrycivp1bty.png" alt="Anomaly Detection Alarms Using AWS CLI" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices Checklist
&lt;/h2&gt;

&lt;p&gt;Use this checklist when reviewing any CloudWatch Alarm — whether newly created or inherited. Every alarm that cannot satisfy these criteria is a candidate for deletion or rework.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] Alarm is actionable — the engineer knows what to do immediately&lt;/li&gt;
&lt;li&gt;[x] Metric correlates directly with user-facing impact&lt;/li&gt;
&lt;li&gt;[x]Threshold set from observed baseline data, not intuition&lt;/li&gt;
&lt;li&gt;[x] M-of-N evaluation configured (minimum 3 of 5 for most metrics)&lt;/li&gt;
&lt;li&gt;[x] TreatMissingData is explicitly configured&lt;/li&gt;
&lt;li&gt;[x] OKAction defined — team gets an automated all-clear&lt;/li&gt;
&lt;li&gt;[x] Correct priority tier / SNS topic assigned&lt;/li&gt;
&lt;li&gt;[x] Runbook URL in alarm description&lt;/li&gt;
&lt;li&gt;[x] Defined in Terraform or CloudFormation — not the console&lt;/li&gt;
&lt;li&gt;[x] Reviewed and tested in the last 90 days&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Designing alerts that matters takes focus and discipline. CloudWatch has lots of great features like anomaly detection, composite alarms, and metric math, but the goal isn’t to use everything everywhere. Instead, pick just the few alarms that give your team clear, useful info without causing too much noise.&lt;/p&gt;

&lt;p&gt;Start with the Four Golden Signals. Use composite alarms to reduce false alarms. Use anomaly detection for metrics that have patterns or seasonal changes. Make sure alerts are prioritized right and include runbooks in the alarm descriptions. Define alerts as code so they’re easy to manage. And regularly review your alarms to remove anything outdated—old alarms can cause more harm than good.&lt;/p&gt;

&lt;p&gt;A well-designed alert means your on-call engineer gets a clear alert at 2 AM, knows exactly what’s wrong, where to check, and who to call. That’s what a good alerting system looks like.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sre</category>
      <category>devops</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to pass the CKA Exam on the first try [GUARANTEED]</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Mon, 26 Jan 2026 19:02:59 +0000</pubDate>
      <link>https://dev.to/monarene/how-to-pass-the-cka-exam-on-the-first-try-guaranteed-44jf</link>
      <guid>https://dev.to/monarene/how-to-pass-the-cka-exam-on-the-first-try-guaranteed-44jf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Before I took the Certified Kubernetes Administrator (CKA) Exam, I read every article there was about taking the exam, and yet I failed it. I knew every shortcut and every command, and yet I scored 5 marks below the passing score. In this article, I tell you about the silent key things that helped me succeed the second time that I wish I had known the first time. This article is not about what to study; it is more about how to study and mostly how to pass the exam. If you read this article at least twice and diligently note everything therein, I guarantee you will pass the exam. &lt;/p&gt;

&lt;h2&gt;
  
  
  Knowledge is more important than Speed
&lt;/h2&gt;

&lt;p&gt;Every article you read about the CKA exam says you have to be fast, you have to know shortcuts, you have to practise speed, blah blah blah. I insist that, first of all, you have to know what you are doing. Speed in the wrong direction will hurt you more than it would help. &lt;/p&gt;

&lt;p&gt;The CKA Exam is more about breadth of knowledge than depth; almost every single topic listed in the curriculum is tested. Also, if you are pretty fast at solving practice questions that you already have answers to, you are not really improving. Write out all the topics you are weak in and unsure about, and review questions on these topics multiple times. Remember not to only practice your strong areas, you need to prioritise even your weak areas. &lt;/p&gt;

&lt;p&gt;Use the CKA and CKAD practice exams and other questions online, generate AI questions if you have to, but practice everything in the curriculum. &lt;/p&gt;

&lt;h2&gt;
  
  
  Pay attention to details
&lt;/h2&gt;

&lt;p&gt;The most painful part about failing the CKA Exam was that most of the questions I failed were the really easy ones, and the questions I got right were the really difficult ones. Apart from me not mastering the basics during prep, I also did not pay attention to little details. When I started prepping the second time, I realised that I had come across most of the questions that I failed at least once or twice during prep, but I either did not acknowledge them, or I just quickly glossed over them. &lt;/p&gt;

&lt;p&gt;If you intend to pass the CKA Exam, ensure that you pay complete and undivided attention to the most minute details during practice and while studying. &lt;/p&gt;

&lt;h2&gt;
  
  
  Study for the CKAD Exam
&lt;/h2&gt;

&lt;p&gt;The CKA Exam is more like the CKAD++; most of the topics in the CKA Exam are in the CKAD Exam, infact I found practice questions from the CKAD Exam more useful than the CKA Exam. I believe this was because practice questions from the CKA Exam mostly focused on troubleshooting rather than the basics. &lt;/p&gt;

&lt;p&gt;If you practice only the CKAD Exam really well, you are most likely to be close to at least 60% of the passing score, infact the Linux Foundation advises that you study the CKAD Exam before taking the CKA Exam. &lt;/p&gt;

&lt;p&gt;P.S: You should take the CKAD Exam first; it's pretty straightforward and not difficult to pass. &lt;/p&gt;

&lt;h2&gt;
  
  
  Do not be persistent on a question
&lt;/h2&gt;

&lt;p&gt;If you fail a question, flag it and simply move on. You probably would get it on a second try.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be conversant with the testing environment
&lt;/h2&gt;

&lt;p&gt;A lot of people have failed the exam because they were not conversant with the exam environment. The testing environment is on the PSI browser with access to a remote virtual machine. It is very important that you understand the testing environment, especially how to search and find on Mozilla Firefox.&lt;/p&gt;

&lt;p&gt;Take time out to play with the testing environment on killer.sh - paying for the exam gives you access to the session. I exhausted the sessions on my first try and paid for some more. Despite that, I still lost the first two minutes when I logged into the exam, and it was zoomed in to the max.&lt;/p&gt;

&lt;p&gt;P.S: If you can use a monitor, it creates a world of difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Believe you can do it
&lt;/h2&gt;

&lt;p&gt;Mentality is a key part of the game; if you do not believe you can get the job done, you simply won't. Believe you can get the job done and work towards it. First of all, be confident in your abilities, do not make silly mistakes, read the question in detail, and pay attention to the remarks. Ensure that you check your solution works. &lt;/p&gt;

&lt;p&gt;Be calm, you are prepared.&lt;/p&gt;

&lt;p&gt;It's okay to shake a little; Who doesn’t? What’s more important is the fact that you are already a Kubernetes administrator even before you sit for this exam, so just go prove yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I deliberately left out specific materials because everyone knows KodeKloud is the best at this kind of stuff; their practice exams just give you the kind of practice you need to pass. &lt;/p&gt;

&lt;p&gt;If this article helps you to pass your exam, I am waiting to hear your success story. &lt;/p&gt;

&lt;p&gt;Please re-read this article until it sticks. Feel free to like, share and subscribe.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>sre</category>
      <category>linux</category>
    </item>
    <item>
      <title>How to Score 93% in the Prometheus Certified Associate Exam</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Wed, 07 May 2025 10:40:35 +0000</pubDate>
      <link>https://dev.to/monarene/how-to-score-93-in-the-prometheus-certified-associate-exam-1g07</link>
      <guid>https://dev.to/monarene/how-to-score-93-in-the-prometheus-certified-associate-exam-1g07</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Passing technical certifications often feels daunting and intimidating. The topics often feel endless, tabs pile up, and you may begin to wonder if you really know anything or if you’re prepared for it. But with the right approach, time and resources, it switches from unnerving and becomes both achievable and rewarding. Recently, I sat for and completed the Prometheus Certified Associate exam with a score of 93%, and the journey highlighted a few key principles worth sharing.  This article is not a hack or guide of certainty to that exact score; rather, it is a reflection on what helped and how anyone preparing for this exam (and any other exam) can do it with better clarity and less burnout. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq9jy0q179abdqznuurr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq9jy0q179abdqznuurr.jpg" alt="Passing the Prometheus Exam" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a breakdown for anyone preparing for the exam and aiming for not just a pass, but true mastery. &lt;/p&gt;

&lt;h2&gt;
  
  
  Get to Know Prometheus
&lt;/h2&gt;

&lt;p&gt;Before you start breaking down the grand topic of  Prometheus and building your study plan, you want to focus on really understanding it. Why does Prometheus exist? How does it fit into the tech world? How does it interact with other tools? What problem does it solve?&lt;/p&gt;

&lt;p&gt;To build this base of knowledge for myself, I used the &lt;a href="https://learn.kodekloud.com/user/courses/prometheus-certified-associate-pca" rel="noopener noreferrer"&gt;Prometheus Certified Associate PCA Course on KodeKloud&lt;/a&gt;. It is structured and hands-on, making complex concepts easy to approach. This excellent course from KodeKloud not only taught concepts but also allowed for real-world lab practice. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwg3who3zg9tpjo8gwtc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwg3who3zg9tpjo8gwtc8.png" alt="Prometheus - KodeKloud" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re preparing for this exam, do not rush past this stage. Spend enough time here to read and understand what Prometheus is about. &lt;/p&gt;

&lt;h2&gt;
  
  
  Build a Structured Study Plan
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5yc6ej8n7wlrbx23evw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5yc6ej8n7wlrbx23evw.png" alt="Structured Study Plan" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consistency beats intensity. Preparing for the exam took roughly 400 minutes per week, which I stretched over eight weeks. Rather than cramming close to exam day, breaking the learning journey into focused daily or weekly sessions is significantly more effective.&lt;br&gt;
Setting a target (for example, 90 minutes per day or a few dedicated hours across the week) ensures that momentum stays steady and learning remains layered. This is much better than putting off all the reading till a week before the exam. This will affect your ability to identify and fill in gaps in knowledge. &lt;/p&gt;

&lt;h2&gt;
  
  
  Document Everything While Studying
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyujof5sozxqye3mj4qx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyujof5sozxqye3mj4qx.jpg" alt="Document Everything" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note-taking isn’t just for school — it’s a superpower.&lt;br&gt;
Throughout my preparation, every topic, command, concept, and tricky detail was documented using Evernote. Having a single repository where everything lives — explanations, commands, tricky questions — builds confidence and revises faster. Don't just consume information; organise it.&lt;br&gt;
Create sections, notebooks, or mind maps that can be quickly skimmed before the exam.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prioritise Practice Tests
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3ps4pcjpllam50jien7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3ps4pcjpllam50jien7.png" alt="Udemy Practice Course" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Theory creates familiarity, but practice cements understanding.&lt;br&gt;
Taking practice exams early and often will reveal weak areas that need reinforcement. The Udemy Prometheus Certified Associate Practice Exam I took in preparation mimicked the actual exam’s structure and timing well, offering a good measure of readiness. It included 4 exams of 60 minutes each. Approach practice exams with seriousness: simulate real conditions (no pausing, no peeking at notes) and time every session.&lt;br&gt;
Over time, your scores will improve, but more importantly, your confidence will be built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manage Time on Exam Day
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgwxfpf1pqxwz6iy51sk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgwxfpf1pqxwz6iy51sk.png" alt="Time Management" width="299" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One often overlooked skill is time management.&lt;br&gt;
I completed the Prometheus exam in under 30 minutes — not because I rushed, but because I was familiar with the exam structure and the pace practiced during mock tests.&lt;br&gt;
Pacing is crucial:&lt;br&gt;
Read questions carefully but efficiently.&lt;br&gt;
Eliminate wrong options methodically.&lt;br&gt;
Trust first instincts when confident, and avoid second-guessing unless absolutely necessary.&lt;/p&gt;

&lt;p&gt;If you spend sufficient time preparing, be confident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Success in technical certifications isn’t about cramming or getting lucky — it’s about building daily discipline, choosing excellent resources, documenting learning clearly, and practicing intentionally. Whether aiming for a Prometheus certification or any other professional milestone, these principles apply universally. And if you follow the steps above diligently, you will find that you are acing your exams and certifications with ease. &lt;br&gt;
Dedicate the time.&lt;/p&gt;

&lt;p&gt;Trust the process.&lt;/p&gt;

&lt;p&gt;And most importantly, enjoy the journey of becoming a little sharper, a little stronger, and a lot more confident.&lt;br&gt;
Thank you for reading. If you have found this helpful, like, share, and follow me for more helpful articles. You can also go through my page to check other articles I’ve written. I’m positive you will find at least one article helpful.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>sre</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Designing a fault-tolerant etcd cluster</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Wed, 07 May 2025 07:39:19 +0000</pubDate>
      <link>https://dev.to/monarene/designing-a-fault-tolerant-etcd-cluster-17d0</link>
      <guid>https://dev.to/monarene/designing-a-fault-tolerant-etcd-cluster-17d0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we are going to discuss a strongly consistent, distributed key-value pair datastore used for shared configuration, service discovery, and scheduler coordination in Kubernetes, this database is called etcd  (pronounced et-see-dee). This article is part of a series that will focus on understanding, mastering, and designing efficient etcd clusters. In this article, we will discuss the justification behind using etcd, the leader election process, and finally, the consensus algorithm used in etcd, in the following parts, we will follow up with a technical implementation of a highly available etcd cluster. and also backing up an etcd database to prevent failures. This article requires a basic understanding of Kubernetes, algorithms, and system design. &lt;/p&gt;

&lt;h2&gt;
  
  
  etcd
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscg24m4mip6xv4yyn9uz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscg24m4mip6xv4yyn9uz.png" alt="Image description" width="385" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;etcd (&lt;a href="https://etcd.io/" rel="noopener noreferrer"&gt;https://etcd.io/&lt;/a&gt;) is an open-source leader-based distributed key-value datastore designed by a vibrant team of engineers at CoreOS in 2013 and donated to Cloud Native Computing Foundation (CNCF) in 2018. Since then, etcd has grown to be adopted as a datastore in major projects like Kubernetes, CoreDNS, OpenStack, and other relevant tools. etcd is built to be simple, secure, reliable, and fast (benchmarked 10,000 writes/sec), it is written in Go and uses the Raft consensus algorithm to manage a highly-available replicated log. etcd is strongly consistent because it has strict serializability (&lt;a href="https://jepsen.io/consistency/models/strict-serializable" rel="noopener noreferrer"&gt;https://jepsen.io/consistency/models/strict-serializable&lt;/a&gt;), which means a consistent global ordering of events, to be practical, no client subscribed to an etcd database will ever see a stale database (this isn't the case for NoSQl databases the eventual consistency of NoSQL databases ). Also unlike traditional SQL databases, etcd is distributed in nature, allowing high availability without sacrificing consistency. &lt;/p&gt;

&lt;p&gt;etcd is that guy. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why etcd?
&lt;/h2&gt;

&lt;p&gt;Why is etcd used in Kubernetes as the key-value store? Why not some SQL database or a NoSQL database? The key to answering this question is understanding the core storage requirements of the Kubernetes API-server.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Designing a fault-tolerant etcd cluster on AWS</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Mon, 04 Nov 2024 10:40:38 +0000</pubDate>
      <link>https://dev.to/monarene/designing-a-fault-tolerant-etcd-cluster-1da7</link>
      <guid>https://dev.to/monarene/designing-a-fault-tolerant-etcd-cluster-1da7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we are going to discuss a strongly consistent, distributed key-value pair datastore used for shared configuration, service discovery, and scheduler coordination in Kubernetes, this database is called etcd  (pronounced et-see-dee). This article is part of a series that will focus on understanding, mastering, and designing efficient etcd clusters. In this article, we will discuss the justification behind using etcd, the leader election process, and finally, the consensus algorithm used in etcd, in the following parts, we will follow up with a technical implementation of a highly available etcd cluster. and also backing up an etcd database to prevent failures. This article requires a basic understanding of Kubernetes, algorithms, and system design. We are also going to focus our cloud service on AWS&lt;/p&gt;

&lt;h2&gt;
  
  
  etcd
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscg24m4mip6xv4yyn9uz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscg24m4mip6xv4yyn9uz.png" alt="Infrastructure Built on ETCD" width="385" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://etcd.io/" rel="noopener noreferrer"&gt;etcd&lt;/a&gt; is an open-source leader-based distributed key-value datastore designed by a vibrant team of engineers at CoreOS in 2013 and donated to Cloud Native Computing Foundation (CNCF) in 2018. Since then, etcd has grown to be adopted as a datastore in major projects like Kubernetes, CoreDNS, OpenStack, and other relevant tools. etcd is built to be simple, secure, reliable, and fast (benchmarked 10,000 writes/sec), it is written in Go and uses the Raft consensus algorithm to manage a highly-available replicated log. etcd is strongly consistent because it has &lt;a href="https://jepsen.io/consistency/models/strict-serializable" rel="noopener noreferrer"&gt;strict serializability&lt;/a&gt;, which means a consistent global ordering of events, to be practical, no client subscribed to an etcd database will ever see a stale database (this isn't the case for NoSQl databases due to the eventual consistency of NoSQL databases ). Also unlike traditional SQL databases, etcd is distributed in nature, allowing high availability without sacrificing consistency. &lt;/p&gt;

&lt;p&gt;etcd is that guy. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why etcd?
&lt;/h2&gt;

&lt;p&gt;Why is etcd used in Kubernetes as the key-value store? Why not some SQL database or a NoSQL database? The key to answering this question is understanding the core storage requirements of the Kubernetes API-server.&lt;/p&gt;

&lt;p&gt;The datastore attached to Kubernetes must have the following requirements, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Change notification&lt;/strong&gt;: Kubernetes is designed to watch over the state of a cluster and adapt the cluster to the desired state, hence the API server acts as a central coordinator between the different clients,  streaming changes to the different parts of the controlplane. The most optimal datastore for kubernetes would allow the API-server to conveniently subscribe to a key or some set of keys and would promptly update the API-server on any key change, while simultaneously performing the updates efficiently at scale. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Kubernetes is a fast-moving orchestrator, it would be a disaster if the datastore powering the kube api-server has eventual consistency. Imagine your kubernetes cluster creating two deployments from a deployment spec because the datastore did not broadcast the update that a deployment already exists. etcd is strongly consistent, meaning that data across each node is the same, infact etcd does not complete a write till the update has been written to every member of the etcd cluster. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability&lt;/strong&gt;: the Kube-Episerver requires its datastore to be highly available, this is because if the datastore is unavailable the Kube-apiserver goes down immediately and Kubernetes is unavailable. etcd solves this problem by being a distributed cluster with many nodes, this means that if a leader node is unhealthy another leader is elected and if a follower node is unhealthy requests are no longer sent to it. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The aforementioned characteristics highlight why etcd is the best choice as a datastore for Kubernetes. Other datastores like Hashicorp Consul, Zookeeper and CockroachDB can replace etcd in the way it is used in Kubernetes but etcd is by far the most popular just due to its share ability to be highly performant.  &lt;/p&gt;

&lt;h2&gt;
  
  
  etcd internals
&lt;/h2&gt;

&lt;p&gt;etcd in itself is a distributed consensus-based system, this means that for the cluster to function there must be a leader and the rest of the nodes would be followers. &lt;a href="https://en.wikipedia.org/wiki/Consensus_(computer_science)" rel="noopener noreferrer"&gt;The distributed consensus&lt;/a&gt; system, offers one of computer science's biggest problems, "How do multiple independent processes decide on a single value for something?". etcd solves this problem by using the &lt;a href="https://raft.github.io/" rel="noopener noreferrer"&gt;Raft Algorithm&lt;/a&gt;. the Raft algorithm is etcd's secret tool for maintaining a balance between string consistency and high availability.&lt;/p&gt;

&lt;p&gt;The Raft algorithm works by electing a leader among nodes in the cluster and then ensuring all write requests go through that leader, any changes made by the leader are broadcast to the other nodes, a write is not also complete until all other nodes receive the write, hence the larger the etcd cluster the longer it takes for a write to occur. Since all the nodes are maintained in the same state, a read request can be sent to any node.  &lt;/p&gt;

&lt;p&gt;How does the Raft elect the leader in a group of nodes in a cluster? At the beginning of every etcd cluster, every node is a follower generally, nodes can exist in three states, follower, candidate, and leader. If followers at any point in time do not hear the heartbeat of a leader, they can then become candidates and request votes from other nodes to become the leader, nodes then respond with their vote, and the candidate with the highest number of votes becomes the leader. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbp1ywklgx2uzxwbapa8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbp1ywklgx2uzxwbapa8r.png" alt="Raft Algorithm" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintaining Quorum
&lt;/h2&gt;

&lt;p&gt;in distributed consensus-based systems that use the Raft algorithm for leader election and voting, decisions are made using a majority vote, which means that if a majority cannot be reached the cluster becomes unavailable. in a cluster of 3 nodes, the majority is 2, if the leader goes offline and an election is held there would be no majority, and the cluster would remain unavailable, hence the quorum of this cluster is 2. &lt;/p&gt;

&lt;p&gt;Theoretically, Quorum refers to the minimum number of members or nodes in a distributed system that must agree (reach consensus) for the cluster to make a decision or perform an operation. The quorum is typically defined as (N/2) + 1, where N is the total number of nodes in the cluster rounded down. For example, in a 5-node cluster, the quorum would be 3 nodes (5/2 rounded down + 1 = 3). This means at least 3 nodes must agree to proceed with a given operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fst0ybqpmi341w517hx0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fst0ybqpmi341w517hx0x.png" alt="Quorum" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fault tolerance is the number of nodes that can be allowed to fail for a cluster to still maintain Quorum, For example, in a 5-node cluster, losing 2 nodes still allows the system to maintain a quorum (3 nodes), but losing 3 nodes would cause the cluster to lose its quorum, hence the fault tolerance of a 5-node cluster is 2. &lt;/p&gt;

&lt;p&gt;Fault Tolerance = N - Q&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4ncbygnt52pk238s95s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4ncbygnt52pk238s95s.png" alt="Fault Tolerance" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look very closely at the image above, you will realize that the difference in fault tolerance between odd-numbered clusters and consecutive even-numbered clusters are the same (For example, 3 and 4 have the same cluster, 5 and 6 also have the same fault tolerance). There is genuinely no competitive advantage of having an even-numbered cluster, hence it is a rule of thumb for consensus-based clusters to have an odd number of nodes. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we discussed one of the most fundamental parts of Kubernetes, the Kubernetes datastore "etcd". We discussed the requirements that Kubernetes API-server has for its datastore, we also went in-depth on why etcd is the most popular datastore and what competitive advantages it has over SQL and NoSQL databases. Finally, we discussed concepts fundamental to the design of distributed clusters and how to ensure and guarantee availability for distributed clusters and not just etcd. In the next article, we will build an etcd cluster from the ground up and put the practice the things discussed in this article. If you enjoyed reading this article, feel free to share and also subscribe to my page. &lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>sre</category>
      <category>sitereliabilityengineering</category>
    </item>
    <item>
      <title>Inside the AWS Kubernetes Control Plane</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Wed, 10 Apr 2024 08:18:53 +0000</pubDate>
      <link>https://dev.to/monarene/inside-the-kubernetes-control-plane-28ie</link>
      <guid>https://dev.to/monarene/inside-the-kubernetes-control-plane-28ie</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I will be discussing the essential parts of the Kubernetes brainbox, namely parts of the Kubernetes control plane. This article is a foundation for a deep dive into Kubernetes that I will be doing throughout the year. This piece of writing aims to give a clear foundation to the different parts of the Kubernetes control plane, and how they work individually and collectively to ensure pods are deployed smoothly and efficiently. This body of work is also solely focused on the master node components, other components that function on the worker nodes will be discussed in a later article. To get the best of this article you require no prior knowledge of Kubernetes or any programming experience. &lt;/p&gt;

&lt;p&gt;Kindly note that the word "Kubernetes" is used interchangeably with the word "kube", especially when describing a core component of the control plane, this is an abbreviation that has been accepted as a standard in the community. &lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Kubernetes Control Plane
&lt;/h2&gt;

&lt;p&gt;Kubernetes is an open-source orchestration platform that is used to manage, deploy, and scale containerized applications anywhere. Kubernetes (short for k8s) is portable and extensible and can be used to manage workloads that facilitate both declarative configuration and automation. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fun fact: The name Kubernetes originates from Greek, meaning helmsman or pilot&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article, we are focused on a series of components in the Kubernetes ecosystem called the Control plane. The Kubernetes control plane is a set of components in Kubernetes that collectively work to reconfigure the state of a cluster with the main goal of achieving a desired state. The control plane utilizes information such as cluster activity and node date to ensure that deployed applications are fault-tolerant and highly available. &lt;/p&gt;

&lt;p&gt;In this article, we will discuss the four major parts of the control plane namely, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kube api-server &lt;/li&gt;
&lt;li&gt;etcd &lt;/li&gt;
&lt;li&gt;Kube-scheduler &lt;/li&gt;
&lt;li&gt;Kube controller-manager
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Caveat: I am not including the Cloud controller manager because this article is solely focused on native parts of Kubernetes, outside of any cloud provider or third-party system. 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feo2v2u94q2j1li9sb4ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feo2v2u94q2j1li9sb4ng.png" alt="A flow chart showing the different parts of the Kubernetes Control-Plane" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Kube api-server
&lt;/h2&gt;

&lt;p&gt;The Kube api-server exposes the Kubernetes API through multiple pathways (such as HTTP, gRPC, and kubectl), making it the only external-facing component of the control plane. The kube api-server validates and configures data for native Kubernetes objects such as pods, deployments, and services. The api-server is also solely responsible for authentication of API requests, authorization of roles and groups, and admission control of Kubernetes objects.&lt;/p&gt;

&lt;p&gt;The api-server's state is stored in a distributed and highly consistent database (etcd) so the api-server itself is stateless and can be easily replicated across different machines. The api-server supports both the SPDY protocol, as well as HTTP2/WebSocket however, SPDY is being deprecated for HTTP2/Websocket. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2b6ys9my81rb6h1ubqxk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2b6ys9my81rb6h1ubqxk.png" alt="Flowchart detailing how the kube api-server interacts with other components of the Kubernetes control plane" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  etcd
&lt;/h2&gt;

&lt;p&gt;etcd is an open-source, strongly consistent distributed key-value store that is used to store configuration, state data, and meta information for Kubernetes. Kubernetes utilizes the highly available nature of etcd to provide a single consistent source of truth about the status of its distributed clusters, this is inclusive of the pods and application instances deployed on the same pods. &lt;/p&gt;

&lt;p&gt;etcd is designed to have no single point of failure and gracefully tolerate hardware failure and network partitions, it is also pretty fast as it has been benchmarked to be able to handle over 1,000 writes per second (&lt;a href="https://etcd.io/docs/v3.5/benchmarks/etcd-2-1-0-alpha-benchmarks/" rel="noopener noreferrer"&gt;https://etcd.io/docs/v3.5/benchmarks/etcd-2-1-0-alpha-benchmarks/&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Due to the highly sensitive nature of the data that resides in Kubernetes, etcd also supports automatic Transport Layer Security (TLS) and secure socket layer (SSL) client certificate authentication. &lt;/p&gt;

&lt;p&gt;The kube api-server stores each cluster's state data in etcd, it also makes use of etcd's watch function to monitor saved state data and initiate a response when the state data deviates from the intended data. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5duvtalya9gayf4a5br.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5duvtalya9gayf4a5br.png" alt="etcd: The ultimate database for distributed systems " width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Kube scheduler
&lt;/h2&gt;

&lt;p&gt;The Kubernetes scheduler is the component of the control plane that is responsible for assigning pods to Nodes. The scheduler watches for newly created pods and then assigns the best possible available node to those pods. Multiple different schedulers can be used in the same cluster, depending on the intended scheduling algorithm.  &lt;/p&gt;

&lt;p&gt;In a cluster, nodes that satisfy the scheduling requirements of a pod are called &lt;em&gt;feasible nodes&lt;/em&gt;, if none of the nodes are suitable the pod is left in a pending state until it is allocated to a node. &lt;/p&gt;

&lt;p&gt;The process by which the kube-scheduler assigns pods to nodes is pretty straightforward. When a new pod is created, the scheduler picks up its requirements and proceeds to find feasible nodes for the pod in a process known as &lt;em&gt;filtering&lt;/em&gt;. After filtering, the scheduler runs a set of functions to score feasible nodes in a process known as &lt;em&gt;scoring&lt;/em&gt;. After scoring is complete, the scheduler then picks the node with the highest score among the feasible nodes to run the pod and proceeds to attach that node to the pod, this process is known as &lt;em&gt;binding&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;The kube scheduler assigns the pod to the node with the highest score, and if there is more than one node with the highest score, the scheduler selects one at random and attaches the pod to that node. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd11o44l4xr6wvxxjwa9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd11o44l4xr6wvxxjwa9r.png" alt="Kube scheduler flow chart" width="715" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Kube controller-manager
&lt;/h2&gt;

&lt;p&gt;To completely understand the role of the Kubernetes controller-manager, it is imperative that you understand the complete function of A controller.  &lt;/p&gt;

&lt;p&gt;A controller is a non-terminating loop that regulates the state of the Kubernetes system, a controller works to bring the whole system to the desired functioning state. When the current state of an object deviates from the desired state, the control loop takes corrective steps to make sure that the current state is the same as the desired state. A controller can perform the aforementioned duties with the help of the kube api-server. &lt;/p&gt;

&lt;p&gt;An example of a controller is the node controller, which is responsible for monitoring the state of nodes and taking the necessary actions to keep applications running when a node is faulty. Another example is the replication controller, this controller monitors the state of the replica sets and ensures that the desired number of pods are available at all times within the set. If a pod terminates, another one will be created in its place. &lt;/p&gt;

&lt;p&gt;The kube controller-manager is a core component of the control place that is responsible for running multiple controllers that maintain the desired state of a cluster. The kube controller manager runs as a daemon (on every node in the cluster) and it comes pre-packaged with the necessary controllers to successfully run a Kubernetes cluster. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forjcur14v0282zmfywul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forjcur14v0282zmfywul.png" alt="Differents controllers that are packaged in the Kubernetes Controller-Manager - KodeKloud" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we have described the core parts of the Kubernetes Control plane, their roles, and how they collectively function to ensure that the cluster is healthy. This article is a primer to a series of articles that I will be writing on Kubernetes through the course of the year. Thank you for reading! Please like, share, and subscribe to enjoy more articles from me. Thank you!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>sre</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Hardening Cluster Security in Google Kubernetes Engine</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Tue, 12 Dec 2023 19:34:51 +0000</pubDate>
      <link>https://dev.to/monarene/hardening-cluster-security-in-google-kubernetes-engine-3n30</link>
      <guid>https://dev.to/monarene/hardening-cluster-security-in-google-kubernetes-engine-3n30</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This technical article is a detailed continuation of my talk at &lt;a href="https://x.com/gdgikorodu/status/1732741112811421924?s=20" rel="noopener noreferrer"&gt;DevFest Ikorodu&lt;/a&gt;, where I spoke extensively about key security concepts in Kubernetes and how to build a truly secure cluster network. In this article, I will highlight security concepts that are wholly focused on Google Kubernetes Engine (GKE) on the Google Cloud Platform, I will discuss security policies native to Google Cloud and particularly to Kubernetes, I will also discuss container-optimized images and some security practices that run at deploy-time. This article requires some knowledge of Kubernetes, Google Cloud, and a passion for building secure systems. &lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Shared responsibility model
&lt;/h2&gt;

&lt;p&gt;Google's ideology towards building secure systems is called the &lt;a href="https://cloud.google.com/architecture/framework/security/shared-responsibility-shared-fate" rel="noopener noreferrer"&gt;Shared Responsibility Model&lt;/a&gt;, the shared responsibility model details that the security of your workloads, networks, and data is a joint liability between Google and the Client (You). As regards the Google Kubernetes Engine, Google has a responsibility to secure the master control plane and its components like the API server, etcd database, and controller manager while the user is responsible for securing nodes, containers, and pods. Across IaaS, PaaS, and SaaS, Google properly defines its responsibility and also the Client's responsibility. This is clearly shown in the diagram below &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58igrrw3rm389rpe78pf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58igrrw3rm389rpe78pf.png" alt="Google's Shared Responsibility Model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Security Practices in Kubernetes on Google Cloud
&lt;/h2&gt;

&lt;p&gt;Google Cloud provides a series of services, policies, and configurations that strengthen authentication and authorization across Kubernetes networks, data systems, and workloads. A majority of these policies are configurable and this makes it the whole responsibility of the client to ensure that their cluster has the appropriate security policy in use. In this article, we will focus briefly on the following security concepts,  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network Policies &lt;/li&gt;
&lt;li&gt;Shielded GKE nodes &lt;/li&gt;
&lt;li&gt;Container-optimised OS images&lt;/li&gt;
&lt;li&gt;Binary Authorization&lt;/li&gt;
&lt;li&gt;Private Clusters&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Network Policies
&lt;/h2&gt;

&lt;p&gt;By default all pods in Kubernetes can communicate with each other, however, Kubernetes provides access to objects that can limit inter-pod communication. These Kubernetes objects are called network policies. Kubernetes network policies allow you to specify how a pod can communicate with various network entities based on pods matching label selectors or specific IP addresses with port combinations. These policies can be defined for both ingress and egress. &lt;/p&gt;

&lt;p&gt;GKE provides the option to enforce the use of a network policy when a cluster is created to ensure that inter-pod communication is controlled. You can easily configure this by running the following command,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Enforce network policy on new clusters 
gcloud container clusters create &amp;lt;cluster-name&amp;gt; --enable-network-policy

# Enforce network policy on existing clusters
gcloud container clusters update &amp;lt;cluster-name&amp;gt; --update-addons=NetworkPolicy=ENABLED


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

&lt;/div&gt;

&lt;p&gt;An example of a simple network policy can be found &lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shielded GKE nodes
&lt;/h2&gt;

&lt;p&gt;Google also provides Shielded GKE nodes that increase cluster security by providing strong verifiable node identity and integrity. Google Cloud simply uses Shielded Compute Engine virtual machines as Kubernetes cluster nodes. These virtual machines cannot be compromised at the boot or kernel level, because they use virtual Trusted Platform modules and also secure boot. Shielded VMs enforce and verify the signature of all the components at the boot process to make sure that individual components and modules in the VMs are safe and secure. &lt;/p&gt;

&lt;p&gt;Shielded GKE nodes prevent attackers from impersonating nodes in a cluster in the event of a pod vulnerability being exploited. &lt;/p&gt;

&lt;p&gt;You can enable Shielded nodes in new/existing clusters with the following commands, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Enable Shielded GKE nodes on new cluster
gcloud container clusters create &amp;lt;cluster-name&amp;gt; --enable-shielded-nodes

# Enable Shielded GKE nodes on existing cluster
gcloud container clusters update &amp;lt;cluster-name&amp;gt; --enable-shielded-nodes

# Verify that Shielded GKE nodes are enabled (check for enabled under shieldedNodes as true) 
gcloud container clusters describe &amp;lt;cluster-name&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;There is no extra cost for using Shielded nodes in GKE, however, they generate more logs which will generally lead to an overall increase in cost. &lt;/p&gt;

&lt;h2&gt;
  
  
  Container Optimised OS images
&lt;/h2&gt;

&lt;p&gt;Container-optimized OS (also known as &lt;code&gt;cos_containerd image&lt;/code&gt; )   are Linux-based kernel images provided by Google to run secure and production-ready workloads. They are optimized and hardened specifically for running enterprise workloads. They continuously scan for vulnerabilities at the kernel level and patch and update any package in case of a vulnerability. Their root filesystem is always mounted as read-only, this prevents attackers from making changes to the filesystem. They are completely stateless, however, can be customized to allow for writes on a specific directory. &lt;/p&gt;

&lt;h2&gt;
  
  
  Binary Authorization
&lt;/h2&gt;

&lt;p&gt;Binary Authorization ensures that only trusted containers are deployed to clusters in GKE. It is a deploy-time security service provided by Google. Binary Authorization has seamless integration with Container analysis, a GCP service that scans container images stored in the Container registry for Vulnerabilities. Binary Authorization comprises one or more rules before the image is allowed to be deployed in the cluster. Binary authorization also ensures that only attested images are deployed, an attested image is an image that is verified by an attestor. At the time of deployment, Binary Authorization enforces the use of an attestor to verify the attestation. Any unauthorized image that does not match the Binary Authorization policy is rejected and will not be deployed. &lt;/p&gt;

&lt;p&gt;The following commands will enable binary authorization on GKE clusters, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Enable binary authorization on a new cluster 
gcloud container clusters create  CLUSTER_NAME --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE --zone ZONE

# Enable binary authorization on an existing cluster
gcloud container clusters update CLUSTER_NAME --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE --zone ZONE


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

&lt;/div&gt;

&lt;p&gt;Even though binary authorization prevents unauthorized images from being deployed, you can specify the &lt;code&gt;break the glass&lt;/code&gt;  flag as an annotation in the pod deployment to allow the pods to be created even if the image violates the Binary authorization policy. The following is an example of a pod specification that uses the &lt;code&gt;break-glass&lt;/code&gt;  annotation,&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;&lt;br&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;&lt;br&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-break-glass-pod&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;alpha.image-policy.k8s.io/break-glass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Private Clusters&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;GKE Private Clusters are used to isolate the network connectivity of a cluster to the public internet, this includes both inbound and outbound traffic. This is possible because the nodes in the cluster will have only an internal private IP address and no public-facing IP address. If nodes require outbound internet traffic then a managed Network Address Translation gateway will be used. For inbound internet access, external clients can reach the applications inside the cluster through Kubernetes service objects. Such services should be of the type &lt;code&gt;NodePort&lt;/code&gt; or &lt;code&gt;LoadBalancer&lt;/code&gt;. Since inbound or outbound traffic is not allowed, it will be impossible to use public docker containers to deploy images in clusters. To access public containers, it is advised that you create a Cloud NAT gateway or upload the images to a private Container Registry and then point your cluster to them.&lt;/p&gt;

&lt;p&gt;The level of access to a private cluster via endpoints can be controlled through any of the following configurations;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public endpoint access disabled &lt;/li&gt;
&lt;li&gt;Public endpoint access enabled; authorized networks enabled for limited access &lt;/li&gt;
&lt;li&gt;Public endpoint access enabled; authorized networks disabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following code snippets will enable you to create private clusters in any of the aforementioned configurations,&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;h1&gt;
  
  
  Public Access Disabled
&lt;/h1&gt;

&lt;p&gt;gcloud container clusters create my-private-cluster --create-subnetwork name=my-subnet --enable-master-authorized-networks --enable-ip-alias --enable-private-nodes --enable-private-endpoint --master-ipv4-cidr 172.20.4.32/28&lt;/p&gt;
&lt;h1&gt;
  
  
  Public endpoint access enabled; authorized networks enabled for limited access
&lt;/h1&gt;

&lt;p&gt;gcloud container clusters create my-private-cluster-1 --create-subnetwork name=my-subnet-1 --enable-master-authorized-networks --enable-ip-alias --enable-private-nodes --master-ipv4-cidr 172.20.8.0/28&lt;/p&gt;
&lt;h1&gt;
  
  
  Public endpoint access enabled; authorized networks disabled
&lt;/h1&gt;

&lt;p&gt;gcloud container clusters create my-private-cluster-2 --create-subnetwork name=my-subnet-2 --no-enable-master-authorized-networks --enable-ip-alias --enable-private-nodes --master-ipv4-cidr 172.20.10.32/28&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;In this article, I discussed in detail the active steps you can take to secure your cluster and ensure that your Kubernetes workloads are safe and secure. If you enjoyed reading this article, kindly follow me on &lt;a href="https://twitter.com/monnarene" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. You can also like and share this article with anyone interested in learning about securing their GKE clusters. Thank you and be safe!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Google-Cloud-DevOps-Engineers-certification/dp/1839218010" rel="noopener noreferrer"&gt;Google Cloud for DevOps Engineers: A practical guide to SRE and achieving Google's Professional Cloud DevOps Engineer certification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/kubernetes-engine/docs/concepts/security-overview" rel="noopener noreferrer"&gt;GKE Security Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sre.google/" rel="noopener noreferrer"&gt;SRE at Google&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>gcp</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Taints and Tolerations in Kubernetes: A Pocket Guide</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Sat, 30 Sep 2023 20:00:55 +0000</pubDate>
      <link>https://dev.to/monarene/taints-and-tolerations-in-kubernetes-a-pocket-guide-37jb</link>
      <guid>https://dev.to/monarene/taints-and-tolerations-in-kubernetes-a-pocket-guide-37jb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This body of work is the conclusion of a three-part series on Pod scheduling in Kubernetes. We started this series with &lt;a href="https://dev.to/monarene/intentional-kubernetes-pod-scheduling-nodeselector-3p7i"&gt;Intentional Pod Scheduling using Node Selectors&lt;/a&gt;, We went on to further explore &lt;a href="https://dev.to/monarene/kubernetes-node-affinity-a-love-story-between-nodes-and-pods-2cfg"&gt;Pod scheduling with Node Affinity&lt;/a&gt; and finally we will be touching on Pod scheduling using Taints and tolerations. In the article, we will discuss the practical use of taints and tolerations in pod scheduling, how to apply taints to multiple nodes/node pools, and how to apply tolerations to pods.&lt;/p&gt;

&lt;p&gt;This article requires that you have a working knowledge of Kubernetes, yaml files and you understand how to use the command line interface (CLI). &lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Taints and Tolerations
&lt;/h2&gt;

&lt;p&gt;Kubernetes version 1.8 came with a feature called "Taints and Tolerations", the main goal of this feature was to prevent unwanted pods from being scheduled on some particular nodes. Kubernetes also used this feature to prevent pods from being scheduled on the master node and to ensure the master node was free from taking on workloads. Taints are generally applied on nodes to prevent unwanted scheduling, tolerations are applied on pods to allow them to be scheduled on nodes that have taints 🥲. &lt;/p&gt;

&lt;p&gt;Another practical application of taints is scheduling pods with special compute requirements to nodes that have those requirements, with this we can deliberately schedule compute-intensive pods to special hardware nodes. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tainting a node
&lt;/h2&gt;

&lt;p&gt;To taint a node, we will need to run the following command,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl taint nodes &amp;lt;node name&amp;gt; &amp;lt;taint key&amp;gt;=&amp;lt;taint value&amp;gt;:&amp;lt;taint effect&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here,  is the name of the node that you want to taint, and the taint is described with the key-value pair. In the above example,  is mapped to the  and the taint effect is correlated to the key-value pair.&lt;/p&gt;

&lt;p&gt;Taint effects also define what will happen to pods if they don’t tolerate the taints. The three taint effects are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NoSchedule: A strong effect where the system lets the pods already scheduled in the nodes run, but enforces taints from the subsequent pods.&lt;/li&gt;
&lt;li&gt;PreferNoSchedule: A soft effect where the system will try to avoid placing a pod that does not tolerate the taint on the node.&lt;/li&gt;
&lt;li&gt;NoExecute: A strong effect where all previously scheduled pods are evicted, and new pods that don’t tolerate the taint will not be scheduled.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding a Toleration to a pod
&lt;/h2&gt;

&lt;p&gt;Tolerations help you schedule pods on nodes with taints. Tolerations are usually applied to pod manifests in the following format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the toleration and the taints match, the pod can be scheduled on that node, however, the pod with the toleration can still be scheduled on any other node even without the taint. This is why it is advised to taint all the nodes in your cluster if you intend on using taints for pod scheduling.  &lt;/p&gt;

&lt;p&gt;In the end, your pod would look like this,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent
    resources:
      requests:
        memory: "128Mi"
        cpu: "250m"
      limits:
        memory: "512Mi"
        cpu: "500m"
  tolerations:
  - key: &amp;lt;taint key&amp;gt;
    operator: "Equal"
    value: &amp;lt;taint value&amp;gt;
    effect: &amp;lt;taint effect&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conclusion. &lt;br&gt;
Understanding and effectively utilizing pod scheduling in Kubernetes through taints and tolerations is essential for optimizing resource allocation, ensuring high availability, and maintaining the reliability of your containerized applications. By carefully defining taints on nodes and specifying tolerations in your pod specifications, you can achieve a fine-grained level of control over where and how your pods are placed within the cluster.&lt;/p&gt;

&lt;p&gt;Please give this article a like if you enjoyed reading it, and feel free to subscribe to my page. Thank You!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>sre</category>
    </item>
    <item>
      <title>Kubernetes Node Affinity; A Love Story between Nodes and Pods</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Thu, 31 Aug 2023 19:33:35 +0000</pubDate>
      <link>https://dev.to/monarene/kubernetes-node-affinity-a-love-story-between-nodes-and-pods-2cfg</link>
      <guid>https://dev.to/monarene/kubernetes-node-affinity-a-love-story-between-nodes-and-pods-2cfg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will discuss designing a one-to-one heartfelt relationship between a node and a Kubernetes object (pod/deployment). This article at its core is centered on intentional pod scheduling with Kubernetes with a focus on Node affinity. In this piece of art, we will explore the definition of Node Affinity, the reason we instruct certain nodes to be deployed on certain pods, why this form of pod scheduling is preferred, and finally how to set up Node Affinity for a set of nodes and some pods. To follow this body of work it is important you understand the basic concepts of Kubernetes, you have a cluster up and running and you genuinely have your heart open to a love story. &lt;/p&gt;

&lt;h2&gt;
  
  
  A Love Story
&lt;/h2&gt;

&lt;p&gt;In this love story, we have two friends called Claire and Fiona. Claire and Fiona are looking to get married but they have specific requirements for a suitor. They both insist on marrying tall men while Fiona would like a tall man who has a beard (however she is not adamant about it). Both friends would rather not get married if they don't find tall men, However in a surplus of tall men, Fiona would like a man with a beard. Our task in this article is to design "Men" for both Claire and Fiona. &lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Node affinity
&lt;/h2&gt;

&lt;p&gt;To understand Node affinity assume the two friends are pods and the Suitors (Men) are nodes. Some pods have &lt;code&gt;Hard&lt;/code&gt; requirements (tall men) these requirements are compulsory and an absence of them would leave the pod unscheduled, while some pods have &lt;code&gt;Soft&lt;/code&gt; requirements (bearded men), these requirements are optional and they just streamline the process of finding a suitable node.  &lt;/p&gt;

&lt;p&gt;We deliberately place such requirements on pods to ensure that those pods are deployed on certain nodes, for example, pods that require a lot of compute/memory resources should be deployed on nodes with heavy compute/memory resources. The primary reason for such a design is for deliberate pod scheduling and to override the default scheduling mechanism of the kube-scheduler. We mask the properties of these nodes by using labels and then we design the pods to look for these labels.  Let's move on to designing nodes that will be suitable for both Claira and Fiona. &lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a Node for Claire
&lt;/h2&gt;

&lt;p&gt;For this article, it is assumed that we have two fresh nodes running. On the terminal, run the following command, to view the nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foz41mu1ygaxywigfwwnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foz41mu1ygaxywigfwwnq.png" alt="Nodes running in a cluster" width="800" height="64"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that we have two distinct nodes, one ending with 54 on the first sub-string of the node name and another ending in 197 on the first substring of the node name. &lt;/p&gt;

&lt;p&gt;To deploy the claire pod, save the following configuration into &lt;code&gt;claire.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: claire
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: height
            operator: In
            values:
            - tall
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a keen look at &lt;code&gt;spec.affinity.nodeAffinity&lt;/code&gt; , you will find a field called &lt;code&gt;requiredDuringSchedulingIgnoreDuringExecution&lt;/code&gt;, &lt;code&gt;requiredDuringScheduling&lt;/code&gt; means that for the pod to be assigned a node, it is a must that the expressions on the pod match the requirements. In this case the requirement is that one of the labels of the pod must be &lt;code&gt;height=tall&lt;/code&gt; . &lt;code&gt;IgnoreDuringExecution&lt;/code&gt;  means that after scheduling on the node,  ignore further changes to the labels on the nodes, in simpler terms even if the labels on the node change to  &lt;code&gt;height=short&lt;/code&gt; the pod should still continue to be assigned to the node. &lt;/p&gt;

&lt;p&gt;To start the deployment, run the following command on the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; claire.yaml 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a few seconds, check the state of the pod by running the following command,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjoc3exmnwn8pdx8bl1lg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjoc3exmnwn8pdx8bl1lg.png" alt="Deployment of pods" width="800" height="51"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see that claire is stuck in a pending phase, this is the case because none of the nodes have the label &lt;code&gt;height=tall&lt;/code&gt; Hence claire will not be scheduled on any of the nodes as this is a &lt;code&gt;Hard&lt;/code&gt; requirement for scheduling. &lt;/p&gt;

&lt;p&gt;To further verify that this is the case, run the following command,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl describe pods claire 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14zemfmhxy13rdufpfly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14zemfmhxy13rdufpfly.png" alt="kubectl describe pods claire" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fix this, assign the node that ends with 54 the label &lt;code&gt;height=tall&lt;/code&gt; , we do this by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl label nodes ip-172-31-15-54.us-east-2.compute.internal &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let us check the status of the pod by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feplo1cnjbjbqhvmdes1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feplo1cnjbjbqhvmdes1y.png" alt="kubectl get pods" width="800" height="38"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now see that the pod is running as expected, And claire has been paired with a Suitor 🥰. To further show that claire was scheduled to the node ending with 54, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl describe pods claire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4x0gt959x337lj4lrt90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4x0gt959x337lj4lrt90.png" alt="kubectl describe pods claire" width="800" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a Node for Fiona
&lt;/h2&gt;

&lt;p&gt;To setup the pod definition for Fiona, save the following configuration into &lt;code&gt;fiona.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: fiona
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: height
            operator: In
            values:
            - tall
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: beard
            operator: In
            values:
            - present
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As per the configuration, you can see that Fiona requires that her man be tall, but amongst tall men, she would also like a bearded man. This is illustrated in the &lt;code&gt;preferredDuringScheduling&lt;/code&gt; field , this requirement is what we referred to as a  &lt;code&gt;Soft&lt;/code&gt; requirement. To further illustrate this, we will assign the node that ends with 197 with the labels &lt;code&gt;height=tall&lt;/code&gt; and &lt;code&gt;beard=present&lt;/code&gt; .  We can do this by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl label nodes ip-172-31-37-197.us-east-2.compute.internal &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tall &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl label nodes ip-172-31-37-197.us-east-2.compute.internal &lt;span class="nv"&gt;beard&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;present
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we deploy fiona and see what node she moves to, We do this by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectt apply &lt;span class="nt"&gt;-f&lt;/span&gt; fiona.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we check to see if she is running as expected, &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktwj82n4kjdym7z68e3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktwj82n4kjdym7z68e3v.png" alt="kubectl get pods" width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that Fiona is running and has been successfully scheduled to a pod. Now to check the pod she has been scheduled to we run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdz8ujj6x5vdfq1citj9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdz8ujj6x5vdfq1citj9d.png" alt="kubectl get pods -o wide" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see that Fiona has been scheduled on the appropriate node, the Suitor that is tall and also has beards, and this is how we schedule pods to suitable nodes. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article we used an illustration of a courting process to describe the intentional pod scheduling using Node Affinity, We configured two nodes with appropriate labels, we deployed our pods to their respective suitors (Nodes) and finally, we showed how to assign labels to nodes and pods to labels. Feel free to like, share, and comment on this article. Don't forget to subscribe to receive notifications when I post new articles. Thank you!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>aws</category>
      <category>gcp</category>
    </item>
    <item>
      <title>Intentional Kubernetes Pod Scheduling - NodeSelector</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Mon, 31 Jul 2023 20:02:09 +0000</pubDate>
      <link>https://dev.to/monarene/intentional-kubernetes-pod-scheduling-nodeselector-3p7i</link>
      <guid>https://dev.to/monarene/intentional-kubernetes-pod-scheduling-nodeselector-3p7i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will be discussing deliberate pod scheduling in Kubernetes with a focus on using Node Selectors. First, we will focus on the default mechanism for pod scheduling in Kubernetes, then we will justify the need to deliberately schedule pods to specific nodes, outlining the different methods of pod scheduling. Finally, we will do a run-through on scheduling a pod to a specific node on an actual cluster using the nodeSelector method. This article requires that you have a strong working knowledge of Kubernetes and that you are conversant with using the &lt;code&gt;kubectl&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a 3-part series that focuses on different methods of pod scheduling, This is part 1, focused on using  the NodeSelector method, other parts will be focused on Node Affinity with Interpod-Afffinty and Anti-affinity and finally, Taints and Tolerations.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Kubernetes Schedule Pods by default?
&lt;/h2&gt;

&lt;p&gt;Kubernetes schedules pods with a component of the control plane called the kube-scheduler. The Kube-scheduler is responsible for selecting an optimal node to run newly created or not yet scheduled (unscheduled) pods. The kube-scheduler is also built to allow you write your own scheduling component when you need to. &lt;/p&gt;

&lt;p&gt;Nodes that meet the pod requirements are called &lt;em&gt;feasible nodes&lt;/em&gt;, the kube-scheduler is tasked with the responsibility of finding feasible nodes for a pod among the nodes in a cluster (in a process called filtering), running a set of algorithms on the feasible nodes to pick the node with the highest score (this process is called scoring) and finally assigning the pod to that node. If there are no feasible nodes to run a pod, the pod remains unscheduled. If there is more than one node with the same high score, the kube-scheduler selects a node at random to run the pod. &lt;/p&gt;

&lt;p&gt;At the end of this selection process, the kube-scheduler notifies the kube api-server about the final node selected to run the pod in a seperate process called binding, and then the pod is deployed to that node. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why deliberate pod scheduling?
&lt;/h2&gt;

&lt;p&gt;Deliberate pod scheduling is most important when you have multiple node pools in a customized cluster. For example, if you want a pod to run on a node that has SSD for faster processes, you can schedule the pod to run on just that node. Perhaps you want to co-locate pods to run on a particular node in the same zone for less latency or  you want strongly related services to run on the same node, deliberate pod scheduling can be used to write your scheduling algorithm and override the default scoring process used by the kube-scheduler. &lt;/p&gt;

&lt;p&gt;There are a number of distinct ways to deliberately schedule pods on nodes, below are a few of them;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NodeSelector &lt;/li&gt;
&lt;li&gt;Node Affinity &lt;/li&gt;
&lt;li&gt;Inter-pod affinity and Anti-affinity&lt;/li&gt;
&lt;li&gt;NodeName&lt;/li&gt;
&lt;li&gt;Taints and Tolerations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  NodeSelector
&lt;/h2&gt;

&lt;p&gt;In this article, we will focus on using a NodeSelector and we will explore other options in a later article. Using &lt;code&gt;nodeSelector&lt;/code&gt; is the simplest recommended form of node selection constraint. You can add the nodeSelector field to your Pod specification and specify the node labels you want the target node to have. &lt;/p&gt;

&lt;p&gt;Kubernetes only schedules the Pod on nodes that have each of the labels you specify. NodeSelector is a Pod attribute that forces kube-scheduler to schedule a pod only against a node with a matching label and corresponding value for the label. &lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up NodeSelector
&lt;/h2&gt;

&lt;p&gt;The first thing to do in setting up NodeSelector is to view the labels already on your intended node, you can use kubectl to do this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get nodes
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz93t87n3yqtstnspdx2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz93t87n3yqtstnspdx2t.png" alt="Result of kubectl get"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you select the intended node and view the labels on the cluster using kubectl, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl describe nodes ip-172-31-28-239.us-east-2.compute.internal
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqknwrlpfszarc689dzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqknwrlpfszarc689dzx.png" alt="Describing the Selected node"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You then add the labels on the intended node node by using &lt;code&gt;kubectl&lt;/code&gt; , note that the structure for the command should be &lt;code&gt;kubectl label nodes &amp;lt;node-name&amp;gt; &amp;lt;label-key&amp;gt;=&amp;lt;label-value&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl label nodes ip-172-31-28-239.us-east-2.compute.internal &lt;span class="nv"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;web
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Verify that the label was added to the node using the &lt;code&gt;kubectl describe&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl describe nodes ip-172-31-28-239.us-east-2.compute.internal
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffim27pi9hqssdv6z4mcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffim27pi9hqssdv6z4mcd.png" alt="label added to node"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the new label added to the node. &lt;/p&gt;

&lt;p&gt;Assign a pod to the node you just labeled, Save this spec below to the &lt;code&gt;test-pod.yaml&lt;/code&gt; &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

apiVersion: v1
kind: Pod
metadata:
  name: httpd
  labels:
    env: prod
spec:
  containers:
  - name: httpd
    image: httpd
    imagePullPolicy: IfNotPresent
  nodeSelector:
    platform: web


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

&lt;/div&gt;

&lt;p&gt;Go ahead to deploy the pod using the &lt;code&gt;kubectl create&lt;/code&gt; command. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; test-pod.yaml
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Finally, verify that the pod is scheduled on the right node by using the &lt;code&gt;kubectl get&lt;/code&gt; command. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy9s9ubp5m9k0af4z8ox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy9s9ubp5m9k0af4z8ox.png" alt="Pod running on labeled node"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Constraints
&lt;/h2&gt;

&lt;p&gt;To prevent malicious users from scheduling pods to their own nodes, ensure to choose label keys that the kubelet cannot modify. This prevents a compromised node from setting those labels on itself so that the scheduler schedules workloads onto the compromised node. &lt;/p&gt;

&lt;p&gt;Kubernetes has a NodeRestriction admission plugin that prevents kubelets from setting or modifying labels with a &lt;code&gt;node-restriction.kubernetes.io/&lt;/code&gt; prefix, ensure to Add those labels under &lt;code&gt;node-restriction.kubernetes.io/&lt;/code&gt; prefix to your Node objects, and use those labels in your node selectors. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we discussed intentional Pod scheduling in Kubernetes and we also explored using a NodeSelector to schedule pods on nodes, If you enjoyed this article feel free to like, share and subscribe.Thank you!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Designing a Kubernetes Cluster in GCP using OSS terraform modules</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Fri, 30 Jun 2023 09:17:42 +0000</pubDate>
      <link>https://dev.to/monarene/designing-a-kubernetes-cluster-in-gcp-using-oss-terraform-modules-1m3j</link>
      <guid>https://dev.to/monarene/designing-a-kubernetes-cluster-in-gcp-using-oss-terraform-modules-1m3j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will be leveraging Terraform modules in the &lt;a href="https://cloud.google.com/foundation-toolkit" rel="noopener noreferrer"&gt;Google Foundation toolkit &lt;/a&gt; to deploy a private compute network and a Kubernetes cluster. The Google Foundation took-kit is a set of tools, modules, and packages that follow Google's best practices for deploying and maintaining architecture on the Google Cloud Platform. First, we will deploy a private compute network, and then we will go ahead to deploy a Kubernetes Cluster in the same network using only open-source modules. This article requires that you have a working knowledge of Terraform and you are at least conversant with the Google Cloud Platform. &lt;/p&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;

&lt;p&gt;The entire project is uploaded in this &lt;a href="https://github.com/Monarene/deploy-gcp-k8s-modules" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. The folder structure used in the project is defined below,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;auth.tf&lt;/strong&gt; : This file contains code for creating the service account and adding the necessary IAM permissions to those service accounts. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gcloud.sh&lt;/strong&gt;: This file contains commands that are run to enable container and compute services on Google cloud. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gke.tf&lt;/strong&gt;: This files contains code on deploying the Google Kubernetes Engine using the open source module from the Google Foundation Tool-kit. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;outputs.tf&lt;/strong&gt;: This file contains code on the output of the configuration. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;provider.tf&lt;/strong&gt;: This file contains code that initializes the entire configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;terraform.tfvars&lt;/strong&gt;: This file contains code that sets values for the variables declared in variables.tf&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;variables.tf&lt;/strong&gt;: This file contains variable declarations that will be used in the comfiguration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;vpc.tf&lt;/strong&gt;: This file contains code on deploying the Private compute network in Google Cloud using the open-source foundation kit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;I genuinely consider this section extremely important if not most important, because authenticating properly in Google Cloud can be frustrating if not done properly. First, head over to the official documentation to read on how to authenticate with Google Cloud properly (&lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started" rel="noopener noreferrer"&gt;https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started&lt;/a&gt;) . Ensure you have your project properly configured with the appropriate permissions, take note to ensure the service account you are using has the &lt;code&gt;Service Acount Token Creator&lt;/code&gt; as we will be creating service accounts on the fly. &lt;/p&gt;

&lt;p&gt;For the next step head over to &lt;code&gt;gcloud.sh&lt;/code&gt; and run the script to enable both compute and container APIs on your Google Cloud Account. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

gcloud services enable container.googleapis.com
gcloud services enable compute.googleapis.com


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

&lt;/div&gt;

&lt;p&gt;You can choose to run these commands individually or run the file as a script. &lt;/p&gt;

&lt;p&gt;Next head over to &lt;code&gt;auth.tf&lt;/code&gt; to see how we create the IAM role and enable the necessary permissions. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

resource "google_project_service" "this" {
  for_each           = toset(var.services)
  service            = "${each.key}.googleapis.com"
  disable_on_destroy = false
}

resource "google_service_account" "this" {
  account_id   = var.service_account.name
  display_name = "${var.service_account.name} Service Account"
}

resource "google_project_iam_member" "this" {
  project = var.project_id
  count   = length(var.service_account.roles)
  role    = "roles/${var.service_account.roles[count.index]}"
  member  = "serviceAccount:${google_service_account.this.email}"
}


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

&lt;/div&gt;

&lt;p&gt;Here we define a list of services to enable, and we also create a service account and an IAM member for each service account role to enable the service account be properly authorised to deploy our compute network and cluster. You can view the list of services enabled in &lt;code&gt;terraform.tfvars&lt;/code&gt;. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

services = [
  "cloudresourcemanager",
  "compute",
  "iam",
  "servicenetworking",
  "container"
]


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

&lt;/div&gt;

&lt;p&gt;You don't need to run any configuration here as everything will be created when we run our terraform configuration. &lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the Compute network
&lt;/h2&gt;

&lt;p&gt;To deploy the compute network, head over to &lt;code&gt;vpc.tf&lt;/code&gt;, here we define the module for the compute network, the version to be used, and also the compute project IAM before deploying the compute network. We also deploy just one subnet, with secondary ranges. And finally, we enable Identity aware proxy to have SSH access over port 22 to our cluster. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

module "vpc" {
  source  = "terraform-google-modules/network/google"
  version = "5.2.0"

  depends_on = [google_project_service.this["compute"]]

  project_id   = var.project_id
  network_name = var.network.name

  subnets = [
    {
      subnet_name           = var.network.subnetwork_name
      subnet_ip             = var.network.nodes_cidr_range
      subnet_region         = var.region
      subnet_private_access = "true"
    },
  ]

  secondary_ranges = {
    (var.network.subnetwork_name) = [
      {
        range_name    = "${var.network.subnetwork_name}-pods"
        ip_cidr_range = var.network.pods_cidr_range
      },
      {
        range_name    = "${var.network.subnetwork_name}-services"
        ip_cidr_range = var.network.services_cidr_range
      },
    ]
  }

  firewall_rules = [
    {
      name      = "${var.network.name}-allow-iap-ssh-ingress"
      direction = "INGRESS"
      ranges    = ["35.235.240.0/20"]
      allow = [{
        protocol = "tcp"
        ports    = ["22"]
      }]
    },
  ]
}


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Designing the Kubernetes Cluster
&lt;/h2&gt;

&lt;p&gt;Now to the exciting part, head over to &lt;code&gt;gke.tf&lt;/code&gt; to see how we deploy the Kubernetes cluster. First, we use the data object to grab the default google client config, then we initialise the Kubernetes provider with a token and the certificate.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

data "google_client_config" "default" {
}
provider "kubernetes" {
  host                   = "https://${module.gke.endpoint}"
  token                  = data.google_client_config.default.access_token
  cluster_ca_certificate = base64decode(module.gke.ca_certificate)
}


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

&lt;/div&gt;

&lt;p&gt;Next, we go over to define the Kubernetes module, we set the network as the VPC from the earlier module, and we grab the subnet from the VPC in the earlier module. We enable horizontal pod scaling and http load balancing . We also over-ride the default node pool with an object of custom node pool machine definition definitions. The rest of the defaults are the Google-advised parameters to get our cluster production ready. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

module "gke" {
  source  = "terraform-google-modules/kubernetes-engine/google"
  version = "23.3.0"
  project_id = var.project_id
  region     = var.region
  name     = var.gke.name
  regional = var.gke.regional
  zones    = var.gke.zones
  network           = module.vpc.network_name
  subnetwork        = local.subnetwork_name
  ip_range_pods     = "${local.subnetwork_name}-pods"
  ip_range_services = "${local.subnetwork_name}-services"
  service_account = google_service_account.this.email
  node_pools = [
    {
      name               = var.node_pool.name
      machine_type       = var.node_pool.machine_type
      disk_size_gb       = var.node_pool.disk_size_gb
      spot               = var.node_pool.spot
      initial_node_count = var.node_pool.initial_node_count
      max_count          = var.node_pool.max_count
      disk_type          = "pd-ssd"
    },
  ]
  # Fixed values
  network_policy             = true
  horizontal_pod_autoscaling = true
  http_load_balancing        = true
  create_service_account     = false
  initial_node_count       = 1
  remove_default_node_pool = true
}


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Deploying the services
&lt;/h2&gt;

&lt;p&gt;This is definitely my favorite part. Before you deploy the service, ensure to configure your project id in &lt;code&gt;terraform.tfvars&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

project_id = "&amp;lt;PROJECT_ID&amp;gt;"


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

&lt;/div&gt;

&lt;p&gt;To deploy, first initialize the entire configuration by running terraform init, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform init
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Next, we view the intended configuration to be deployed, to do that we run terraform plan on the configuration, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform plan
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj5k7e1l4wp79i07gqy3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj5k7e1l4wp79i07gqy3.png" alt="Terraform Plan on Kubernetes Configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we deploy the configuration by running the following, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;terraform.tfvars &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;If the deployment is successful and everything goes well log in to the &lt;a href="https://console.cloud.google.com" rel="noopener noreferrer"&gt;console&lt;/a&gt; to verify the deployment. First, we check &lt;a href="https://console.cloud.google.com/kubernetes" rel="noopener noreferrer"&gt;Kubernetes Engine&lt;/a&gt; to verify that our cluster is properly deployed. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5w9wvpnp80w2tn64dh68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5w9wvpnp80w2tn64dh68.png" alt="Kubernetes Deployed Cluster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we check the &lt;a href="https://console.cloud.google.com/networking/networks/" rel="noopener noreferrer"&gt;VPC Network tab&lt;/a&gt; to verify that the Compute network is deployed correctly. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfe9xqz9zi6al7ga7dxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfe9xqz9zi6al7ga7dxp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, we check our node pool to verify that the instances were indeed created, &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fat81iv1n95xefcc4jsb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fat81iv1n95xefcc4jsb3.png" alt="Node Pool Created in GCP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yayyyy! Our deployment is successful and we have a working Kubernetes Cluster in a Private Compute Network. &lt;/p&gt;

&lt;p&gt;Lastly, please ensure to delete all the resources created by running the following command,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform destroy &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;*&lt;em&gt;Please do this so as not to attract additional charges on the deployment. *&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Thank you for following me on the journey to making this deployment, feel free to extend this deployment and even raise a PR against the main repository (&lt;a href="https://github.com/Monarene/deploy-gcp-k8s-modules" rel="noopener noreferrer"&gt;https://github.com/Monarene/deploy-gcp-k8s-modules&lt;/a&gt;). If you enjoyed this article, feel free to share it and also star the repository. Thank you!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Terraform-Google-Cloud-Essential-Guide/dp/1804619620" rel="noopener noreferrer"&gt;Terraform for Google Cloud Essential Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/foundation-toolkit" rel="noopener noreferrer"&gt;Google Foundation Tool-Kit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/modules/terraform-google-modules/network/google/latest" rel="noopener noreferrer"&gt;Terraform Module for Compute Network by Google&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/modules/terraform-google-modules/kubernetes-engine/google/latest" rel="noopener noreferrer"&gt;Terraform Module for Kubernetes Engine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Utilizing Google Cloud Storage as a remote backend for Terraform</title>
      <dc:creator>Michael Mekuleyi</dc:creator>
      <pubDate>Wed, 31 May 2023 08:39:08 +0000</pubDate>
      <link>https://dev.to/monarene/utilizing-google-cloud-storage-as-a-remote-backend-for-terraform-3ijk</link>
      <guid>https://dev.to/monarene/utilizing-google-cloud-storage-as-a-remote-backend-for-terraform-3ijk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I will be discussing using Google Cloud storage as a remote backend for your Terraform configuration, This article is a sequel to my article on &lt;a href="https://hackernoon.com/deploying-a-terraform-remote-state-backend-with-aws-s3-and-dynamodb" rel="noopener noreferrer"&gt;Deploying a Remote backend with AWS S3 and Terraform&lt;/a&gt; , feel free to check out that article to learn more on remote state backends using AWS. &lt;/p&gt;

&lt;p&gt;In this article, we will provision a Google Cloud Storage (GCS) bucket and utilize it to store its own state, then we will go ahead to provision a compute instance on Google Cloud Platform and store its statefile in the remote backend we enabled earlier. This article assumes a working knowledge of Google Cloud (cloud.google.com ) and an understanding of Terraform (&lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;https://www.terraform.io/&lt;/a&gt; ). You can find the repository for this tutorial &lt;a href="https://github.com/Monarene/gcp-remote-backend-tf" rel="noopener noreferrer"&gt;here&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the remote backend
&lt;/h2&gt;

&lt;p&gt;The idea of a remote backend is to safely move your statefile from your local computer to a reliable and remote location, this is to ease collaboration and multi-tasking. To get started please head to &lt;code&gt;global-resources&lt;/code&gt; folder in the &lt;a href="https://github.com/Monarene/gcp-remote-backend-tf" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; to view the configuration scripts. First, we will deploy a GCS bucket using the local state, then we will use the GCS bucket to manage its own state. Head over to &lt;code&gt;global-resources/terraform.tf&lt;/code&gt;. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

terraform {
required_version = "&amp;gt;= 1.3.0, &amp;lt; 2.0.0"
 /* backend "gcs" {
    bucket = "&amp;lt;YOUR-BUCKET-NAMR&amp;gt;"
    prefix = "global-resources/"
  } */

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~&amp;gt; 4.40"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
  zone    = var.zone
}


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

&lt;/div&gt;

&lt;p&gt;Here we initialize the required providers and set the necessary values for the GCS bucket, note that the backend is commented out, this is because we are yet to deploy the GCS bucket. Head over to &lt;code&gt;global-resources/bucket.tf&lt;/code&gt; to see the configuration to deploy the cloud storage bucket. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

resource "google_storage_bucket" "default" {
  name          = var.bucket_name
  force_destroy = true
  location      = "US"
  storage_class = "STANDARD"
  versioning {
    enabled = true
  }
}


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

&lt;/div&gt;

&lt;p&gt;Here, we define the bare necessary values for a GCS bucket and we enable versioning to help us preserve data.  Also, remote backends with GCS support state-locking by default, hence is no need to provision a Key store. After entering the necessary variables in &lt;code&gt;global-resources/variables.tf&lt;/code&gt;, we go on to deploy the configuration. &lt;/p&gt;

&lt;p&gt;First we initialize the configuration,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform init
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm77hhrvaqqimgv37kg23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm77hhrvaqqimgv37kg23.png" alt="Terraform Init on Configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we go on to check the configuration plan,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform plan
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvtllzhsqp3p9m3ehv9o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvtllzhsqp3p9m3ehv9o.png" alt="Terraform plan on Configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we apply the configuration, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform apply &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Next, we log in to the GCP console to check that the storage bucket is already created. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jscwnlx33k57m1ppbjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jscwnlx33k57m1ppbjl.png" alt="Google Cloud Storage Bucket on GCP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are going to switch the storage bucket to use itself to manage its state file. Head over to &lt;code&gt;global-resources/terraform.tf&lt;/code&gt; and uncomment the &lt;code&gt;backend&lt;/code&gt; object in the terraform block. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

terraform {
required_version = "&amp;gt;= 1.3.0, &amp;lt; 2.0.0"
 backend "gcs" {
    bucket = "&amp;lt;YOUR-BUCKET-NAMR&amp;gt;"
    prefix = "global-resources/"
  }

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~&amp;gt; 4.40"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
  zone    = var.zone
}


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

&lt;/div&gt;

&lt;p&gt;Now migrate to the remote state by re-initializing the Terraform configuration. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform init
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;When prompted on copying the existing state to the new backend, type "yes". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudqy803vh4o3fd67yv21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudqy803vh4o3fd67yv21.png" alt="Re-initializing the Terraform State file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we head over to the console to confirm that our remote state is in GCS, you can find the state file in the &lt;code&gt;global-resources&lt;/code&gt; folder in the GCS bucket.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1sqh0f3kd7tn2w9xfv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1sqh0f3kd7tn2w9xfv8.png" alt="State file in Google Cloud Storage Bucket"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have a well configured backend with a Google Cloud storage bucket. &lt;/p&gt;

&lt;h2&gt;
  
  
  Applying the remote backend in other configurations
&lt;/h2&gt;

&lt;p&gt;Now we will provision three compute instances using the count keyword in Terraform and store the statefile in the GCS bucket. First, head over to &lt;code&gt;compute-instance/terraform.tf&lt;/code&gt; to see the terraform configuration. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

terraform {
  required_version = "~&amp;gt; 1.3"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~&amp;gt; 4.40"
    }
  }
  backend "gcs" {
    bucket = "&amp;lt;YOUR-BUCKET-NAME&amp;gt;"
    prefix = "compute-instance"
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
  zone    = var.zone
}


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

&lt;/div&gt;

&lt;p&gt;Here we declare the necessary variables to run the configuration, also notice that we set the backend object in the terraform block to gcs and we are pointing to our remote backend which was created earlier. Let's head over to &lt;code&gt;compute-instance/main.tf&lt;/code&gt; to view the main configuration. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

resource "google_compute_instance" "this" {
  provider     = google
  count        = 3
  name         = "${var.server_name}-${count.index}"
  machine_type = var.machine_type
  zone         = var.zone

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-11"
    }
  }
  network_interface {
    network = "default"
    access_config {
      // Ephemeral public IP
    }
  }
  metadata_startup_script = file("startup.sh")

  tags = ["http-server"]
}


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

&lt;/div&gt;

&lt;p&gt;Here we define 3 compute instances using the &lt;code&gt;count&lt;/code&gt; block, we also set other important variables like the machine type and server name. You can also check out &lt;code&gt;compute-instance/startup.sh&lt;/code&gt; for the startup script that runs when the server is spun, finally I have also added a http-server tag to allow ingress on default http ports. Please go ahead to study the configuration to understand the different connecting parts. &lt;/p&gt;

&lt;p&gt;To deploy this configuration, we start by initializing it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform init
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpgtggxbwoea87az7u12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpgtggxbwoea87az7u12.png" alt="Initiliazing the Google Compute Resource on Terraform"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the movement to Google Cloud storage backend. Next, we view the plan to run the configuration, &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform plan
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1qe5glneu54pzrmq6tj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1qe5glneu54pzrmq6tj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we apply the configuration and get its outputs. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;michael@monarene:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform apply &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0pul4r0fikdwequ71in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0pul4r0fikdwequ71in.png" alt="Applying the Terraform Configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify that our compute instances have been accurately deployed, we login to the compute console on GCP to check, &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0pn08s5hejjxfb88at9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0pn08s5hejjxfb88at9.png" alt="GCP Compute Console showing the deployed Compute Resources"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we check our GCS bucket to verify that the instance state file is stored in the bucket. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjklrxkks3tqxuctpfx0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjklrxkks3tqxuctpfx0k.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have successfully created a remote backend with a GCS bucket and we have utilized the bucket in storing our state files. Please go ahead to destroy all the resources you have created to avoid extra-billing charges. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article we have explored creating a Google Cloud storage bucket, using it to store our state files and further utilizing the state files for other deployments. We have done everything using Terraform as an IaC tool to manage infrastructure. You can also find the github repository for this article &lt;a href="https://github.com/Monarene/gcp-remote-backend-tf" rel="noopener noreferrer"&gt;here&lt;/a&gt;,I hope you learnt a lot, feel free to like, share and comment on this article. Thank you!&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>googlecloud</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
