<?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[Akash Pawar]]></title><description><![CDATA[Welcome to DevOps POC – Hands-on insights into DevOps, from Kubernetes to CI/CD, infra as code, and beyond.]]></description><link>https://blog.akashpawar.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 12 Oct 2026 03:23:55 GMT</lastBuildDate><atom:link href="https://blog.akashpawar.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Kubernetes NetworkPolicy Explained: Ingress, Egress, Default Deny, Selectors, and IP-Based Rules]]></title><description><![CDATA[A practical guide to standard Kubernetes NetworkPolicy using only the Kubernetes NetworkPolicy API.
This article intentionally does not cover Cilium, Calico-specific policy features, or other extended]]></description><link>https://blog.akashpawar.com/kubernetes-networkpolicy-explained-ingress-egress-default-deny-selectors-and-ip-based-rules</link><guid isPermaLink="true">https://blog.akashpawar.com/kubernetes-networkpolicy-explained-ingress-egress-default-deny-selectors-and-ip-based-rules</guid><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Thu, 27 Aug 2026 07:47:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/634fb963770c93961f03be04/7b755b0e-fe14-466b-a675-271deb679e4b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>A practical guide to <strong>standard Kubernetes NetworkPolicy</strong> using only the Kubernetes NetworkPolicy API.</p>
<p>This article intentionally does <strong>not</strong> cover Cilium, Calico-specific policy features, or other extended policy engines. Those are separate topics.</p>
</blockquote>
<hr />
<h2>Why NetworkPolicy feels confusing</h2>
<p>NetworkPolicy looks straightforward:</p>
<pre><code class="language-yaml">kind: NetworkPolicy
spec:
  podSelector:
  policyTypes:
  ingress:
  egress:
</code></pre>
<p>But several concepts interact:</p>
<ul>
<li><p>Is the Pod isolated for ingress?</p>
</li>
<li><p>Is it isolated for egress?</p>
</li>
<li><p>Does an empty rule mean allow everything or deny everything?</p>
</li>
<li><p>What happens when multiple policies select the same Pod?</p>
</li>
<li><p>Does <code>from</code> mean the source or destination?</p>
</li>
<li><p>Does <code>to</code> mean the source or destination?</p>
</li>
<li><p>Can I select a Pod?</p>
</li>
<li><p>Can I select a namespace?</p>
</li>
<li><p>Can I select an external IP?</p>
</li>
<li><p>What happens when I specify a port?</p>
</li>
<li><p>Can standard NetworkPolicy explicitly deny one IP/port?</p>
</li>
<li><p>What happens to return traffic?</p>
</li>
</ul>
<p>The easiest way to understand all of this is to build the mental model first.</p>
<hr />
<h1>1. The two directions: Ingress and Egress</h1>
<p>Think of every Pod as having two doors:</p>
<pre><code class="language-text">                         POD
                  ┌──────────────┐
                  │              │
       INGRESS ──&gt;│              │&lt;── EGRESS
                  │              │
                  └──────────────┘
</code></pre>
<h3>Ingress</h3>
<p>Ingress is traffic <strong>coming into</strong> the Pod.</p>
<pre><code class="language-text">frontend ───────────────&gt; backend
                         INGRESS
</code></pre>
<p>From the backend's point of view, this is ingress traffic.</p>
<h3>Egress</h3>
<p>Egress is traffic <strong>leaving</strong> the Pod.</p>
<pre><code class="language-text">frontend ───────────────&gt; backend
   EGRESS
</code></pre>
<p>From the frontend's point of view, this is egress traffic.</p>
<p>The same connection can therefore be:</p>
<pre><code class="language-text">frontend                         backend
   │                               │
   │ ─────── TCP connection ─────&gt; │
   │                               │
 EGRESS                           INGRESS
</code></pre>
<p>This distinction is fundamental.</p>
<hr />
<h1>2. The default behavior: everything is open</h1>
<p>If there is <strong>no NetworkPolicy selecting a Pod</strong>, the Pod is non-isolated.</p>
<p>In the normal Kubernetes NetworkPolicy model:</p>
<pre><code class="language-text">Ingress = allowed
Egress  = allowed
</code></pre>
<p>So without applicable policies:</p>
<pre><code class="language-text">Pod A ───────────────&gt; Pod B       ✓

Pod B ───────────────&gt; Pod A       ✓

Pod A ───────────────&gt; Internet    ✓
</code></pre>
<p>NetworkPolicy is not a firewall that automatically blocks traffic just because the API object exists somewhere in the cluster.</p>
<p>The important question is:</p>
<blockquote>
<p><strong>Does a NetworkPolicy select this Pod for this direction?</strong></p>
</blockquote>
<hr />
<h1>3. A NetworkPolicy has a target: <code>podSelector</code></h1>
<p>Every NetworkPolicy has a <code>podSelector</code>.</p>
<p>Example:</p>
<pre><code class="language-yaml">spec:
  podSelector:
    matchLabels:
      app: backend
</code></pre>
<p>This means:</p>
<blockquote>
<p>Apply this policy to Pods with the label <code>app=backend</code> in the policy's namespace.</p>
</blockquote>
<p>For example:</p>
<pre><code class="language-yaml">metadata:
  labels:
    app: backend
</code></pre>
<p>matches:</p>
<pre><code class="language-text">Pod A: app=backend       ← selected
Pod B: app=frontend      ← not selected
Pod C: app=database      ← not selected
</code></pre>
<h2>Empty <code>podSelector</code></h2>
<p>This:</p>
<pre><code class="language-yaml">podSelector: {}
</code></pre>
<p>means:</p>
<blockquote>
<p>Select <strong>all Pods in the policy's namespace</strong>.</p>
</blockquote>
<p>That is why default-deny policies commonly use:</p>
<pre><code class="language-yaml">podSelector: {}
</code></pre>
<hr />
<h1>4. <code>policyTypes</code>: which direction are we controlling?</h1>
<p><code>policyTypes</code> tells Kubernetes whether the policy applies to:</p>
<ul>
<li><p><code>Ingress</code></p>
</li>
<li><p><code>Egress</code></p>
</li>
<li><p>or both</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-yaml">policyTypes:
- Ingress
</code></pre>
<p>means:</p>
<blockquote>
<p>This policy isolates the selected Pods for incoming traffic.</p>
</blockquote>
<p>Example:</p>
<pre><code class="language-yaml">policyTypes:
- Egress
</code></pre>
<p>means:</p>
<blockquote>
<p>This policy isolates the selected Pods for outgoing traffic.</p>
</blockquote>
<p>Example:</p>
<pre><code class="language-yaml">policyTypes:
- Ingress
- Egress
</code></pre>
<p>means:</p>
<blockquote>
<p>Isolate the selected Pods in both directions.</p>
</blockquote>
<p>A useful mental model is:</p>
<pre><code class="language-text">policyTypes
     │
     ├── Ingress → control incoming traffic
     │
     └── Egress  → control outgoing traffic
</code></pre>
<p>For clarity, it is a good practice to explicitly specify <code>policyTypes</code>, especially for an egress-only policy.</p>
<hr />
<h1>5. Standard NetworkPolicy is an ALLOW-list</h1>
<p>This is the single most important concept.</p>
<p>Standard Kubernetes NetworkPolicy does <strong>not</strong> provide an explicit <code>deny:</code> rule.</p>
<p>You cannot write:</p>
<pre><code class="language-yaml">egress:
- deny:
  - ipBlock:
      cidr: 192.168.121.17/32
</code></pre>
<p>There is no standard <code>deny</code> field in the Kubernetes NetworkPolicy API.</p>
<p>Instead, NetworkPolicy answers:</p>
<blockquote>
<p><strong>What traffic should be allowed?</strong></p>
</blockquote>
<p>Everything else is implicitly denied <strong>once the Pod is isolated for that direction</strong>.</p>
<p>Think:</p>
<pre><code class="language-text">No policy selecting Pod
        ↓
     ALLOW ALL

Policy selects Pod for Egress
        ↓
     ALLOW only
     what matches rules
</code></pre>
<hr />
<h1>6. An Egress rule automatically creates egress isolation</h1>
<p>This is a very common point of confusion.</p>
<p>Suppose there is no default-deny policy.</p>
<p>You create:</p>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-database
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: backend

  policyTypes:
  - Egress

  egress:
  - to:
    - ipBlock:
        cidr: 192.168.121.17/32
    ports:
    - protocol: TCP
      port: 9999
</code></pre>
<p>You do <strong>not</strong> need another empty egress policy first.</p>
<p>The selected Pods become egress-isolated.</p>
<p>The result is:</p>
<pre><code class="language-text">backend Pod
    │
    ├──&gt; 192.168.121.17:9999/TCP     ✓ ALLOW
    │
    ├──&gt; 192.168.121.17:80           ✗ DENY
    │
    ├──&gt; 8.8.8.8:443                 ✗ DENY
    │
    └──&gt; another Pod                 ✗ DENY
</code></pre>
<p>Why?</p>
<p>Because the Pod is now egress-isolated and the policy only allows traffic matching its egress rule.</p>
<hr />
<h1>7. Compare that with having no Egress policy</h1>
<h3>Scenario A: no egress policy</h3>
<pre><code class="language-text">Pod
 │
 ├──&gt; 192.168.121.17:9999     ✓
 ├──&gt; 192.168.121.17:80       ✓
 ├──&gt; 8.8.8.8:443             ✓
 └──&gt; anywhere else           ✓
</code></pre>
<p>Egress is open.</p>
<h3>Scenario B: an Egress policy allows one destination</h3>
<pre><code class="language-yaml">policyTypes:
- Egress

egress:
- to:
  - ipBlock:
      cidr: 192.168.121.17/32
  ports:
  - protocol: TCP
    port: 9999
</code></pre>
<p>Now:</p>
<pre><code class="language-text">192.168.121.17:9999     ✓
Everything else         ✗
</code></pre>
<p>This distinction is critical:</p>
<blockquote>
<p><strong>The existence of an applicable Egress policy changes the selected Pod from non-isolated to egress-isolated.</strong></p>
</blockquote>
<hr />
<h1>8. Default-deny Ingress</h1>
<p>A classic default-deny ingress policy is:</p>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Ingress
</code></pre>
<p>Notice there is no <code>ingress:</code> rule.</p>
<p>This means:</p>
<pre><code class="language-text">All Pods in namespace
        │
        ↓
Ingress isolation enabled
        │
        ↓
No ingress rules
        │
        ↓
DENY ALL INGRESS
</code></pre>
<p>So:</p>
<pre><code class="language-text">frontend ───────X──────&gt; backend
random   ───────X──────&gt; backend
external ───────X──────&gt; backend
</code></pre>
<p>The empty rule set is not "allow everything."</p>
<p>It means:</p>
<blockquote>
<p>The selected Pods are isolated, and this policy allows no ingress traffic.</p>
</blockquote>
<hr />
<h1>9. Default-deny Egress</h1>
<p>Same concept:</p>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress
</code></pre>
<p>Result:</p>
<pre><code class="language-text">All Pods
    │
    ↓
Egress isolation
    │
    ↓
No egress rules
    │
    ↓
DENY ALL EGRESS
</code></pre>
<p>So:</p>
<pre><code class="language-text">Pod ─────X────&gt; Internet
Pod ─────X────&gt; database
Pod ─────X────&gt; another Pod
</code></pre>
<p>All are blocked unless another applicable NetworkPolicy allows them.</p>
<h3>Important: DNS</h3>
<p>A default-deny egress policy also blocks DNS traffic unless DNS is explicitly allowed by another applicable policy.</p>
<p>That is a common reason applications suddenly stop resolving service names after egress lockdown.</p>
<hr />
<h1>10. Default-deny both directions</h1>
<p>You can isolate both directions with one policy:</p>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
</code></pre>
<p>No ingress rules.</p>
<p>No egress rules.</p>
<p>Therefore:</p>
<pre><code class="language-text">                  POD
                   │
          ┌────────┴────────┐
          │                 │
       INGRESS           EGRESS
          │                 │
        DENY              DENY
</code></pre>
<p>This is the strongest baseline:</p>
<pre><code class="language-text">DEFAULT DENY
      +
EXPLICIT ALLOW
</code></pre>
<hr />
<h1>11. <code>ingress.from</code>: Who can connect to me?</h1>
<p>Suppose the backend has:</p>
<pre><code class="language-yaml">podSelector:
  matchLabels:
    app: backend

policyTypes:
- Ingress

ingress:
- from:
  - podSelector:
      matchLabels:
        app: frontend
</code></pre>
<p>Read this as:</p>
<blockquote>
<p>Allow traffic <strong>from frontend Pods</strong> into backend Pods.</p>
</blockquote>
<p>So:</p>
<pre><code class="language-text">frontend ─────────&gt; backend
          ✓
</code></pre>
<p>but:</p>
<pre><code class="language-text">random-pod ───────&gt; backend
          ✗
</code></pre>
<p>Remember:</p>
<pre><code class="language-text">INGRESS
   │
   └── from = SOURCE
</code></pre>
<hr />
<h1>12. <code>egress.to</code>: Where can I connect?</h1>
<p>Now reverse the direction.</p>
<pre><code class="language-yaml">podSelector:
  matchLabels:
    app: frontend

policyTypes:
- Egress

egress:
- to:
  - podSelector:
      matchLabels:
        app: backend
</code></pre>
<p>Read this as:</p>
<blockquote>
<p>Allow frontend Pods to connect <strong>to backend Pods</strong>.</p>
</blockquote>
<p>So:</p>
<pre><code class="language-text">frontend ─────────&gt; backend
          ✓
</code></pre>
<p>Remember:</p>
<pre><code class="language-text">EGRESS
   │
   └── to = DESTINATION
</code></pre>
<p>A simple memory trick:</p>
<pre><code class="language-text">Ingress  → from = who is coming TO me?
Egress   → to   = where am I going?
</code></pre>
<hr />
<h1>13. What can <code>from</code> and <code>to</code> target?</h1>
<p>Standard Kubernetes NetworkPolicy provides three main peer selector mechanisms:</p>
<ol>
<li><p><code>podSelector</code></p>
</li>
<li><p><code>namespaceSelector</code></p>
</li>
<li><p><code>ipBlock</code></p>
</li>
</ol>
<p>These can be used under:</p>
<pre><code class="language-yaml">ingress:
- from:
</code></pre>
<p>and:</p>
<pre><code class="language-yaml">egress:
- to:
</code></pre>
<p>Let's look at each.</p>
<hr />
<h1>14. Target type #1: <code>podSelector</code></h1>
<p>Example:</p>
<pre><code class="language-yaml">ingress:
- from:
  - podSelector:
      matchLabels:
        app: frontend
</code></pre>
<p>This selects Pods with:</p>
<pre><code class="language-text">app=frontend
</code></pre>
<p>in the <strong>same namespace as the NetworkPolicy</strong> when <code>podSelector</code> is used by itself.</p>
<p>Example:</p>
<pre><code class="language-text">Namespace: default

frontend Pod
  app=frontend

backend Pod
  app=backend
</code></pre>
<p>Policy attached to backend:</p>
<pre><code class="language-text">frontend ─────────&gt; backend
          ✓
</code></pre>
<p>A frontend Pod in another namespace is not selected by this standalone <code>podSelector</code>.</p>
<hr />
<h1>15. Target type #2: <code>namespaceSelector</code></h1>
<p>You can select namespaces by their labels.</p>
<p>Example:</p>
<pre><code class="language-yaml">ingress:
- from:
  - namespaceSelector:
      matchLabels:
        team: platform
</code></pre>
<p>Read this as:</p>
<blockquote>
<p>Allow traffic from Pods in namespaces labeled <code>team=platform</code>.</p>
</blockquote>
<p>This can allow Pods from multiple namespaces.</p>
<p>Think:</p>
<pre><code class="language-text">namespace-a   team=platform
namespace-b   team=platform
namespace-c   team=payments
</code></pre>
<p>Then:</p>
<pre><code class="language-text">namespace-a Pods ──┐
                   ├──&gt; backend ✓
namespace-b Pods ──┘

namespace-c Pods ─────&gt; backend ✗
</code></pre>
<hr />
<h1>16. Target type #3: <code>ipBlock</code></h1>
<p><code>ipBlock</code> lets you select an IP range using CIDR.</p>
<p>Example:</p>
<pre><code class="language-yaml">egress:
- to:
  - ipBlock:
      cidr: 192.168.121.17/32
</code></pre>
<p><code>/32</code> represents exactly one IPv4 address:</p>
<pre><code class="language-text">192.168.121.17
</code></pre>
<p>You can also specify a range:</p>
<pre><code class="language-yaml">ipBlock:
  cidr: 192.168.121.0/24
</code></pre>
<p>which represents the CIDR range.</p>
<p>You can exclude a smaller CIDR from the range:</p>
<pre><code class="language-yaml">ipBlock:
  cidr: 192.168.121.0/24
  except:
  - 192.168.121.17/32
</code></pre>
<p>Conceptually:</p>
<pre><code class="language-text">192.168.121.0/24
        │
        ├── most addresses   ✓
        │
        └── .17              ✗ excluded
</code></pre>
<h3>Important limitation</h3>
<p><code>ipBlock.except</code> excludes an IP/CIDR range. It is not a port-specific deny mechanism.</p>
<p>So it can express:</p>
<pre><code class="language-text">ALLOW 192.168.121.0/24
EXCEPT 192.168.121.17
</code></pre>
<p>but standard NetworkPolicy cannot express:</p>
<pre><code class="language-text">ALLOW 192.168.121.17:80
DENY  192.168.121.17:9999
ALLOW 192.168.121.17:443
</code></pre>
<p>using an explicit deny rule.</p>
<hr />
<h1>17. Can <code>ipBlock</code> target external IPs?</h1>
<p>Yes. <code>ipBlock</code> is intended for IP/CIDR-based policy, including traffic between Pods and the outside world.</p>
<p>For example:</p>
<pre><code class="language-yaml">egress:
- to:
  - ipBlock:
      cidr: 8.8.8.8/32
</code></pre>
<p>can express an allow rule for traffic to that IP.</p>
<p>But there is an important Kubernetes caveat:</p>
<blockquote>
<p>The exact source/destination IP seen by NetworkPolicy can depend on address rewriting performed by the network implementation, cloud provider, Services, and other networking components.</p>
</blockquote>
<p>For example, traffic involving a Service or load balancer may undergo source/destination NAT, so the IP you expect may not be the IP the NetworkPolicy implementation evaluates.</p>
<p>This is one reason <code>ipBlock</code> behavior should be tested in the actual cluster/network implementation.</p>
<hr />
<h1>18. <code>podSelector</code> + <code>namespaceSelector</code>: the AND case</h1>
<p>You can combine them in the <strong>same peer entry</strong>:</p>
<pre><code class="language-yaml">ingress:
- from:
  - namespaceSelector:
      matchLabels:
        team: platform
    podSelector:
      matchLabels:
        app: frontend
</code></pre>
<p>This means:</p>
<blockquote>
<p>Allow Pods labeled <code>app=frontend</code> <strong>AND</strong> located in namespaces labeled <code>team=platform</code>.</p>
</blockquote>
<p>Conceptually:</p>
<pre><code class="language-text">namespace must match
        AND
Pod must match
</code></pre>
<p>Example:</p>
<pre><code class="language-text">namespace-a
team=platform
  └── frontend
       ✓

namespace-b
team=payments
  └── frontend
       ✗

namespace-c
team=platform
  └── database
       ✗
</code></pre>
<hr />
<h1>19. A very common YAML mistake: AND vs OR</h1>
<p>Compare these two.</p>
<h2>Case A: same peer entry</h2>
<pre><code class="language-yaml">from:
- namespaceSelector:
    matchLabels:
      team: platform
  podSelector:
    matchLabels:
      app: frontend
</code></pre>
<p>This means:</p>
<pre><code class="language-text">namespace = platform
AND
pod = frontend
</code></pre>
<h2>Case B: two peer entries</h2>
<pre><code class="language-yaml">from:
- namespaceSelector:
    matchLabels:
      team: platform

- podSelector:
    matchLabels:
      app: frontend
</code></pre>
<p>This means:</p>
<pre><code class="language-text">namespace = platform
OR
Pod = frontend in the policy's namespace
</code></pre>
<p>The indentation changes the meaning.</p>
<p>This is a very important NetworkPolicy YAML detail.</p>
<hr />
<h1>20. Ports: another important concept</h1>
<p>Suppose you have:</p>
<pre><code class="language-yaml">egress:
- to:
  - ipBlock:
      cidr: 192.168.121.17/32
  ports:
  - protocol: TCP
    port: 9999
</code></pre>
<p>The destination and port both need to match.</p>
<p>Think:</p>
<pre><code class="language-text">destination MATCH
        AND
port MATCH
        ↓
      ALLOW
</code></pre>
<p>Therefore:</p>
<pre><code class="language-text">192.168.121.17:9999/TCP     ✓
192.168.121.17:80/TCP       ✗
8.8.8.8:9999/TCP             ✗
</code></pre>
<p>The rule is not saying "allow the IP OR the port."</p>
<p>It is saying:</p>
<blockquote>
<p>Allow traffic that matches the destination <strong>and</strong> the port.</p>
</blockquote>
<hr />
<h1>21. Multiple ports in one rule are OR</h1>
<p>Example:</p>
<pre><code class="language-yaml">ports:
- protocol: TCP
  port: 80
- protocol: TCP
  port: 443
</code></pre>
<p>means:</p>
<pre><code class="language-text">TCP/80   ✓
TCP/443  ✓
</code></pre>
<p>The ports inside that list are logically ORed.</p>
<hr />
<h1>22. Multiple destinations in one rule are OR</h1>
<p>Example:</p>
<pre><code class="language-yaml">to:
- ipBlock:
    cidr: 10.0.0.0/24
- ipBlock:
    cidr: 192.168.1.0/24
</code></pre>
<p>means:</p>
<pre><code class="language-text">10.0.0.0/24      ✓
192.168.1.0/24   ✓
</code></pre>
<p>The destination peers are logically ORed.</p>
<hr />
<h1>23. Multiple rules are also additive</h1>
<p>Suppose:</p>
<pre><code class="language-yaml">egress:
- to:
  - podSelector:
      matchLabels:
        app: database

- to:
  - podSelector:
      matchLabels:
        app: redis
</code></pre>
<p>Then:</p>
<pre><code class="language-text">backend ─────&gt; database     ✓
backend ─────&gt; redis        ✓
backend ─────&gt; frontend     ✗
</code></pre>
<p>Think:</p>
<pre><code class="language-text">Rule 1 OR Rule 2 OR Rule 3 ...
</code></pre>
<p>not:</p>
<pre><code class="language-text">Rule 1 AND Rule 2
</code></pre>
<hr />
<h1>24. Multiple NetworkPolicies are additive</h1>
<p>This is one of the most important concepts.</p>
<p>Suppose Policy A allows:</p>
<pre><code class="language-text">frontend → backend:8080
</code></pre>
<p>and Policy B allows:</p>
<pre><code class="language-text">monitoring → backend:9090
</code></pre>
<p>If both policies select the same backend Pods:</p>
<pre><code class="language-text">frontend   ───&gt; backend:8080     ✓
monitoring ───&gt; backend:9090     ✓
</code></pre>
<p>The policies do not override each other.</p>
<p>Their allowed traffic is combined.</p>
<p>Think:</p>
<pre><code class="language-text">Policy A
   │
   ├── allow X
   │
Policy B
   │
   ├── allow Y
   │
Policy C
   │
   ├── allow Z
   │
   ↓
X + Y + Z are allowed
</code></pre>
<p>This also means there is no standard policy ordering such as:</p>
<pre><code class="language-text">Policy 1
   ↓
Policy 2
   ↓
Policy 3
</code></pre>
<p>The policies are combined additively.</p>
<hr />
<h1>25. There is no "deny wins" in standard NetworkPolicy</h1>
<p>Because standard NetworkPolicy does not have explicit deny rules, you cannot normally create:</p>
<pre><code class="language-text">Policy A:
ALLOW 10.0.0.0/24

Policy B:
DENY 10.0.0.5
</code></pre>
<p>and expect the second policy to override the first.</p>
<p>There is no standard <code>deny</code> rule.</p>
<p>Instead, the design is:</p>
<pre><code class="language-text">DEFAULT DENY
       +
EXPLICIT ALLOW
</code></pre>
<p>This is why NetworkPolicy is best understood as an allow-list.</p>
<hr />
<h1>26. Pod-to-Pod traffic: both directions matter</h1>
<p>Suppose:</p>
<pre><code class="language-text">frontend Pod ───────────────&gt; backend Pod
</code></pre>
<p>There are potentially two policy checks:</p>
<pre><code class="language-text">frontend                       backend
   │                              │
   │ EGRESS                       │ INGRESS
   │                              │
   └──────── connection ─────────&gt;│
</code></pre>
<p>For a Pod-to-Pod connection to work when both Pods are isolated:</p>
<pre><code class="language-text">SOURCE EGRESS = ALLOW
        AND
DESTINATION INGRESS = ALLOW
        ↓
CONNECTION = ALLOW
</code></pre>
<p>If either side blocks it:</p>
<pre><code class="language-text">SOURCE EGRESS = DENY
        OR
DESTINATION INGRESS = DENY
        ↓
CONNECTION = BLOCKED
</code></pre>
<hr />
<h1>27. Scenario: both sides are default-deny</h1>
<p>Suppose we start with:</p>
<pre><code class="language-text">Frontend:
  Egress = DENY ALL

Backend:
  Ingress = DENY ALL
</code></pre>
<p>Then:</p>
<pre><code class="language-text">frontend ─────────X────────&gt; backend
</code></pre>
<p>Blocked.</p>
<hr />
<h1>28. Scenario: only frontend Egress is allowed</h1>
<p>Now we add:</p>
<pre><code class="language-text">Frontend:
  Egress → ALLOW backend:8080

Backend:
  Ingress → still DENY ALL
</code></pre>
<p>Result:</p>
<pre><code class="language-text">frontend ─────────X────────&gt; backend
</code></pre>
<p>Still blocked.</p>
<p>Why?</p>
<p>Because:</p>
<pre><code class="language-text">Frontend Egress = ✓
Backend Ingress = ✗
</code></pre>
<p>Both sides are not allowed.</p>
<hr />
<h1>29. Scenario: only backend Ingress is allowed</h1>
<p>Now instead:</p>
<pre><code class="language-text">Frontend:
  Egress → still DENY ALL

Backend:
  Ingress → ALLOW frontend:8080
</code></pre>
<p>Result:</p>
<pre><code class="language-text">frontend ─────────X────────&gt; backend
</code></pre>
<p>Still blocked.</p>
<p>Why?</p>
<pre><code class="language-text">Frontend Egress = ✗
Backend Ingress = ✓
</code></pre>
<p>Again, both sides are not allowed.</p>
<hr />
<h1>30. Scenario: both sides are allowed</h1>
<p>Now:</p>
<pre><code class="language-text">Frontend:
  Egress → ALLOW backend:8080

Backend:
  Ingress → ALLOW frontend:8080
</code></pre>
<p>Result:</p>
<pre><code class="language-text">frontend ──────────────────&gt; backend
             ALLOWED ✓
</code></pre>
<p>Now:</p>
<pre><code class="language-text">Frontend Egress = ✓
Backend Ingress = ✓
</code></pre>
<p>The connection works.</p>
<hr />
<h1>31. But do you always need both policies?</h1>
<p>No.</p>
<p>This is an important nuance.</p>
<p>Suppose:</p>
<pre><code class="language-text">Frontend:
  Egress = NOT isolated

Backend:
  Ingress = ALLOW frontend:8080
</code></pre>
<p>Then the connection can work.</p>
<p>Why?</p>
<p>Because the frontend has no applicable Egress isolation, so its egress is allowed by default.</p>
<p>Likewise:</p>
<pre><code class="language-text">Frontend:
  Egress = ALLOW backend:8080

Backend:
  Ingress = NOT isolated
</code></pre>
<p>can also work.</p>
<p>The actual rule is:</p>
<blockquote>
<p>If the source is egress-isolated, its egress must allow the connection.</p>
<p>If the destination is ingress-isolated, its ingress must allow the connection.</p>
</blockquote>
<p>If neither side is isolated, traffic is allowed by default.</p>
<hr />
<h1>32. Return traffic</h1>
<p>Suppose:</p>
<pre><code class="language-text">frontend ─────────&gt; backend
       request
</code></pre>
<p>is allowed.</p>
<p>The backend's response traffic for that connection is permitted as reply traffic.</p>
<p>You do not normally need to create a separate NetworkPolicy rule just to permit the response to an already allowed connection.</p>
<p>Think:</p>
<pre><code class="language-text">frontend                    backend

   ─────── request ────────&gt;
   &lt;────── response ────────

        ONE CONNECTION
</code></pre>
<p>NetworkPolicy is concerned with whether the connection is allowed; it is not normally necessary to model the return packet as an independent application connection.</p>
<hr />
<h1>33. A complete example</h1>
<p>Suppose we have:</p>
<pre><code class="language-text">frontend Pod
app=frontend

backend Pod
app=backend

database Pod
app=database
</code></pre>
<p>We want:</p>
<pre><code class="language-text">frontend → backend:8080
backend  → database:5432
</code></pre>
<p>and we want everything else denied.</p>
<p>A common design is:</p>
<pre><code class="language-text">Default deny
      +
Allow frontend → backend
      +
Allow backend → database
</code></pre>
<h3>Default deny</h3>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
</code></pre>
<h3>Allow frontend to backend</h3>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
</code></pre>
<h3>Allow backend to database</h3>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-database
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 5432
</code></pre>
<p>If backend egress is still denied by the default-deny policy, you would also need an egress allow for backend → database.</p>
<p>For example:</p>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-egress-to-database
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
</code></pre>
<p>Now the flow is:</p>
<pre><code class="language-text">frontend
   │
   │ Egress must be allowed
   ↓
backend
   │
   │ Egress must be allowed
   ↓
database
</code></pre>
<p>And the database must allow the corresponding ingress.</p>
<hr />
<h1>34. A practical way to read any NetworkPolicy</h1>
<p>Whenever you see a NetworkPolicy, read it in this order:</p>
<h2>Step 1 — Who is selected?</h2>
<p>Look at:</p>
<pre><code class="language-yaml">podSelector:
</code></pre>
<p>Ask:</p>
<blockquote>
<p>Which Pods does this policy apply to?</p>
</blockquote>
<hr />
<h2>Step 2 — Which direction?</h2>
<p>Look at:</p>
<pre><code class="language-yaml">policyTypes:
</code></pre>
<p>Ask:</p>
<blockquote>
<p>Is this controlling ingress, egress, or both?</p>
</blockquote>
<hr />
<h2>Step 3 — Who/where is allowed?</h2>
<p>For ingress:</p>
<pre><code class="language-yaml">from:
</code></pre>
<p>Ask:</p>
<blockquote>
<p>Who can connect to these Pods?</p>
</blockquote>
<p>For egress:</p>
<pre><code class="language-yaml">to:
</code></pre>
<p>Ask:</p>
<blockquote>
<p>Where can these Pods connect?</p>
</blockquote>
<hr />
<h2>Step 4 — Which ports?</h2>
<p>Look at:</p>
<pre><code class="language-yaml">ports:
</code></pre>
<p>Ask:</p>
<blockquote>
<p>Which protocol and destination port are allowed?</p>
</blockquote>
<hr />
<h2>Step 5 — Remember the implicit deny</h2>
<p>If the Pod is isolated for that direction:</p>
<pre><code class="language-text">matches a rule → ALLOW
doesn't match   → DENY
</code></pre>
<hr />
<h1>35. Your original policy, translated</h1>
<p>Your original policy was:</p>
<pre><code class="language-yaml">apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-metadata
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: app
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 192.168.121.17/32
    ports:
    - protocol: TCP
      port: 9999
</code></pre>
<p>Read it in plain English:</p>
<blockquote>
<p>Select Pods with <code>app=app</code>.</p>
</blockquote>
<p>Then:</p>
<blockquote>
<p>Isolate them for Egress.</p>
</blockquote>
<p>Then:</p>
<blockquote>
<p>Allow TCP traffic to <code>192.168.121.17</code> on port <code>9999</code>.</p>
</blockquote>
<p>Therefore:</p>
<pre><code class="language-text">app=app Pod

192.168.121.17:9999/TCP     ✓ ALLOW
192.168.121.17:80           ✗ DENY
192.168.121.17:443          ✗ DENY
8.8.8.8:443                 ✗ DENY
another Pod                 ✗ DENY
</code></pre>
<p>So despite its name:</p>
<pre><code class="language-yaml">name: deny-metadata
</code></pre>
<p>the policy is actually <strong>allowing</strong> the metadata destination.</p>
<p>The name does not change policy behavior.</p>
<hr />
<h1>36. If I want to block one specific IP/port, what should I remember?</h1>
<p>Suppose your requirement is:</p>
<pre><code class="language-text">ALLOW everything
EXCEPT
192.168.121.17:9999
</code></pre>
<p>Standard Kubernetes NetworkPolicy does not provide an explicit deny rule for this.</p>
<p>You cannot simply write:</p>
<pre><code class="language-yaml">deny:
- ipBlock:
    cidr: 192.168.121.17/32
  ports:
  - port: 9999
</code></pre>
<p>That field does not exist in the standard NetworkPolicy API.</p>
<p>Instead, standard NetworkPolicy works by defining the traffic that <strong>is allowed</strong>.</p>
<p>This is why a precise "deny one IP + port while allowing everything else" requirement can require careful allow-list design, or a policy engine with capabilities beyond the standard Kubernetes NetworkPolicy API.</p>
<p>We will cover those extended policy engines separately.</p>
<hr />
<h1>37. What NetworkPolicy can target</h1>
<p>For standard Kubernetes NetworkPolicy, keep these three peer mechanisms in your mental model:</p>
<table>
<thead>
<tr>
<th>Selector</th>
<th>What it targets</th>
<th>Typical use</th>
</tr>
</thead>
<tbody><tr>
<td><code>podSelector</code></td>
<td>Pods</td>
<td>frontend → backend</td>
</tr>
<tr>
<td><code>namespaceSelector</code></td>
<td>Namespaces, and therefore Pods in them</td>
<td>allow traffic from another team/namespace</td>
</tr>
<tr>
<td><code>ipBlock</code></td>
<td>IP/CIDR ranges</td>
<td>external networks, specific IPs, CIDRs</td>
</tr>
</tbody></table>
<p>You can also combine <code>namespaceSelector</code> and <code>podSelector</code> in the same peer entry to select specific Pods in selected namespaces.</p>
<hr />
<h1>38. What about Services?</h1>
<p>NetworkPolicy fundamentally operates at the network traffic level, not at the Kubernetes Service object level.</p>
<p>For example, an application may connect to:</p>
<pre><code class="language-text">my-backend.default.svc.cluster.local:8080
</code></pre>
<p>The traffic ultimately reaches a Pod endpoint.</p>
<p>How the Service IP, endpoint IP, NAT, routing, and NetworkPolicy implementation interact can depend on the cluster's networking implementation.</p>
<p>Therefore, don't assume that writing a policy around a Service's virtual IP is always equivalent to writing a policy around the actual Pod endpoints.</p>
<p>For exact behavior involving Services and IP rewriting, test with your actual network plugin.</p>
<hr />
<h1>39. NetworkPolicy requires enforcement support</h1>
<p>Creating a NetworkPolicy object does not magically enforce traffic filtering.</p>
<p>Your cluster must use a network plugin that supports NetworkPolicy enforcement.</p>
<p>For example, the Kubernetes API can accept:</p>
<pre><code class="language-bash">kubectl apply -f network-policy.yaml
</code></pre>
<p>but actual traffic enforcement depends on the networking implementation.</p>
<p>This is why NetworkPolicy behavior can sometimes differ between clusters.</p>
<hr />
<h1>40. The mental model to remember</h1>
<p>If you remember only these rules, you can understand most standard NetworkPolicies.</p>
<h3>Rule 1 — No applicable policy means open</h3>
<pre><code class="language-text">No policy selecting Pod
        ↓
Ingress = allowed
Egress  = allowed
</code></pre>
<h3>Rule 2 — An applicable policy isolates that direction</h3>
<pre><code class="language-text">Ingress policy selects Pod
        ↓
Ingress becomes allow-list
</code></pre>
<pre><code class="language-text">Egress policy selects Pod
        ↓
Egress becomes allow-list
</code></pre>
<h3>Rule 3 — Empty rules mean deny everything for that isolated direction</h3>
<pre><code class="language-text">Ingress isolation
+
no ingress rules
        ↓
DENY ALL INGRESS
</code></pre>
<pre><code class="language-text">Egress isolation
+
no egress rules
        ↓
DENY ALL EGRESS
</code></pre>
<h3>Rule 4 — NetworkPolicy is allow-list based</h3>
<pre><code class="language-text">MATCHES an allowed rule → ALLOW
DOES NOT MATCH          → DENY
</code></pre>
<p>for an isolated direction.</p>
<h3>Rule 5 — Multiple policies are additive</h3>
<pre><code class="language-text">Policy A allows X
Policy B allows Y

Result:
X + Y are allowed
</code></pre>
<h3>Rule 6 — Pod-to-Pod traffic can involve both sides</h3>
<pre><code class="language-text">SOURCE EGRESS ✓
       +
DESTINATION INGRESS ✓
       =
CONNECTION ✓
</code></pre>
<p>when both directions are isolated.</p>
<hr />
<h1>41. The simplest possible picture</h1>
<p>Keep this picture in your head:</p>
<pre><code class="language-text">                         Kubernetes Pod
                              │
                    ┌─────────┴─────────┐
                    │                   │
                 INGRESS              EGRESS
                    │                   │
                 "from"               "to"
                    │                   │
                    ↓                   ↓
             Who can reach me?   Where can I reach?
                    │                   │
                    └─────────┬─────────┘
                              │
                        ALLOW-LIST
                              │
                     Everything else
                         is denied
                    when isolated
</code></pre>
<p>And for peer selection:</p>
<pre><code class="language-text">             from / to
                 │
       ┌─────────┼─────────┐
       │         │         │
       ↓         ↓         ↓
      Pod     Namespace    IP/CIDR
 selector    selector     ipBlock
</code></pre>
<p>That's the standard Kubernetes NetworkPolicy model.</p>
<hr />
<h1>42. Final cheat sheet</h1>
<pre><code class="language-text">podSelector
    ↓
Which Pods does this policy apply to?

policyTypes
    ↓
Which direction becomes isolated?

Ingress
    ↓
Traffic coming INTO selected Pods

Egress
    ↓
Traffic going OUT OF selected Pods

from
    ↓
Ingress source

to
    ↓
Egress destination

podSelector
    ↓
Select Pods

namespaceSelector
    ↓
Select namespaces

ipBlock
    ↓
Select IP/CIDR ranges

ports
    ↓
Restrict protocol + destination port

No applicable policy
    ↓
Traffic allowed by default

Applicable policy + no matching rule
    ↓
Traffic denied for that isolated direction

Multiple policies
    ↓
Allowed traffic is combined additively

Explicit deny
    ↓
Not part of the standard Kubernetes NetworkPolicy API
</code></pre>
<hr />
<h2>Official Kubernetes sources</h2>
<p>This article intentionally uses the standard Kubernetes NetworkPolicy model and official Kubernetes documentation as the source of truth.</p>
<ul>
<li><p>Kubernetes Network Policies: <a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/">https://kubernetes.io/docs/concepts/services-networking/network-policies/</a></p>
</li>
<li><p>Kubernetes NetworkPolicy API reference: <a href="https://kubernetes.io/docs/reference/kubernetes-api/networking/network-policy-v1/">https://kubernetes.io/docs/reference/kubernetes-api/networking/network-policy-v1/</a></p>
</li>
</ul>
<p>The official API reference defines <code>podSelector</code>, <code>policyTypes</code>, <code>ingress</code>, <code>egress</code>, <code>podSelector</code>, <code>namespaceSelector</code>, <code>ipBlock</code>, ports, and the additive behavior of applicable policies.</p>
<blockquote>
<p><strong>Note:</strong> NetworkPolicy enforcement requires a network plugin that supports NetworkPolicy. Exact behavior around source/destination IP rewriting, Services, load balancers, and other networking features can depend on the cluster's networking implementation.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[How to Sync Google Workspace Users to AWS Identity Center using ssosync]]></title><description><![CDATA[Introduction
If you are moving to AWS Identity Center (formerly SSO) and want to keep your Google Workspace users and groups in sync, ssosync is the go-to tool. However, setting up the permissions can be tricky.
If you’ve seen the error unauthorized_...]]></description><link>https://blog.akashpawar.com/how-to-sync-google-workspace-users-to-aws-identity-center-using-ssosync</link><guid isPermaLink="true">https://blog.akashpawar.com/how-to-sync-google-workspace-users-to-aws-identity-center-using-ssosync</guid><category><![CDATA[ssosync]]></category><category><![CDATA[awsgooglesso]]></category><category><![CDATA[ #AWSIdentityCenter]]></category><category><![CDATA[#Google workspace Admin]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Sun, 01 Feb 2026 17:17:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769965229699/a01a84b2-f481-4fe6-83f7-28a5a8338d8e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>If you are moving to AWS Identity Center (formerly SSO) and want to keep your Google Workspace users and groups in sync, <a target="_blank" href="https://github.com/awslabs/ssosync"><code>ssosync</code></a> is the go-to tool. However, setting up the permissions can be tricky.</p>
<p>If you’ve seen the error <code>unauthorized_client: Client is unauthorized to retrieve access tokens</code>, you are likely missing a specific scope or delegation step.</p>
<p>Here is the easy way to set it up from scratch.</p>
<h2 id="heading-step-1-create-your-google-cloud-credentials">Step 1: Create your Google Cloud Credentials</h2>
<ol>
<li><p>Go to the <a target="_blank" href="https://console.cloud.google.com/">Google Cloud Console</a>.</p>
</li>
<li><p>Create a <strong>New Project</strong> (e.g., "AWS-SSO-Sync").</p>
</li>
<li><p>Enable the <strong>Admin SDK API</strong>: Search for it in the Library and click <strong>Enable</strong>.</p>
</li>
<li><p>Create a <strong>Service Account</strong>:</p>
<ul>
<li><p>Go to <strong>IAM &amp; Admin &gt; Service Accounts</strong>.</p>
</li>
<li><p>Create an account, name it <code>ssosync-service-account</code>.</p>
</li>
<li><p>Once created, click on the account, go to the <strong>Keys</strong> tab, and <strong>Add Key &gt; Create new key (JSON)</strong>.</p>
</li>
<li><p><strong>Save this file!</strong> You will need to base64 encode this for your <code>.env</code> file.</p>
</li>
<li><pre><code class="lang-bash">    <span class="hljs-comment"># Replace file name `ssosync-486114-bb1ce7f2ebb9.json` with your file name.</span>
    cat ssosync-486114-bb1ce7f2ebb9.json | base64 | tr -d <span class="hljs-string">"\n"</span> | pbcopy

    <span class="hljs-comment"># And result of this we need to store in GOOGLE_CREDENTIALS_JSON variable in .env file</span>
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Copy the Unique ID:</strong> Back on the Service Account details page, copy the <strong>Unique ID</strong> (a long string of numbers). You'll need this for the next step.</p>
</li>
</ol>
<h2 id="heading-step-2-grant-domain-wide-delegation">Step 2: Grant "Domain-Wide Delegation"</h2>
<p>This is the part most people miss. You have to tell Google Workspace that this Service Account is allowed to "read" your organization's data.</p>
<ol>
<li><p>Open the <a target="_blank" href="https://admin.google.com/">Google Admin Console</a>.</p>
</li>
<li><p>Go to <strong>Security &gt; Access and data control &gt; API controls</strong>.</p>
</li>
<li><p>Click <strong>Manage Domain Wide Delegation</strong>.</p>
</li>
<li><p>Click <strong>Add new</strong> and enter:</p>
<ul>
<li><p><strong>Client ID:</strong> Paste the <em>Unique ID</em> you copied in Step 1.</p>
</li>
<li><p><strong>OAuth Scopes:</strong> Copy and paste this exact list: <code>https://www.googleapis.com/auth/admin.directory.group.readonly, https://www.googleapis.com/auth/admin.directory.group.member.readonly, https://www.googleapis.com/auth/admin.directory.user.readonly</code></p>
</li>
</ul>
</li>
<li><p>Click <strong>Authorize</strong>.</p>
</li>
</ol>
<h2 id="heading-step-3-get-your-aws-scim-details">Step 3: Get your AWS SCIM Details</h2>
<ol>
<li><p>Open the <strong>AWS Identity Center</strong> console.</p>
</li>
<li><p>Go to <strong>Settings &gt; Provisioning</strong>.</p>
</li>
<li><p>Enable <strong>Automatic Provisioning</strong>.</p>
</li>
<li><p>Copy the <strong>SCIM endpoint</strong> and the <strong>Access token</strong>. (Keep the token safe; it only shows once!)</p>
</li>
</ol>
<h2 id="heading-step-4-configure-your-environment">Step 4: Configure your Environment</h2>
<p>Create a <code>.env</code> file. To get your <code>GOOGLE_CREDENTIALS_JSON</code>, run <code>cat your-key.json | base64</code> in your terminal and paste the result.</p>
<p>Bash</p>
<pre><code class="lang-plaintext"># .env example

# 1. Base64 encoded service account JSON key
GOOGLE_CREDENTIALS_JSON=your_base64_string_here

# 2. A real Admin email address from your Google Workspace
GOOGLE_USER=admin@yourdomain.com

# 3. AWS SCIM Details
SCIM_ENDPOINT=https://scim.us-east-1.amazonaws.com/xxx/scim/v2/
SCIM_TOKEN=your_secret_token

# 4. AWS Identity Store &amp; Region
AWS_IDENTITY_STORE_ID=d-123456789
AWS_DEFAULT_REGION=us-east-1

# 5. AWS Access Keys (to talk to the AWS API)
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
</code></pre>
<h2 id="heading-step-5-run-the-sync">Step 5: Run the Sync</h2>
<p>Now you can run the sync command. Bash</p>
<pre><code class="lang-plaintext">ssosync \
  -e $SCIM_ENDPOINT \
  -t $SCIM_TOKEN \
  -u $GOOGLE_USER \
  -i $AWS_IDENTITY_STORE_ID \
  -r $AWS_DEFAULT_REGION \
  --debug
</code></pre>
<blockquote>
<p><strong>Note</strong> : You can refer official github repo <a target="_blank" href="https://github.com/awslabs/ssosync">ssosync</a> to know about more sync commands</p>
</blockquote>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Result :</div>
</div>

<p>Groups in Google Workspace</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769965379346/6fecd67b-2a02-4949-85f4-081c4e557eb1.png" alt class="image--center mx-auto" /></p>
<p>Groups In AWS Identity Center</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769965454701/0d866e10-0071-4c3c-89e9-fcfdb838ca14.png" alt class="image--center mx-auto" /></p>
<p>Admin Group Members in Google Workspace</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769965536940/119a75c4-8745-452e-a75d-3401701b2a3c.png" alt class="image--center mx-auto" /></p>
<p>Admin Group Members in AWS Identity Center</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769965577649/aa78c86a-995a-4e4a-9ba8-1bdeb06aa51c.png" alt class="image--center mx-auto" /></p>
<p>Dev Group Members in Google Workspace</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769965636686/31f4d302-619d-408a-ad87-e2616c937283.png" alt class="image--center mx-auto" /></p>
<p>Dev Group Members in AWS Identity Center</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769965665881/bb3f1cd7-8a86-4a93-aeb4-a9d27dc79a38.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-why-this-works">Why this works:</h3>
<p>By adding the <code>admin.directory.group.readonly</code> and <code>admin.directory.user.readonly</code> scopes in the Google Admin panel, you are explicitly giving <code>ssosync</code> the permission to "look" at your users. Without this, Google rejects the connection (401 Unauthorized), even if your password/key is correct!</p>
]]></content:encoded></item><item><title><![CDATA[Configure AWS-IAM Identity Center with Google Workspace]]></title><description><![CDATA[Introduction
In this tutorial, I will guide you through establishing a SAML connection between Google Workspace and AWS Identity Center. Later, you will synchronize users from Google Workspace using SCIM. To verify that everything is configured corre...]]></description><link>https://blog.akashpawar.com/configure-aws-iam-identity-center-with-google-workspace</link><guid isPermaLink="true">https://blog.akashpawar.com/configure-aws-iam-identity-center-with-google-workspace</guid><category><![CDATA[aws google sync]]></category><category><![CDATA[awsssosync]]></category><category><![CDATA[AWS IAM Identity Center]]></category><category><![CDATA[Google Workspace]]></category><category><![CDATA[Devops]]></category><category><![CDATA[AWS]]></category><category><![CDATA[solutionarchitect]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Sun, 01 Feb 2026 12:54:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769948860562/5852a993-cb17-428e-bdec-d1e6fafb5ff7.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction"><strong>Introduction</strong></h3>
<p>In this tutorial, I will guide you through establishing a SAML connection between Google Workspace and AWS Identity Center. Later, you will synchronize users from Google Workspace using SCIM. To verify that everything is configured correctly, you will sign in as a Google Workspace user and verify access to AWS resources. This tutorial is based on a small Google Workspace directory test environment and does not include directory structures such as groups and organisational units. After completing this tutorial, your users will be able to access the AWS access portal using their Google Workspace credentials by altering your Identity Center to an external identity provider.</p>
<h3 id="heading-understanding-iam-identity-center"><strong>Understanding IAM Identity Center</strong></h3>
<p>IAM Identity Center is the recommended AWS service for managing human user access to AWS resources. It provides a <strong>single place to manage users, groups, and consistent access</strong> to multiple AWS accounts and applications. The best part is that IAM Identity Center is <strong><em>offered at no additional cost.</em></strong></p>
<h3 id="heading-why-integrate-google-workspace-with-iam-identity-center"><strong>Why Integrate Google Workspace with IAM Identity Center?</strong></h3>
<p>Integrating Google Workspace with AWS SSO streamlines user management and enhances security. It allows for seamless single sign-on, reducing the need for multiple passwords and improving user convenience. Additionally, it centralizes access control, making it easier to enforce security policies and manage permissions across both platforms.</p>
<h3 id="heading-how-it-works"><strong>How It Works</strong></h3>
<p>User information from Google Workspace is synchronized into IAM Identity Center using the <strong>System for Cross-domain Identity Management (SCIM) v2.0 protocol</strong>. You configure this connection in Google Workspace using your SCIM endpoint for IAM Identity Center and an IAM Identity Center bearer token. This involves setting up Google Workspace as an IAM identity provider and an IAM Identity Center identity provider.</p>
<blockquote>
<p><strong><em>Prerequisites</em></strong></p>
</blockquote>
<ol>
<li><p>Access to the Google Workspace admin portal to configure the SAML app.</p>
</li>
<li><p>Access to IAM Identity Center.</p>
</li>
</ol>
<blockquote>
<p><strong><em>Considerations</em></strong></p>
</blockquote>
<ol>
<li><p>Before configuring SCIM provisioning, review the considerations listed <a target="_blank" href="https://docs.aws.amazon.com/singlesignon/latest/userguide/provision-automatically.html#auto-provisioning-considerations">here.</a></p>
</li>
<li><p>Currently SCIM automatic synchronisation from Google Workspace is <strong>limited to user provisioning</strong>. Automatic group provisioning is not supported at this time. Group creation and user management will be covered in the next tutorial.</p>
</li>
</ol>
<h2 id="heading-steps-to-configure-the-saml-application"><strong>Steps to Configure the SAML Application</strong></h2>
<h3 id="heading-step-1-configure-the-saml-application-in-google-workspace"><strong>Step 1: Configure the SAML Application in Google Workspace</strong></h3>
<ul>
<li><p>Sign in to your <a target="_blank" href="https://admin.google.com/">Google Admin Console</a> using an account with administrator permissions.</p>
</li>
<li><p>Navigate to <strong>Apps → Web and Mobile Applications</strong>.</p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*BWS46xDv1D2VQMfpRRFCXQ.png" alt /></p>
<ul>
<li><p>From the Add App dropdown, search for “<strong>Amazon Web Services</strong>” and select the Amazon Web Services (SAML) app from the list.</p>
</li>
<li><p>Download the <strong>IdP metadata.</strong></p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*QHtL9bc-uvDXNAtjslRaJg.png" alt /></p>
<ul>
<li>Leave this page open and move to the IAM Identity Center console.</li>
</ul>
<h3 id="heading-step-2-enable-iam-identity-center"><strong>Step 2: Enable IAM Identity Center</strong></h3>
<ul>
<li><p>Go to the <strong>IAM Identity Center</strong> console page and select the <strong>Enable</strong> button if it is not enabled.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769950302273/304c9613-dac1-4476-988e-68e41516db17.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<ul>
<li><p>Select your organization to allow access to multiple AWS accounts using IAM Identity Center.</p>
</li>
<li><p>After a few minutes, the IAM Identity Center will be ready to use.</p>
</li>
<li><p>In the left navigation pane, choose Settings.</p>
</li>
<li><p>On the Settings page, choose Actions and then <strong>Change Identity Source</strong>.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769950048106/d5b2c61f-caa4-497f-839e-d8fba1292f27.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<ul>
<li><p>On the Choose Identity Source page, select <strong>External Identity Provider</strong>, and then choose Next.</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769950072356/635d76ab-edda-466d-b68d-b3a61744102c.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<ul>
<li><p>On the Configure External Identity Provider page, complete the following:</p>
</li>
<li><p><strong>Upload the Google SAML metadata as the IdP SAML metadata</strong> in the IAM Identity Center console.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769950123182/c1d9c222-7100-4c36-b0ea-21b378cee334.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Confirm the change and click Next, then <strong>ACCEPT</strong> the Change request.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769950156631/6b7153c2-3279-40bc-b68d-87966d0f17ab.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<ul>
<li><p>After providing the Google metadata, copy the AWS access portal sign-in URL, IAM Identity Assertion Consumer Service (ACS) URL, and IAM Identity Center issuer URL. Provide these URLs in the Google Admin console.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769950175967/bdef85f3-96f6-446d-a7ea-0e7e4824fed7.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<ul>
<li><p>On the next page, you will get the SAML Authentication metadata.</p>
</li>
<li><p>Map the AWS URLs in Google Workspace.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769949976020/8801d8cd-a707-48e8-abe9-46abe7ae250c.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<ul>
<li><p>On the Service Provider Details page, complete the fields under Name ID:</p>
</li>
<li><p>For Name ID format, select <strong>EMAIL</strong>.</p>
</li>
<li><p>For Name ID, select Basic Information &gt; <strong>Department</strong></p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*WoeWzyGqPWuE-2pUkYUc9A.png" alt /></p>
<ul>
<li><p>Choose <strong>Finish</strong>.</p>
</li>
<li><p>On the Attribute Mapping page, choose ADD MAPPING and configure these fields under Google Directory attribute.</p>
</li>
<li><p>Choose Finish.</p>
</li>
</ul>
<h3 id="heading-step-3-google-workspace-enable-the-app"><strong>Step 3: Google Workspace: Enable the App</strong></h3>
<ul>
<li><p>Return to the Google Admin Console and locate the AWS IAM Identity Center application under <strong>Apps → Web and Mobile Apps</strong>.</p>
</li>
<li><p>In the User Access panel, expand <strong>User Access</strong> to display the Service Status panel.</p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*9K1uO16j0IhDO8LICThM5g.png" alt /></p>
<ul>
<li>In the Service Status panel, choose <strong>ON for everyone</strong>, and then choose <strong>SAVE</strong>.</li>
</ul>
<h3 id="heading-step-4-set-up-iam-identity-center-automatic-provisioning"><strong>Step 4: Set Up IAM Identity Center Automatic Provisioning</strong></h3>
<ul>
<li><p>Return to the IAM Identity Center console.</p>
</li>
<li><p>On the Settings page, enable <strong>Automatic provisioning</strong>.</p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*CyHicG1g0eIFOI7K9BsATA.png" alt /></p>
<ul>
<li>Copy the <strong>SCIM endpoint and Access token information</strong> displayed.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*EGY7HUIyHMLhN6gK5_3EXg.png" alt /></p>
<ul>
<li>Close the dialog box.</li>
</ul>
<h3 id="heading-step-5-configure-auto-provisioning-on-google-workspace"><strong>Step 5: Configure Auto Provisioning on Google Workspace</strong></h3>
<ul>
<li><p>Return to the Google Admin Console, Select <strong>Apps → Web and Mobile Apps → Amazon Web Services (SAML)</strong></p>
</li>
<li><p>In the Auto Provisioning section, choose <strong>Configure Auto Provisioning.</strong></p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*tQfYJip8zjXABZAd37bZXg.png" alt /></p>
<ul>
<li><p>Paste the <strong>Access token</strong> and <strong>SCIM endpoint</strong> values copied earlier.</p>
</li>
<li><p>Verify that all mandatory IAM Identity Center attributes are mapped to Google Cloud Directory attributes.</p>
</li>
<li><p>In the <strong>Provisioning Scope</strong> section, optionally choose a group to provide access to the Amazon Web Services app.</p>
</li>
<li><p>In the Deprovisioning section, specify how to respond to different events that remove access from a user.</p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*vURapLGfWPH_e8Xkn-Tt-g.png" alt /></p>
<ul>
<li><p>Choose <strong>Finish</strong> and <strong>turn on the auto-provisioning toggle</strong> switch.</p>
</li>
<li><p>To verify user synchronization, return to the IAM Identity Center console → Users.</p>
</li>
</ul>
<h3 id="heading-access-aws-management-console-using-google-workspace"><strong>Access AWS Management Console using Google Workspace</strong></h3>
<ul>
<li><p>Go to IAM Identity Center → Settings → Identity Source</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769949916913/709de7ba-3662-48f6-adde-f2e1b66e938f.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<ul>
<li><p>Click on the AWS access portal URL</p>
</li>
<li><p>This will open a window and then you can select your Google Workspace ID.</p>
</li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>You have successfully set up a SAML connection between Google Workspace and AWS and verified that automatic provisioning is working.</p>
]]></content:encoded></item><item><title><![CDATA[NVIDIA Tesla GPU Scheduling: From HPC to Kubernetes with Volcano MLOps]]></title><description><![CDATA[When You Need Gang Scheduling
Applications that require coordinated multi-pod execution:

Distributed ML Training: Multi-GPU model training (PyTorch DDP, TensorFlow Distributed)

High-Performance Computing: Weather simulation, molecular dynamics

Par...]]></description><link>https://blog.akashpawar.com/nvidia-tesla-gpu-scheduling-from-hpc-to-kubernetes-with-volcano-mlops</link><guid isPermaLink="true">https://blog.akashpawar.com/nvidia-tesla-gpu-scheduling-from-hpc-to-kubernetes-with-volcano-mlops</guid><category><![CDATA[batch-jobs]]></category><category><![CDATA[ml-infrastructure]]></category><category><![CDATA[gang-scheduling]]></category><category><![CDATA[akash pawar]]></category><category><![CDATA[akashpawar]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[NVIDIA]]></category><category><![CDATA[AWS]]></category><category><![CDATA[mlops]]></category><category><![CDATA[GPU]]></category><category><![CDATA[distributed tracing]]></category><category><![CDATA[spot instances]]></category><category><![CDATA[Volcano]]></category><category><![CDATA[GPU optimization]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Sat, 26 Jul 2025 07:53:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753514462685/8b28d887-ca41-4f02-b1d9-46f2ac2b6a55.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-when-you-need-gang-scheduling">When You Need Gang Scheduling</h2>
<p><strong>Applications that require coordinated multi-pod execution:</strong></p>
<ul>
<li><p><strong>Distributed ML Training</strong>: Multi-GPU model training (PyTorch DDP, TensorFlow Distributed)</p>
</li>
<li><p><strong>High-Performance Computing</strong>: Weather simulation, molecular dynamics</p>
</li>
<li><p><strong>Parallel Data Processing</strong>: Large-scale ETL with coordinated workers</p>
</li>
<li><p><strong>Multi-node Databases</strong>: Distributed database initialization</p>
</li>
</ul>
<p><strong>The Pain Point</strong>: Traditional Kubernetes schedules pods individually. For a 4-GPU training job:</p>
<ul>
<li><p>Pod 1 starts → claims 1 GPU</p>
</li>
<li><p>Pods 2-4 wait indefinitely for resources</p>
</li>
<li><p><strong>Result</strong>: $8/hour burning while GPUs sit idle</p>
</li>
</ul>
<p><strong>Our Demo</strong>: Simulates distributed TensorFlow training requiring 2 GPUs across 2 nodes.</p>
<p><strong>Repository</strong>: <a target="_blank" href="https://github.com/akash202k/nvidia-gpu-volcano-k8s">nvidia-gpu-volcano-k8s</a> ⭐</p>
<hr />
<h2 id="heading-the-problem">The Problem</h2>
<p>Traditional Kubernetes scheduling with expensive GPU workloads:</p>
<pre><code class="lang-plaintext">Job needs: 2 GPUs total
Available: 2 GPUs across 2 nodes

❌ Standard Scheduler:
Pod 1: Scheduled immediately (claims 1 GPU)
Pod 2: Waits for "sufficient resources" 
Result: 1 GPU idle, job fails

✅ Volcano Gang Scheduler: 
Both pods: Wait until 2 GPUs available
Then: Start simultaneously
Result: Efficient resource utilization
</code></pre>
<hr />
<h2 id="heading-implementation">Implementation</h2>
<h3 id="heading-1-gpu-node-setup">1. GPU Node Setup</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># GPU-enabled AMI with pre-installed NVIDIA drivers</span>
eksctl create nodegroup \
  --node-type g4dn.xlarge \
  --nodes 2 --spot \
  --node-ami AL2_x86_64_GPU
</code></pre>
<h3 id="heading-2-gpu-support">2. GPU Support</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Install NVIDIA device plugin</span>
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.1/nvidia-device-plugin.yml

<span class="hljs-comment"># Setup GPU node taints</span>
chmod +x gpu_node_setup.sh
./gpu_node_setup.sh
</code></pre>
<p>From <code>gpu_node_setup.sh</code>:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Setting up GPU nodes for Volcano demo..."</span>

<span class="hljs-comment"># Get GPU node names</span>
GPU_NODES=$(kubectl get nodes --no-headers | grep g4dn | awk <span class="hljs-string">'{print $1}'</span>)

<span class="hljs-comment"># Apply standard NVIDIA GPU taints</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Applying NVIDIA GPU taints..."</span>
<span class="hljs-keyword">for</span> node <span class="hljs-keyword">in</span> <span class="hljs-variable">$GPU_NODES</span>; <span class="hljs-keyword">do</span>
    kubectl taint nodes <span class="hljs-variable">$node</span> nvidia.com/gpu=present:NoSchedule --overwrite
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Tainted node: <span class="hljs-variable">$node</span>"</span>
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-3-install-volcano">3. Install Volcano</h3>
<pre><code class="lang-bash">helm repo add volcano-sh https://volcano-sh.github.io/helm-charts
helm install volcano volcano-sh/volcano -n volcano-system --create-namespace
</code></pre>
<h3 id="heading-4-gang-scheduled-job">4. Gang Scheduled Job</h3>
<p>From <code>manifests/tensorflow-job.yaml</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">batch.volcano.sh/v1alpha1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Job</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">gang-tf-job</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">minAvailable:</span> <span class="hljs-number">2</span>          <span class="hljs-comment"># Critical: Both pods or none</span>
  <span class="hljs-attr">schedulerName:</span> <span class="hljs-string">volcano</span>   <span class="hljs-comment"># Use Volcano instead of default</span>
  <span class="hljs-attr">queue:</span> <span class="hljs-string">ml-queue</span>
  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">replicas:</span> <span class="hljs-number">2</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">trainer</span>
      <span class="hljs-attr">template:</span>
        <span class="hljs-attr">spec:</span>
          <span class="hljs-attr">tolerations:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"nvidia.com/gpu"</span>
              <span class="hljs-attr">operator:</span> <span class="hljs-string">"Exists"</span>
              <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoSchedule"</span>
          <span class="hljs-attr">affinity:</span>
            <span class="hljs-attr">nodeAffinity:</span>
              <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span>
                <span class="hljs-attr">nodeSelectorTerms:</span>
                <span class="hljs-bullet">-</span> <span class="hljs-attr">matchExpressions:</span>
                  <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"node.kubernetes.io/instance-type"</span>
                    <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span>
                    <span class="hljs-attr">values:</span> [<span class="hljs-string">"g4dn.xlarge"</span>, <span class="hljs-string">"g4dn.2xlarge"</span>, <span class="hljs-string">"g5.xlarge"</span>]
          <span class="hljs-attr">containers:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">trainer</span>
              <span class="hljs-attr">image:</span> <span class="hljs-string">akash202k/tf-volcano-demo:v1</span>
              <span class="hljs-attr">resources:</span>
                <span class="hljs-attr">requests:</span>
                  <span class="hljs-attr">cpu:</span> <span class="hljs-string">"2"</span>
                  <span class="hljs-attr">memory:</span> <span class="hljs-string">"4Gi"</span>
                  <span class="hljs-attr">nvidia.com/gpu:</span> <span class="hljs-number">1</span>
                <span class="hljs-attr">limits:</span>
                  <span class="hljs-attr">cpu:</span> <span class="hljs-string">"2"</span>
                  <span class="hljs-attr">memory:</span> <span class="hljs-string">"4Gi"</span>
                  <span class="hljs-attr">nvidia.com/gpu:</span> <span class="hljs-number">1</span>
</code></pre>
<hr />
<h2 id="heading-demo-pain-point-simulation">Demo: Pain Point Simulation</h2>
<h3 id="heading-normal-gang-scheduling">Normal Gang Scheduling</h3>
<pre><code class="lang-bash">kubectl apply -f manifests/volcano-queue.yaml
kubectl apply -f manifests/tensorflow-job.yaml
kubectl get pods -l volcano.sh/job-name=gang-tf-job -w
</code></pre>
<p><strong>Result</strong>: Both pods start simultaneously ✅</p>
<h3 id="heading-resource-contention-the-pain-point">Resource Contention (The Pain Point)</h3>
<p>From <code>manifests/gpu-blocker-pod.yaml</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">blocker</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">tolerations:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"nvidia.com/gpu"</span>
      <span class="hljs-attr">operator:</span> <span class="hljs-string">"Exists"</span>
      <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoSchedule"</span>
  <span class="hljs-attr">containers:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">stress</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">nvidia/cuda:11.0-base</span>
      <span class="hljs-attr">command:</span> [<span class="hljs-string">"/bin/sh"</span>]
      <span class="hljs-attr">args:</span> [<span class="hljs-string">"-c"</span>, <span class="hljs-string">"sleep 60"</span>]
      <span class="hljs-attr">resources:</span>
        <span class="hljs-attr">requests:</span>
          <span class="hljs-attr">nvidia.com/gpu:</span> <span class="hljs-number">1</span>
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment"># Simulate real-world contention - block 1 GPU</span>
kubectl apply -f manifests/gpu-blocker-pod.yaml

<span class="hljs-comment"># Deploy gang job (needs 2 GPUs, only 1 available)</span>
kubectl apply -f manifests/tensorflow-job.yaml
</code></pre>
<p><strong>Traditional scheduler would</strong>: Start 1 pod, waste resources<br /><strong>Volcano gang scheduler</strong>: Keeps both pods <code>Pending</code> until sufficient resources ✅</p>
<h3 id="heading-resource-release">Resource Release</h3>
<pre><code class="lang-bash">kubectl delete pod blocker
</code></pre>
<p><strong>Result</strong>: Both pods start together immediately ✅</p>
<hr />
<h2 id="heading-results-achieved">Results Achieved</h2>
<h3 id="heading-gang-scheduling-success">Gang Scheduling Success</h3>
<pre><code class="lang-bash">NAME                    READY   STATUS      RESTARTS   AGE
gang-tf-job-trainer-0   0/1     Completed   0          3m46s
gang-tf-job-trainer-1   0/1     Completed   0          3m46s
</code></pre>
<h3 id="heading-gpu-training-confirmed">GPU Training Confirmed</h3>
<p>From our actual training logs in <code>logs/volcano_training_success_20250726_045527_d4ead352.json</code>:</p>
<p><strong>Pod 1 (ap-southeast-1c)</strong>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"aws_metadata"</span>: {
    <span class="hljs-attr">"instance_id"</span>: <span class="hljs-string">"i-0bd485d90b62fcc01"</span>,
    <span class="hljs-attr">"instance_type"</span>: <span class="hljs-string">"g4dn.xlarge"</span>,
    <span class="hljs-attr">"availability_zone"</span>: <span class="hljs-string">"ap-southeast-1c"</span>
  },
  <span class="hljs-attr">"gpu_info"</span>: {
    <span class="hljs-attr">"gpu_details"</span>: [{
      <span class="hljs-attr">"details"</span>: {
        <span class="hljs-attr">"compute_capability"</span>: [<span class="hljs-number">7</span>, <span class="hljs-number">5</span>],
        <span class="hljs-attr">"device_name"</span>: <span class="hljs-string">"Tesla T4"</span>
      }
    }],
    <span class="hljs-attr">"nvidia_smi"</span>: [{
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Tesla T4"</span>,
      <span class="hljs-attr">"memory_total_mb"</span>: <span class="hljs-string">"15360"</span>,
      <span class="hljs-attr">"memory_used_mb"</span>: <span class="hljs-string">"103"</span>,
      <span class="hljs-attr">"utilization_percent"</span>: <span class="hljs-string">"0"</span>,
      <span class="hljs-attr">"temperature_c"</span>: <span class="hljs-string">"26"</span>
    }]
  },
  <span class="hljs-attr">"training_results"</span>: {
    <span class="hljs-attr">"device_used"</span>: <span class="hljs-string">"/GPU:0"</span>,
    <span class="hljs-attr">"final_loss"</span>: <span class="hljs-number">0.08402693271636963</span>,
    <span class="hljs-attr">"status"</span>: <span class="hljs-string">"success"</span>
  }
}
</code></pre>
<p><strong>Pod 2 (ap-southeast-1b)</strong> from <code>logs/volcano_training_success_20250726_045528_65078d2b.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"aws_metadata"</span>: {
    <span class="hljs-attr">"instance_id"</span>: <span class="hljs-string">"i-0edff736d5e7d3a59"</span>,
    <span class="hljs-attr">"instance_type"</span>: <span class="hljs-string">"g4dn.xlarge"</span>, 
    <span class="hljs-attr">"availability_zone"</span>: <span class="hljs-string">"ap-southeast-1b"</span>
  },
  <span class="hljs-attr">"training_results"</span>: {
    <span class="hljs-attr">"device_used"</span>: <span class="hljs-string">"/GPU:0"</span>,
    <span class="hljs-attr">"final_loss"</span>: <span class="hljs-number">0.08474162966012955</span>,
    <span class="hljs-attr">"status"</span>: <span class="hljs-string">"success"</span>
  }
}
</code></pre>
<p><strong>Perfect coordination</strong>: Both pods trained on separate Tesla T4 GPUs simultaneously across different AZs.</p>
<hr />
<h2 id="heading-pain-point-solved">Pain Point Solved</h2>
<h3 id="heading-why-distributed-training-needs-gang-scheduling">Why Distributed Training Needs Gang Scheduling</h3>
<p><strong>Single Machine Limitations</strong>:</p>
<ul>
<li><p>Large models can't fit in single GPU memory (e.g., LLaMA, GPT models)</p>
</li>
<li><p>Training time becomes prohibitive (weeks vs days)</p>
</li>
<li><p>Memory constraints limit batch sizes and model complexity</p>
</li>
</ul>
<p><strong>Distributed Training Requirements</strong>:</p>
<ul>
<li><p><strong>All workers must start together</strong> for synchronized gradient updates</p>
</li>
<li><p><strong>Coordinated parameter sharing</strong> across nodes</p>
</li>
<li><p><strong>Consistent training state</strong> - partial deployments corrupt the training process</p>
</li>
</ul>
<h3 id="heading-before-gang-scheduling">Before Gang Scheduling</h3>
<ul>
<li><p><strong>Resource waste</strong>: Partial deployments burn GPU time while waiting for missing workers</p>
</li>
<li><p><strong>Training failures</strong>: Incomplete worker allocation breaks distributed algorithms</p>
</li>
<li><p><strong>Cost inefficiency</strong>: $2-3/hour per idle GPU waiting for coordination</p>
</li>
<li><p><strong>Model corruption</strong>: Partial worker sets produce invalid gradients</p>
</li>
</ul>
<h3 id="heading-after-gang-scheduling">After Gang Scheduling</h3>
<ul>
<li><p><strong>Resource efficiency</strong>: 100% GPU utilization across all workers simultaneously</p>
</li>
<li><p><strong>Training reliability</strong>: All workers start together, ensuring proper distributed training</p>
</li>
<li><p><strong>Cost control</strong>: No GPU time wasted on incomplete worker deployments</p>
</li>
<li><p><strong>Model integrity</strong>: Consistent distributed training with complete worker sets</p>
</li>
</ul>
<p><strong>Demo Economics</strong>: $0.16 for complete coordinated training vs. potential hours of idle GPU costs waiting for worker coordination.</p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>For distributed GPU workloads requiring multiple nodes, gang scheduling isn't optional—it's <strong>mandatory for functional training</strong>.</p>
<p><strong>Single machine can't handle</strong>: Modern ML workloads requiring distributed computation<br /><strong>Volcano delivers</strong>: Coordinated multi-node scheduling that ensures training actually works</p>
<p><strong>Repository</strong>: <a target="_blank" href="https://github.com/akash202k/nvidia-gpu-volcano-k8s">nvidia-gpu-volcano-k8s</a> ⭐</p>
]]></content:encoded></item><item><title><![CDATA[Speed Up Your Azure Web App Deployment with WEBSITE_RUN_FROM_PACKAGE]]></title><description><![CDATA[Quick Solution

If you're in a hurry to solve deployment time issues, simply add the WEBSITE_RUN_FROM_PACKAGE environment variable to your Azure Web App settings and set its value to 1. This change can significantly reduce your deployment times.

 
W...]]></description><link>https://blog.akashpawar.com/speed-up-your-azure-web-app-deployment-with-websiterunfrompackage</link><guid isPermaLink="true">https://blog.akashpawar.com/speed-up-your-azure-web-app-deployment-with-websiterunfrompackage</guid><category><![CDATA[WEBSITE_RUN_FROM_PACKAGE]]></category><category><![CDATA[Faster Azure deployments]]></category><category><![CDATA[Azure web app settings]]></category><category><![CDATA[Speed up Azure Web App]]></category><category><![CDATA[Optimize Azure Web App deployment]]></category><category><![CDATA[how to Optimize Azure Web App deployment]]></category><category><![CDATA[Speed Up Your Azure Web App Deployment with WEBSITE_RUN_FROM_PACKAGE]]></category><category><![CDATA[How to Implement WEBSITE_RUN_FROM_PACKAGE]]></category><category><![CDATA[Set the WEBSITE_RUN_FROM_PACKAGE Setting]]></category><category><![CDATA[Reduced Risk of File Corruption in azure webapp deployment]]></category><category><![CDATA[azure webapp Deployment best practices]]></category><category><![CDATA[Azure Web App Optimization]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Thu, 13 Jun 2024 07:18:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718262521837/d7dc79fa-8539-46ec-99d2-10fc6bbf4368.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-quick-solution">Quick Solution</h3>
<blockquote>
<p>If you're in a hurry to solve deployment time issues, simply add the <code>WEBSITE_RUN_FROM_PACKAGE</code> environment variable to your Azure Web App settings and set its value to <code>1</code>. This change can significantly reduce your deployment times.</p>
</blockquote>
<h3 id="heading-ia"> </h3>
<h3 id="heading-why-slow-deployment-in-azure-webapp">Why slow deployment in azure webapp ?</h3>
<p>When deploying applications to Azure Web Apps without the <code>WEBSITE_RUN_FROM_PACKAGE</code> setting, the process typically involves:</p>
<ol>
<li><p><strong>Uploading Files</strong>: Your deployment pipeline uploads the application files to the Azure Web App.</p>
</li>
<li><p><strong>Extracting Files</strong>: If the application is uploaded as a compressed package (like a ZIP file), the web app service extracts the contents to the file system.</p>
</li>
<li><p><strong>Restarting the Web App</strong>: The web app might need to restart to pick up the new files.</p>
</li>
<li><p><strong>File Synchronisation</strong>: The Azure Web App synchronises the new files with the existing file system.</p>
</li>
</ol>
<h3 id="heading-problems-with-traditional-deployment">Problems with Traditional Deployment</h3>
<ul>
<li><p><strong>Long Deployment Times</strong>: Extracting and synchronising files can be slow, especially for large applications or many files.</p>
</li>
<li><p><strong>File Corruption</strong>: There is a risk of file corruption during extraction or synchronisation.</p>
</li>
<li><p><strong>Downtime</strong>: The web app might be unavailable during the file extraction and restart process, causing service disruption.</p>
</li>
</ul>
<h3 id="heading-what-websiterunfrompackage-does">What <code>WEBSITE_RUN_FROM_PACKAGE</code> Does ?</h3>
<p>The <code>WEBSITE_RUN_FROM_PACKAGE</code> setting simplifies the deployment process by running the application directly from a package file, such as a ZIP file. This eliminates the need to extract the files, reducing deployment times and potential issues.</p>
<h3 id="heading-possible-values-for-websiterunfrompackage">Possible Values for <code>WEBSITE_RUN_FROM_PACKAGE</code></h3>
<ul>
<li><p><code>1</code>: This value tells Azure to run the web app directly from a package file that has been deployed. The app reads the package and executes it without extracting the contents.</p>
</li>
<li><p><strong>URL</strong>: You can also set <code>WEBSITE_RUN_FROM_PACKAGE</code> to a URL pointing to a package file stored in Azure Blob Storage. This can be useful for scenarios where you manage your application packages in Blob Storage and want the web app to fetch the package directly from there.</p>
</li>
</ul>
<h3 id="heading-how-to-implement-websiterunfrompackage">How to Implement <code>WEBSITE_RUN_FROM_PACKAGE</code> ?</h3>
<p>Here’s a step-by-step guide to implementing <code>WEBSITE_RUN_FROM_PACKAGE</code> in your deployment process:</p>
<ol>
<li><p><strong>Prepare Your Deployment Package</strong></p>
<ul>
<li>Compress your application into a ZIP file. Ensure that the package contains all the necessary files for your application to run.</li>
</ul>
</li>
<li><p><strong>Upload the Package</strong></p>
<ul>
<li>Use your CI/CD pipeline to upload the ZIP file to Azure. This can be done using Azure DevOps, GitHub Actions, or any other CI/CD tool you prefer.</li>
</ul>
</li>
<li><p><strong>Set the</strong> <code>WEBSITE_RUN_FROM_PACKAGE</code> Setting</p>
<ul>
<li><pre><code class="lang-bash">  az webapp config appsettings <span class="hljs-built_in">set</span> --resource-group &lt;group-name&gt; --name &lt;app-name&gt; --settings WEBSITE_RUN_FROM_PACKAGE=1
</code></pre>
<p>  Replace <code>&lt;group-name&gt;</code> and <code>&lt;app-name&gt;</code> with your resource group name and web app name.</p>
</li>
</ul>
</li>
<li><p>Either do above step or simply add env in webapp manually from azure console</p>
</li>
</ol>
<h3 id="heading-example-github-actions-workflow">Example GitHub Actions Workflow</h3>
<p>Here’s an example of how you can set up a GitHub Actions workflow to deploy your Node.js application using <code>WEBSITE_RUN_FROM_PACKAGE</code>:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy</span>
<span class="hljs-comment"># More GitHub Actions for Azure: https://github.com/Azure/actions</span>

name: Build and deploy Node.js app to Azure Web App - my-webapp

on:
  push:
    branches:
      - main
  workflow_dispatch:

<span class="hljs-built_in">jobs</span>:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js version
        uses: actions/setup-node@v3
        with:
          node-version: <span class="hljs-string">"20.x"</span>

      - name: npm install, build, and <span class="hljs-built_in">test</span>
        run: |
          npm install
          npm run build --if-present


      - name: Zip artifact <span class="hljs-keyword">for</span> deployment
        run: zip release.zip ./* -r

      - name: Upload artifact <span class="hljs-keyword">for</span> deployment job
        uses: actions/upload-artifact@v3
        with:
          name: node-app
          path: release.zip

  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: <span class="hljs-string">"Production"</span>
      url: <span class="hljs-variable">${{ steps.deploy-to-webapp.outputs.webapp-url }</span>}
    permissions:
      id-token: write <span class="hljs-comment">#This is required for requesting the JWT</span>

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v3
        with:
          name: node-app

      - name: Unzip artifact <span class="hljs-keyword">for</span> deployment
        run: unzip release.zip

      - name: Login to Azure
        uses: azure/login@v1
        with:
          client-id: <span class="hljs-variable">${{ secrets.AZUREAPPSERVICE_CLIENTID }</span>}
          tenant-id: <span class="hljs-variable">${{ secrets.AZUREAPPSERVICE_TENANTID }</span>}
          subscription-id: <span class="hljs-variable">${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID }</span>}

      - name: <span class="hljs-string">"Deploy to Azure Web App"</span>
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: <span class="hljs-string">"my-webapp"</span>
          slot-name: <span class="hljs-string">"Production"</span>
          package: .
</code></pre>
<p>Follow me on</p>
<p>LinkedIn : <a target="_blank" href="https://www.linkedin.com/in/akash202k/">https://www.linkedin.com/in/akash202k/</a></p>
<p>Twitter : <a target="_blank" href="https://x.com/akash202k_">https://x.com/akash202k_</a></p>
<p>Thank You</p>
<p>Happy Learning : )</p>
]]></content:encoded></item><item><title><![CDATA[Scaling JioCinema for IPL: A Technical Deep Dive into High Traffic Management]]></title><description><![CDATA[💡
Introduction


The Indian Premier League (IPL) presents a colossal challenge for streaming platforms like JioCinema due to the surge in viewership during key moments. This technical analysis delves into JioCinema's infrastructure and strategies fo...]]></description><link>https://blog.akashpawar.com/scaling-jiocinema-for-ipl-a-technical-deep-dive-into-high-traffic-management</link><guid isPermaLink="true">https://blog.akashpawar.com/scaling-jiocinema-for-ipl-a-technical-deep-dive-into-high-traffic-management</guid><category><![CDATA[ipltech]]></category><category><![CDATA[ipl 2023 latest updates]]></category><category><![CDATA[ipllivescore]]></category><category><![CDATA[Jio Cinema Party]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Tue, 26 Mar 2024 16:39:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711470949829/4048bf9b-ce1d-45e2-aa56-aef5741d35b6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Introduction</strong></div>
</div>

<p>The Indian Premier League (IPL) presents a colossal challenge for streaming platforms like JioCinema due to the surge in viewership during key moments. This technical analysis delves into JioCinema's infrastructure and strategies for handling the unprecedented traffic spikes during IPL matches, focusing on the utilization of cutting-edge technologies to ensure seamless streaming experiences.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Understanding the Scale</strong></div>
</div>

<ul>
<li><p>Delve into the sheer magnitude of IPL viewership and its impact on streaming platforms.</p>
</li>
<li><p>Introduction to JioCinema's pivotal role as a primary streaming provider for IPL matches.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Technology Stack Overview</strong></div>
</div>

<ul>
<li><p>In-depth exploration of JioCinema's technology stack, comprising:</p>
<ul>
<li><p>Content Delivery Network (CDN) architecture for efficient content distribution.</p>
</li>
<li><p>Load Balancers leveraging algorithms like round-robin or least connections for optimal traffic distribution.</p>
</li>
<li><p>Auto-scaling mechanisms facilitated by container orchestration platforms like Kubernetes for dynamic resource allocation.</p>
</li>
<li><p>Advanced content caching strategies utilizing technologies such as Redis or Varnish to alleviate server load during peak periods.</p>
</li>
</ul>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Handling Traffic Spikes</strong></div>
</div>

<ul>
<li><p>Detailed elucidation of JioCinema's proactive approach to managing traffic surges during IPL matches.</p>
</li>
<li><p>Utilization of predictive analytics powered by machine learning algorithms to forecast viewer demand and preemptively scale resources.</p>
</li>
<li><p>Implementation of intelligent caching policies, including edge caching and micro-caching, to serve popular content with minimal latency.</p>
</li>
<li><p>Seamless integration with cloud providers such as AWS or Google Cloud for elastic scaling of infrastructure resources based on demand.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Case Study: Virat Kohli's Century</strong></div>
</div>

<ul>
<li><p>Granular examination of the traffic influx during a significant IPL moment, such as Virat Kohli's century in a particular match.</p>
</li>
<li><p>Real-time analysis of JioCinema's infrastructure response to the sudden surge in viewership.</p>
</li>
<li><p>Evaluation of key performance metrics, including latency, throughput, and server response times, during the peak period.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Case Study: Dhoni's Final Innings</strong></div>
</div>

<ul>
<li><p>Analogous analysis spotlighting the traffic surge during Dhoni's pivotal innings in the IPL final.</p>
</li>
<li><p>Comparative assessment of infrastructure performance between regular traffic and high-demand scenarios.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Ensuring Reliability and Availability</strong></div>
</div>

<ul>
<li><p>Thorough discussion on JioCinema's measures to uphold service reliability and ensure high availability during critical IPL moments.</p>
</li>
<li><p>Deployment of redundant systems and fault-tolerant architectures, such as active-active clusters and multi-region setups, to mitigate potential failures.</p>
</li>
<li><p>Continuous monitoring and observability through tools like Prometheus and Grafana for proactive issue detection and remediation.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Conclusion</strong></div>
</div>

<ul>
<li><p>Recapitulation of the robust technological foundation and proactive strategies employed by JioCinema to navigate the challenges posed by IPL traffic spikes.</p>
</li>
<li><p>Reflection on the significance of scalability, reliability, and performance optimization in the realm of streaming platforms.</p>
</li>
<li><p>Insights into how JioCinema's tech-centric approach serves as a benchmark for addressing similar scalability challenges in the streaming industry.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>References</strong></div>
</div>

<ul>
<li>Citations and resources documenting the technologies, methodologies, and case studies referenced throughout the analysis.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Streamlining Data Flow: Integrating SFTP User Access with S3, SQS, and VM Directory Mounting]]></title><description><![CDATA[Introduction :In the blog post, we can explore how to set up a seamless data flow by integrating various components such as SFTP user access, S3 storage, SQS queues, and VM directory mounting. We'll discuss the steps involved in configuring SFTP user...]]></description><link>https://blog.akashpawar.com/streamlining-data-flow-integrating-sftp-user-access-with-s3-sqs-and-vm-directory-mounting</link><guid isPermaLink="true">https://blog.akashpawar.com/streamlining-data-flow-integrating-sftp-user-access-with-s3-sqs-and-vm-directory-mounting</guid><category><![CDATA[decoulpting]]></category><category><![CDATA[#LearninPublic]]></category><category><![CDATA[SFTP]]></category><category><![CDATA[S3-bucket]]></category><category><![CDATA[AWS SQS]]></category><category><![CDATA[azure-devops]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Sat, 23 Mar 2024 18:22:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711217942110/427aa73c-9d05-4f49-8ec3-5be22602276d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<details><summary>Introduction :</summary><div data-type="detailsContent">In the blog post, we can explore how to set up a seamless data flow by integrating various components such as SFTP user access, S3 storage, SQS queues, and VM directory mounting. We'll discuss the steps involved in configuring SFTP users with limited access to specific directories, mounting S3 buckets to a virtual machine (VM) directory, and utilizing SQS for event notification.</div></details>

<h3 id="heading-pre-requisite">Pre-requisite</h3>
<ul>
<li><p>Create s3 bucket.</p>
</li>
<li><p>Create sqs queue.</p>
</li>
<li><p>create ec2 instance (ubuntu)</p>
</li>
<li><p>create IAM User for accessing s3 bucket and add s3 permission to it.</p>
</li>
</ul>
<h3 id="heading-creating-user-for-sftp">Creating user for sftp</h3>
<p>Install s3fs</p>
<pre><code class="lang-bash">sudo apt update
sudo apt install s3fs
</code></pre>
<p>Setup creds for s3fs</p>
<p>Create file</p>
<pre><code class="lang-bash">sudo vim /etc/passwd-s3fs

<span class="hljs-comment"># write below content with its actual value for iam user created</span>
ACCESS_KEY_ID:SECRET_KEY_ID
</code></pre>
<p>create user for sftp</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Usage: sudo ./create-sftp-user.sh username</span>

USERNAME=<span class="hljs-variable">$1</span>
S3_BUCKET_NAME=<span class="hljs-string">"s3-bucket-name"</span>
S3_BUCKET_PATH=<span class="hljs-string">"<span class="hljs-variable">$S3_BUCKET_NAME</span>:/incoming/<span class="hljs-variable">$USERNAME</span>"</span>
MOUNT_DIR=<span class="hljs-string">"/mnt/sftp-s3/<span class="hljs-variable">$USERNAME</span>"</span>

sudo useradd -m -d <span class="hljs-string">"<span class="hljs-variable">$MOUNT_DIR</span>"</span> -s /bin/bash -G sftponly <span class="hljs-string">"<span class="hljs-variable">$USERNAME</span>"</span>
<span class="hljs-comment"># Set password for the user</span>
sudo passwd <span class="hljs-string">"<span class="hljs-variable">$USERNAME</span>"</span>
sudo s3fs <span class="hljs-string">"<span class="hljs-variable">$S3_BUCKET_PATH</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$MOUNT_DIR</span>"</span> -o passwd_file=/etc/passwd-s3fs -o allow_other -o nonempty
sudo mkdir -p <span class="hljs-string">"<span class="hljs-variable">$MOUNT_DIR</span>/data"</span>
sudo s3fs <span class="hljs-string">"<span class="hljs-variable">$S3_BUCKET_PATH</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$MOUNT_DIR</span>/data"</span> -o passwd_file=/etc/passwd-s3fs -o allow_other -o nonempty
<span class="hljs-built_in">echo</span> <span class="hljs-string">"User <span class="hljs-variable">$USERNAME</span> added and configured."</span>
</code></pre>
<p><code>sudo vim /etc/ssh/sshd_config</code></p>
<p>at last write</p>
<pre><code class="lang-bash">Match User userName
    ChrootDirectory /mnt/sftp-s3/<span class="hljs-variable">$USERNAME</span>
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no
    PasswordAuthentication yes
</code></pre>
<p>now try to access over sftp .</p>
<h3 id="heading-update-sqs-queue-access-policy">Update sqs queue access policy :</h3>
<pre><code class="lang-bash">{
  <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-string">"Id"</span>: <span class="hljs-string">"Policy1679925546977"</span>,
  <span class="hljs-string">"Statement"</span>: [
    {
      <span class="hljs-string">"Sid"</span>: <span class="hljs-string">"Stmt1679925532180"</span>,
      <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-string">"Principal"</span>: <span class="hljs-string">"*"</span>,
      <span class="hljs-string">"Action"</span>: <span class="hljs-string">"sqs:*"</span>,
      <span class="hljs-string">"Resource"</span>: <span class="hljs-string">"arn:aws:sqs:region:1111111111:sqs_name"</span>,
      <span class="hljs-string">"Condition"</span>: {
        <span class="hljs-string">"ArnEquals"</span>: {
          <span class="hljs-string">"aws:SourceArn"</span>: <span class="hljs-string">"arn:aws:s3:::s3-buket-name"</span>
        }
      }
    }
  ]
}
</code></pre>
<p>Create s3 event notification.</p>
<p>Thanks</p>
<p>For more such content follow me on :</p>
<p>Twitter : <code>https://x.com/akash202k_</code></p>
]]></content:encoded></item><item><title><![CDATA[Serverless Url Shortener apiGW Lambda dynamoDb]]></title><description><![CDATA[Architecture

IntroductionWe'll walk through the process of building a URL shortener service using (serverless architecture) AWS Lambda (python : boto3) and API Gateway (http). By the end of this tutorial, you'll have a fully functional URL shortener...]]></description><link>https://blog.akashpawar.com/serverless-url-shortener-apigw-lambda-dynamodb</link><guid isPermaLink="true">https://blog.akashpawar.com/serverless-url-shortener-apigw-lambda-dynamodb</guid><category><![CDATA[cloudcdk.com]]></category><category><![CDATA[short link]]></category><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[shorturl]]></category><category><![CDATA[DynamoDB]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[projects]]></category><category><![CDATA[serverless]]></category><category><![CDATA[boto3]]></category><category><![CDATA[#learning-in-public]]></category><category><![CDATA[#LearninPublic]]></category><category><![CDATA[day1]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Wed, 20 Mar 2024 12:14:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711003263214/05bbd223-477f-4aab-bc7f-b7ee1ecd7501.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-architecture">Architecture</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710933424872/46a870dc-c8fa-4c56-8855-5f3212ed6f45.webp" alt class="image--center mx-auto" /></p>
<details><summary>Introduction</summary><div data-type="detailsContent">We'll walk through the process of building a URL shortener service using (serverless architecture) AWS Lambda (python : boto3) and API Gateway (http). By the end of this tutorial, you'll have a fully functional URL shortener service that you can deploy and use to shorten URLs.</div></details><details><summary>Prerequisites</summary><div data-type="detailsContent">Before we begin, ensure that you have an AWS account set up and that you're familiar with basic AWS services like Lambda and API Gateway.</div></details><details><summary>Step 1: Create a Lambda Function:</summary><div data-type="detailsContent">The first step is to create a Lambda function that will generate short URLs for long URLs. Here's a Python code snippet for the Lambda function</div></details>

<pre><code class="lang-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> string
<span class="hljs-keyword">import</span> random

dynamodb = boto3.resource(<span class="hljs-string">'dynamodb'</span>)
table_name = <span class="hljs-string">'url-shortener-table'</span>
table = dynamodb.Table(table_name)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    print(event)

    http_method = event[<span class="hljs-string">'requestContext'</span>][<span class="hljs-string">'http'</span>][<span class="hljs-string">'method'</span>]

    <span class="hljs-keyword">if</span> http_method == <span class="hljs-string">'POST'</span>:
        body = json.loads(event[<span class="hljs-string">'body'</span>])
        long_url = body[<span class="hljs-string">'long_url'</span>]
        short_url = generate_short_url()
        table.put_item(Item={<span class="hljs-string">'short_id'</span>: short_url, <span class="hljs-string">'long_url'</span>: long_url})

        response = {
            <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
            <span class="hljs-string">'body'</span>: json.dumps({<span class="hljs-string">'short_url'</span>: short_url})
        }
    <span class="hljs-keyword">elif</span> http_method == <span class="hljs-string">'GET'</span>:
        short_url = event[<span class="hljs-string">'rawPath'</span>][<span class="hljs-number">1</span>:]
        response = table.get_item(Key={<span class="hljs-string">'short_id'</span>: short_url})
        <span class="hljs-keyword">if</span> <span class="hljs-string">'Item'</span> <span class="hljs-keyword">in</span> response:
            long_url = response[<span class="hljs-string">'Item'</span>][<span class="hljs-string">'long_url'</span>]
            response = {
                <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">301</span>,
                <span class="hljs-string">'headers'</span>: {
                    <span class="hljs-string">'Location'</span>: long_url
                }
            }
        <span class="hljs-keyword">else</span>:
            response = {
                <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">404</span>,
                <span class="hljs-string">'body'</span>: json.dumps({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Short URL not found'</span>})
            }
    <span class="hljs-keyword">return</span> response

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_short_url</span>():</span>
    characters = string.ascii_letters + string.digits
    short_url = <span class="hljs-string">''</span>.join(random.choice(characters) <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">3</span>))
    <span class="hljs-keyword">return</span> short_url
</code></pre>
<p>NOTE : Replace <code>table_name</code> with your dynamoDb table name once we create it later in this steps . and partitionKey should be <code>short_id</code></p>
<details><summary>Step 2: Set Up DynamoDB Table:</summary><div data-type="detailsContent">Create a DynamoDB table named <code>url-shortener-table</code> with <code>short_id</code> as the partition key and <code>long_url</code> as an attribute.</div></details>

<p><strong>Step 3: Create an API Gateway:</strong></p>
<p><strong>Lets create api first and later we can do configuration</strong></p>
<p>Open the API Gateway console.</p>
<ul>
<li><p>Click on "Create API" and select "HTTP API". Review and create api</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710934836045/a45879b4-9a17-4876-b3ed-054d71f406e8.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Once we created API Define routes for POST and GET requests.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710934938437/3c855932-4cd5-4074-a7cf-11966bbbb718.png" alt class="image--center mx-auto" /></p>
<p>  Select <code>create</code> and keep route empty and method <code>POST</code> then select <code>create</code></p>
<p>  ref following image</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710935037986/451758db-5762-41ca-8c1e-f3c7a5ea0c25.png" alt class="image--center mx-auto" /></p>
<p>  once created select <code>POST</code> like this</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710935102092/fad76dd3-e844-47c1-b35b-2598224c9019.png" alt class="image--center mx-auto" /></p>
<p>  click on attach integration and go ahead with option</p>
<p>  <code>create and attach integration</code></p>
</li>
<li><p>Next select integration type as lambda function and then select lambda function created earlier for this task</p>
</li>
<li><p>Make sure this option is enabled and go ahead with create option</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710935263688/f32126bd-d6e7-45e0-aca8-3795ff3e04d1.png" alt class="image--center mx-auto" /></p>
<p>  Cool we just created our first route , lets go and create second route which is <code>GET</code></p>
<p>  for redirecting shorturl we got in previous response</p>
</li>
<li><p>Lets create second route , now go back to Routes and select <code>create</code></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710935836468/af116820-b4ee-4135-a4cf-91ecdaabcccb.png" alt class="image--center mx-auto" /></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710935975277/57fc5e1b-fe1f-462d-acd5-61f59f09ebed.png" alt class="image--center mx-auto" /></p>
<p>  Make sure its <code>/{short_id}</code></p>
</li>
<li><p>Now select GET Method attach integration and click on attach just right side option</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710936062697/3f03ca09-bda1-4a67-8823-98b864506acb.png" alt class="image--center mx-auto" /></p>
<p>  From this option you can get your APIGW url to make request</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710936161899/878a0ad8-ab50-4086-bfbe-80a3fe0c6590.png" alt class="image--center mx-auto" /></p>
<p>  These are the curls to test Replace url with actual url</p>
</li>
</ul>
<p>Create Short Url : POST</p>
<pre><code class="lang-bash">curl --location <span class="hljs-string">'https://4tr7c6bru76e.execute-api.us-east-1.amazonaws.com'</span> \
--header <span class="hljs-string">'Content-Type: application/json'</span> \
--data <span class="hljs-string">'{"long_url": "https://cloudcdk.com"}'</span>
</code></pre>
<p>Access Short Url : GET</p>
<pre><code class="lang-bash">curl --location <span class="hljs-string">'https://4tr7c6bru76e.execute-api.us-east-1.amazonaws.com/{Replace_with_id_u_get_in_response_for_above_post_request}'</span>
</code></pre>
<p>Follow me on Twitter for more : <a target="_blank" href="https://twitter.com/akashpawar72218">Twitter</a></p>
]]></content:encoded></item><item><title><![CDATA[🌐 Exposing Services in Amazon EKS: Simplified Methods and Use Cases]]></title><description><![CDATA[Exposing services in an Amazon Elastic Kubernetes Service (EKS) cluster allows external users to access applications running within the Kubernetes environment. Let's explore some straightforward ways to achieve this:

LoadBalancer Service Type:


Pur...]]></description><link>https://blog.akashpawar.com/eks-service-exposing-methods</link><guid isPermaLink="true">https://blog.akashpawar.com/eks-service-exposing-methods</guid><category><![CDATA[Amazon EKS]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[kubernetes ingress]]></category><category><![CDATA[cloudcdk]]></category><category><![CDATA[DailyDevOps]]></category><dc:creator><![CDATA[Akash Pawar]]></dc:creator><pubDate>Fri, 28 Jul 2023 18:28:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690567427323/ee2fbbc4-8e3b-427a-8288-70c81c65e6f9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Exposing services in an Amazon Elastic Kubernetes Service (EKS) cluster allows external users to access applications running within the Kubernetes environment. Let's explore some straightforward ways to achieve this:</p>
<ol>
<li>LoadBalancer Service Type:</li>
</ol>
<ul>
<li><p>Purpose: Creates an external Elastic Load Balancer (ELB) that distributes traffic to the service's pods.</p>
</li>
<li><p>Advantages:</p>
<ul>
<li><p>Provides a stable public IP or DNS for the service.</p>
</li>
<li><p>Handles load balancing automatically among pods.</p>
</li>
</ul>
</li>
<li><p>Limitations:</p>
<ul>
<li><p>Additional cost for the ELB.</p>
</li>
<li><p>Limited flexibility for advanced routing configurations.</p>
</li>
</ul>
</li>
</ul>
<ol>
<li><p>NodePort Service Type:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690568043700/a73b7e0b-e5c5-4830-8756-b32271463d2d.jpeg" alt class="image--center mx-auto" /></p>
</li>
</ol>
<ul>
<li><p>Purpose: Allocates a static port on each cluster node, forwarding external traffic to the service's pods.</p>
</li>
<li><p>Advantages:</p>
<ul>
<li><p>No additional cost for load balancing.</p>
</li>
<li><p>Simple setup without external load balancers.</p>
</li>
</ul>
</li>
<li><p>Limitations:</p>
<ul>
<li><p>Not suitable for large-scale production deployments due to potential port conflicts.</p>
</li>
<li><p>The exposed port range might be limited based on the cluster configuration.</p>
</li>
</ul>
</li>
</ul>
<ol>
<li><p>Ingress Resource with Application Load Balancer (ALB) { Recommended }:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690567961090/c7114e73-d02e-4df2-b247-3f67206fd2f5.jpeg" alt class="image--center mx-auto" /></p>
</li>
</ol>
<ul>
<li><p>Purpose: Uses an AWS Application Load Balancer (ALB) as the Ingress controller, enabling advanced HTTP routing and SSL termination.</p>
</li>
<li><p>Advantages:</p>
<ul>
<li><p>Advanced HTTP-based routing, host-based routing, and SSL termination.</p>
</li>
<li><p>Better integration with AWS services, like AWS Certificate Manager (ACM).</p>
</li>
</ul>
</li>
<li><p>Limitations:</p>
<ul>
<li><p>Ingress controllers require additional resources, which might impact cluster performance.</p>
</li>
<li><p>Setting up ALB Ingress might involve some initial configuration.</p>
</li>
</ul>
</li>
</ul>
<ol>
<li><p>ClusterIP Service Type:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690568128496/24a963d6-a681-40f7-b550-c45369096110.jpeg" alt class="image--center mx-auto" /></p>
</li>
</ol>
<ul>
<li><p>Purpose: Exposes the service internally within the cluster for communication between services.</p>
</li>
<li><p>Advantages:</p>
<ul>
<li><p>Ideal for internal communication between services within the cluster.</p>
</li>
<li><p>Provides a stable internal IP address for service discovery.</p>
</li>
</ul>
</li>
<li><p>Limitations:</p>
<ul>
<li>Not accessible from outside the cluster, limiting external access.</li>
</ul>
</li>
</ul>
<ol>
<li><p>ExternalName Service Type:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690568331432/87329433-56b7-45c7-b0b4-5284227261ed.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
<ul>
<li><p>Purpose: Maps a service to an external DNS name, enabling access to resources outside the cluster.</p>
</li>
<li><p>Advantages:</p>
<ul>
<li><p>Allows services in the cluster to access external resources easily.</p>
</li>
<li><p>Simplifies the transition from external services to internal Kubernetes services.</p>
</li>
</ul>
</li>
<li><p>Limitations:</p>
<ul>
<li><p>Only supports mapping to external DNS names, not IP addresses.</p>
</li>
<li><p>Limited to read-only access to the external resource.</p>
</li>
</ul>
</li>
</ul>
<ol>
<li>External Load Balancer (Manually Provisioned):</li>
</ol>
<ul>
<li><p>Purpose: Manually provisions an external load balancer outside of EKS and points it to the service's pods.</p>
</li>
<li><p>Advantages:</p>
<ul>
<li><p>Full control over load balancer configuration and capabilities.</p>
</li>
<li><p>Flexibility to choose a load balancer from any provider.</p>
</li>
</ul>
</li>
<li><p>Limitations:</p>
<ul>
<li><p>Requires manual setup and maintenance, which can be time-consuming.</p>
</li>
<li><p>May involve additional costs and complexities depending on the external load balancer.</p>
</li>
</ul>
</li>
</ul>
<p>Conclusion:</p>
<p>Exposing services in an Amazon EKS cluster can be achieved through multiple straightforward methods, each with its advantages and limitations. Consider the specific requirements of your application, such as scalability, security, and ease of management, to choose the method that best aligns with your needs.</p>
]]></content:encoded></item></channel></rss>