<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Damilare Ogundele</title>
    <description>The latest articles on Forem by Damilare Ogundele (@kahuna04).</description>
    <link>https://forem.com/kahuna04</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%2F1702862%2F104f395a-e254-4327-8dcc-ddf80158b851.jpg</url>
      <title>Forem: Damilare Ogundele</title>
      <link>https://forem.com/kahuna04</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kahuna04"/>
    <language>en</language>
    <item>
      <title>Building a Multi-Application Kubernetes Marketplace: TCP/UDP App Onboarding at Scale</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Wed, 25 Jun 2025 22:56:30 +0000</pubDate>
      <link>https://forem.com/kahuna04/building-a-multi-application-kubernetes-marketplace-tcpudp-app-onboarding-at-scale-1i74</link>
      <guid>https://forem.com/kahuna04/building-a-multi-application-kubernetes-marketplace-tcpudp-app-onboarding-at-scale-1i74</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, I tackled a comprehensive marketplace app onboarding project that involved deploying various TCP/UDP and HTTP applications on Kubernetes. This post shares the technical journey, challenges faced, and solutions implemented while onboarding applications like MySQL, MongoDB, RabbitMQ, and others to our marketplace platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;The goal was to create a standardized onboarding process for diverse applications with different networking requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TCP/UDP applications&lt;/strong&gt; requiring Network Load Balancers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP applications&lt;/strong&gt; needing ingress controllers and SSL termination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database applications&lt;/strong&gt; requiring persistent storage and proper health checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message brokers&lt;/strong&gt; with multiple port configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Our solution leverages several key AWS and Kubernetes components:&lt;/p&gt;

&lt;h3&gt;
  
  
  Network Load Balancer Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3306&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3306&lt;/span&gt;
    &lt;span class="na"&gt;lb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internet-facing&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nlb-ip&lt;/span&gt;
      &lt;span class="na"&gt;target_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ip&lt;/span&gt;
      &lt;span class="na"&gt;healthcheck_protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;healthcheck_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;healthcheck_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Helm Chart Structure
&lt;/h3&gt;

&lt;p&gt;We standardized our deployments using a consistent Helm chart pattern with these key sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global configuration&lt;/strong&gt; for app identification and DNS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment specs&lt;/strong&gt; with resource limits and security contexts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service definitions&lt;/strong&gt; for both internal and external access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage management&lt;/strong&gt; with persistent volumes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets handling&lt;/strong&gt; for sensitive configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Case Study: RabbitMQ Deployment
&lt;/h2&gt;

&lt;p&gt;RabbitMQ presented an interesting challenge with its dual-port requirement (AMQP protocol on 5672 and Management UI on 15672). Here's how we handled it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rabbitmq&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rabbitmq:4-management&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;5672&lt;/span&gt;  &lt;span class="c1"&gt;# AMQP&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;15672&lt;/span&gt; &lt;span class="c1"&gt;# Management UI&lt;/span&gt;
    &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15672&lt;/span&gt;
      &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15672&lt;/span&gt;
      &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amqp&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5672&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5672&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;management&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15672&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15672&lt;/span&gt;
    &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cert_issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;letsencrypt"&lt;/span&gt;
      &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database Deployment Patterns
&lt;/h2&gt;

&lt;p&gt;For database applications like MySQL, we focused on:&lt;/p&gt;

&lt;h3&gt;
  
  
  Persistent Storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql-data-volume&lt;/span&gt;
    &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ebs-sc&lt;/span&gt;
    &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8Gi&lt;/span&gt;
    &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Health Checks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tcpSocket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3306&lt;/span&gt;
  &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
  &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tcpSocket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3306&lt;/span&gt;
  &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
  &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Standardization is Critical
&lt;/h3&gt;

&lt;p&gt;Creating a consistent Helm chart structure across all applications significantly reduced deployment complexity and improved maintainability.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Health Check Strategy
&lt;/h3&gt;

&lt;p&gt;Different applications require different health check approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP applications&lt;/strong&gt;: Use HTTP GET requests to health endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databases&lt;/strong&gt;: Use TCP socket checks on primary ports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message brokers&lt;/strong&gt;: Check management interfaces when available&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Security Context Management
&lt;/h3&gt;

&lt;p&gt;Proper user and group ID management is essential for persistent storage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;999&lt;/span&gt;
  &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Resource Management
&lt;/h3&gt;

&lt;p&gt;Setting appropriate resource limits prevents applications from consuming excessive cluster resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;200m&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1000m&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1Gi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automation and Testing
&lt;/h2&gt;

&lt;p&gt;We implemented automated testing procedures to ensure each application deployment meets our marketplace standards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connectivity tests&lt;/strong&gt; for TCP/UDP services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL certificate validation&lt;/strong&gt; for HTTPS services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent storage verification&lt;/strong&gt; for stateful applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health check validation&lt;/strong&gt; for all deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results and Impact
&lt;/h2&gt;

&lt;p&gt;The standardized onboarding process has enabled us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce deployment time by 70%&lt;/li&gt;
&lt;li&gt;Maintain consistent security policies across all applications&lt;/li&gt;
&lt;li&gt;Simplify troubleshooting with standardized logging and monitoring&lt;/li&gt;
&lt;li&gt;Scale our marketplace offerings efficiently&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;p&gt;Looking ahead, we're planning to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement GitOps workflows for automated deployments&lt;/li&gt;
&lt;li&gt;Add application-specific monitoring dashboards&lt;/li&gt;
&lt;li&gt;Develop self-service onboarding tools for developers&lt;/li&gt;
&lt;li&gt;Expand support for more complex multi-tier applications&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Building a multi-application Kubernetes marketplace requires careful planning, standardization, and attention to the unique requirements of each application type. By leveraging Helm charts, AWS Load Balancers, and Kubernetes best practices, we've created a robust platform that can scale with our growing marketplace needs.&lt;/p&gt;

&lt;p&gt;The key takeaway is that while each application has unique requirements, a well-designed template system can accommodate this diversity while maintaining operational consistency.&lt;/p&gt;




</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>Building a High-Performance Web Scraper with Python</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Mon, 17 Mar 2025 08:44:17 +0000</pubDate>
      <link>https://forem.com/kahuna04/building-a-high-performance-web-scraper-with-python-2fbo</link>
      <guid>https://forem.com/kahuna04/building-a-high-performance-web-scraper-with-python-2fbo</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This article explores the architecture and implementation of a high-performance web scraper built to extract product data from e-commerce platforms. The scraper uses multiple Python libraries and techniques to efficiently process thousands of products while maintaining resilience against common scraping challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Architecture
&lt;/h2&gt;

&lt;p&gt;The scraper is built on a fully asynchronous foundation using Python's &lt;code&gt;asyncio&lt;/code&gt; ecosystem, with these key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network Layer&lt;/strong&gt;: &lt;code&gt;aiohttp&lt;/code&gt; for async HTTP requests with connection pooling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOM Processing&lt;/strong&gt;: &lt;code&gt;BeautifulSoup4&lt;/code&gt; for HTML parsing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Content&lt;/strong&gt;: &lt;code&gt;Playwright&lt;/code&gt; for JavaScript-rendered content extraction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Processing&lt;/strong&gt;: &lt;code&gt;pandas&lt;/code&gt; for data manipulation and export&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Highlights
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Concurrency Management
&lt;/h3&gt;

&lt;p&gt;The scraper implements a worker pool pattern with configurable concurrency limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Concurrency settings
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MAX_WORKERS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_connections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MAX_CONNECTIONS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# TCP connection pooling
&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TCPConnector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_connections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;  &lt;span class="c1"&gt;# Custom DNS resolver
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents overwhelming the target server while maximizing throughput.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilient Network Requests
&lt;/h3&gt;

&lt;p&gt;The network layer implements sophisticated retry logic with exponential backoff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;# Additional headers omitted for brevity
&lt;/span&gt;            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request_timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="c1"&gt;# Rate limit handling
&lt;/span&gt;                    &lt;span class="n"&gt;retry_after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Retry-After&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry_backoff&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
                    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retry_after&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Retry with exponential backoff
&lt;/span&gt;            &lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;wait_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry_backoff&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;TimeoutError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Network error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hybrid Content Extraction
&lt;/h3&gt;

&lt;p&gt;The scraper employs a two-phase extraction approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static HTML Parsing&lt;/strong&gt;: Uses BeautifulSoup to extract readily available content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Content Extraction&lt;/strong&gt;: Uses Playwright to handle JavaScript-rendered elements
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Static content extraction
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;concurrent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_in_executor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="nf"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scrape_product_html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Dynamic content extraction
&lt;/span&gt;    &lt;span class="n"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrape_dynamic_content_playwright&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image_url&lt;/span&gt;
    &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach optimizes for both speed and completeness.&lt;/p&gt;

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

&lt;p&gt;The scraper implements DNS fallbacks to handle potential DNS resolution issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aiodns&lt;/span&gt;
    &lt;span class="n"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nameservers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8.8.8.8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.1.1.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aiodns library not found. Falling back to default resolver.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Data Processing Pipeline
&lt;/h3&gt;

&lt;p&gt;The scraper implements a thread-safe queue for handling scraped data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Thread-safe queue for results
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results_queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Data processing
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_results_from_queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_nowait&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Save to CSV with proper encoding and escaping
&lt;/span&gt;        &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8-sig&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;escapechar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;quoting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QUOTE_ALL&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Optimizations
&lt;/h2&gt;

&lt;p&gt;Several techniques are employed to maximize throughput:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Batch Processing&lt;/strong&gt;: Products are processed in configurable batches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random Delays&lt;/strong&gt;: Randomized delays between requests prevent detection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection Pooling&lt;/strong&gt;: TCP connection reuse reduces overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ThreadPoolExecutor&lt;/strong&gt;: CPU-bound tasks are offloaded to prevent blocking the event loop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sampling&lt;/strong&gt;: For large datasets, statistical sampling is used to estimate total counts&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Error Handling and Reliability
&lt;/h2&gt;

&lt;p&gt;The scraper implements comprehensive error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Scraping logic
&lt;/span&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error in scrape_all_products: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Save any results in queue before exiting
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_results_from_queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that even if the scraper crashes, partial results are saved.&lt;/p&gt;

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

&lt;p&gt;The architecture outlined here demonstrates how to build a high-performance web scraper that balances speed, reliability, and target server courtesy. By leveraging asynchronous programming, connection pooling, and hybrid content extraction techniques, the scraper can efficiently process thousands of products while maintaining resilience against common scraping challenges.&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asynchronous programming is essential for high-performance web scraping&lt;/li&gt;
&lt;li&gt;Hybrid static/dynamic extraction maximizes data completeness&lt;/li&gt;
&lt;li&gt;Proper error handling and resilience mechanisms are crucial for production use&lt;/li&gt;
&lt;li&gt;Configurable parameters allow for fine-tuning based on target site characteristics&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>automation</category>
      <category>web3</category>
    </item>
    <item>
      <title>Certificate Unlocked! 🎓</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Tue, 03 Sep 2024 07:41:25 +0000</pubDate>
      <link>https://forem.com/kahuna04/certificate-unlocked-f5c</link>
      <guid>https://forem.com/kahuna04/certificate-unlocked-f5c</guid>
      <description>&lt;p&gt;I’m thrilled to announce that I’ve officially received my certificate for completing the HNG11 internship!&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%2Fvdvyr02tndmfybkaydkd.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%2Fvdvyr02tndmfybkaydkd.png" alt="HNG Finalist Cert." width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being one of the 500 finalists chosen from over 20,000 applicants was a journey of growth, learning, and resilience. Just a few hours ago, I reflected on this incredible experience, the projects I took on, and the amazing people I had the privilege to work with. (You can check out that post &lt;a href="https://dev.to/kahuna04/reflecting-on-my-journey-at-hng11-internship-1k82"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This certificate is more than just a piece of paper—it’s a testament to the hard work, sleepless nights, countless lines of code, debugging sessions, and the relentless pursuit of excellence. From developing and deploying the &lt;a href="https://remote.bingo" rel="noopener noreferrer"&gt;Remote Bingo&lt;/a&gt; game app to mastering tools like Helm, Ansible, Docker, Kubernetes, and GitHub Actions, this journey has been truly transformative.&lt;/p&gt;

&lt;p&gt;I’m immensely grateful to the mentors, teammates, and everyone who supported me along the way. This is just the beginning, and I can’t wait to continue pushing boundaries and achieving more.&lt;/p&gt;

&lt;p&gt;To everyone out there, keep striving, keep learning, and never forget the power of perseverance.&lt;/p&gt;

&lt;p&gt;Connect with me on &lt;a href="https://linkedin.com/in/damilare-ogundele" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and also &lt;a href="https://github.com/Kahuna04" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>devops</category>
      <category>careerdevelopment</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Reflecting on My Journey at HNG11 Internship!</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Mon, 02 Sep 2024 12:02:50 +0000</pubDate>
      <link>https://forem.com/kahuna04/reflecting-on-my-journey-at-hng11-internship-1k82</link>
      <guid>https://forem.com/kahuna04/reflecting-on-my-journey-at-hng11-internship-1k82</guid>
      <description>&lt;p&gt;After a thrilling and intense few months, I’ve just wrapped up the HNG11 internship, and I’m incredibly proud to share that I was among the 500 finalists selected from over 20,000 applicants! 🎉&lt;/p&gt;

&lt;p&gt;Starting on June 27th, thousands of interns across various tracks, including Project Managers, Developers, Designers, Video Marketers, Data Analysts, and DevOps specialists, embarked on this journey. Being part of the DevOps track, I faced numerous challenging tasks that tested my abilities and expanded my skill set. I worked with tools like Helm, Ansible, Docker, Kubernetes, Bash, Python, GitHub Actions, and more.&lt;/p&gt;

&lt;p&gt;As the Chief Mentor, Mark Essien, put it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“All of you are special. 20k wanted to be finalists, 500 made it. You have proven something to yourself. You are special, never forget that. Where everyone gave up, you kept going. You have that strength, you have that willpower, you have that intelligence.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the highlights was collaborating with a fantastic team to develop and deploy a game app &lt;a href="https://remote.bingo" rel="noopener noreferrer"&gt;Remote Bingo&lt;/a&gt; from dev to staging and finally to production. This project, along with many others, taught me invaluable lessons about teamwork, resilience, and the power of persistence.&lt;/p&gt;

&lt;p&gt;A big shoutout to all my team members and mentors who made this experience so rewarding. I’m grateful for your support, guidance, and the memories we created together. &lt;/p&gt;

&lt;p&gt;On to the next challenge!💯&lt;/p&gt;

&lt;p&gt;Connect with me on &lt;a href="https://linkedin.com/in/damilare-ogundele" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and also &lt;a href="https://github.com/Kahuna04" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>githubactions</category>
      <category>softwaredevelopment</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Scheduled Test Workflow Documentation</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Sat, 24 Aug 2024 21:51:24 +0000</pubDate>
      <link>https://forem.com/kahuna04/scheduled-test-workflow-documentation-3b8h</link>
      <guid>https://forem.com/kahuna04/scheduled-test-workflow-documentation-3b8h</guid>
      <description>&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This documentation provides a detailed explanation of the setup for a cron job scheduled test in a GitHub Actions workflow. The purpose of this setup is to automate the execution of test scripts against a Postman collection every 15 minutes, ensuring continuous testing and monitoring of the boilerplate repository's API endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Actions Workflow Configuration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scheduled Test&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*/15&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run test script&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;POSTMAN_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.POSTMAN_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.API_URL }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;cd qa&lt;/span&gt;
          &lt;span class="s"&gt;chmod +x test.sh&lt;/span&gt;
          &lt;span class="s"&gt;./test.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Components:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schedule Trigger (&lt;code&gt;on: schedule&lt;/code&gt;):&lt;/strong&gt; The workflow is triggered every 15 minutes as specified by the cron expression &lt;code&gt;'*/15 * * * *'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Variables:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POSTMAN_API_KEY&lt;/code&gt;: The API key for accessing the Postman collection (stored securely in GitHub Secrets).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API_URL&lt;/code&gt;: The base URL of the API under test.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Steps:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Checkout Repository:&lt;/strong&gt; Uses the &lt;code&gt;actions/checkout@v3&lt;/code&gt; action to check out the repository code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run Test Script:&lt;/strong&gt; Executes the &lt;code&gt;test.sh&lt;/code&gt; script located in the &lt;code&gt;qa&lt;/code&gt; directory, which handles the installation of necessary dependencies and triggers the test execution.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Test Script (&lt;code&gt;test.sh&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;newman
npm &lt;span class="nb"&gt;install &lt;/span&gt;axios
npm &lt;span class="nb"&gt;install &lt;/span&gt;big-json
node ./index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Installation:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;newman&lt;/code&gt;: A command-line tool to run Postman collections.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;axios&lt;/code&gt;: A promise-based HTTP client for making API requests.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;big-json&lt;/code&gt;: A module to handle large JSON files.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Running &lt;code&gt;index.js&lt;/code&gt;:&lt;/strong&gt; Executes the main script that handles the Postman collection run, compression of results, and subsequent API requests.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Main Script (&lt;code&gt;index.js&lt;/code&gt;)&lt;/strong&gt; (&lt;strong&gt;Not included in this documentation&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;index.js&lt;/code&gt; script orchestrates the entire process, from executing the tests with Newman to compressing the results and sending them to the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This setup provides a robust mechanism for automated testing of the boilerplate repository. The workflow ensures that API endpoints are tested every 15 minutes, with detailed logs and results management, enhancing the overall quality and reliability of the project.&lt;/p&gt;

</description>
      <category>github</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
    <item>
      <title>DevOps Task - Kubernetes Self-Hosted GitHub Runners</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Tue, 20 Aug 2024 14:16:08 +0000</pubDate>
      <link>https://forem.com/kahuna04/devops-task-kubernetes-self-hosted-github-runners-32jc</link>
      <guid>https://forem.com/kahuna04/devops-task-kubernetes-self-hosted-github-runners-32jc</guid>
      <description>&lt;p&gt;This documentation outlines the process of setting up self-hosted GitHub Actions runners on a Kubernetes cluster as part of the "Stage 7 DevOps Task." The goal was to establish scalable and manageable CI/CD pipelines using the GitHub Actions Runner Controller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Environment Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This section details the setup of the Kubernetes environment required to host the GitHub Actions runners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.1. Kubernetes Cluster Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We utilized Minikube for its simplicity and ease of use in creating a local Kubernetes cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
&lt;span class="nb"&gt;sudo install &lt;/span&gt;minikube-linux-amd64 /usr/local/bin/minikube
minikube start &lt;span class="nt"&gt;--driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cluster verification was performed using &lt;code&gt;kubectl cluster-info&lt;/code&gt; to ensure it was operational.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2. Helm Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Helm simplifies Kubernetes application management. Installation was achieved through the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://baltocdn.com/helm/signing.asc | gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /usr/share/keyrings/helm.gpg &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/helm-stable-debian.list
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;helm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1.3. Docker Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker is essential for containerized applications within the Kubernetes cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates curl
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.asc
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.asc
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1.4. kubectl Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;kubectl is the command-line tool for managing Kubernetes clusters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;KUBECTL_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1.29.0
wget https://storage.googleapis.com/kubernetes-release/release/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KUBECTL_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bin/linux/amd64/kubectl
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x kubectl
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;kubectl /usr/local/bin/
kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. GitHub Actions Runner Controller Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This section focuses on deploying the GitHub Actions Runner Controller, which manages the runners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.1. Namespace Creation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A dedicated namespace, &lt;code&gt;github-actions-runner&lt;/code&gt;, was created to isolate the runner controller and its resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace github-actions-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.2. Runner Deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The YAML configuration below defines the RunnerDeployment, specifying the desired number of replicas and the runner image to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions.summerwind.dev/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RunnerDeployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kahuna-runner&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Stage7-GitHub-Runner/hng_boilerplate_nestjs&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Kahuna&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deployment was achieved using &lt;code&gt;kubectl apply -f runnerdeployment.yaml&lt;/code&gt;, and the pod status was verified using &lt;code&gt;kubectl get pods -n github-actions-runner&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.3. Service Account and Permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A service account (&lt;code&gt;runner-sa&lt;/code&gt;) was created and granted the &lt;code&gt;cluster-admin&lt;/code&gt; role to ensure the runner had the necessary permissions to access Kubernetes resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create serviceaccount runner-sa &lt;span class="nt"&gt;-n&lt;/span&gt; github-actions-runner
kubectl create rolebinding runner-rb &lt;span class="nt"&gt;--serviceaccount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github-actions-runner:runner-sa &lt;span class="nt"&gt;--clusterrole&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cluster-admin &lt;span class="nt"&gt;-n&lt;/span&gt; github-actions-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Runner Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This section details the configuration and deployment of the GitHub Actions runners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1. Runner Registration Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The runner registration token was retrieved from the GitHub repository. This token is used to authenticate the runners with GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2. Runner Spec Definition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The runner specifications were defined in the YAML file, including labels (&lt;code&gt;Kahuna&lt;/code&gt;) and resource requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.3. Runner Deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Kubernetes manifests were applied using &lt;code&gt;kubectl apply -f runnerdeployment.yaml&lt;/code&gt; to deploy the runners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.4. Verification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Runner logs were monitored using &lt;code&gt;kubectl logs -f kahuna-runner-jkbv6-lpdqp&lt;/code&gt; to ensure successful registration and 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%2Fd6qgc1grqzgbzwi6zsus.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%2Fd6qgc1grqzgbzwi6zsus.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Testing and Validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This section describes the testing and validation process for the self-hosted runners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.1. Workflow Creation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A sample GitHub Actions workflow was created to utilize the self-hosted runners. This workflow included steps for code checkout, dependency installation, linting, building, and testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint, Build and Test&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;workflow_dispatch&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;lint-build-and-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Kahuna&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install --include=dev&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run lint&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run lint&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build project&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.2. Workflow Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workflow was executed to verify that the self-hosted runners were correctly processing jobs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.3. Monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Runner performance was monitored, and any issues were debugged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.4. Scalability Testing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scalability was tested by adjusting the number of workflows and observing how the runners scaled up or down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Self-hosted GitHub Actions runners were successfully set up on a Kubernetes cluster, providing a scalable and manageable solution for CI/CD pipelines. The runners were thoroughly tested, validated, and documented, demonstrating proficiency with Kubernetes and CI/CD tools.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Linux User Creation Bash Script</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Sun, 30 Jun 2024 22:59:25 +0000</pubDate>
      <link>https://forem.com/kahuna04/linux-user-creation-bash-script-1p97</link>
      <guid>https://forem.com/kahuna04/linux-user-creation-bash-script-1p97</guid>
      <description>&lt;p&gt;Hello everyone, I am Kahuna, and I’m excited to share my latest technical article. As a DevOps engineer, I was asked to manage user accounts and groups. Today, I’ll walk you through a script I wrote to automate this process. This script reads a text file containing usernames and their respective groups, creates users and groups as specified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;I ensured I have the necessary permissions to create users and groups, and write to the /var/log/ and /var/secure/ directories.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Script
&lt;/h2&gt;

&lt;p&gt;Here’s a breakdown of the create_users.sh script:&lt;/p&gt;

&lt;h3&gt;
  
  
  Log and Password Files:
&lt;/h3&gt;

&lt;p&gt;The script uses /var/log/user_management.log for logging actions and /var/secure/user_passwords.csv to securely store generated passwords. The /var/secure/ directory is set with restrictive permissions to ensure password security.&lt;/p&gt;

&lt;h3&gt;
  
  
  Input Validation:
&lt;/h3&gt;

&lt;p&gt;The script checks if an input file is provided and exits with usage instructions if not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging Function:
&lt;/h3&gt;

&lt;p&gt;A simple function logs messages with timestamps to the log file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Password Generation:
&lt;/h3&gt;

&lt;p&gt;A function generates random 12-character passwords using /dev/urandom.&lt;/p&gt;

&lt;h3&gt;
  
  
  Processing the Input File:
&lt;/h3&gt;

&lt;p&gt;The script reads each line of the input file, extracts the username and groups, and processes them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User Existence Check: If the user already exists, it logs the information and skips to the next line.&lt;/li&gt;
&lt;li&gt;User Creation: It creates the user with the specified personal group and a home directory.&lt;/li&gt;
&lt;li&gt;Additional Groups: If additional groups are specified, the script creates them if they don’t exist and adds the user to these groups.&lt;/li&gt;
&lt;li&gt;Password Setting: It generates and sets a random password for the user and logs this action.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running the Script
&lt;/h2&gt;

&lt;p&gt;To run the script, I have saved it as create_users.sh, and I have provided the input file as an argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x create_users.sh
sudo ./create_users.sh employee_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Input File
&lt;/h4&gt;

&lt;p&gt;Here’s the input file (employee_file) looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kahuna; Backend,DevOps,HR
Dami; DevOps,HR
Sola; Backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This script automates the process of creating and managing users and groups, ensuring consistency and security. I am currently on a DevOps journey with HNG Internship. To learn more, check &lt;a href="https://hng.tech/internship" rel="noopener noreferrer"&gt;HNG Internship&lt;/a&gt; and &lt;a href="https://hng.tech/premium" rel="noopener noreferrer"&gt;HNG Premium&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>backenddevelopment</category>
      <category>bash</category>
      <category>devops</category>
    </item>
    <item>
      <title>My Backend World: Tackling my first NestJS project</title>
      <dc:creator>Damilare Ogundele</dc:creator>
      <pubDate>Sat, 29 Jun 2024 12:20:14 +0000</pubDate>
      <link>https://forem.com/kahuna04/my-backend-world-tackling-my-first-nestjs-project-16fi</link>
      <guid>https://forem.com/kahuna04/my-backend-world-tackling-my-first-nestjs-project-16fi</guid>
      <description>&lt;p&gt;Hello everyone, I am Kahuna, and this is my first ever technical article. I am a graduate of Mechanical Engineering, but my enthusiasm for technology led me to dive into the world of backend development.&lt;/p&gt;

&lt;p&gt;Recently, I had the opportunity to be part of a team for a project that involved using a lovely framework I had never used before: NestJS. This was challenging, but at the same time, I knew it would be an interesting experience. Stay with me as I highlight how I navigated through the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Understanding the framework
&lt;/h3&gt;

&lt;p&gt;I learned that you don't jump into using a tool or framework without understanding it first. With this in mind, I delved into the NestJS documentation online and studied it to gain a solid understanding of what this framework does and how it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Trusting in mentors
&lt;/h3&gt;

&lt;p&gt;Upon joining the team, I met some other developers who had more experience with the framework. I discussed my situation with them, explaining that this was my first project using NestJS. They provided guidance, and I was accountable to them. Whenever I got stuck with my code, they were there to help.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Consistency
&lt;/h3&gt;

&lt;p&gt;As advised by a senior developer, consistency brings about mastery. I committed to writing code every day, which significantly improved my skills in NestJS and coding in general.&lt;/p&gt;

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

&lt;p&gt;The above process has been incredibly beneficial and continues to aid me in my journey in backend development. Currently, I am embarking on a new journey in backend development with the HNG Internship. I highly recommend this platform to every newbie in tech. It offers the experience you need to grow and excel. To learn more, check out &lt;a href="https://hng.tech/internship" rel="noopener noreferrer"&gt;HNG Internship&lt;/a&gt; and &lt;a href="https://hng.tech/premium" rel="noopener noreferrer"&gt;HNG Premium&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>node</category>
      <category>nestjs</category>
      <category>beginners</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
