Skip to content

Commit a045141

Browse files
authored
add nested kubernetes example (#163)
* add nested kubernetes example welcome to the matrix! Here we are running a Kubernetes cluster with the flux operator, setting up K3s inside of it running across the nodes, and then deploying a deployment WITHIN that cluster. It is nuts, yes. Signed-off-by: vsoch <[email protected]>
1 parent 72aac4d commit a045141

File tree

7 files changed

+342
-4
lines changed

7 files changed

+342
-4
lines changed

docs/tutorials/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ The following tutorials are provided from their respective directories (and are
2020

2121
- [Parsl](https://github.com/flux-framework/flux-operator/tree/main/examples/launchers/parsl)
2222

23+
### Experimental
24+
25+
#### Nested
26+
27+
- [K3s](https://github.com/flux-framework/flux-operator/tree/main/examples/nested/k3s/basic): instiatiate k3s inside Flux, and deploy an app.
28+
29+
2330
### Machine Learning
2431

2532
- [Fireworks](https://github.com/flux-framework/flux-operator/blob/main/examples/machine-learning/fireworks)
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# K3s
2+
3+
This is a small experiment to try and run kubernetes within the Flux operator,
4+
which of course is within Kubernetes! This is a "quasi" Kubernetes
5+
because we are going to try using k3s.
6+
7+
## Background
8+
9+
For some background, I was during a survey of [tooling available](https://github.com/converged-computing/operator-experiments/tree/main/google/rootless-kubernetes)
10+
and stumbled on a few attributes of k3s that I liked:
11+
12+
- I could run it in rootless mode
13+
- I could get it working in docker-compose
14+
- It would have separate commands to start a main server and register an agent
15+
16+
These qualities (I think) made it my first contender to try within Flux.
17+
Right now I don't have a good sense of the limitations of an HPC environment,
18+
but I can comment on the general challenges that I saw for each approach:
19+
20+
- cgroups2 is required
21+
- if containerization is required, might be challenging due to systemctl usage
22+
23+
However, I do think there might be an avenue to pursue making this work on a more
24+
traditional HPC system, and likely with a container. I don't understand
25+
or know these environments well, so for now I decided to try in the Flux operator.
26+
My strategy was first to try running k3s through a singularity container,
27+
and that didn't work because of permissions. Then I decided to get k3s installed in a container with Flux
28+
that could be used as a base image for the operator, and that's the approach
29+
I'm taking here. We are currently running flux as root because I'm trying
30+
to reproduce the docker-compose exampel (that works) and hopefully we can
31+
step away from this!
32+
33+
## Usage
34+
35+
First, let's create a kind cluster. From the context of this directory:
36+
37+
```bash
38+
$ kind create cluster --config ../../../kind-config.yaml
39+
```
40+
41+
And then install the operator, create the namespace, and apply the MiniCluster YAML here.
42+
43+
```bash
44+
$ kubectl apply -f ../../../dist/flux-operator.yaml
45+
$ kubectl create namespace flux-operator
46+
$ kubectl apply -f ./minicluster.yaml
47+
```
48+
49+
If you watch the broker logs, a "successful state" is when you see a ton of output but nothing
50+
is exiting. Once I saw it look consistent, I shelled in to the broker to try and apply
51+
a yaml file to the k3s cluster within the Flux cluster (weird, right?)
52+
53+
```bash
54+
$ kubectl exec -it -n flux-operator flux-sample-0-jlsp6 bash
55+
$ flux proxy local:///run/flux/local bash
56+
```
57+
58+
These commands are from the bottom of the [start.sh](start.sh)
59+
script, which technically will never be reached if the agents and server are running
60+
without issue. Note that we can first try to look at our cluster:
61+
62+
```bash
63+
$ kubectl --kubeconfig=./kubeconfig.yaml get nodes
64+
NAME STATUS ROLES AGE VERSION
65+
flux-sample-2 Ready <none> 3m38s v1.27.1+k3s-b32bf495
66+
flux-sample-3 Ready <none> 3m38s v1.27.1+k3s-b32bf495
67+
flux-sample-0 Ready control-plane,master 3m44s v1.27.1+k3s-b32bf495
68+
flux-sample-1 Ready <none> 3m38s v1.27.1+k3s-b32bf495
69+
```
70+
71+
And you can sanity check it's not hitting the local machine!
72+
73+
```bash
74+
# kubectl get -n flux-operator pods
75+
No resources found in flux-operator namespace.
76+
```
77+
78+
Note that it would show this message even for namespaces that don't exist.
79+
This still feels really weird because I'm looking at the same pods that I'm running on my machine as they
80+
are seen in the container... but they are Kubernetes pods running a Flux node, and the Flux node running
81+
another Kubernetes agent (kubelet). So they are [same, same, but different, but still same](https://youtu.be/7tTfL-DtpXk)! 😂️
82+
An easy indicator is looking at the timestamps andn node names - they were created after, and they don't have the indexed job
83+
random letters after the hostname, they are just the hostnames seen by Flux. Here are the pods from the outside
84+
(on my local machine) for comparison:
85+
86+
```bash
87+
kubectl get -n flux-operator pods
88+
NAME READY STATUS RESTARTS AGE
89+
flux-sample-0-k6xvs 1/1 Running 0 109s
90+
flux-sample-1-xk5x9 1/1 Running 0 109s
91+
flux-sample-2-bfwkr 1/1 Running 0 109s
92+
flux-sample-3-kktlk 1/1 Running 0 109s
93+
```
94+
95+
I also ran into a weird bug I couldn't explain - it had the default namespace as "flux-operator"
96+
and this is probably inherited somewhere (and I didn't look into it) but instead I decided
97+
to just set it back to be default:
98+
99+
```bash
100+
kubectl --kubeconfig=./kubeconfig.yaml config set-context --current --namespace=default
101+
```
102+
103+
I'm not super worried because We likely won't be dealing with this on an actual Flux cluster (that doesn't have a second layer of Kubernetes)
104+
and this is where I'd like to test it next. Despite that weirdness,
105+
everything actually works:
106+
107+
```bash
108+
$ kubectl --kubeconfig=./kubeconfig.yaml apply -f my-echo.yaml
109+
until kubectl --kubeconfig=./kubeconfig.yaml rollout status deployment my-echo; do sleep 1; done
110+
deployment "my-echo" successfully rolled out
111+
```
112+
113+
And I actually found I don't need to target the kubeconfig.yaml, it seems to hit the right cluster
114+
either way:
115+
116+
```bash
117+
root@flux-sample-0:/workflow# kubectl get deploy
118+
NAME READY UP-TO-DATE AVAILABLE AGE
119+
my-echo 1/1 1 1 29s
120+
```
121+
```bash
122+
$ kubectl get pods
123+
NAME READY STATUS RESTARTS AGE
124+
my-echo-74dc6c4f7b-lh89p 1/1 Running 0 27s
125+
```
126+
127+
But not on my local machine! So we are getting somewhere:
128+
129+
```bash
130+
$ kubectl get deploy
131+
No resources found in default namespace.
132+
```
133+
134+
I don't know how an echoserver is supposed to work, but we would need to
135+
expose the port to see the service. For now we can inspect that the server is running:
136+
137+
```bash
138+
$ curl http://127.0.0.1:8080
139+
```
140+
```console
141+
Hostname: my-echo-74dc6c4f7b-lh89p
142+
143+
Pod Information:
144+
-no pod information available-
145+
146+
Server values:
147+
server_version=nginx: 1.13.3 - lua: 10008
148+
149+
Request Information:
150+
client_address=10.42.0.4
151+
method=GET
152+
real path=/
153+
query=
154+
request_version=1.1
155+
request_uri=http://127.0.0.1:8080/
156+
157+
Request Headers:
158+
accept=*/*
159+
host=127.0.0.1:8080
160+
user-agent=curl/7.68.0
161+
162+
Request Body:
163+
-no body in request-
164+
```
165+
166+
To step back - we have Flux running inside Kubernetes, and now a dummy Kubernetes
167+
running inside Flux. The four nodes in the cluster are registered, with one control
168+
plane and three agents. We will want to test this next on a Flux cluster that doesn't
169+
have an external Kubernetes wrapper, and then likely run something that is more like an HPC job.
170+
Once that is working, we will want to slowly step back and figure out the steps necessary
171+
to run this entirely rootless. I've gotten [k3s working with rootless](https://github.com/converged-computing/operator-experiments/tree/main/google/rootless-kubernetes/k3s)
172+
but it has a few more steps! You can then run kubernetes commands as you please!
173+
174+
I had a death wish so I installed the operator again...
175+
176+
```bash
177+
cd /tmp
178+
wget https://raw.githubusercontent.com/flux-framework/flux-operator/main/examples/dist/flux-operator.yaml
179+
kubectl --kubeconfig=/workflow/kubeconfig.yaml apply -f flux-operator.yaml
180+
```
181+
182+
Is the operator pod running?
183+
184+
```bash
185+
root@flux-sample-0:/tmp# kubectl --kubeconfig=/workflow/kubeconfig.yaml get -n operator-system pods
186+
NAME READY STATUS RESTARTS AGE
187+
operator-controller-manager-658b4c6787-7stwv 2/2 Running 0 46s
188+
```
189+
190+
!!! Let's try applying a Minicluster with hello world...
191+
192+
```bash
193+
$ wget https://raw.githubusercontent.com/flux-framework/flux-operator/main/examples/tests/hello-world/minicluster.yaml
194+
```
195+
```bash
196+
$ kubectl --kubeconfig=/workflow/kubeconfig.yaml apply -f minicluster.yaml
197+
minicluster.flux-framework.org/flux-sample created
198+
```
199+
200+
Is it running?
201+
202+
```bash
203+
root@flux-sample-0:/tmp# kubectl --kubeconfig=/workflow/kubeconfig.yaml get pods -n flux-operator
204+
NAME READY STATUS RESTARTS AGE
205+
my-echo-74dc6c4f7b-snkt9 1/1 Running 0 6m1s
206+
flux-sample-0-x45q6 0/1 ContainerCreating 0 16s
207+
flux-sample-2-7wltv 0/1 ContainerCreating 0 16s
208+
flux-sample-3-l668b 0/1 ContainerCreating 0 16s
209+
flux-sample-1-8t6fd 0/1 ContainerCreating 0 16s
210+
```
211+
212+
WHAT IS HAPPENING! 🤣️ I'm going to stop here because I'm afraid of it actually pulling this
213+
second layer of (rather large) container with Flux, already in a container, and we will
214+
embark on this second layer of the onion once we have addressed the issues above and
215+
tested in different environments. Let's clean up before we do something that we will regret!
216+
217+
```bash
218+
$ kubectl delete -f minicluster.yaml
219+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: v1
2+
clusters:
3+
- cluster:
4+
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyT0RNME9UVTJNRFl3SGhjTk1qTXdOVEEzTWpFME1EQTJXaGNOTXpNd05UQTBNakUwTURBMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyT0RNME9UVTJNRFl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFRMm9tWStmbXV3UGxIYlFiWFg0ZUNVTUs1L2tSaEVYeHVPSGVSSElQV1cKV1l2bGs4L1ZtRFROYi8rdkZNemg1bENrNnJKbmltL1R0eWovUit1QUxDWXdvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVVRrcllydWZsYWUyTXM5RzdhWTBPCnZVNjJ3Y1V3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnR2ptNjV5eVZ2SFhadHl5ZDJDTDR4WC9oQTFTWmlyaGoKTEo3QkcrNHNLT2dDSUQ5NCtEekZubFBmMlA5TXJ1dXpDa0tOTGlQWG92T1NKV2FGa3RNSUlIZG4KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
5+
server: https://127.0.0.1:6443
6+
name: default
7+
contexts:
8+
- context:
9+
cluster: default
10+
namespace: default
11+
user: default
12+
name: default
13+
current-context: default
14+
kind: Config
15+
preferences: {}
16+
users:
17+
- name: default
18+
user:
19+
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJYzU4b01lYkZ0cG93Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOamd6TkRrMU5qQTJNQjRYRFRJek1EVXdOekl4TkRBd05sb1hEVEkwTURVdwpOakl4TkRBd05sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJGeE8vdkxHZzNHN1U0MWQKYnVyL1N3Q0thSnhkR1ZhMDZxclB1dWcwSlpJRkVienZVS09ETjJtN1lrZVMyVnQ5UkgwM0IvRU5IUitCdGh5NgpKN3BUZHpxalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCU2NYMkl1Y0RqSkpBWXNuNUppQkJxMjhNY3FhakFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlBdlE0REJTVzN3emxycThXdnR3aWxEWkE5d20rQTZYb3llTWVrSjJJbkF6Z0loQUxlUDZRTnYwa1hkSWU2WQpaK1czWHJhZnpLaldxZEJLaUhBcUE4NVIzLzRNCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUyT0RNME9UVTJNRFl3SGhjTk1qTXdOVEEzTWpFME1EQTJXaGNOTXpNd05UQTBNakUwTURBMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUyT0RNME9UVTJNRFl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTWXhSYjh1U0RtTVJRM09lTkoxSFlKWlh4eXVWTjl0LzZ3emdQaVBtSkMKWG03RjZBc1hSYlRaZXYzeGIzRER0NnRzcXk4N1doNHY5K0FFeTFZNkVzQXJvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVW5GOWlMbkE0eVNRR0xKK1NZZ1FhCnR2REhLbW93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnYmorcndqZDJaS3p0WDNYYlNUd1FMU0l6TDlLMzg0TGwKQVp1MmhCN0lrMXdDSVFDbGpKMXA1bFRlcUwyK0JuWkV6enlqQnc1ZEc2Y0M2SlQ2NDNhek92cGwvdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
20+
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUc3NkJFeUpxMHlWTWhYTndYQkFuUzhKcS9ibVU2WktvaXlmWFNPY0dPeW1vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWEU3KzhzYURjYnRUalYxdTZ2OUxBSXBvbkYwWlZyVHFxcys2NkRRbGtnVVJ2TzlRbzRNMwphYnRpUjVMWlczMUVmVGNIOFEwZEg0RzJITG9udWxOM09nPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
apiVersion: flux-framework.org/v1alpha1
2+
kind: MiniCluster
3+
metadata:
4+
name: flux-sample
5+
namespace: flux-operator
6+
spec:
7+
size: 4
8+
# ensure the job is launcher to each of the nodes
9+
# a server will be started on flux-sample-0, otherwise an agent
10+
tasks: 4
11+
logging:
12+
quiet: true
13+
14+
volumes:
15+
data:
16+
storageClass: hostpath
17+
path: /tmp/workflow
18+
19+
containers:
20+
- image: ghcr.io/rse-ops/k3s:tag-focal
21+
workingDir: /workflow
22+
command: /bin/bash ./start.sh
23+
24+
# Doing this for now to try and reproduce docker-compose setup
25+
commands:
26+
runFluxAsRoot: true
27+
28+
# Note that agent/server specific environment is in start.sh
29+
environment:
30+
PYTHONPATH: /usr/lib/python3.10/site-packages
31+
LD_LIBRARY_PATH: /opt/conda/lib
32+
33+
ports:
34+
- 6443
35+
- 8080
36+
37+
volumes:
38+
data:
39+
path: /workflow
40+
41+
# Container flux owner is "fluxuser"
42+
fluxUser:
43+
name: fluxuser
44+
45+
securityContext:
46+
privileged: true
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
apiVersion: v1
3+
kind: Service
4+
metadata:
5+
name: my-echo
6+
spec:
7+
selector:
8+
app: my-echo
9+
ports:
10+
- protocol: TCP
11+
port: 8080
12+
targetPort: 8080
13+
type: LoadBalancer
14+
15+
---
16+
apiVersion: apps/v1
17+
kind: Deployment
18+
metadata:
19+
name: my-echo
20+
labels:
21+
app: my-echo
22+
spec:
23+
replicas: 1
24+
selector:
25+
matchLabels:
26+
app: my-echo
27+
template:
28+
metadata:
29+
labels:
30+
app: my-echo
31+
spec:
32+
containers:
33+
- name: my-echo
34+
image: docker.io/vanessa/my-echo:latest
35+
ports:
36+
- containerPort: 8080

examples/nested/k3s/basic/start.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
name=$(hostname)
4+
echo "Hello I am ${name}"
5+
if [[ "${name}" == "flux-sample-0" ]]; then
6+
export K3S_TOKEN=secret
7+
export K3S_KUBECONFIG_OUTPUT=/workflow/kubeconfig.yaml
8+
export K3S_KUBECONFIG_MODE=666
9+
/bin/k3s server
10+
else
11+
export K3S_URL="https://flux-sample-0.flux-service.flux-operator.svc.cluster.local:6443"
12+
export K3S_TOKEN=secret
13+
/bin/k3s agent
14+
fi

sdk/python/v1alpha1/.openapi-generator/FILES

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,4 @@ setup.cfg
5353
setup.py
5454
test-requirements.txt
5555
test/__init__.py
56-
test/test_mini_cluster.py
57-
test/test_mini_cluster_container.py
58-
test/test_mini_cluster_list.py
59-
test/test_mini_cluster_spec.py
6056
tox.ini

0 commit comments

Comments
 (0)