Skip to content

Commit 8ef9806

Browse files
authored
[OCTRL-1081] Wrap kubectl into Mesos executor task (#805)
* OCTRL-1081 kubectl task to create rudimentary bridge to kubernetes user infor setup properly... kubectl passes arguments properly to the kubernetes attempt for fairmq bla * fixed documentation * adding kubeconfig to kubectl calls so it works even without $HOME set * fixing documentation and typos * strip quotes in kubectl_task.go * remove leftover
1 parent e613d36 commit 8ef9806

9 files changed

Lines changed: 645 additions & 15 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ There are two ways of interacting with AliECS:
196196
* Kubernetes
197197
* [Operator controller](/control-operator/README.md)
198198
* [Testing manifests](/control-operator/ecs-manifests/kubernetes-ecs.md)
199+
* [ECS bridge to Kubernetes](/docs/kubernetes_ecs.md)
199200
* Resources
200201
* T. Mrnjavac et. al, [AliECS: A New Experiment Control System for the ALICE Experiment](https://doi.org/10.1051/epjconf/202429502027), CHEP23
201202

common/controlmode/controlmode.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const (
3939
FAIRMQ
4040
BASIC
4141
HOOK
42+
KUBECTL_DIRECT
43+
KUBECTL_FAIRMQ
4244
)
4345

4446
func (cm ControlMode) String() string {
@@ -51,6 +53,10 @@ func (cm ControlMode) String() string {
5153
return "basic"
5254
case HOOK:
5355
return "hook"
56+
case KUBECTL_DIRECT:
57+
return "kubectl_direct"
58+
case KUBECTL_FAIRMQ:
59+
return "kubectl_fairmq"
5460
}
5561
return "direct"
5662
}
@@ -71,6 +77,10 @@ func (cm *ControlMode) UnmarshalText(b []byte) error {
7177
*cm = BASIC
7278
case "hook":
7379
*cm = HOOK
80+
case "kubectl_direct":
81+
*cm = KUBECTL_DIRECT
82+
case "kubectl_fairmq":
83+
*cm = KUBECTL_FAIRMQ
7484
default:
7585
*cm = DIRECT
7686
}

control-operator/README.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,76 @@
11
# operator
2-
// TODO(user): Add simple overview of use/purpose
2+
3+
Folder with operators regarding Task and Environment deployment.
34

45
## Description
5-
// TODO(user): An in-depth paragraph about your project and overview of use
6+
7+
In order to deploy Task and Environment workflows to the k8s cluster you need controllers and operators
8+
controlling custom CRDs defining ALICE custom workload. This Folder defines and implements all moving parts together with Makefile
9+
to build, deploy, install CRDs and operators.
610

711
## Getting Started
8-
You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster.
9-
**Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).
12+
13+
You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. Author had the most success with K3s [see](/docs/kubernetes_ecs.md).
14+
**Note:** Your controller will automatically use the current context in your kubeconfig (usually ~/.kube/config) file (i.e. whatever cluster `kubectl cluster-info` shows).
1015

1116
### Running on the cluster
17+
18+
Following commands show basic use of Makefile. However this isn't exhaustive list.
19+
1220
1. Install Instances of Custom Resources:
1321

1422
```sh
1523
kubectl apply -f config/samples/
1624
```
1725

18-
2. Build and push your image to the location specified by `IMG`:
26+
1. Build and push your image to the location specified by `IMG`:
1927

2028
```sh
2129
make docker-build docker-push IMG=<some-registry>/operator:tag
2230
```
2331

24-
3. Deploy the controller to the cluster with the image specified by `IMG`:
32+
1. Deploy the controller to the cluster with the image specified by `IMG`:
2533

2634
```sh
2735
make deploy IMG=<some-registry>/operator:tag
2836
```
2937

3038
### Uninstall CRDs
39+
3140
To delete the CRDs from the cluster:
3241

3342
```sh
3443
make uninstall
3544
```
3645

3746
### Undeploy controller
47+
3848
UnDeploy the controller from the cluster:
3949

4050
```sh
4151
make undeploy
4252
```
4353

4454
## Contributing
55+
4556
// TODO(user): Add detailed information on how you would like others to contribute to this project
4657

4758
### How it works
59+
4860
This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/).
4961

5062
It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/),
5163
which provide a reconcile function responsible for synchronizing resources until the desired state is reached on the cluster.
5264

5365
### Test It Out
66+
5467
1. Install the CRDs into the cluster:
5568

5669
```sh
5770
make install
5871
```
5972

60-
2. Run your controller (this will run in the foreground, so switch to a new terminal if you want to leave it running):
73+
1. Run your controller (this will run in the foreground, so switch to a new terminal if you want to leave it running):
6174

6275
```sh
6376
make run
@@ -66,6 +79,7 @@ make run
6679
**NOTE:** You can also run this in one step by running: `make install run`
6780

6881
### Modifying the API definitions
82+
6983
If you are editing the API definitions, generate the manifests such as CRs or CRDs using:
7084

7185
```sh
@@ -91,4 +105,3 @@ distributed under the License is distributed on an "AS IS" BASIS,
91105
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92106
See the License for the specific language governing permissions and
93107
limitations under the License.
94-

core/task/scheduler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1432,7 +1432,8 @@ func makeTaskForMesosResources(
14321432
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", "OCC_CONTROL_PORT", controlPort))
14331433
}
14341434

1435-
if cmd.ControlMode == controlmode.FAIRMQ {
1435+
if cmd.ControlMode == controlmode.FAIRMQ ||
1436+
cmd.ControlMode == controlmode.KUBECTL_FAIRMQ {
14361437
cmd.Arguments = append(cmd.Arguments, "--control-port", strconv.FormatUint(controlPort, 10))
14371438
}
14381439

core/task/task.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,9 @@ func (t *Task) BuildTaskCommand(role parentRole) (err error) {
286286
if class.Control.Mode == controlmode.BASIC ||
287287
class.Control.Mode == controlmode.HOOK ||
288288
class.Control.Mode == controlmode.DIRECT ||
289-
class.Control.Mode == controlmode.FAIRMQ {
289+
class.Control.Mode == controlmode.FAIRMQ ||
290+
class.Control.Mode == controlmode.KUBECTL_DIRECT ||
291+
class.Control.Mode == controlmode.KUBECTL_FAIRMQ {
290292
var varStack map[string]string
291293

292294
// First we get the full varStack from the parent role, and
@@ -393,7 +395,8 @@ func (t *Task) BuildTaskCommand(role parentRole) (err error) {
393395
}
394396
}
395397

396-
if class.Control.Mode == controlmode.FAIRMQ {
398+
if class.Control.Mode == controlmode.FAIRMQ ||
399+
class.Control.Mode == controlmode.KUBECTL_FAIRMQ {
397400
// FIXME read this from configuration
398401
// if the task class doesn't provide an id, we generate one ourselves
399402
if !utils.StringSliceContains(cmd.Arguments, "--id") {
@@ -635,7 +638,9 @@ func (t *Task) BuildPropertyMap(bindMap channel.BindMap) (propMap controlcommand
635638

636639
// For FAIRMQ tasks, we append FairMQ channel configuration
637640
if class.Control.Mode == controlmode.FAIRMQ ||
638-
class.Control.Mode == controlmode.DIRECT {
641+
class.Control.Mode == controlmode.DIRECT ||
642+
class.Control.Mode == controlmode.KUBECTL_DIRECT ||
643+
class.Control.Mode == controlmode.KUBECTL_FAIRMQ {
639644
for _, inbCh := range channel.MergeInbound(parent.CollectInboundChannels(), class.Bind) {
640645
// We get the FairMQ-formatted propertyMap from the inbound channel spec
641646
var chanProps controlcommands.PropertyMap

docs/kubernetes_ecs.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# ECS with Kubernetes
2+
3+
> ⚠️ **Warning**
4+
> All Kubernetes work done is in a stage of prototype.
5+
6+
## Kubernetes Cluster
7+
8+
While prototyping we used many Kubernetes clusters, namely [`kind`](https://kind.sigs.k8s.io/), [`minikube`](https://minikube.sigs.k8s.io/docs/) and [`k3s`](https://k3s.io/)
9+
in both local and remote cluster deployment. We used Openstack for remote deployment.
10+
Follow the guides at the individual distributions in order to create the desired cluster setup.
11+
k3s is recommended to run this prototype, as it is lightweight
12+
and easily installed distribution which is also [`CNCF`](https://www.cncf.io/training/certification/) certified.
13+
14+
All settings of `k3s` were used as default except one: locked-in-memory size. Use `ulimit -l` to learn
15+
what is the limit for the current user and `LimitMEMLOCK` inside the k3s systemd service config
16+
to set it for correct value. Right now the `flp` user has unlimited size (`LimitMEMLOCK=infinity`).
17+
This config is necessary because even if you are running Pods with the privileged security context
18+
under user flp, Kubernetes still sets limits according to its internal settings and doesn't
19+
respect linux settings.
20+
21+
Another setup we expect at this moment to be present at the target nodes
22+
is ability to run Pods with privileged permissions and also under user `flp`.
23+
This means that the machine has to have `flp` user setup the same way as
24+
if you would do the installation with [`o2-flp-setup`](https://alice-flp.docs.cern.ch/Operations/Experts/system-configuration/utils/o2-flp-setup/).
25+
26+
## Task Controller
27+
28+
Following text assumes that there is a Task Controller from `control-operator` running
29+
at your K8s cluster and Task CRD installed at your cluster.
30+
You can find the details about the usage in the [documentation](/control-operator/README.md).
31+
32+
## Running tasks (`KubectlTask`)
33+
34+
ECS is setup to run tasks through Mesos on all required hosts baremetal with active
35+
task management (see [`ControllableTask`](/executor/executable/controllabletask.go))
36+
and OCC gRPC communication. When running docker task through ECS we could easily
37+
wrap command to be run into the docker container with proper settings
38+
([see](/docs/running_docker.md)). This is however not possible for Kubernetes
39+
workloads as the Pods are "hidden" inside the cluster. So we plan
40+
to deploy our own Task Controller which will connect to and guide
41+
OCC state machine of required tasks. Thus we need to create custom
42+
POC way to communicate with Kubernetes cluster from Mesos executor.
43+
44+
The reason why we don't call Kubernetes cluster directly from ECS core
45+
is that ECS does a lot of heavy lifting while deploying workloads,
46+
monitoring workloads and by generating a lot of configuration which
47+
is not trivial to replicate manually. However, if we create some class
48+
that would be able to deploy one task into the Kubernetes and monitor its
49+
state we could replicate `ControllableTask` workflow and leave ECS
50+
mostly intact for now, save a lot of work and focus on prototyping
51+
Kubernetes operator pattern.
52+
53+
Thus [`KubectlTask`](/executor/executable/kubectltask.go) was created. This class
54+
is written as a wrapper around `kubectl` utility to manage Kubernetes cluster.
55+
It is based on following `kubectl` commands:
56+
57+
* `apply` => `kubectl apply -f manifest.yaml` - deploys resource described inside given manifest
58+
* `delete` => `kubectl delete -f manifest.yaml` - deletes resource from cluster
59+
* `patch` => `kubectl patch -f exampletask.yaml --type='json' -p='[{"op": "replace", "path": "/spec/state", "value": "running"}]` - changes the state of resource inside cluster
60+
* `get` => `kubectl get -f manifest.yaml -o jsonpath='{.spec.state}'` - queries exact field of resource (`state` in the example) inside cluster.
61+
62+
These four commands allow us to deploy and monitor status of the deployed
63+
resource without necessity to interact with it directly. However `KubectlTask`
64+
expects that resource is the CRD [Task](/control-operator/api/v1alpha1/task_types.go).
65+
66+
In order to activate `KubectlTask` you need to change yaml template
67+
inside the `ControlWorkflows` directory. Namely:
68+
69+
* add path to the kubectl manifest as the first argument in `.command.arguments` field
70+
* change `.control.mode` to either `kubectl_direct` or `kubectl_fairmq`
71+
You can find working template inside `control-operator/ecs-manifests/control-workflows/*-kube.yaml`
72+
73+
Working kubectl manifests are to be found in `control-operator/ecs-manifests/kubernetes-manifests`.
74+
You can see `*test.yaml` for concrete deployable manifests by `kubectl apply`, the rest
75+
are the templates with variables to be filled in in a `${var}` format. `KubectlTask`
76+
fills these variables from env vars.

0 commit comments

Comments
 (0)