- Published on
- // 27 min read
Teaching AI models about Kubernetes network policy
- Authors
- Name
- Shane Boulden
- @shaneboulden
Want to hear a NotebookLM-generated podcast created from this article? Check it out here.
In a previous article I introduced Instructlab, an open source framework for enhancing generative AI models. In this article I want to look at a practical and helpful implementation of InstructLab - generating accurate Kubernetes network policy, and reducing the burden on platform engineers and DevOps practitioners.
Kubernetes network policy primer
Before I get too far into this article I want to provide a brief primer on Kubernetes network policy, and why we're interested in generative AI models applied to this problem space.
Kubernetes network policy allows us to declaratively specify permitted network connections within a Kubernetes namespace. These rules are additive, which means that the most restrictive network policy looks like this, disallowing all ingress and egress traffic for all pods within a namespace:
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-in-namespace
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
This policy applies to all pods in the default
namespace (note the wildcard pod selector, {}
), and applies to traffic originating within pods (Egress
), as well as traffic destined for pods (Ingress
).
Network policy is implemented by the Container Network Interface (CNI) plugin used by your Kubernetes cluster. In OpenShift, the default CNI plugin is OVN-Kubernetes.
OVN-Kubernetes implements network policy using access control lists (ACLs). For example, the network policy above would result in four ACLs being created by OVN-Kubernetes, two for egress and two for ingress. These ACLs deny any egress / ingress traffic for pods in the namespace, while preserving Address Resolution Protocol (ARP) communications.
## Egress
action : allow
direction : from-lport
external_ids : {
direction=Egress,
"k8s.ovn.org/id"="default-network-controller:NetpolNamespace:default:Egress:arpAllow",
"k8s.ovn.org/name"=default,
"k8s.ovn.org/owner-controller"=default-network-controller,
"k8s.ovn.org/owner-type"=NetpolNamespace,
type=arpAllow
}
label : 0
log : false
match : "inport == @a16982411286042166782_egressDefaultDeny && (arp || nd)"
meter : acl-logging
name : "NP:default:Egress"
options : {apply-after-lb="true"}
priority : 1001
severity : []
action : drop
direction : from-lport
external_ids : {
direction=Egress,
"k8s.ovn.org/id"="default-network-controller:NetpolNamespace:default:Egress:defaultDeny",
"k8s.ovn.org/name"=default,
"k8s.ovn.org/owner-controller"=default-network-controller,
"k8s.ovn.org/owner-type"=NetpolNamespace,
type=defaultDeny
}
label : 0
log : false
match : "inport == @a16982411286042166782_egressDefaultDeny"
meter : acl-logging
name : "NP:default:Egress"
options : {apply-after-lb="true"}
priority : 1000
severity : []
## Ingress
action : allow
direction : to-lport
external_ids : {
direction=Ingress,
"k8s.ovn.org/id"="default-network-controller:NetpolNamespace:default:Ingress:arpAllow",
"k8s.ovn.org/name"=default,
"k8s.ovn.org/owner-controller"=default-network-controller,
"k8s.ovn.org/owner-type"=NetpolNamespace,
type=arpAllow
}
label : 0
log : false
match : "outport == @a16982411286042166782_ingressDefaultDeny && (arp || nd)"
meter : acl-logging
name : "NP:default:Ingress"
options : {}
priority : 1001
severity : []
action : drop
direction : to-lport
external_ids : {
direction=Ingress,
"k8s.ovn.org/id"="default-network-controller:NetpolNamespace:default:Ingress:defaultDeny",
"k8s.ovn.org/name"=default,
"k8s.ovn.org/owner-controller"=default-network-controller,
"k8s.ovn.org/owner-type"=NetpolNamespace,
type=defaultDeny
}
label : 0
log : false
match : "outport == @a16982411286042166782_ingressDefaultDeny"
meter : acl-logging
name : "NP:default:Ingress"
options : {}
priority : 1000
severity : []
Let's look at a more complex example. Suppose I have a number of applications running as pods inside the same Kubernetes namespace - application A, B and C. Application C should be able to communicate with application A, but not application B, per below.
This application architecture can be implemented with Kubernetes network policy, which would look like this:
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
name: application-netpol
namespace: my-app
spec:
podSelector:
matchLabels:
app: application-a
ingress:
- from:
- podSelector:
matchLabels:
app: application-c
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-in-namespace
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
One of the challenge here is that this isn't very intuitive for experienced network security engineers, or any security teams. These teams are usually made responsible for network security policy, but this looks completely different to other platforms they use. Common questions I hear are:
- "So, we don't specify "blocked" connections, but only add "allowed" connections?"
- "How do I block all connections into a namespace, but allow all outgoing traffic?"
- "What if I have a specified IP that I want to block traffic to?"
Usually these users know what they need to do, but don't know how to express it as a Kuberbetes network policy (or multiple policies). This is a great use case for generative AI (and InstructLab) - communicating requirements through natural language prompts, and generating (or explaining) network policy for the user. Let's see if InstructLab can help!
Lab environment
I'm using the same lab environment as my previous article, and running InstructLab on the Windows Subsystem for Linux (WSL). You can see a pic here:
The main reason I'm running this on WSL is its support for Unified Shared Memory, which effectively allows the CUDA library (which is supporting our AI enhancements) to access both RAM (addressable by the CPU) and VRAM (addressable by the GPU) as a single memory address space.
Why do I need Unified Shared Memory? I only have an 8GB video card, but I need the entire model to 'fit' into GPU-addressable VRAM to support training my new generative AI model (~17GB) with InstructLab. I have a couple of options here:
Use a larger GPU card. This might not be an option for most users - though I'm currently experimenting with an NVIDIA K80, and will be sure to update the blog when I have this working.
Use Unified Shared Memory to expand the memory address space available for InstructLab model training.
Setting up InstructLab
Setting up InstructLab for this article is largely the same as the last. Firstly, we need to install InstructLab into a Python virtual environment:
$ python -m venv ~/ilab-venv
$ source ~/ilab-venv/bin/activate
$(venv) pip3 install instructlab
Once installed you can configure your InstructLab environment:
$ (venv) ilab config init
Welcome to InstructLab CLI. This guide will help you to setup your environment.
Please provide the following values to initiate the environment [press Enter for defaults]:
Path to taxonomy repo [taxonomy]:
Path to your model [models/merlinite-7b-lab-Q4_K_M.gguf]:
Generating `config.yaml` in the current directory...
Initialization completed successfully, you're ready to start using `ilab`. Enjoy!
Chatting with the model
In this article we're going to train an IBM Granite model. Granite is a series of large language models (LLMs) that span generative AI use cases, involving language and code. Specifically we're going to use the Granite 7B model that's already been trained with using the LAB methodology, available from HuggingFace.
Granite only recently became the default for InstructLab (which to-date has been Merlinite), and we will need to download it:
ilab model download --repository=instructlab/granite-7b-lab
We need to serve this model out before chatting with it. Because this is a vLLM model we will need to specify --backend=vllm
. Note that you may need to tune the model-max-len
parameter - for me, I had to go down to 512, but you may be able to use 2048
or 4096
depending on your GPU.
ilab model serve --model-path /home/user/.cache/instructlab/models/instructlab/granite-7b-lab/ --backend=vllm -- --max-model-len 512
Now we're ready to start chatting. In another terminal window I've started out by asking the model what it knows about network policy, and then to generate me a simple network policy:
Ok - this isn't that bad! The policy uses the right Kubernetes API, and the podSelectors
are correct, but the ingress
section looks a bit strange. In fact, this policy doesn't even deploy correctly to my Openshift cluster:
For reference, the correct pod selectors could have looked like this:
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-ingress
We know at the point that the model has some limitations. It seems to 'know' what network policy should look like, and has pretty good knowledge about network policy generally. But, seems to have challenges with the 'skill' of creating network policy in response to user requests.
This aligns well with the way that InstructLab distinguishes enhancements to Generative AI models. InstructLab supports enhancements to both knowledge and skills, and these look a bit different. Skills are performative ("things the model can do"), whereas knowledge is based more on answering questions that involve facts, data, or references.
Improving the model
In our case we're not looking at facts, data or references, so we should look at a 'Skill' enhancement to the model - generating correct Kubernetes network policy in response to user questions.
To create a skill enhancement we need to create a qna.yaml
file, holding questions and answers about Kubernetes network policy. My InstructLab taxonomy is stored at ~/.local/share/instructlab/taxonomy/
, and I've created an additional folder structure to hold my Kubernetes network policy files, at .local/share/instructlab/taxonomy/compositional_skills/technology/kubernetes/netpol/qna.yaml
.
I need to provide at least five seed example questions and answers to support InstructLab synthetic data generation, and I've provided seven, which you can see here. For each seed example I've provided the user question, and the expected response from the model:
created_by: shaneboulden
version: 2
task_description: Generate Kubernetes network policy from natural language inputs
seed_examples:
- question: |
Create a network policy for a Kubernetes pod labelled `app: web` that permits ingress
through the default OpenShift ingress.
answer: |
Here's a Kubernetes network policy that allows ingress via the default OpenShift ingress to a
pod labelled `app:web`. The policy also permits DNS queries to the OpenShift CoreDNS service.
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
name: web-netpol
namespace: devops
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector:
matchLabels:
network.openshift.io/policy-group: ingress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
policyTypes:
- Ingress
- Egress
```
- question: |
Create a Kubernetes network policy that denies all ingress and
egress traffic for the `devops` namespace.
answer: |
Here's a Kubernetes network policy that denies all ingress and egress traffic
to the `devops` namespace.
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
name: default-deny-in-namespace-devops
namespace: devops
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
```
- question: |
Create a Kubernetes network policy for the pod hosted on OpenShift labelled `app: web`
that allows egress to the service labelled `app: leaderboard`
answer: |
Here's a Kubernetes network policy for the pod hosted on OpenShift labelled `app: web`
that permits egress to the service labelled `app: leaderboard`app:
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
name: web-netpol
namespace: devops
spec:
podSelector:
matchLabels:
app: web
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
- to:
- podSelector:
matchLabels:
app: leaderboard
policyTypes:
- Egress
```
- question: |
Create a Kubernetes network policy for the pod hosted on OpenShift labelled
`app: leaderboard` that accepts ingress from the pod labelled `app: web`.
The pods are hosted in the namespace devops.
answer: |
Here's a Kubernetes network policy for the pod hosted on OpenShift labelled
`app: leaderboard` that accepts ingress from the pod labelled `app: web`app:
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
creationTimestamp: null
labels:
name: leaderboard-netpol
namespace: devops
spec:
podSelector:
matchLabels:
app: leaderboard
ingress:
- from:
- podSelector:
matchLabels:
app: web
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
policyTypes:
- Ingress
- Egress
```
- question: |
Create a Kubernetes network policy for a pod labelled `app: web` on OpenShift
that accepts ingress from the OpenShift default ingress controller, makes
connections to google.com.au, makes connections to a pod labelled
`app: database`, and doesn't permit any other ingress or egress."
answer: |
Here's a Kubernetes network policy for a pod labelled `app: web` on
OpenShift that accepts ingress from the OpenShift default ingress controller,
makes connections to 172.217.167.67/24, makes connections to a pod
labelled `app: database`, and doesn't permit any other ingress or egress.
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
name: web-netpol
namespace: devops
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector:
matchLabels:
network.openshift.io/policy-group: ingress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
- to:
- podSelector:
matchLabels:
app: leaderboard
- to:
- ipBlock:
cidr: 172.217.167.67/24
policyTypes:
- Ingress
- Egress
```
- question: |
Create a Kubernetes network policy for a pod labelled `app: web` on OpenShift
that accepts ingress from the OpenShift default ingress controller, makes
connections to google.com.au, makes connections to a pod labelled
`app: database`"
answer: |
Here's a Kubernetes network policy for a pod labelled `app: web` on
OpenShift that accepts ingress from the OpenShift default ingress controller,
makes connections to 172.217.167.67/24, makes connections to a pod
labelled `app: database`.
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
name: web-netpol
namespace: devops
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector:
matchLabels:
network.openshift.io/policy-group: ingress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
- to:
- podSelector:
matchLabels:
app: leaderboard
- to:
- ipBlock:
cidr: 172.217.167.67/24
policyTypes:
- Ingress
- Egress
```
- question: |
Create a Kubernetes network policy that allows ingress from pods labelled with `app: bookstore`
and `role: api` from pods labelled with `app: bookstore`.
answer: |
Here is a Kubernetes network policy that allows ingress from pods labelled with `app: bookstore`
and `role: api` from pods labelled with `app: bookstore`:
```
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: api-allow
spec:
podSelector:
matchLabels:
app: bookstore
role: api
ingress:
- from:
- podSelector:
matchLabels:
app: bookstore
```
Now that we have our seed examples, we can generate synthetic data. This is a core component of InstructLab, and essentially generates a "curriculum" of millions of question-and-answers from the seed examples I've provided, that is then used to teach the "student" model.
If I run ilab data generate
I should start seeing this synthetic data generation:
$ ilab data generate
INFO 2024-10-28 10:13:14,335 numexpr.utils:161: NumExpr defaulting to 16 threads.
INFO 2024-10-28 10:13:14,571 datasets:54: PyTorch version 2.4.0 available.
INFO 2024-10-28 10:13:15,582 instructlab.model.backends.llama_cpp:104: Trying to connect to model server at http://127.0.0.1:8000/v1
WARNING 2024-10-28 10:13:26,368 instructlab.data.generate:291: Disabling SDG batching - unsupported with llama.cpp serving
Generating synthetic data using 'simple' pipeline, '/home/user/.cache/instructlab/models/merlinite-7b-lab-Q4_K_M.gguf' model, '/home/user/.local/share/instructlab/taxonomy' taxonomy, against http://127.0.0.1:56023/v1 server
INFO 2024-10-28 10:13:26,567 instructlab.sdg:375: Synthesizing new instructions. If you aren't satisfied with the generated instructions, interrupt training (Ctrl-C) and try adjusting your YAML files. Adding more examples may help.
…
INFO 2024-10-28 10:22:36,035 instructlab.sdg.datamixing:200: Mixed Dataset saved to /home/user/.local/share/instructlab/datasets/skills_train_msgs_2024-10-28T10_13_26.jsonl
INFO 2024-10-28 10:22:36,036 instructlab.sdg:438: Generation took 549.67s
You can see in the output that this synthetic data set has been saved to /home/user/.local/share/instructlab/datasets/skills_train_msgs_2024-10-28T11_35_10.jsonl
. If we take a look at this file, we can see that it contains more example questions-and-answers about Kubernetes network policy:
{
{
"id": "fa550d31-5400-4857-ac30-bc90f5f20686",
"messages": [{
"content": "I am, Red Hat\u00ae Instruct Model based on Granite 7B, an AI language model developed by Red Hat and IBM Research, based on the Granite-7b-base language model. My primary function is to be a chat assistant.",
"role": "system"
}, {
"content": "",
"role": "user"
}, {
"content": "Create a Kubernetes network policy that allows ingress traffic from pods with `app:bookstore-api` label to `app:bookstore` pods.'\n\nResponse:\n```\napiVersion: networking.k8s.io\/v1\nkind: NetworkPolicy\nmetadata:\n name: bookstore-policy\nspec:\n podSelector:\n matchLabels:\n app: bookstore\n ingress:\n - from:\n - namespaceSelector:\n matchLabels:\n app: bookstore-api\n```",
"role": "assistant"
}],
"metadata": "{\"task_description\": \"Generate Kubernetes network policy from natural language inputs\", \"seed_question\": \"Create a Kubernetes network policy that allows ingress from pods labelled with `app: bookstore`\\nand `role: api` from pods labelled with `app: bookstore`.\\n\", \"seed_response\": \"Here is a Kubernetes network policy that allows ingress from pods labelled with `app: bookstore`\\nand `role: api` from pods labelled with `app: bookstore`:\\n```\\nkind: NetworkPolicy\\napiVersion: networking.k8s.io\/v1\\nmetadata:\\n name: api-allow\\nspec:\\n podSelector:\\n matchLabels:\\n app: bookstore\\n role: api\\n ingress:\\n - from:\\n - podSelector:\\n matchLabels:\\n app: bookstore\\n```\\n\", \"output\": \"Create a Kubernetes network policy that allows ingress traffic from pods with `app:bookstore-api` label to `app:bookstore` pods.'\\n\\nResponse:\\n```\\napiVersion: networking.k8s.io\/v1\\nkind: NetworkPolicy\\nmetadata:\\n name: bookstore-policy\\nspec:\\n podSelector:\\n matchLabels:\\n app: bookstore\\n ingress:\\n - from:\\n - namespaceSelector:\\n matchLabels:\\n app: bookstore-api\\n```\"}"
}
...
Ok. At this point we have our seed examples, and we've used this to create synthetic data. Now we can train the model!
In this last article I looked at how you can configure InstructLab to use NVIDIA CUDA, and I'm going to use my NVIDIA card again to support model training.
ilab model train --device cuda --data-path /home/user/.local/share/instructlab/datasets/skills_train_msgs_2024-10-28T11_35_10.jsonl
Note that you may come across a few errors here. I ran into an issue OverflowError: int too big to convert
when trying this out initially. The solution is to change the effective batch size to 16 (which worked for my GPU):
ilab model train --device cuda --data-path /home/user/.local/share/instructlab/datasets/skills_train_msgs_2024-10-28T11_35_10.jsonl --effective-batch-size 16
There's probably also a couple of additional libraries you need to install into your virtual env:
pip install bitsandbytes flash_attn
If all else fails, you can always use the --legacy
flag to train the model.
ilab model train --device cuda --data-path /home/user/.local/share/instructlab/datasets/ --legacy
Once your environment is happy, you should start seeing training results reported:
Saving model in huggingface format at samples_seen: 208
Model saved in /home/user/.local/share/instructlab/checkpoints/hf_format/samples_208
[13:54:00] INFO saving took 7.2973291873931885 seconds utils.py:611
Epoch 3: 100%|██████████| 3/3 [00:42<00:00, 14.31s/it]
total tokens: 1932 num samples: 6 num padding tokens: 578 - rank: 0 max len: 322 min len: 109 avg len: 225.66666666666666 num_loss_counted_tokens: 870
total tokens: 3000 num samples: 12 num padding tokens: 1207 - rank: 0 max len: 250 min len: 92 avg len: 149.41666666666666 num_loss_counted_tokens: 829
total tokens: 3588 num samples: 12 num padding tokens: 1148 - rank: 0 max len: 299 min len: 94 avg len: 203.33333333333334 num_loss_counted_tokens: 1485
You can also validate that the NVIDIA device is being used correctly (not the shared memory being used here also):
You should get a message once training is completed:
Saving model in huggingface format at samples_seen: 496
Model saved in /home/user/.local/share/instructlab/checkpoints/hf_format/samples_496
[14:00:35] INFO saving took 7.73287296295166 seconds utils.py:611
Epoch 9: 100%|██████████| 3/3 [01:02<00:00, 20.87s/it]
Operation completed successfully! 🎉
Testing out the new model
At this point we've trained a new model using InstructLab, and you should see a bunch of models saved into /home/user/.local/share/instructlab/checkpoints/hf_format/
$ ls -l /home/user/.local/share/instructlab/checkpoints/hf_format/
total 40K
drwxr-xr-x 2 user user Dec 11 13:51 samples_112
drwxr-xr-x 2 user user Dec 11 13:53 samples_160
drwxr-xr-x 2 user user Dec 11 13:53 samples_208
drwxr-xr-x 2 user user Dec 11 13:54 samples_256
drwxr-xr-x 2 user user Dec 11 13:55 samples_304
drwxr-xr-x 2 user user Dec 11 13:56 samples_352
drwxr-xr-x 2 user user Dec 11 13:58 samples_400
drwxr-xr-x 2 user user Dec 11 13:59 samples_448
drwxr-xr-x 2 user user Dec 11 14:00 samples_496
drwxr-xr-x 2 user user Dec 11 13:49 samples_64
Essentially each of these is a complete InstructLab training pass, using more samples each time. We want to use the model based on the highest samples, in this case samples_496
.
This is also a vLLM model, so we will need to --backend=vllm
again to serve out the model.
ilab model serve --model-path /home/user/.local/share/instructlab/checkpoints/hf_format/samples_496/ --backend=vllm -- --swap-space=1 --tensor-parallel-size=1
You should see that your model is now being served:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO 12-11 14:15:06 metrics.py:295] Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 0.0 tokens/s, Running: 0 reqs, Swapped: 0 reqs, Pending: 0 reqs, GPU KV cache usage: 0.0%, CPU KV cache usage: 0.0%.
In another terminal window, start the model chat:
ilab model chat --model /home/user/.local/share/instructlab/checkpoints/hf_format/samples_496
(venv) [user@HelixWin ~]$ ilab model chat --model /home/user/.local/share/instructlab/checkpoints/hf_format/samples_496
╭──────────────────────────────────────────────────────────── system ─────────────────────────────────────────────────────────────╮
│ Welcome to InstructLab Chat w/ SAMPLES_496 (type /h for help) │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Let's try out an example query:
Create a Kubernetes network policy for the pod hosted on OpenShift labelled `app: leaderboard` that accepts ingress from the p
od labelled `app: web`. The pods are hosted in the namespace devops.
╭────────────────────────────────────────────────────────── samples_496 ──────────────────────────────────────────────────────────╮
│ ```yaml │
│ apiVersion: networking.k8s.io/v1 │
│ kind: NetworkPolicy │
│ metadata: │
│ name: leaderboard-ingress │
│ spec: │
│ podSelector: │
│ matchLabels: │
│ app: leaderboard │
│ ingress: │
│ - from: │
│ - ipBlock: │
│ cidr: "10.0.0.0/32" │
│ - port: │
│ number: 80 │
│ ``` │
│ │
│ This network policy allows ingress traffic to the leaderboard pod from any IP address on the range `10.0.0.0/32` on port `80`. │
│ The `podSelector` matches any pod with the label `app: leaderboard`. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────── elapsed 6.694 seconds ─╯
Ok - this is actually pretty good! The podSelector
is correct for the target pod, and the ingress
section is correctly defined - but I didn't ask for the ipblock
and ports
sections (the ipblock
at least looks correct). Let's see if the model can correct this:
>>> Thanks, but I didn't ask for ports or protocols. I expected the `ingress` section to use a podselector to match based on the la
bel `app: web`. Can you please fix this?
╭────────────────────────────────────────────────────────── samples_496 ──────────────────────────────────────────────────────────╮
│ Of course! I understand that you're looking for a network policy that accepts ingress traffic from the pod labelled `app: web`. │
│ Here's the corrected network policy: │
│ │
│ ```yaml │
│ apiVersion: networking.k8s.io/v1 │
│ kind: NetworkPolicy │
│ metadata: │
│ name: leaderboard-ingress │
│ spec: │
│ podSelector: │
│ matchLabels: │
│ app: leaderboard │
│ ingress: │
│ - from: │
│ - podSelector: │
│ matchLabels: │
│ app: web │
│ - port: │
│ number: 80 │
│ ``` │
│ │
│ In this corrected version, the `ingress` section includes a `podSelector` that matches any pod with the label `app: web`. The │
│ `port` is set to `80`, allowing ingress traffic on this port. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────── elapsed 8.120 seconds ─╯
Great! Now we have the correct pod selector, but we still have that pesky port
. Let's try again:
>>> Just remove the port 80 section and we're good :) [S][default]
╭────────────────────────────────────────────────────────── samples_496 ──────────────────────────────────────────────────────────╮
│ Certainly! Based on your requirements, I've updated the network policy to only allow ingress traffic from any IP address on the │
│ range `10.0.0.0/32` to the pod labelled `app: leaderboard`. Here's the corrected network policy: │
│ │
│ ```yaml │
│ apiVersion: networking.k8s.io/v1 │
│ kind: NetworkPolicy │
│ metadata: │
│ name: leaderboard-ingress │
│ spec: │
│ podSelector: │
│ matchLabels: │
│ app: leaderboard │
│ ingress: │
│ - from: │
│ - ipBlock: │
│ cidr: "10.0.0.0/32" │
│ ``` │
│ │
│ In this corrected version, the `ingress` section includes an `ipBlock` that matches any IP address on the range `10.0.0.0/32`. │
│ This will allow ingress traffic from any IP address on this range to the pod labelled `app: leaderboard`. I hope this version │
│ meets your requirements. Let me know if you have any further questions! │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────── elapsed 9.992 seconds ─╯
Oh no. The port
was removed, but we're back to ipBlock
sections. Let's try one more time:
>>> No no. I don't need an ipblock - just a podselector for the label 'app: web' in the ingress section. [S][default]
╭────────────────────────────────────────────────────────── samples_496 ──────────────────────────────────────────────────────────╮
│ I apologize for any confusion earlier. Based on your requirements, I've updated the network policy to only allow ingress │
│ traffic from the pod labelled `app: web`. Here's the corrected network policy: │
│ │
│ ```yaml │
│ apiVersion: networking.k8s.io/v1 │
│ kind: NetworkPolicy │
│ metadata: │
│ name: leaderboard-ingress │
│ spec: │
│ podSelector: │
│ matchLabels: │
│ app: leaderboard │
│ ingress: │
│ - from: │
│ - podSelector: │
│ matchLabels: │
│ app: web │
│ ``` │
│ │
│ In this corrected version, the `ingress` section includes a `podSelector` that matches any pod with the label `app: web`. I │
│ hope this version meets your requirements. Let me know if you have any further questions! │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────── elapsed 7.554 seconds ─╯
We got there. The ingress
section now looks correct, and while I would have liked this to also include the namespace devops
in the metadata, and the policyTypes
to include ingress
, I think I can live with this.
There's one final test - can I create this network policy on OpenShift?
Success! The network policy created by my new generative AI model I trained with InstructLab successfully deploys.
Wrapping up
In this article I looked at how we can teach a generative AI model to create better Kubernetes network policy, using InstructLab. This can help reduce the burden on platform engineers and security teams, and support better securing cloud-native applications.
The model we created still has some limitations. I'm using a consumer GPU on my desktop, meaning that the synthetic data generated is low fidelity, as is the model created. It's likely I would get far better outcomes using RHEL AI and more powerful GPUs (e.g. H100 :)).
I also used a pretty generic model as the base here, granite-7b-lab
. I could use a model already tuned for code creation to potentially support better outcomes, such as the Granite code models, or Code Llama.
That's the great thing about InstructLab - it can be used to enhance any generative AI model.