<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[GitHub × OpenShift: Cloud CI/CD]]></title><description><![CDATA[GitHub × OpenShift: Cloud CI/CD]]></description><link>https://ci-cd-using-github-actions-openshift.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 17 Sep 2026 12:16:28 GMT</lastBuildDate><atom:link href="https://ci-cd-using-github-actions-openshift.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[When GitHub Actions Sync with OpenShift Pipelines: CI/CD]]></title><description><![CDATA[“Automation is not a luxury — it’s the lifeline of modern DevOps.”

Modern software delivery thrives on automation — not just for speed, but for consistency, scalability, and confidence.From GitHub Actions (for code testing and validation) to OpenShi...]]></description><link>https://ci-cd-using-github-actions-openshift.hashnode.dev/when-github-actions-sync-with-openshift-pipelines-cicd</link><guid isPermaLink="true">https://ci-cd-using-github-actions-openshift.hashnode.dev/when-github-actions-sync-with-openshift-pipelines-cicd</guid><category><![CDATA[openshift]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[YAML]]></category><category><![CDATA[Continuous Integration]]></category><category><![CDATA[continuous deployment]]></category><category><![CDATA[Enterprise Cloud]]></category><category><![CDATA[Tekton]]></category><dc:creator><![CDATA[Hardik Arora]]></dc:creator><pubDate>Sat, 01 Nov 2025 10:47:12 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p><em>“Automation is not a luxury — it’s the lifeline of modern DevOps.”</em></p>
</blockquote>
<p>Modern software delivery thrives on automation — not just for speed, but for <strong>consistency, scalability, and confidence</strong>.<br />From <strong>GitHub Actions</strong> (for code testing and validation) to <strong>OpenShift Pipelines</strong> (for containerized deployment), this post takes you through a full CI/CD journey — <strong><mark>from commit to cluster</mark></strong><mark>.</mark></p>
<hr />
<h2 id="heading-part-1-continuous-integration-with-github-actions">🧩 Part 1 — Continuous Integration with GitHub Actions</h2>
<p>Before deploying code, we must ensure it’s <strong>clean, tested, and reliable</strong>.<br />GitHub Actions provides an elegant way to automate these checks with minimal setup.<br /><a target="_blank" href="https://github.com/barbaria888/ci-cd-final-project">Actual Github Repository</a>: <a target="_blank" href="https://github.com/barbaria888/ci-cd-final-project">https://github.com/barbaria888/ci-cd-final-project</a></p>
<hr />
<h3 id="heading-step-1-set-up-your-workflow">⚙️ Step 1: Set Up Your Workflow</h3>
<p>Create a file at <code>.github/workflows/workflow.yml</code> and define your CI pipeline:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">CI</span> <span class="hljs-string">workflow</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"main"</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"main"</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">container:</span> <span class="hljs-string">python:3.9-slim</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
</code></pre>
<p>✅ This ensures the latest version of your repository is fetched on every commit or pull request.</p>
<hr />
<h3 id="heading-step-2-install-dependencies">⚙️ Step 2: Install Dependencies</h3>
<p>Install your Python dependencies to prepare for linting and testing:</p>
<pre><code class="lang-yaml">      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          python -m pip install --upgrade pip
          pip install -r requirements.txt</span>
</code></pre>
<blockquote>
<p>💡 Use <code>pip3</code> if you encounter permission issues.</p>
</blockquote>
<hr />
<h3 id="heading-step-3-lint-the-code-with-flake8">⚙️ Step 3: Lint the Code with Flake8</h3>
<p>Catch style violations, undefined variables, and syntax issues:</p>
<pre><code class="lang-yaml">      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Lint</span> <span class="hljs-string">with</span> <span class="hljs-string">flake8</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          flake8 service --count --select=E9,F63,F7,F82 --show-source --statistics
          flake8 service --count --max-complexity=10 --max-line-length=127 --statistics</span>
</code></pre>
<p>Clean, consistent code saves hours during review and debugging.</p>
<hr />
<h3 id="heading-step-4-test-code-coverage-with-nose">⚙️ Step 4: Test Code Coverage with Nose</h3>
<p>Run automated unit tests to ensure nothing breaks:</p>
<pre><code class="lang-yaml">      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">unit</span> <span class="hljs-string">tests</span> <span class="hljs-string">with</span> <span class="hljs-string">nose</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">nosetests</span> <span class="hljs-string">-v</span> <span class="hljs-string">--with-spec</span> <span class="hljs-string">--spec-color</span> <span class="hljs-string">--with-coverage</span> <span class="hljs-string">--cover-package=app</span>
</code></pre>
<p>If tests pass — your CI pipeline gives the green light for deployment.</p>
<hr />
<h3 id="heading-step-5-commit-and-push">⚙️ Step 5: Commit and Push</h3>
<pre><code class="lang-bash">git add .
git commit -m <span class="hljs-string">"Added CI workflow"</span>
git push origin main
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761993633475/d7f99262-2d5b-47b7-a8ee-ad8097d4de85.png" alt class="image--center mx-auto" /></p>
<hr />
<h3 id="heading-why-github-actions-matters">🧠 Why GitHub Actions Matters</h3>
<p>This is your <strong>Continuous Integration (CI)</strong> foundation — ensuring that every code change is validated, linted, and tested before reaching production.<br />Now, let’s extend this into <strong>Continuous Deployment (CD)</strong> using OpenShift Pipelines.</p>
<hr />
<h2 id="heading-part-2-continuous-deployment-with-openshift-pipelines-tekton">🌍 Part 2 — Continuous Deployment with OpenShift Pipelines (Tekton)</h2>
<p>Once your code passes all CI checks, it’s time to <strong>build</strong>, <strong>containerize</strong>, and <strong>deploy</strong> automatically.<br />That’s where <strong>OpenShift Pipelines</strong>, powered by <strong>Tekton</strong>, comes in.</p>
<hr />
<h2 id="heading-prerequisites">🧩 Prerequisites</h2>
<p>Ensure <strong>Tekton tasks</strong> installed and access to an OpenShift cluster.</p>
<p>Create a <code>tasks.yaml</code> file for defining your reusable tasks.</p>
<hr />
<h3 id="heading-task-1-cleanup-task">🧹 Task 1: Cleanup Task</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">tekton.dev/v1beta1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Task</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">cleanup</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">Cleans</span> <span class="hljs-string">up</span> <span class="hljs-string">workspace</span> <span class="hljs-string">before</span> <span class="hljs-string">a</span> <span class="hljs-string">new</span> <span class="hljs-string">run.</span>
  <span class="hljs-attr">workspaces:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">source</span>
  <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">remove</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">alpine:3</span>
      <span class="hljs-attr">script:</span> <span class="hljs-string">|
        #!/usr/bin/env sh
        set -eu
        echo "Cleaning workspace..."
        rm -rf $(workspaces.source.path)/*</span>
</code></pre>
<hr />
<h3 id="heading-task-2-nose-unit-test-task">🧪 Task 2: Nose Unit Test Task</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">tekton.dev/v1beta1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Task</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">nose</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">workspaces:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">source</span>
  <span class="hljs-attr">params:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">args</span>
      <span class="hljs-attr">default:</span> <span class="hljs-string">"-v"</span>
  <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nosetests</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">python:3.9-slim</span>
      <span class="hljs-attr">workingDir:</span> <span class="hljs-string">$(workspaces.source.path)</span>
      <span class="hljs-attr">script:</span> <span class="hljs-string">|
        #!/bin/bash
        set -e
        python -m pip install --upgrade pip wheel
        pip install -r requirements.txt
        nosetests $(params.args)</span>
</code></pre>
<p>Apply the tasks:</p>
<pre><code class="lang-bash">kubectl apply -f tasks.yaml
</code></pre>
<hr />
<h2 id="heading-step-1-create-a-persistent-volume-claim-pvc">🗄 Step 1: Create a Persistent Volume Claim (PVC)</h2>
<p>Create a PVC in OpenShift → <strong>Storage → PersistentVolumeClaims</strong><br />Use:</p>
<ul>
<li><p>Name: <code>oc-lab-pvc</code></p>
</li>
<li><p>Size: <code>1Gi</code></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761993167979/de5f3cd5-3043-446d-8bee-12b5b64db1b1.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<p>This workspace will persist data between Tekton tasks.</p>
<hr />
<h2 id="heading-step-2-build-your-pipeline">🔧 Step 2: Build Your Pipeline</h2>
<p>In <strong>Adminstrator Perspective → Pipelines → Create Pipeline</strong><br />Name it <code>ci-cd-pipeline</code>, add a workspace called <code>output</code>, and use the <strong>Pipeline Builder</strong>.</p>
<hr />
<h2 id="heading-step-3-add-tasks-sequentially">🧱 Step 3: Add Tasks Sequentially</h2>
<ol>
<li><p><strong>Cleanup</strong> — wipes the workspace.</p>
</li>
<li><p><strong>git-clone</strong> — fetches source code.</p>
</li>
<li><p><strong>flake8</strong> — lints your application.</p>
</li>
<li><p><strong>nose</strong> — runs Python unit tests.</p>
</li>
<li><p><strong>buildah</strong> — builds your container image.</p>
</li>
<li><p><strong>openshift-client</strong> — deploys the app.</p>
</li>
</ol>
<hr />
<p>Install the task from Tekton Hub if missing:</p>
<pre><code class="lang-bash">tkn hub install task git-clone
</code></pre>
<hr />
<h2 id="heading-add-linting-with-flake8">🧹 Add Linting with Flake8</h2>
<p>If Flake8 isn’t installed:</p>
<pre><code class="lang-bash">tkn hub install task flake8
</code></pre>
<p>Then attach it to the pipeline after git-clone.</p>
<hr />
<h2 id="heading-add-nose-unit-tests">🧪 Add Nose Unit Tests</h2>
<p>Link the <code>nose</code> task created earlier, using the same workspace <code>output</code>.</p>
<hr />
<h2 id="heading-build-with-buildah">🏗 Build with Buildah</h2>
<p>After testing, add the <strong>buildah</strong> task:</p>
<p>This builds and pushes your container image to the registry.</p>
<hr />
<h2 id="heading-deploy-with-openshift-client">🚀 Deploy with OpenShift Client</h2>
<p>Add the final task using the <strong>openshift-client</strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761993257371/594b05b4-6858-42e1-9c97-407771d84219.png" alt class="image--center mx-auto" /></p>
<p>Finally the tasks should look like the image above</p>
<hr />
<h2 id="heading-validate-the-deployment">✅ Validate the Deployment</h2>
<p>Open <strong>Topology</strong> → Click your Pods→ <strong>Logs</strong> tab.<br />You should see:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761993295426/ad6b1832-3cc4-4416-af24-6f2699f3916d.png" alt class="image--center mx-auto" /></p>
<p>Your CI/CD flow is now live — end-to-end automation achieved.</p>
<hr />
<h2 id="heading-final-thoughts">🎯 Final Thoughts</h2>
<p>You just built a <strong>complete CI/CD system</strong> combining:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Stage</td><td>Tool</td><td>Purpose</td></tr>
</thead>
<tbody>
<tr>
<td><strong>CI</strong></td><td>GitHub Actions</td><td>Code checkout, linting, and testing</td></tr>
<tr>
<td><strong>CD</strong></td><td>OpenShift Pipelines (Tekton)</td><td>Build, push, and deploy containers</td></tr>
</tbody>
</table>
</div><p>This architecture powers everything from <strong>startups to global enterprises</strong>, providing speed, safety, and scalability across hybrid clouds.</p>
<blockquote>
<p>⚙️ <em>GitHub Actions validates your ideas. OpenShift Pipelines turns them into reality.</em></p>
</blockquote>
<hr />
<hr />
]]></content:encoded></item><item><title><![CDATA[Kubernetes Deployments: The Backbone of Application Stability]]></title><description><![CDATA[This post outlines how Deployments simplify and stabilize application delivery on Kubernetes.

🔹 What Is a Deployment?
A Deployment is a Kubernetes resource object that defines the desired lifecycle of an application, including:

The container image...]]></description><link>https://ci-cd-using-github-actions-openshift.hashnode.dev/kubernetes-deployments-the-backbone-of-application-stability</link><guid isPermaLink="true">https://ci-cd-using-github-actions-openshift.hashnode.dev/kubernetes-deployments-the-backbone-of-application-stability</guid><category><![CDATA[deployment]]></category><category><![CDATA[replicaset]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[k3s]]></category><dc:creator><![CDATA[Hardik Arora]]></dc:creator><pubDate>Thu, 23 Oct 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-this-post-outlines-how-deployments-simplify-and-stabilize-application-delivery-on-kubernetes">This post outlines how Deployments simplify and stabilize application delivery on Kubernetes.</h3>
<hr />
<h2 id="heading-what-is-a-deployment">🔹 What Is a Deployment?</h2>
<p>A <strong>Deployment</strong> is a Kubernetes resource object that defines the <strong>desired lifecycle of an application</strong>, including:</p>
<ul>
<li><p>The <strong>container image</strong> to run</p>
</li>
<li><p>The <strong>number of pod replicas</strong></p>
</li>
<li><p>The <strong>update strategy</strong></p>
</li>
</ul>
<p>Kubernetes ensures that this desired state is continuously maintained. When a pod fails or is deleted, it is automatically replaced.</p>
<h2 id="heading-behind-the-scenes-replicasets">💡 Behind the Scenes: ReplicaSets</h2>
<p>Each Deployment manages one or more <strong>ReplicaSets</strong>, which are responsible for maintaining the correct number of pods.<br />Every update generates a new ReplicaSet, while older ones are retained for rollback purposes.</p>
<hr />
<h2 id="heading-practical-demonstration">🧠 Practical Demonstration</h2>
<p>A deployment can be created and inspected with the following commands:</p>
<pre><code class="lang-bash">kubectl create deployment nginx --image=nginx --dry-run=client -o yaml | tee nginx-deployment.yaml
kubectl apply -f nginx-deployment.yaml
kubectl get deployment
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762018447451/7e790c7e-f920-4427-b45f-b8f96244b7e4.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>the age of replicasets and deployment is same as both are created at same time simultaneously.</p>
</blockquote>
<p>Upon creation, Kubernetes automatically generates a corresponding ReplicaSet and pods.</p>
<hr />
<h2 id="heading-rollout-history-and-annotations">📜 Rollout History and Annotations</h2>
<p>Deployment revisions can be viewed using:</p>
<pre><code class="lang-bash">kubectl rollout <span class="hljs-built_in">history</span> deployment/nginx
</code></pre>
<p>Annotations can be added to make revision history more descriptive:</p>
<pre><code class="lang-bash">kubectl annotate deployment/nginx kubernetes.io/change-cause=<span class="hljs-string">"Initial Deployment"</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762018607868/cdb29aaf-296b-458d-91c0-3268184f6a85.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-scaling-deployments">📈 Scaling Deployments</h2>
<p>Deployments can be scaled seamlessly:</p>
<pre><code class="lang-bash">kubectl scale deployment nginx --replicas=10
</code></pre>
<p>Kubernetes increases or decreases pods dynamically while keeping the service available.</p>
<p><strong><em>Placeholder: Terminal output showing pods scaling in real-time</em></strong></p>
<hr />
<h2 id="heading-rolling-update-strategy">🔄 Rolling Update Strategy</h2>
<p>By default, deployments use the <strong>RollingUpdate</strong> strategy:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762018905529/5ef688a3-a20d-4279-a202-a6c73e03537d.png" alt="nginx-deployment.yaml | more" class="image--center mx-auto" /></p>
<p>This configuration allows Kubernetes to:</p>
<ul>
<li><p>Temporarily exceed the desired pod count by <strong>25%</strong> during updates</p>
</li>
<li><p>Tolerate up to <strong>25%</strong> unavailable pods — ensuring smooth rollouts without excessive resource usage</p>
<h2 id="heading-updating-the-image">🧩 Updating the Image</h2>
</li>
</ul>
<p>Applications can be updated either through the YAML manifest or via the command line.</p>
<h3 id="heading-rollout-1-by-editing-yaml">Rollout 1: by Editing YAML</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:stable</span>
</code></pre>
<p>Apply the changes:</p>
<pre><code class="lang-bash">kubectl apply -f nginx-deployment.yaml
kubectl rollout status deployment/nginx
</code></pre>
<h3 id="heading-rollout-2-using-cli-shortcut">Rollout 2: using CLI Shortcut</h3>
<blockquote>
<p><strong><em>pods terminating and recreating</em></strong></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762019117758/7d44d597-4903-4dff-9f84-57200a474487.png" alt class="image--center mx-auto" /></p>
<p>The change can be annotated as follows:</p>
<pre><code class="lang-bash">kubectl annotate deployment/nginx kubernetes.io/change-cause=<span class="hljs-string">"change image to nginx:alpine"</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762019905064/912757fe-90a0-425b-9e2e-b37dec42e7c7.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">kubectl <span class="hljs-built_in">set</span> image deployment/nginx=nginx:perl
</code></pre>
<p>We can also rollout to previous versions:</p>
<pre><code class="lang-bash">kubectl rollout undo deployment/nginx
</code></pre>
<p>Kubernetes automatically restores the last functional ReplicaSet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762020693024/3480e1ac-98f3-48a3-8160-deb027863664.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-cleaning-up">🧹 Cleaning Up</h2>
<p>The deployment and all associated resources can be removed using:</p>
<pre><code class="lang-bash">kubectl delete deployment/nginx --now
</code></pre>
<p>This command deletes the deployment, ReplicaSets, and all linked pods without any grace-period.</p>
<hr />
<h2 id="heading-conclusion">🌟 Conclusion</h2>
<p>Kubernetes Deployments form the <strong>core of resilient DevOps operations</strong>.<br />They provide teams with a <strong>structured, safe, and automated mechanism</strong> for delivering updates at scale.</p>
<p>Deployments ensure:</p>
<ul>
<li><p>Consistent scaling</p>
</li>
<li><p>Predictable rolling updates</p>
</li>
<li><p>Reliable rollbacks</p>
</li>
</ul>
<p>Mastering Deployments is a major step toward mastering Kubernetes as a whole.</p>
<hr />
<hr />
]]></content:encoded></item></channel></rss>