Compare commits

..

50 Commits

Author SHA1 Message Date
Ganesh Maharaj Mahalingam f9b260088d Update README to take not of k8s-migration package (#334)
Signed-off-by: Ganesh Maharaj Mahalingam <ganesh.mahalingam@intel.com>
2021-03-17 13:26:12 -07:00
Mark Horn 5ca64545b1 Disable swap before OS Update (#333)
Mask all swap targets.

Signed-off-by: Mark D Horn <mark.d.horn@intel.com>
2020-12-17 10:38:43 -08:00
Hyunsun Moon e74b3ca892 Add support of CNI version 0.3.1 to vfioveth
Tested with K8S v1.18.6 and the latest multus stable image.
2020-09-10 15:14:19 -07:00
António Meireles 55b2aa2d19 accommodate upstream libvirt boxes changes in a forward and backward way (#330)
per https://github.com/AntonioMeireles/ClearLinux-packer/issues/24 the
ClearLinux libvirt boxes default size was lowered from the previous 40G
to a more manageable 5G, having the user the ability to, at box
instantiation, to increase that value to whatever is adequate.

In order to avoid surprises in this side the Vagrantfile was modded to
have hardcoded the previous default value.

This change is forward and backward compatible as it will just be
ignored when using older boxes ( since the 'new' root volume size just
the same as original one) and will resize the box to the expected 40G in
newer ones.

Signed-off-by: António Meireles <antonio.meireles@reformi.st>
2020-08-19 14:23:51 -07:00
Ganesh Maharaj Mahalingam aad050f944 Switch private network for Vagrant VMs (#324)
Previously used IP range seems to be allocated to NASA and probably not
a good idea to use it here. Switching the private network to be part of
the IPs allocated to private networks.

Signed-off-by: Ganesh Maharaj Mahalingam <ganesh.mahalingam@intel.com>
2020-06-15 09:40:22 -07:00
David Lyle a872ccca6c adding support for HA clusters, using HAProxy (#317)
* adding support for HA clusters, using HAProxy

Signed-off-by: David Lyle <dklyle0@gmail.com>

* fixing typo

* fixing load balancer port default value
2020-06-15 09:38:34 -07:00
Saikrishna Edupuganti 1525407bd4 Update multi-net components to latest releases (#329)
* Update multi-net components to latest releases

Multus CNI 3.4.2
SR-IOV CNI 2.3
SR-IOV DP  3.2

Tested as per the README. Works fine.

Signed-off-by: Saikrishna Edupuganti <saikrishna.edupuganti@intel.com>

* Provide dpdk-stable 3 LTS and 1 recent release

Testpmd manifests of the last 3 LTS and 1 latest release from stable
repo to help test actual DPDK app instead of sleep.

Currently 17.11 and 19.11 are the only functioning ones without
privileged.

```
NAME              READY   STATUS    RESTARTS   AGE
dpdk-1711         1/1     Running   0          3m5s
dpdk-1811         0/1     Error     0          3m5s
dpdk-1911         1/1     Running   0          3m5s
dpdk-2002         0/1     Error     0          3m5s
```

```
EAL: PCI device 0000:07:06.4 on NUMA socket 0
EAL:   probe driver: 8086:154c net_i40e_vf
EAL: Getting a vfio_dev_fd for 0000:07:06.4 failed
EAL: Requested device 0000:07:06.4 cannot be used
…
testpmd: No probed ethernet devices
EAL: Error - exiting with code: 1
  Cause: Invalid port 1
```

Signed-off-by: Saikrishna Edupuganti <saikrishna.edupuganti@intel.com>
2020-06-05 09:06:08 -07:00
Miguel Bernal Marin 9b4f0a8582 setup_system: use local admin path for proxy.conf (#328)
Currently the system.conf.d/proxy.conf file is saved at /usr/lib
which is the vendor path, and can be dropped by
"swupd repair --picky --force".

This commit creates the local administrator /etc/systemd/system.conf.d
directory and store the proxy.conf inside.
2020-05-26 10:45:25 -07:00
Antti Kervinen 696861ce66 metrics: change collectd output to host /opt/collectd/run
Currently we loose collectd data from a node when scaling ends to a
system failure on the node - yet this data can be very helpful in root
causing the failure. This patch changes collectd configuration so that
the output will be continuously written to host filesystem instead of
the collectd container overlay that will be lost unless scaling
reaches graceful exit.

Signed-off-by: Antti Kervinen <antti.kervinen@intel.com>
2020-05-19 19:56:54 +01:00
Graham Whaley 07fd8412da metrics: report: Error more cleanly
Clean up the rest of the report R files to allow them to quit
cleanly when they find an error or missing data, so that the
final PDF report gives meaninful errors such as 'No data found',
rather than cryptic R errors.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2020-05-19 19:53:23 +01:00
Graham Whaley 058e1753ae metrics: report: quit cleanly on tidy_scaling failure
When there are no files to process, we tend to quit with a loud
and not helpful error. Improve that by spotting the obvious error
cases (such as no files to process for a specific test), and quit
with a nicer error/warning message that ends up in the rendered
report.

Start with the tidy_scaling test. The only clean way to quit a
fragment of Rmarkdown R looks to be to place it inside a function
so we can 'return'. Otherwise, all other forms of 'quit', quit the
whole Rmarkdown render pipeline, which is not what we want - we
want to carry on and try to process the rest of the fragments for
the rest of the tests.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2020-05-19 19:53:23 +01:00
Graham Whaley 091e76c3d8 metrics: k8s_scale_net: whitespace fixes
Fix some indentation that had gone rogue.
Note, there are other whitespace fixes that can be done in this file,
it appears to have a mix of tabs and spaces.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2020-05-15 09:11:37 -06:00
Graham Whaley 7121418dd3 metrics: Imrove documentation
Improve and expand the documents across the metrics subsystem.
Clarify and re-order some documents. Add some more details around
each individual test.
Note that only the 'rapid' test is currently actively used, and the
other tests may need some nurturing if they are found to be useful.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2020-05-15 09:11:37 -06:00
Gabriela Cervantes e10260e99c metrics: Use a specific version of rocker/tidyverse
This PR uses a specific version of rocker/tidyverse as the latest version
does not have the latex-xcolor package which makes impossible to create
the metrics report.

Signed-off-by: Gabriela Cervantes <gabriela.cervantes.tellez@intel.com>
2020-05-13 15:18:05 +01:00
CraigSterrett e732cc693b Updated metrics github location (#323)
The metrics-server package has moved out of the kubernetes incubator
github location and is now in the sigs location.

Signed-off-by: Craig Sterrett <craig.Sterrett@intel.com>
2020-05-06 13:00:07 -07:00
Hyunsun Moon cf9b85cf03 Update vfioveth CNI to add VF device id as alias 2020-04-15 11:20:27 -07:00
CraigSterrett a82b9d9601 Modified setup_system to use Systemd level proxy (#316)
Modified setup_system.sh to use Systemd level proxy instead of per
service proxy.
Tested create_stack all with defaults and ran Sonobuoy successfully against change.

Closes issue #307

Signed-off-by: Craig Sterrett <craig.Sterrett@intel.com>
2020-04-03 09:09:30 -07:00
CraigSterrett 96978c5228 Rook updated to v1.2.6 (#315)
Rook updated to V1.2.6

Closes issue #312
https://github.com/clearlinux/cloud-native-setup/issues/312

Signed-off-by: Craig Sterrett <craig.Sterrett@intel.com>
2020-03-27 11:22:17 -07:00
CraigSterrett 8c17c4b47c removed v0.8.3 of rook (#314)
Rook v0.8.3 was found to be not working and is too old to continue
supporting.

Signed-off-by: Craig Sterrett <craig.Sterrett@intel.com>
2020-03-26 10:25:13 -07:00
CraigSterrett 514efd6592 Fix rook single node setup (#313)
* Fix rook single node setup

Modified the rook installation to support both multinode kubernetes
clusters and standalone kubernetes clusters. Multinode installations
will occur as before, with changes for standalone installations.
v0.8.3 I found it was not currently working and
is too old to continue to support, will submit a PR for removing it. Also made a couple minor spacing
changes to yaml as detected by yamllint

closes issue 306 https://github.com/clearlinux/cloud-native-setup/issues/306
closes issue 311 https://github.com/clearlinux/cloud-native-setup/issues/311

Signed-off-by: Craig Sterrett <craig.Sterrett@intel.com>

* Fix rook single node setup

Modified the rook installation to support both multinode kubernetes
clusters and standalone kubernetes clusters. Multinode installations
will occur as before, with changes for standalone installations.
v0.8.3 I found it was not currently working and
is too old to continue to support, will submit a PR for removing it. Also made a couple minor spacing
changes to yaml as detected by yamllint

closes issue 306 clearlinux#306
closes issue 311 clearlinux#311

Signed-off-by: Craig Sterrett <craig.Sterrett@intel.com>
2020-03-26 10:07:18 -07:00
Julio Rivera 603c42703f Add initial Jenkinsfile (#301)
Signed-off-by: Rivera Gonzalez, Julio C <julio.c.rivera.gonzalez@intel.com>
2020-02-12 09:44:18 -08:00
Khanak Nangia 52d1a8406b Updating flannel (#299) 2020-01-11 01:04:53 -08:00
CraigSterrett 61b8702472 Added --force flag to swupd repair command (#298)
Running setup_system.sh on a system setting the OS version to keep setup_system from upgrading the OS causes an error because a package has been removed. Need to add the --force flag to the
sudo swupd repair -m "${CLR_VER}" --picky command

Closes issue #297
2020-01-09 14:22:39 -08:00
Justin Scott 00c1d60470 Update kubeadm.yaml to 1.17 version (#296)
Closes #295

Signed-off-by: Justin Scott <justin.a.scott@intel.com>
2020-01-08 12:43:57 -08:00
Khanak Nangia 07c2231e62 Updating ingress-nginx to v0.26.1 (#285)
* Updating ingress-nginx to v0.26.1

* removing extra line
2019-12-09 10:44:25 -08:00
Morales Quispe, Marcela 4ef8d34671 Make and rename net server process variables configurable
Some CNIs takes longer for its related deployments to become ready, that
is why `proc_wait_time` needs to be customized. Now `proc_wait_time` can
be set at execution time and has a default value too for time to pod
network test harness.

Signed-off-by: Morales Quispe, Marcela <marcela.morales.quispe@intel.com>
2019-12-09 14:56:29 +00:00
Khanak Nangia bc0f257176 Updating metrics to v0.3.6 (#286) 2019-12-05 14:47:46 -08:00
Khanak Nangia e70e32d36e Updating MetalLB to v0.8.3 (#284) 2019-12-05 14:40:11 -08:00
Khanak Nangia 518fa87f27 Updating cilium to v1.6.4 (#289) 2019-12-05 14:39:46 -08:00
Khanak Nangia 6cd87d74be Updating rook to v1.1.7 (#283) 2019-12-05 12:36:42 -08:00
Morales Quispe, Marcela 927ceddc9c Add time to pod network metric.
To measure the time to pod network, a deployment that uses agnhost
image is used, which get exposed as a net server and replies to curl
calls, the test measure this reply time and saves it for further reporting.
Then, only the exposed net service gets deleted.

Signed-off-by: Morales Quispe, Marcela <marcela.morales.quispe@intel.com>
2019-12-05 11:00:41 +00:00
Graham Whaley 6298cf2054 metrics: tidy: widen the graphs
Move the legends to the bottom (underneath) for the tidy scaling graphs
to make them wider on the page, and thus easier to read with more
resolution.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-12-04 10:02:36 +00:00
Graham Whaley acf5a95177 metrics: tidy: move local assign inside loop
The bootdata assignments were outside the 'valid file' check loop,
which meant in the case there was a data directory which did not
contain a valid scaling file, we would fail the assignment (as the
`local_bootdata` would be empty).

Fix by moving the assignments into the loop, thus only assigning when
we know we have valid data.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-12-04 10:02:36 +00:00
Graham Whaley 68a62f50bc metrics: report: improve interface y axis divs
Most of the time we have 0 interface errors or drops, so we pin the y
scale to '1', so we don't hit 'infinity' errors. That left us with a
strange y-axis label anomoly - as the axis was automatically divided
into 5 labels, and we got for some reason the sequence '0,0,0,1,1'.
That just plain looked wrong and confusing.
Fix it by using `pretty_breaks()` for the error/drop y axis, whilst
maintaining the `comma` count for the pod count y axis.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-12-04 10:02:36 +00:00
Graham Whaley 9431dd9f38 metrics: report: shrink page margins for more resolution
The pdf output by default has large page margins, which wastes a lot of
page space, and reduces our 'resolution'. Shrink the margins to a pretty
minimal 1cm to increase the graph resolution. The document itself then
does not look as 'pretty', but we can see more data visually.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-12-04 10:02:36 +00:00
Graham Whaley 9b8c7c093f metrics: collectd: move legends under graphs
Move the legends under the graphs to give more width, and thus
resolution, to the final pictures.
This works well for the collectd graphs as they are spread out
into sets of single column graphs per page.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-12-04 10:02:36 +00:00
Syed Ahsan bdcb4fb5b7 Update Kata to v1.9.1 (#282)
This patch updates Kata to use v 1.9.1 and adds the kustomization.

Signed-off: Syed Ahsan <syed.ahsan.shamim.zaidi@intel.com>
2019-12-02 16:34:10 -08:00
Eric Ernst a0ca2a2017 set the snapshotter to devmapper in setup script
Signed-off-by: Eric Ernst <eric.ernst@intel.com>
2019-11-25 13:29:13 -08:00
Obed N Munoz 46b3f230ee scaling: Remove tty parameter in report's generation cmd
This is in order to avoid tty-related issues in our CI systems
which by default is not supporting tty. With this change we'll
avoid the following faling report's generation `docker run` command.
```
the input device is not a TTY
```

Signed-off-by: Obed N Munoz <obed.n.munoz@intel.com>
2019-11-22 09:17:03 +00:00
Graham Whaley b6c7cf1b8e metrics: make pods_per_gb valid JSON
Under some circumstances, the pod_per_gb value would come out as <0,
and be generated without any leading 0's (such as `.14` rather than
`0.14`). This is not valid JSON, and would break the report generation
parsing in R.

Use `printf` to force a leading 0 prefix onto the value.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-11-21 08:48:40 -07:00
Syed Ahsan f146c771cc Using consistent ENV variable for RUNNER (#273)
Earlier there was confusion between CLRK8S_RUNNER and RUNNER in
setup_system.sh and create_stack.sh script, this patch fixes this
and now the variable can either be RUNNER/CLRK8S_RUNNER.

Signed-off: syed.ahsan.shamim.zaidi@intel.com
2019-11-20 15:35:18 -08:00
Graham Whaley 9510b068e0 metrics: cpu-load: save cpu-load config in JSON
If the cpu-load function is enabled, save its config settings into the
JSON results file.
This required a little bit of re-sequencing of the json library calls,
to ensure we did the init of the JSON early enough, but not more than
once.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-11-15 12:23:38 -06:00
Graham Whaley 952e037420 metrics: enable cpu-load ability across tests
Rejig the framework a little to unify the init/shutdown calls and code,
which allows us to add the cpu-load enable/disable ability to all the
existing metrics.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-11-15 12:23:38 -06:00
Graham Whaley c846e9753d metrics: add cpu-load generator code and docs
Add library code that can generate a variety of cpu loads across the
cluster. Configuration is via environment variables, documented in the
.md file.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-11-15 12:23:38 -06:00
Graham Whaley 39c7cc643a metrics: README: add some information about stats gathering
Add the `collectd` subdir to the top level description of the code
layout, and add some developer details about how and where the stats
code lives and is configured/enabled.

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-11-15 12:23:38 -06:00
CraigSterrett 54adf53cdd Adding non-routable IPs to no_proxy (#270)
Adding non-routable IPs to the no_proxy, non-routable addresses will
never go out the proxy, and are used as internal IPs by VM's running
cloud-native setup. Without the addition of the IP's in no_proxy,
kubernetes nodes will not be able to communicate to the Kubernetes IP as
the traffic will be routed out the proxy server.

Signed-off-by: Craig Sterrett <craig.Sterrett@intel.com>
2019-11-14 11:18:59 -08:00
CraigSterrett 00df885b45 Fixed code for no_proxy in setup_system.sh (#265)
The sed command for updating no-proxy settings in the proxy.sh file was
not working. I made it match the above command which was working for
/etc/environment.

Signed-off-by: Craig Sterrett <craig.sterrett@intel.com>
2019-11-13 09:48:59 -08:00
David Lyle a76cc3437e Specify hostname for file name for collectd
On some machines the value for hostname, that is used in naming
the csv directory, isn't always determined by collectd to be
localhost. The scaling code assumes it will always be localhost.

This patch specifies the hostname to be localhost.

Signed-off-by: David Lyle <dklyle0@gmail.com>
2019-11-13 09:25:33 +00:00
David Lyle e09285f1e1 support older network interface naming
KIND uses the older network interface naming standard. Other
operating system images may as well. Adding support for 'eth'
network interface naming prefix.

Signed-off-by: David Lyle <dklyle0@gmail.com>
2019-11-12 16:51:08 -06:00
Eric Ernst b4e6813ed6 devmapper: update to be functional, usable
base_image_size is effectively the max size of the thinpool snapshot,
and this is static (does not resize).  If you are running containers
with larger individual layers, this will fail. (elastic is a good test
for this).

The thinpool should be 10GB, not 1GB (to align with the .img's defined
earlier in file).

Fixes: #266

Signed-off-by: Eric Ernst <eric.ernst@intel.com>
2019-11-11 09:49:35 -08:00
71 changed files with 2800 additions and 1481 deletions
Vendored
+56
View File
@@ -0,0 +1,56 @@
pipeline {
agent {
label 'clearlinux'
}
options {
timeout(time: 1, unit: "HOURS")
}
triggers {
cron('H */12 * * *')
}
environment {
CLR_K8S_PATH="${env.WORKSPACE}/clr-k8s-examples"
}
stages {
stage('Setup system') {
steps {
dir(path: "$CLR_K8S_PATH") {
sh './setup_system.sh'
}
}
}
stage('Init') {
steps {
dir(path: "$CLR_K8S_PATH") {
sh './create_stack.sh init'
sh 'mkdir -p $HOME/.kube'
sh 'sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config'
sh 'sudo chown $(id -u):$(id -g) $HOME/.kube/config'
sh 'kubectl version'
}
}
}
stage('CNI') {
steps {
dir(path: "$CLR_K8S_PATH") {
sh './create_stack.sh cni'
sh 'kubectl rollout status deployment/coredns -n kube-system --timeout=5m'
sh 'kubectl get pods -n kube-system'
}
}
}
stage('Reset Stack') {
steps {
dir(path: "$CLR_K8S_PATH") {
sh './reset_stack.sh'
}
}
}
}
post {
always {
sh 'uname -a'
sh 'swupd info'
}
}
}
@@ -0,0 +1,2 @@
resources:
cilium/cilium.yaml
@@ -0,0 +1,2 @@
resources:
- flannel/Documentation/kube-flannel.yml
@@ -0,0 +1,8 @@
resources:
- metrics-server/deploy/1.8+/aggregated-metrics-reader.yaml
- metrics-server/deploy/1.8+/auth-delegator.yaml
- metrics-server/deploy/1.8+/auth-reader.yaml
- metrics-server/deploy/1.8+/metrics-apiservice.yaml
- metrics-server/deploy/1.8+/metrics-server-deployment.yaml
- metrics-server/deploy/1.8+/metrics-server-service.yaml
- metrics-server/deploy/1.8+/resource-reader.yaml
@@ -0,0 +1,3 @@
resources:
- ingress-nginx/deploy/static/mandatory.yaml
- ingress-nginx/deploy/static/provider/baremetal/service-nodeport.yaml
@@ -0,0 +1,6 @@
resources:
- metallb/manifests/example-layer2-config.yaml
- metallb/manifests/metallb.yaml
patchesStrategicMerge:
- patch_configmap.yaml
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: config
data:
config: |
address-pools:
- name: my-ip-space
protocol: layer2
addresses:
- 10.0.0.240/28
@@ -1,7 +1,11 @@
resources:
- rook/cluster/examples/kubernetes/ceph/common.yaml
- rook/cluster/examples/kubernetes/ceph/operator.yaml
- rook/cluster/examples/kubernetes/ceph/cluster.yaml
- rook/cluster/examples/kubernetes/ceph/storageclass.yaml
patchesStrategicMerge:
- patch_operator.yaml
- patch_operator.yaml
# patches rook to use 'directories' instead of partitions.
# comment out to use partitions
- patch_cephcluster.yaml
@@ -1,3 +1,4 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
@@ -6,4 +7,4 @@ metadata:
spec:
storage:
directories:
- path: /var/lib/rook
- path: /var/lib/rook
@@ -0,0 +1,10 @@
resources:
- rook/cluster/examples/kubernetes/ceph/common.yaml
- rook/cluster/examples/kubernetes/ceph/operator.yaml
- rook/cluster/examples/kubernetes/ceph/cluster.yaml
- rook/cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml
patchesStrategicMerge:
# patches rook to use 'directories' instead of partitions.
# comment out to use partitions
- patch_cephcluster.yaml
@@ -0,0 +1,22 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
mon:
allowMultiplePerNode: true
storage:
directories:
- path: /var/lib/rook
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
replicated:
requireSafeReplicaSize: false
size: 1
@@ -0,0 +1,10 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
storage:
directories:
- path: /var/lib/rook
@@ -0,0 +1,10 @@
resources:
- rook/cluster/examples/kubernetes/ceph/common.yaml
- rook/cluster/examples/kubernetes/ceph/operator.yaml
- rook/cluster/examples/kubernetes/ceph/cluster.yaml
- rook/cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml
patchesStrategicMerge:
# patches rook to use 'directories' instead of partitions.
# comment out to use partitions
- patch_cephcluster.yaml
@@ -0,0 +1,22 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
mon:
allowMultiplePerNode: true
storage:
directories:
- path: /var/lib/rook
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
replicated:
requireSafeReplicaSize: false
size: 1
@@ -0,0 +1,10 @@
resources:
- rook/cluster/examples/kubernetes/ceph/common.yaml
- rook/cluster/examples/kubernetes/ceph/operator.yaml
- rook/cluster/examples/kubernetes/ceph/cluster.yaml
- rook/cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml
patchesStrategicMerge:
# patches rook to use 'directories' instead of partitions.
# comment out to use partitions
- patch_cephcluster.yaml
@@ -0,0 +1,10 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
storage:
directories:
- path: /var/lib/rook
@@ -0,0 +1,10 @@
resources:
- rook/cluster/examples/kubernetes/ceph/common.yaml
- rook/cluster/examples/kubernetes/ceph/operator.yaml
- rook/cluster/examples/kubernetes/ceph/cluster.yaml
- rook/cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml
patchesStrategicMerge:
# patches rook to use 'directories' instead of partitions.
# comment out to use partitions
- patch_cephcluster.yaml
@@ -0,0 +1,22 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
mon:
allowMultiplePerNode: true
storage:
directories:
- path: /var/lib/rook
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
replicated:
requireSafeReplicaSize: false
size: 1
@@ -0,0 +1,10 @@
resources:
- rook/cluster/examples/kubernetes/ceph/common.yaml
- rook/cluster/examples/kubernetes/ceph/operator.yaml
- rook/cluster/examples/kubernetes/ceph/cluster.yaml
- rook/cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml
patchesStrategicMerge:
# patches rook to use 'directories' instead of partitions.
# comment out to use partitions
- patch_cephcluster.yaml
@@ -0,0 +1,10 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
storage:
directories:
- path: /var/lib/rook
@@ -0,0 +1,10 @@
resources:
- rook/cluster/examples/kubernetes/ceph/common.yaml
- rook/cluster/examples/kubernetes/ceph/operator.yaml
- rook/cluster/examples/kubernetes/ceph/cluster.yaml
- rook/cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml
patchesStrategicMerge:
# patches rook to use 'directories' instead of partitions.
# comment out to use partitions
- patch_cephcluster.yaml
@@ -0,0 +1,22 @@
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
mon:
allowMultiplePerNode: true
storage:
directories:
- path: /var/lib/rook
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
replicated:
requireSafeReplicaSize: false
size: 1
@@ -0,0 +1,11 @@
resources:
- packaging/kata-deploy/kata-deploy.yaml
- packaging/kata-deploy/kata-rbac.yaml
- packaging/kata-deploy/k8s-1.14/kata-fc-runtimeClass.yaml
- packaging/kata-deploy/k8s-1.14/kata-qemu-runtimeClass.yaml
images:
# change 'latest' to specified version
- name: katadocker/kata-deploy
newName: katadocker/kata-deploy
newTag: 1.9.1
+23 -15
View File
@@ -1,20 +1,28 @@
# Build multus plugin
FROM golang:1.10 AS multus
RUN git clone -q --depth 1 https://github.com/intel/multus-cni.git /go/src/github.com/intel/multus-cni
WORKDIR /go/src/github.com/intel/multus-cni
RUN ./build
FROM busybox AS multus
ARG MULTUS_VER=3.4.2
RUN wget -O multus.tgz https://github.com/intel/multus-cni/releases/download/v${MULTUS_VER}/multus-cni_${MULTUS_VER}_linux_amd64.tar.gz
RUN tar xvzf multus.tgz --strip-components=1 -C /bin
# Build sriov plugin
FROM golang:1.10 AS sriov-cni
RUN git clone -q -b dev/k8s-deviceid-model https://github.com/Intel-Corp/sriov-cni.git /go/src/github.com/intel-corp/sriov-cni
WORKDIR /go/src/github.com/intel-corp/sriov-cni
RUN ./build
FROM golang AS sriov-cni
ARG SRIOV_CNI_VER=2.3
RUN wget -qO sriov-cni.tgz https://github.com/intel/sriov-cni/archive/v${SRIOV_CNI_VER}.tar.gz
RUN mkdir -p sriov-cni && \
tar xzf sriov-cni.tgz --strip-components=1 -C sriov-cni && \
cd sriov-cni && \
make && \
cp build/sriov /bin
# Build sriov device plugin
FROM golang:1.10 AS sriov-dp
RUN git clone -q https://github.com/intel/sriov-network-device-plugin.git /go/src/github.com/intel/sriov-network-device-plugin
WORKDIR /go/src/github.com/intel/sriov-network-device-plugin
RUN make
FROM golang AS sriov-dp
ARG SRIOV_DP_VER=3.2
RUN wget -qO sriov-dp.tgz https://github.com/intel/sriov-network-device-plugin/archive/v${SRIOV_DP_VER}.tar.gz
RUN mkdir -p sriov-dp && \
tar xzf sriov-dp.tgz --strip-components=1 -C sriov-dp && \
cd sriov-dp && \
make && \
cp build/sriovdp /bin
# Build vfioveth plugin
FROM busybox as vfioveth
@@ -25,9 +33,9 @@ RUN chmod +x /bin/vfioveth /bin/jq
# Final image
FROM centos/systemd
WORKDIR /tmp/cni/bin
COPY --from=multus /go/src/github.com/intel/multus-cni/bin/multus .
COPY --from=sriov-cni /go/src/github.com/intel-corp/sriov-cni/bin/sriov .
COPY --from=multus /bin/multus-cni .
COPY --from=sriov-cni /bin/sriov .
COPY --from=vfioveth /bin/vfioveth .
COPY --from=vfioveth /bin/jq .
WORKDIR /usr/bin
COPY --from=sriov-dp /go/src/github.com/intel/sriov-network-device-plugin/build/sriovdp .
COPY --from=sriov-dp /bin/sriovdp .
+4 -5
View File
@@ -9,11 +9,10 @@ directories on the host with the necessary binaries and configuration files.
### Customization
The device plugin will register the SR-IOV enabled devices on the host, specified as
`rootDevices` in [sriov-conf.yaml](sriov-conf.yaml). Helper [systemd unit](systemd/sriov.service)
file is provided, which enables SR-IOV for the above `rootDevices`
> NOTE: This assumes homogenous nodes in the cluster
The device plugin will register the SR-IOV enabled devices on the host, specified with
`selectors` in [sriov-conf.yaml](sriov-conf.yaml). Helper [systemd unit](systemd/sriov.service)
file is provided, which enables SR-IOV for the above devices. More config options
are listed [here](https://github.com/intel/sriov-network-device-plugin#configurations).
### Pre-req (SR-IOV only)
@@ -38,7 +38,7 @@ add_pair_ns() {
ln -sfT $CNI_NETNS /var/run/netns/$CNI_CONTAINERID
ip netns exec $CNI_CONTAINERID ip link add $CNI_IFNAME type veth peer name $peer
ip netns exec $CNI_CONTAINERID ip link set $CNI_IFNAME addr $mac up
ip netns exec $CNI_CONTAINERID ip link set $CNI_IFNAME addr $mac up alias $vfpci
ip netns exec $CNI_CONTAINERID ip link set $peer up
ip netns exec $CNI_CONTAINERID ip addr add $ip dev $CNI_IFNAME
}
@@ -50,9 +50,9 @@ delete_pair_ns() {
case $CNI_COMMAND in
ADD)
res=$(ipam)
ip=$(echo $res | jq -r '.ip4.ip')
ip=$(echo $res | jq -r '.ips[0].address')
add_pair_ns $ip
echo '{"cniVersion":"0.2.0"}' | jq -c --arg ip $ip '.ip4.ip = $ip' >&3
echo '{"cniVersion":"0.3.1"}' | jq -c --arg ip $ip '.ips[0].address = $ip' >&3
;;
DEL)
set +o errexit
@@ -2,25 +2,34 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# name must match the spec fields below, and be in the form: <plural>.<group>
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
# group name to use for REST API: /apis/<group>/<version>
group: k8s.cni.cncf.io
# version name to use for REST API: /apis/<group>/<version>
version: v1
# either Namespaced or Cluster
scope: Namespaced
names:
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
plural: network-attachment-definitions
# singular name to be used as an alias on the CLI and for display
singular: network-attachment-definition
# kind is normally the CamelCased singular type. Your resource manifests use this.
kind: NetworkAttachmentDefinition
# shortNames allow shorter string to match your resource on the CLI
shortNames:
- net-attach-def
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing
Working Group to express the intent for attaching pods to one or more logical or physical
networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec'
type: object
properties:
spec:
description: 'NetworkAttachmentDefinition spec defines the desired state of a network attachment'
type: object
properties:
config:
description: 'NetworkAttachmentDefinition config is a JSON-formatted CNI configuration'
type: string
---
apiVersion: v1
kind: ServiceAccount
@@ -40,17 +49,43 @@ type: kubernetes.io/service-account-token
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: multus-pod-networks-lister
name: multus-pod-updater
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/status"]
verbs: ["update"]
- apiGroups: ["k8s.cni.cncf.io"]
resources: ["*"]
verbs: ["get"]
- apiGroups: ["k8s.cni.cncf.io"]
resources:
- '*'
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods
- pods/status
verbs:
- get
- update
- apiGroups:
- ""
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: multus-rb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: multus-pod-updater
subjects:
- kind: ServiceAccount
name: multus-sa
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
@@ -94,8 +129,9 @@ data:
MASTER_PLUGIN_JSON="$(cat /host/etc/cni/net.d/$MASTER_PLUGIN)"
cat > /host/etc/cni/net.d/00-multus.conf <<EOF
{
"cniVersion": "0.3.1",
"name": "multus-cni-network",
"type": "multus",
"type": "multus-cni",
"logFile": "/var/log/multus.log",
"logLevel": "debug",
"kubeconfig": "/etc/cni/net.d/multus-kubeconfig",
@@ -105,19 +141,6 @@ data:
}
EOF
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: multus-rb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: multus-pod-networks-lister
subjects:
- kind: ServiceAccount
name: multus-sa
namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
@@ -134,14 +157,14 @@ spec:
spec:
initContainers:
- name: multus
image: krsna1729/multus-sriov:k8s-1.13
image: krsna1729/multus-sriov:3.4.2-2.3-3.2
command: [ "bash", "-c" ]
args:
- cp /tmp/cni/bin/{multus,sriov,vfioveth,jq} /host/opt/cni/bin/;
- cp /tmp/cni/bin/{multus-cni,sriov,vfioveth,jq} /host/opt/cni/bin/;
/tmp/multus/install-multus-conf.sh;
/tmp/multus/install-certs.sh;
echo "Restarting crio kubelet";
systemctl restart crio; # Needed when crio manages ns lifecycle
systemctl restart crio;
systemctl restart kubelet;
volumeMounts:
- name: usr-bin
@@ -160,7 +183,7 @@ spec:
mountPath: /run/systemd
containers:
- name: sriovdp
image: krsna1729/multus-sriov:k8s-1.13
image: krsna1729/multus-sriov:3.4.2-2.3-3.2
command: [ "sh", "-c" ]
args:
- /usr/bin/sriovdp --logtostderr -v 10;
@@ -209,4 +232,3 @@ spec:
path: /var/lib/kubelet/device-plugins/
hostNetwork: true
hostPID: true
@@ -11,15 +11,15 @@ data:
[
{
"resourceName": "sriov_netdevice",
"rootDevices": ["07:00.0"],
"sriovMode": true,
"deviceType": "netdevice"
"selectors": {
"drivers": ["i40evf", "iavf"]
}
},
{
"resourceName": "sriov_vfio",
"rootDevices": ["07:00.1"],
"sriovMode": true,
"deviceType": "vfio"
"selectors": {
"drivers": ["vfio-pci"]
}
}
]
}
@@ -0,0 +1,100 @@
---
apiVersion: v1
kind: Pod
metadata:
name: dpdk-1711
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net-dpdk
spec:
restartPolicy: Never
containers:
- name: dpdk
image: krsna1729/dpdk:17.11
stdin: true
tty: true
command: [ "/bin/bash", "-c"]
args:
- ls -l /dev/vfio;
testpmd --no-huge -m 2048 -- --stats-period=10 --nb-port=1 --port-topology=chained --auto-start --total-num-mbufs=2048 --forward-mode=macswap;
securityContext:
capabilities:
add:
- IPC_LOCK
resources:
limits:
intel.com/sriov_vfio: '1'
---
apiVersion: v1
kind: Pod
metadata:
name: dpdk-1811
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net-dpdk
spec:
restartPolicy: Never
containers:
- name: dpdk
image: krsna1729/dpdk:18.11
stdin: true
tty: true
command: [ "/bin/bash", "-c"]
args:
- ls -l /dev/vfio;
testpmd --no-huge -m 2048 -- --stats-period=10 --nb-port=1 --port-topology=chained --auto-start --total-num-mbufs=2048 --forward-mode=macswap;
securityContext:
capabilities:
add:
- IPC_LOCK
resources:
limits:
intel.com/sriov_vfio: '1'
---
apiVersion: v1
kind: Pod
metadata:
name: dpdk-1911
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net-dpdk
spec:
restartPolicy: Never
containers:
- name: dpdk
image: krsna1729/dpdk:19.11
stdin: true
tty: true
command: [ "/bin/bash", "-c"]
args:
- ls -l /dev/vfio;
testpmd --no-huge -m 2048 -- --stats-period=10 --nb-port=1 --port-topology=chained --auto-start --total-num-mbufs=2048 --forward-mode=macswap;
securityContext:
capabilities:
add:
- IPC_LOCK
resources:
limits:
intel.com/sriov_vfio: '1'
---
apiVersion: v1
kind: Pod
metadata:
name: dpdk-2002
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net-dpdk
spec:
restartPolicy: Never
containers:
- name: dpdk
image: krsna1729/dpdk:20.02
stdin: true
tty: true
command: [ "/bin/bash", "-c"]
args:
- ls -l /dev/vfio;
testpmd --no-huge -m 2048 -- --stats-period=10 --nb-port=1 --port-topology=chained --auto-start --total-num-mbufs=2048 --forward-mode=macswap;
securityContext:
capabilities:
add:
- IPC_LOCK
resources:
limits:
intel.com/sriov_vfio: '1'
@@ -0,0 +1,17 @@
FROM ubuntu:bionic as ubuntu-build
RUN apt-get update && \
apt-get -y install \
build-essential \
git \
libnuma-dev
ARG DPDK_VER='master'
ENV DPDK_DIR='/dpdk'
ENV RTE_TARGET='x86_64-native-linuxapp-gcc'
RUN git clone -b $DPDK_VER -q --depth 1 http://dpdk.org/git/dpdk-stable $DPDK_DIR 2>&1
RUN cd ${DPDK_DIR} && \
sed -ri 's,(IGB_UIO=).*,\1n,' config/common_linux* && \
sed -ri 's,(KNI_KMOD=).*,\1n,' config/common_linux* && \
make config T=x86_64-native-linuxapp-gcc && \
make -j $CPUS
ENV PATH="$PATH:$DPDK_DIR/build/app/"
+58 -2
View File
@@ -3,6 +3,11 @@
## Prerequisite
This setup currently will work with Kubernetes 1.14 & above. Any version of Kubernetes before that might work, but is not guaranteed.
## QUICK NOTE
The version of Kubernetes* was bumped from 1.17.7 to 1.19.4 in Clear Linux* OS release 34090. The [guide](https://docs.01.org/clearlinux/latest/guides/clear/k8s-migration.html) and the Clear Linux OS bundle k8s-migration were created to help facilitate migration of a cluster from 1.17.x to the latest 1.19.x .
The new Clear Linux OS bundle k8s-migration was added in Clear Linux* OS release 34270. Please follow the guide for an upgrade.
## Sample multi-node vagrant setup
To be able to test this tool, you can create a 3-node vagrant setup. In this tutorial, we will talk about using [libvirt](https://github.com/vagrant-libvirt/vagrant-libvirt), but you can use any hypervisor that you are familiar with.
@@ -38,6 +43,9 @@ This script ensures the following
script uses the runtime specified in the `RUNNER` environment variable and defaults to `crio`. To use the
`containerd` runtime, set the `RUNNER` environment variable to `containerd`.
In case of vagrant, if you want to spin up VM's using different environment variable than declared in [`setup_system.sh`],
specify when performing vagrant up. E.g., `RUNNER=containerd vagrant up`
### Specify a version of Clear Linux
To specify a particular version of Clear Linux to use, set the CLRK8S_CLR_VER environment variable to the desired
@@ -60,6 +68,28 @@ devicemapper storage. This should not be used for production.
> NOTE: This step is done automatically if using vagrant.
### For HA, setup the load balancer node
Ideally, the load balancer node will be a separate node. However, one of the
master nodes can also serve as the load balancer for the cluster. [HAProxy](https://www.haproxy.org/)
is used in these instructions.
```bash
sudo swupd bundle-add haproxy
sudo systemctl enable haproxy
```
Edit the master IP addresses and load balancer address and ports in [`haproxy.cfg.example`](haproxy.cfg.example)
to match the IPs for the new cluster. If using a master node for the load balancer
make sure that the `frontend bind` port is different than the Kubernetes API port, 6443.
If using a separate machine for load balancing, the port can be 6443 if desired.
```bash
sudo mkdir -p /etc/haproxy
sudo cp haproxy.cfg.example /etc/haproxy/haproxy.cfg
sudo systemctl start haproxy
```
## Bring up the master
Run [`create_stack.sh`](create_stack.sh) on the master node. This sets up the
@@ -72,8 +102,9 @@ more information.
> NOTE: Before running [`create_stack.sh`](create_stack.sh) script, make sure to export
the necessary environment variables if needed to be changed. By default it will use
`CLRK8S_CNI` to be canal, and `CLRK8S_RUNNER` to be crio. Cilium is tested only in the
Vagrant.
`CLRK8S_CNI` to be canal, and `CLRK8S_RUNNER` to be crio. Cilium is tested only in the
Vagrant. If creating an HA cluster, make sure to specify `LOAD_BALANCER_IP` and
`LOAD_BALANCER_PORT`.
```bash
# default shows help
@@ -83,6 +114,13 @@ Vagrant.
In order to enable running greater than 110 pods per node, set the environment
variable `HIGH_POD_COUNT` to any non-empty value.
If creating an HA cluster, join the other master nodes to the cluster.
```bash
kubeadm join <load-balancer-ip>:<load-balancer-port> --token <token> --discovery-token-ca-cert-hash <hash> \
--control-plane --certificate-key <certificate-key> --cri-socket=/run/crio/crio.sock
```
## Join Workers to the cluster
```bash
@@ -91,6 +129,9 @@ kubeadm join <master-ip>:<master-port> --token <token> --discovery-token-ca-cert
Note: Remember to append `--cri-socket=/run/crio/crio.sock` to the join command generated by the master.
If creating an HA cluster, join the other worker nodes to the cluster. The same way,
but replacing the `<master-ip>:<master-port>` with `<load-balancer-ip>:<load-balancer-port>`.
On workers just use the join command that the master spits out. There nothing
else you need to run on the worker. All the other Kubernetes customizations are pushed
in from master via the values setup in the `kubeadm.yaml` file.
@@ -159,3 +200,18 @@ Grafana is available at this URL http://localhost:3000 . Default credentials are
## Cleaning up the cluster (Hard reset to a clean state)
Run `reset_stack.sh` on all the nodes
## Additional Components
### Rook
The default Rook configuration provided is intended for testing purposes only
and is not suitable for a production environment. By default Rook is configured
to provide local storage (/var/lib/rook) and will be provisioned differently
depending on whether or not you startup a single node Kubernetes cluster, or
a multiple node Kubernetes cluster.
- When starting up a single node Kubernetes cluster, Rook will be configured
to start up a single replica, and will allow multiple monitors on the same node.
- When multiple Kubernetes worker nodes are detected, Rook will be configured
to startup a replica on each available node and will schedule monitor processes
on separate nodes providing greater reliability.
+2 -1
View File
@@ -16,7 +16,7 @@ $box = "AntonioMeireles/ClearLinux"
$box_ver = (ENV['CLEAR_VBOX_VER'])
File.exists?("/usr/share/qemu/OVMF.fd") ? $loader = "/usr/share/qemu/OVMF.fd" : $loader = File.join(File.dirname(__FILE__), "OVMF.fd")
$vm_name_prefix = "clr"
$base_ip = IPAddr.new("192.52.100.10")
$base_ip = IPAddr.new("10.10.100.10")
$hosts = {}
$proxy_ip_list = "192.168.121.0/24"
$driveletters = ('a'..'z').to_a
@@ -76,6 +76,7 @@ Vagrant.configure("2") do |config|
lv.loader = $loader
lv.cpus = $cpus
lv.memory = $memory
lv.machine_virtual_size = 40
(1..$disks).each do |d|
lv.storage :file, :device => "hd#{$driveletters[d]}", :path => "disk-#{$disk_prefix}-#{vm_name}-#{d}.disk", :size => $disk_size, :type => "raw"
end
@@ -44,18 +44,21 @@ sudo systemctl enable --now containerd-devmapper
# no. of feature arguments
# Skip zeroing blocks for new volumes.
sudo dmsetup create contd-thin-pool \
--table "0 2097152 thin-pool /dev/loop21 /dev/loop20 512 32768 1 skip_block_zeroing"
--table "0 20971520 thin-pool /dev/loop21 /dev/loop20 512 32768 1 skip_block_zeroing"
sudo mkdir -p /etc/containerd/
if [ -f /etc/containerd/config.toml ]
then
sudo sed -i 's|^\(\[plugins\]\).*|\1\n \[plugins.devmapper\]\n pool_name = \"contd-thin-pool\"\n base_image_size = \"512MB\"|' /etc/containerd/config.toml
sudo sed -i 's|^\(\[plugins\]\).*|\1\n \[plugins.devmapper\]\n pool_name = \"contd-thin-pool\"\n base_image_size = \"4096MB\"|' /etc/containerd/config.toml
else
cat<<EOT | sudo tee /etc/containerd/config.toml
[plugins]
[plugins.devmapper]
pool_name = "contd-thin-pool"
base_image_size = "512MB"
base_image_size = "4096MB"
[plugins.cri]
[plugins.cri.containerd]
snapshotter = "devmapper"
EOT
fi
+27 -16
View File
@@ -14,24 +14,28 @@ SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
: ${MASTER_IP:=}
: ${CERT_SANS:=}
HIGH_POD_COUNT=${HIGH_POD_COUNT:-""}
LOAD_BALANCER_IP=${LOAD_BALANCER_IP:-""}
LOAD_BALANCER_PORT="${LOAD_BALANCER_PORT:-6444}"
# versions
CANAL_VER="${CLRK8S_CANAL_VER:-v3.10}"
CILIUM_VER="${CLRK8S_CILIUM_VER:-v1.6}"
FLANNEL_VER="${CLRK8S_FLANNEL_VER:-16b0fe66285d1ad1f42b154ab852682f6fafb1a7}"
CILIUM_VER="${CLRK8S_CILIUM_VER:-v1.6.4}"
FLANNEL_VER="${CLRK8S_FLANNEL_VER:-960b3243b9a7faccdfe7b3c09097105e68030ea7}"
K8S_VER="${CLRK8S_K8S_VER:-}"
KATA_VER="${CLRK8S_KATA_VER:-1.8.2-kernel-config}"
ROOK_VER="${CLRK8S_ROOK_VER:-v1.1.1}"
METRICS_VER="${CLRK8S_METRICS_VER:-v0.3.5}"
KATA_VER="${CLRK8S_KATA_VER:-1.9.1-kernel-config}"
ROOK_VER="${CLRK8S_ROOK_VER:-v1.2.6}"
METRICS_VER="${CLRK8S_METRICS_VER:-v0.3.6}"
DASHBOARD_VER="${CLRK8S_DASHBOARD_VER:-v2.0.0-beta2}"
INGRES_VER="${CLRK8S_INGRES_VER:-nginx-0.25.1}"
INGRES_VER="${CLRK8S_INGRES_VER:-nginx-0.26.1}"
EFK_VER="${CLRK8S_EFK_VER:-v1.15.1}"
METALLB_VER="${CLRK8S_METALLB_VER:-v0.8.1}"
METALLB_VER="${CLRK8S_METALLB_VER:-v0.8.3}"
NPD_VER="${CLRK8S_NPD_VER:-v0.6.6}"
PROMETHEUS_VER="${CLRK8S_PROMETHEUS_VER:-f458e85e5d7675f7bc253072e1b4c8892b51af0f}"
CNI=${CLRK8S_CNI:-"canal"}
RUNNER=${CLRK8S_RUNNER:-"crio"}
if [[ -z "${RUNNER+x}" ]]; then RUNNER="${CLRK8S_RUNNER:-"crio"}"; fi
NFD_VER="${CLRK8S_NFD_VER:-v0.4.0}"
mode="multinode"
function print_usage_exit() {
exit_code=${1:-0}
@@ -88,7 +92,13 @@ function cluster_init() {
echo "/var/lib/etcd exists! skipping init."
return
fi
sudo -E kubeadm init --config=./kubeadm.yaml
if [[ -n "${LOAD_BALANCER_IP}" ]]; then
sed -i "s/ClusterConfiguration/ClusterConfiguration\ncontrolPlaneEndpoint: ${LOAD_BALANCER_IP}:${LOAD_BALANCER_PORT}/g" ./kubeadm.yaml
fi
# upload-certs will automatically upload certificates that should be shared
# across control-plane nodes in HA clusters. It is harmless in non-HA cases.
sudo -E kubeadm init --upload-certs --config=./kubeadm.yaml
rm -rf "${HOME}/.kube"
mkdir -p "${HOME}/.kube"
@@ -107,6 +117,7 @@ function cluster_init() {
#Ensure single node k8s works
if [ "$(kubectl get nodes | wc -l)" -eq 2 ]; then
kubectl taint nodes --all node-role.kubernetes.io/master-
mode="standalone"
fi
}
@@ -144,10 +155,10 @@ function cni() {
FLANNEL_VER=${1:-$FLANNEL_VER}
FLANNEL_URL="https://github.com/coreos/flannel"
FLANNEL_DIR="0-flannel"
get_repo "${FLANNEL_URL}" "${FLANNEL_DIR}/overlays/${FLANNEL_VER}"
set_repo_version "${FLANNEL_VER}" "${FLANNEL_DIR}/overlays/${FLANNEL_VER}/flannel"
kubectl apply -k "${FLANNEL_DIR}/overlays/${FLANNEL_VER}"
set_repo_version "${FLANNEL_VER}" "${FLANNEL_DIR}/overlays/${FLANNEL_VER}/flannel"
kubectl apply -k "${FLANNEL_DIR}/overlays/${FLANNEL_VER}"
;;
cilium)
CILIUM_VER=${1:-$CILIUM_VER}
@@ -167,7 +178,7 @@ function cni() {
function metrics() {
METRICS_VER="${1:-$METRICS_VER}"
METRICS_URL="https://github.com/kubernetes-incubator/metrics-server.git"
METRICS_URL="https://github.com/kubernetes-sigs/metrics-server.git"
METRICS_DIR="1-core-metrics"
get_repo "${METRICS_URL}" "${METRICS_DIR}/overlays/${METRICS_VER}"
set_repo_version "${METRICS_VER}" "${METRICS_DIR}/overlays/${METRICS_VER}/metrics-server"
@@ -207,9 +218,9 @@ function storage() {
ROOK_DIR=7-rook
# get and apply rook
get_repo "${ROOK_URL}" "${ROOK_DIR}/overlays/${ROOK_VER}"
set_repo_version "${ROOK_VER}" "${ROOK_DIR}/overlays/${ROOK_VER}/rook"
kubectl apply -k "${ROOK_DIR}/overlays/${ROOK_VER}"
get_repo "${ROOK_URL}" "${ROOK_DIR}/overlays/${ROOK_VER}/${mode}"
set_repo_version "${ROOK_VER}" "${ROOK_DIR}/overlays/${ROOK_VER}/${mode}/rook"
kubectl apply -k "${ROOK_DIR}/overlays/${ROOK_VER}/${mode}"
# wait for the rook OSDs to run which means rooks should be ready
while [[ $(kubectl get po --all-namespaces | grep -e 'osd.*Running.*' -c) -lt 1 ]]; do
echo "Waiting for Rook OSD"
+34
View File
@@ -0,0 +1,34 @@
global
log /dev/log local0
chroot /var/lib/haproxy
stats socket /run/haproxy-master.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
timeout tunnel 4h
frontend kubernetes
bind 10.0.0.100:6444
option tcplog
mode tcp
default_backend kubernetes-master-nodes
backend kubernetes-master-nodes
mode tcp
balance source
option tcp-check
server master-1 10.0.0.100:6443 check fall 3 rise 2
server master-2 10.0.0.101:6443 check fall 3 rise 2
server master-3 10.0.0.102:6443 check fall 3 rise 2
+3 -2
View File
@@ -1,4 +1,4 @@
apiVersion: kubeadm.k8s.io/v1beta1
apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
@@ -13,9 +13,10 @@ kubeReserved:
cpu: 500m
memory: 256M
---
apiVersion: kubeadm.k8s.io/v1beta1
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
networking:
dnsDomain: cluster.local
podSubnet: 10.244.0.0/16
serviceSubnet: 10.96.0.0/12
+31 -30
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
@@ -9,15 +8,15 @@ CLR_VER=${CLRK8S_CLR_VER:-""}
HIGH_POD_COUNT=${HIGH_POD_COUNT:-""}
# set no proxy
ADD_NO_PROXY=".svc,10.244.0.0/16,10.96.0.0/12"
ADD_NO_PROXY+=",$(hostname -I | sed 's/[[:space:]]/,/g')"
: "${RUNNER:=crio}"
NO_PROXY_ARRAY=(.svc 10.0.0.0/8 )
NO_PROXY_ARRAY+=( $(hostname -I | awk -F. '{print $1"."$2"."$3".0/24"}'))
if [[ -z "${RUNNER+x}" ]]; then RUNNER="${CLRK8S_RUNNER:-crio}"; fi
# update os version
function upate_os_version() {
if [[ -n "${CLR_VER}" ]];
then
sudo swupd repair -m "${CLR_VER}" --picky
if [[ -n "${CLR_VER}" ]]; then
sudo swupd repair -m "${CLR_VER}" --picky --force
return
fi
sudo swupd update
@@ -30,13 +29,10 @@ function add_os_deps() {
# permanently disable swap
function disable_swap() {
swapcount=$(sudo grep '^/dev/\([0-9a-z]*\).*' /proc/swaps | wc -l)
if [ "$swapcount" != "0" ]; then
sudo systemctl mask "$(sed -n -e 's#^/dev/\([0-9a-z]*\).*#dev-\1.swap#p' /proc/swaps)" 2>/dev/null
else
echo "Swap not enabled"
fi
# disable current swap
sudo swapoff -a
# permanently disable swap
sudo systemctl mask swap.target
}
# enable ip forwarding
@@ -138,16 +134,21 @@ function enable_kubelet_runner() {
# ensure that the system is ready without requiring a reboot
function ensure_system_ready() {
sudo swapoff -a
sudo systemctl restart systemd-modules-load.service
}
# add proxy if found
function setup_proxy() {
set +o nounset
set +o errexit
if [[ ${http_proxy} ]] || [[ ${HTTP_PROXY} ]]; then
echo "Setting up proxy stuff...."
# Setup IP for users too
for ip in "${NO_PROXY_ARRAY[@]}"
do
result=`grep no_proxy /etc/profile.d/proxy.sh | grep $ip`
[ -z "$result" ] && ADD_NO_PROXY+="$ip,"
done
sed_val=${ADD_NO_PROXY//\//\\/}
[ -f /etc/environment ] && sudo sed -i "/no_proxy/I s/$/,${sed_val}/g" /etc/environment
if [ -f /etc/profile.d/proxy.sh ]; then
@@ -156,19 +157,19 @@ function setup_proxy() {
echo "Warning, failed to find /etc/profile.d/proxy.sh to edit no_proxy line"
fi
services=("${RUNNER}" 'kubelet')
for s in "${services[@]}"; do
sudo mkdir -p "/etc/systemd/system/${s}.service.d/"
cat <<EOF | sudo bash -c "cat > /etc/systemd/system/${s}.service.d/proxy.conf"
[Service]
Environment="HTTP_PROXY=${http_proxy}"
Environment="HTTPS_PROXY=${https_proxy}"
Environment="SOCKS_PROXY=${socks_proxy}"
Environment="NO_PROXY=${no_proxy},${ADD_NO_PROXY}"
sudo mkdir -p /etc/systemd/system.conf.d
cat <<EOF | sudo bash -c "cat > /etc/systemd/system.conf.d/proxy.conf"
[Manager]
DefaultEnvironment="HTTP_PROXY=${http_proxy}"
DefaultEnvironment="HTTPS_PROXY=${https_proxy}"
DefaultEnvironment="SOCKS_PROXY=${socks_proxy}"
DefaultEnvironment="NO_PROXY=${no_proxy},${ADD_NO_PROXY}"
EOF
done
fi
set -o nounset
sudo systemctl daemon-reexec
fi
set -o nounset
set -o errexit
}
# init for performing any pre tasks
@@ -187,12 +188,12 @@ fi
echo "Init..."
init
echo "Disabling swap..."
disable_swap
echo "Setting OS Version..."
upate_os_version
echo "Adding OS Dependencies..."
add_os_deps
echo "Disabling swap..."
disable_swap
echo "Enabling IP Forwarding..."
enable_ip_forwarding
echo "Setting up modules to load..."
@@ -205,7 +206,7 @@ if [[ -n "${HIGH_POD_COUNT}" ]]; then
fi
echo "Reloading daemons..."
daemon_reload
echo "Enabling Kublet runner..."
echo "Enabling kubelet runner..."
enable_kubelet_runner
echo "Ensuring system is ready..."
ensure_system_ready
+47 -96
View File
@@ -1,132 +1,83 @@
* [Metric testing for scaling on Kubernetes.](#metric-testing-for-scaling-on-kubernetes)
* [Results storage and analysis](#results-storage-and-analysis)
* [Developers](#developers)
* [Metrics gathering](#metrics-gathering)
* [`collectd` statistics](#collectd-statistics)
* [privileged statistics pods](#privileged-statistics-pods)
* [Configuring constant 'loads'](#configuring-constant-loads)
# Metric testing for scaling on Kubernetes.
This folder contains tools to aid in measuring the scaling capabilities of
Kubernetes clusters.
Primarily these tools were designed to measure scaling of large number of pods on a single node, but
the code is structured to handle multiple nodes, and may also be useful in that scenario.
The tools tend to take one of two forms:
- Tools to take measurements
- Tools to launch jobs and take measurements
- Tools to analyse results
For more details, see individual sub-folders. A brief summary of available tools
is below:
| Tool | Description |
| Folder | Description |
| ---- | ----------- |
| collectd | `collectd` based statistics/metrics gathering daemonset code |
| lib | General library helper functions for forming and launching workloads, and storing results in a uniform manner to aid later analysis |
| scaling | Tests to measure scaling, such as linear or parallel launching of pods |
| report | Rmarkdown based report generator, used to produce a PDF comparison report of 1 or more sets of results |
| lib/[cpu-load*](lib/cpu-load.md) | Helper functions to enable CPU load generation on a cluster whilst under test |
| [report](report/README.md) | Rmarkdown based report generator, used to produce a PDF comparison report of one or more sets of results |
| [scaling](scaling/README.md) | Tests to measure scaling, such as linear or parallel launching of pods |
## Results storage and analysis
The tools generate JSON formatted results files via the `lib/json.bash` functions. The `metrics_json_save()`
function in that file has the ability to also `curl` or `socat` the JSON results to a database defined
The tools generate JSON formatted results files via the [`lib/json.bash`](lib/json.bash) functions. The `metrics_json_save()`
function has the ability to also `curl` or `socat` the JSON results to a database defined
by environment variables (see the file source for details). This method has been used to store results in
Elasticsearch and InfluxDB databases for instance, but should be adaptable to use with any REST API that accepts
JSON input.
## Scaling execution
This section describes a complete step-by-step scaling execution up to results reporting by using `scaling/k8s_scale.sh` tool which launches a series of workloads and take memory metric measurements after each launch.
## Prerequisites
There are some basic pre-requisites required in order to run the test and process the results:
**Requirements**
* A Kubernetes cluster up and running (tested on v1.15.3).
* `bc` and `jq` packages.
* Docker (only for report generation).
The steps to execute a run of the scaling framework are listed below, which need to be executed on the master node of a Kubernetes cluster to avoid network issues:
1. Clone `cloud-native-setup` repository into a preferred directory and change directory up to `cloud-native-setup/metrics`:
```sh
$ git clone https://github.com/clearlinux/cloud-native-setup.git
$ cd cloud-native-setup/metrics
```
2. Launch the execution by:
```sh
$ ./scaling/k8s_scale.sh
INFO: Initialising
command: bc: yes
command: jq: yes
INFO: Checking Kubernetes accessible
INFO: 1 Kubernetes nodes in 'Ready' state found
starting kubectl proxy
Starting to serve on 127.0.0.1:8090
daemonset.apps/stats created
Waiting for daemon set "stats" rollout to finish: 0 of 1 updated pods are available...
daemon set "stats" successfully rolled out
INFO: Running test
INFO: And grab some stats
INFO: idle [98.49] free [29031100] launch [0] node [clr-30f01b5149ba4ab8b05a7ee03b6812a5] inodes_free [31103039]
INFO: Testing replicas 1 of 20
INFO: Content of runtime_command=:/@RUNTIMECLASS@/d
...
```
The above execution might take about 4min because it launch up to 20 pods by default and takes measurements for CPU utilization, memory utilization and pod boot time, finally it will generate a `k8s-scaling.json` result file at `result` directory.
# Developers
**Note**: to test the launch of pods concurrently, `k8s_parallel.sh` may be used. For quicker testing, `k8s_scale_rapid.sh` can be used in place of `k8s_scale.sh`. The rest of the launch instructions remain consistent other than script name.
Below are some architecture and internal details of how the code is structured and configured. This will be
helpful for improving, modifying or submitting fixes to the code base.
**Note**: by default the scaling framework makes call to the Kubernetes API directly so, if facing connectivity issues verify that `kubelet` service's proxies and `no_proxy` environment variable are properly setup.
## Metrics gathering
**Note**: by default the scaling framework uses default values for all its required variables, which can be checked through `scaling/k8s_scale.sh -h` and updated when launching the execution, i.e.:
```
$ ./scaling/k8s_scale.sh -h
Usage: ./scaling/k8s_scale.sh [-h] [options]
Description:
Launch a series of workloads and take memory metric measurements after
each launch.
Options:
-h, Help page.
Metrics can be gathered using either a daemonset deployment of privileged pods used to gather statistics
directly from the nodes using a combination of `mpstat`, `free` and `df`, or a daemonset deployment based
around `collectd`. The general recommendation is to use the `collectd` based collection if possible, as it
is more efficient, as the system does not have to poll and wait for results, and thus executes the test
cycle faster. The `collectd` results are collected asyncronously, and the report generator code later
aligns the results with the pod execution in the timeline.
Environment variables:
Name (default)
Description
TEST_NAME (k8s scaling)
Can be set to over-ride the default JSON results filename
NUM_PODS (20)
Number of pods to launch
STEP (1)
Number of pods to launch per cycle
wait_time (30)
Seconds to wait for pods to become ready
delete_wait_time (600)
Seconds to wait for all pods to be deleted
settle_time (5)
Seconds to wait after pods ready before taking measurements
use_api (yes)
specify yes or no to use the API to launch pods
grace (30)
specify the grace period in seconds for workload pod termination
### `collectd` statistics
$ use_api=no ./scaling/k8s_scale.sh
```
The `collected` based code can be found in the `collectd` subdirectory. It uses the `collected` configuration
found in the `collectd.conf` file to gather statistics, and store the results on the nodes themselves whilst
tests are running. At the end of the test, the results are copied from the nodes and stored in the results
directory for later processing.
The steps to generate the result report are listed below:
The `collectd` statistics are only configured and gathered if the environment variable `SMF_USE_COLLECTD`
is set to non-empty by the test code (that is, it is only enabled upon request).
1. Having the `results/k8s-scaling.json` result file, create a subdirectory in the `results` directory with a preferred name and copy the `k8s-scaling.json` file into it, so the file distribution looks like:
```sh
$ tree result
results/
└── scaling
└── k8s-scaling.json
```
### privileged statistics pods
**Note**: if `k8s_scale_rapid.sh` was run instead of `k8s_scale.sh`, that the `<node_name>.tar.gz` files that appear in the results directory also need to be copied into the newly created subdirectory. And the results file is named `k8s-rapid.json` rather than `k8s-scaling.json`.
If k8s_parallel.sh was run, the results file is named `k8s-parallel.json` rather than `k8s-scaling.json`.
The privileged statistics pods `YAML` can be found in the [`scaling/stats.yaml`](scaling/stats.yaml) file.
An example of how to invoke and use this daemonset to extract statistics can be found in the
[`scaling/k8s_scale.sh`](scaling/k8s_scale.sh) file.
2. Launch the report generation by:
```sh
./report/makereport.sh
```
**Note**: the first time you launch the report generation it will build a docker container to generate the reports and this process can take several minutes. Subsequent runs will be much faster.
## Configuring constant 'loads'
The above execution will generate a `report/output` directory with the final reports, such as:
```sh
$ tree report/output/
report/output/
├── dut-1.png
├── metrics_report.pdf
├── scaling-1.png
├── scaling-2.png
├── scaling-3.png
└── scaling-4.png
```
More details about result reporting can be reviewed at [`report`](./report) directory.
The framework includes some tooling to assist in setting up constant pre-defined 'loads' across the cluster
to aid evaluation of their impacts on the scaling metrics. See the [cpu-load documentation](lib/cpu-load.md)
for more information.
+15 -9
View File
@@ -12,31 +12,37 @@ collectd_pod="collectd"
init_stats() {
local wait_time=$1
# create collectd-config configmap
# create collectd-config configmap, delete old if there is one
kubectl get configmap collectd-config >/dev/null 2>&1 && kubectl delete configmap collectd-config
kubectl create configmap collectd-config --from-file=${COLLECTD_DIR}/collectd.conf
# if there is collectd daemonset already running, delete it
# to make sure that the latest configmap will be used.
kubectl get daemonset collectd >/dev/null 2>&1 && kubectl delete daemonset --wait=true --timeout=${delete_wait_time}s "${collectd_pod}"
# Launch our stats gathering pod
kubectl apply -f ${COLLECTD_DIR}/${collectd_pod}.yaml
kubectl rollout status --timeout=${wait_time}s daemonset/${collectd_pod}
# clear existing collectd output
while read -u 3 name node; do
kubectl exec -ti $name -- sh -c "rm -rf /mnt/opt/collectd/run/localhost/*"
done 3< <(kubectl get pods --selector name=collectd-pods -o json | jq -r '.items[] | "\(.metadata.name) \(.spec.nodeName)"')
# attempting to provide buffer for collectd to be installed and running,
# and CPU collection to build adequate history
# and CPU collection to build adequate history
sleep 12
}
cleanup_stats() {
local delete_wait_time=$1
# attempting to provide buffer for collectd CPU collection to record adequate history
sleep 6
# get logs before shutting down stats daemonset
while read -u 3 name node; do
kubectl exec -ti $name -- sh -c "cd /opt/collectd; tar -czvf localhost.tar.gz localhost"
# make a backup on the host in-case collection fail
kubectl exec -ti $name -- sh -c "mkdir -p /mnt/opt/collectd"
kubectl exec -ti $name -- sh -c "cp /opt/collectd/localhost.tar.gz /mnt/opt/collectd/localhost.tar.gz"
kubectl cp $name:/opt/collectd/localhost.tar.gz ${RESULT_DIR}/${node}.tar.gz
kubectl exec -ti $name -- sh -c "cd /mnt/opt/collectd/run; rm -f ../localhost.tar.gz; tar -czvf ../localhost.tar.gz localhost"
kubectl cp $name:/mnt/opt/collectd/localhost.tar.gz ${RESULT_DIR}/${node}.tar.gz
kubectl exec -ti $name -- sh -c "rm -rf /mnt/opt/collectd/run"
done 3< <(kubectl get pods --selector name=collectd-pods -o json | jq -r '.items[] | "\(.metadata.name) \(.spec.nodeName)"')
kubectl delete daemonset --wait=true --timeout=${delete_wait_time}s "${collectd_pod}" || true
+4 -1
View File
@@ -9,13 +9,15 @@ LoadPlugin memory
LoadPlugin cpufreq
LoadPlugin df
Hostname localhost
<Plugin "cpu">
ReportByCpu true
ReportByState true
ValuesPercentage true
</Plugin>
<Plugin "csv">
DataDir "/opt/collectd"
DataDir "/mnt/opt/collectd/run"
StoreRates true
</Plugin>
<Plugin "interface">
@@ -23,6 +25,7 @@ LoadPlugin df
Interface "/^ens/"
Interface "/^enp/"
Interface "/^em/"
Interface "/^eth/"
IgnoreSelected false
</Plugin>
<Plugin "aggregation">
+62
View File
@@ -10,6 +10,7 @@ RESULT_DIR="${LIB_DIR}/../results"
source ${LIB_DIR}/json.bash
source ${LIB_DIR}/k8s-api.bash
source ${LIB_DIR}/cpu-load.bash
source /etc/os-release || source /usr/lib/os-release
die() {
@@ -67,6 +68,48 @@ init_env()
# been deliberately injected into the cluster under test.
}
framework_init() {
info "Initialising"
check_cmds "${cmds[@]}"
info "Checking k8s accessible"
local worked=$( kubectl get nodes > /dev/null 2>&1 && echo $? || echo $? )
if [ "$worked" != 0 ]; then
die "kubectl failed to get nodes"
fi
info $(get_num_nodes) "k8s nodes in 'Ready' state found"
k8s_api_init
# Launch our stats gathering pod
if [ -n "$SMF_USE_COLLECTD" ]; then
info "Setting up collectd"
init_stats $wait_time
fi
# And now we can set up our results storage then...
metrics_json_init "k8s"
save_config
# Initialise the cpu load generators now - after json init, as they may
# produce some json results (config) data.
cpu_load_init
}
framework_shutdown() {
metrics_json_save
k8s_api_shutdown
cpu_load_shutdown
if [ -n "$SMF_USE_COLLECTD" ]; then
cleanup_stats
fi
}
# finds elements in $1 that are not in $2
find_unique_pods() {
local list_a=$1
@@ -86,3 +129,22 @@ find_unique_pods() {
fi
done
}
# waits for process to complete within a given time range
waitForProcess(){
wait_time="$1"
sleep_time="$2"
cmd="$3"
proc_info_msg="$4"
while [ "$wait_time" -gt 0 ]; do
if eval "$cmd"; then
return 0
else
info "$proc_info_msg"
sleep "$sleep_time"
wait_time=$((wait_time-sleep_time))
fi
done
return 1
}
+104
View File
@@ -0,0 +1,104 @@
#!/bin/bash
#
# Copyright (c) 2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
# Helper routines for setting up a constant CPU load on the cluster/nodes
CPULOAD_DIR=${THIS_FILE%/*}
# Default to testing all cores
SMF_CPU_LOAD_NODES_NCPU=${SMF_CPU_LOAD_NODES_NCPU:-0}
# Default to 100% load (yes, this might kill your node)
SMF_CPU_LOAD_NODES_PERCENT=${SMF_CPU_LOAD_NODES_PERCENT:-}
# Default to not setting any limits or requests, so no cpuset limiting and
# no cpu core pinning
SMF_CPU_LOAD_NODES_LIMIT=${SMF_CPU_LOAD_NODES_LIMIT:-}
SMF_CPU_LOAD_NODES_REQUEST=${SMF_CPU_LOAD_NODES_REQUEST:-}
cpu_load_post_deploy_sleep=${cpu_load_post_deploy_sleep:-30}
cpu_per_node_daemonset=cpu-load
clean_up_cpu_per_node=false
# Use a DaemonSet to place one cpu stressor on each node.
cpu_per_node_init() {
info "Generating per-node CPU load daemonset"
local ds_template=${CPULOAD_DIR}/cpu_load_daemonset.yaml.in
local ds_yaml=${ds_template%\.in}
# Grab a copy of the template
cp -f ${ds_template} ${ds_yaml}
# If a setting is not used (defined), then delete its relevant
# lines from the YAML. Note, the YAML is constructed when necessary
# with comments on the correct lines to ensure all necessary lines are
# deleted
if [ -z "$SMF_CPU_LOAD_NODES_NCPU" ]; then
sed -i '/CPU_NCPU/d' ${ds_yaml}
fi
if [ -z "${SMF_CPU_LOAD_NODES_PERCENT}" ]; then
sed -i '/CPU_PERCENT/d' ${ds_yaml}
fi
if [ -z "${SMF_CPU_LOAD_NODES_LIMIT}" ]; then
sed -i '/CPU_LIMIT/d' ${ds_yaml}
fi
if [ -z "${SMF_CPU_LOAD_NODES_REQUEST}" ]; then
sed -i '/CPU_REQUEST/d' ${ds_yaml}
fi
# And then finally replace all the remaining defined parts with the
# real values.
sed -i \
-e "s|@CPU_NCPU@|${SMF_CPU_LOAD_NODES_NCPU}|g" \
-e "s|@CPU_PERCENT@|${SMF_CPU_LOAD_NODES_PERCENT}|g" \
-e "s|@CPU_LIMIT@|${SMF_CPU_LOAD_NODES_LIMIT}|g" \
-e "s|@CPU_REQUEST@|${SMF_CPU_LOAD_NODES_REQUEST}|g" \
${ds_yaml}
# Launch the daemonset...
info "Deploying cpu-load-per-node daemonset"
kubectl apply -f ${ds_yaml}
kubectl rollout status --timeout=${wait_time}s daemonset/${cpu_per_node_daemonset}
clean_up_cpu_per_node=yes
info "cpu-load-per-node daemonset Deployed"
if [ -n "$cpu_load_post_deploy_sleep" ]; then
info "Sleeping ${cpu_load_post_deploy_sleep}s for cpu-load to settle"
sleep ${cpu_load_post_deploy_sleep}
fi
# And store off our config into the JSON results
metrics_json_start_array
local json="$(cat << EOF
{
"LOAD_NODES_NCPU": "${SMF_CPU_LOAD_NODES_NCPU}",
"LOAD_NODES_PERCENT": "${SMF_CPU_LOAD_NODES_PERCENT}",
"LOAD_NODES_LIMIT": "${SMF_CPU_LOAD_NODES_LIMIT}",
"LOAD_NODES_REQUEST": "${SMF_CPU_LOAD_NODES_REQUEST}"
}
EOF
)"
metrics_json_add_array_element "$json"
metrics_json_end_array "cpu-load"
}
cpu_load_init() {
info "Check if we need CPU load generators..."
# This is defaulted of off (not defined), unless the high level test requests it.
if [ -n "$SMF_CPU_LOAD_NODES" ]; then
info "Initialising per-node CPU load"
cpu_per_node_init
fi
}
cpu_load_shutdown() {
if [ "$clean_up_cpu_per_node" = "yes" ]; then
info "Cleaning up cpu per node load daemonset"
kubectl delete daemonset --wait=true --timeout=${delete_wait_time}s "${cpu_per_node_daemonset}" || true
fi
}
+81
View File
@@ -0,0 +1,81 @@
# `cpu-load` stack stresser
The `cpu-load` stress functionality of the scaling framework allows you to optionally add a constant CPU stress
load to cluster under test whilst the tests are running. This aids impact analysis of CPU load.
The `cpu-load` functionality utilises the [`stress-ng`](https://kernel.ubuntu.com/git/cking/stress-ng.git/) tool
to generate the CPU load. Some of the configuration parameters are taken directoy from the `stress-ng` command line.
## Configuration
`cpu-load` is configured via a number of environment variables:
| Tool | Description |
| ---- | ----------- |
| collectd | `collectd` based statistics/metrics gathering daemonset code |
| lib | General library helper functions for forming and launching workloads, and storing results in a uniform manner to aid later analysis |
| report | Rmarkdown based report generator, used to produce a PDF comparison report of 1 or more sets of results |
| scaling | Tests to measure scaling, such as linear or parallel launching of pods |
| Variable | Description | Default |
| -------- | ----------- | ------- |
| `SMF_CPU_LOAD_NODES` | Set to non-empty to deploy `cpu-load` stressor | unset (off) |
| `SMF_CPU_LOAD_NODES_NCPU` | Number of stressor threads to launch per node | 0 (one per cpu) |
| `SMF_CPU_LOAD_NODES_PERCENT` | Percentage of CPU to load | unset (100%) |
| `SMF_CPU_LOAD_NODES_LIMIT` | k8s cpu resource limit to set | unset (none) |
| `SMF_CPU_LOAD_NODES_REQUEST` | k8s cpu resource request to set | unset (none) |
| `cpu_load_post_deploy_sleep` | Seconds to sleep for `cpu-load` deployment to settle | 30 |
`SMF_CPU_LOAD_NODES` must be set to a non-empty string to enable the `cpu-load` functionality. `cpu-load` uses
a daemonSet to deploy one `stress-ng` single container pod to each active node in the cluster.
Any of the `SMF_CPU_LOAD_NODES_*` variables can be set, or unset, and the daemonSet pods will be configured
appropriately.
## Examples
The combinations of settings available allow a lot of flexibility. Below are some common example setups:
### 50% CPU load on all cores of all nodes (`stress-ng`)
Here we allow `stress-ng` to spawn workers to cover all the CPUs on each node, but ask it to restrict its
bandwidth use to 50% of the CPU. We do not use the k8s limits.
```bash
export SMF_CPU_LOAD_NODES=true
#export SMF_CPU_LOAD_NODES_NCPU=
export SMF_CPU_LOAD_NODES_PERCENT=50
#export SMF_CPU_LOAD_NODES_LIMIT=999m
#export SMF_CPU_LOAD_NODES_REQUEST=999m
```
### 50% CPU load on 1 un-pinned core of all nodes (k8s `limits`)
Here we set `stress-ng` to run a single worker thread at 100% CPU, but use the k8s resource limits to restrict
actual CPU usage to 50%. Because the k8s limit and request are not whole interger units, if the static policy is
in place on the k8s cluster, the pods will be classified as Guaranteed QoS, but will *not* get pinned to a specific
cpuset.
```bash
export SMF_CPU_LOAD_NODES=true
export SMF_CPU_LOAD_NODES_NCPU=1
export SMF_CPU_LOAD_NODES_PERCENT=100
export SMF_CPU_LOAD_NODES_LIMIT=500m
export SMF_CPU_LOAD_NODES_REQUEST=500m
```
### 50% CPU load pinned to 1 core, on all nodes
Here we set `stress-ng` to run a single worker thread at 50% CPU, and use the k8s resource limits to classify the
pod as Guaranteed, and as we are using whole integer units of CPU resource requests, if the static policy manager is
in play, the thread will be pinned to a single cpu cpuset.
```bash
export SMF_CPU_LOAD_NODES=true
export SMF_CPU_LOAD_NODES_NCPU=1
export SMF_CPU_LOAD_NODES_PERCENT=50
export SMF_CPU_LOAD_NODES_LIMIT=1
export SMF_CPU_LOAD_NODES_REQUEST=1
```
+30
View File
@@ -0,0 +1,30 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: cpu-load
spec:
selector:
matchLabels:
name: cpu-load-pods
template:
metadata:
labels:
name: cpu-load-pods
spec:
hostNetwork: true
terminationGracePeriodSeconds: 0
containers:
- name: cpu-load
imagePullPolicy: IfNotPresent
image: polinux/stress-ng
command: ["stress-ng"]
args: # comment fields here so we can *delete* sections on demand
- "--cpu"
- "@CPU_NCPU@"
- "-l" #CPU_PERCENT
- "@CPU_PERCENT@" #CPU_PERCENT
resources:
limits:
cpu: @CPU_LIMIT@
requests:
cpu: @CPU_REQUEST@
+2 -1
View File
@@ -100,7 +100,7 @@ setup() {
}
run() {
docker run -ti --rm -v ${HOSTINPUTDIR}:${GUESTINPUTDIR} -v ${HOSTOUTPUTDIR}:${GUESTOUTPUTDIR} ${extra_volumes} ${IMAGE} ${extra_command}
docker run ${extra_opts} --rm -v ${HOSTINPUTDIR}:${GUESTINPUTDIR} -v ${HOSTOUTPUTDIR}:${GUESTOUTPUTDIR} ${extra_volumes} ${IMAGE} ${extra_command}
ls -la ${HOSTOUTPUTDIR}/*
}
@@ -113,6 +113,7 @@ main() {
# In debug mode, run a shell instead of the default report generation
extra_command="bash"
extra_volumes="-v ${HOSTSCRIPTDIR}:${GUESTSCRIPTDIR}"
extra_opts="-ti"
;;
esac
done
+2 -2
View File
@@ -13,10 +13,10 @@
# We would have used the 'verse' base, that already has some of the docs processing
# installed, but I could not figure out how to add in the extra bits we needed to
# the lite tex version is uses.
FROM rocker/tidyverse:latest
FROM rocker/tidyverse:3.6.0
# Version of the Dockerfile
LABEL DOCKERFILE_VERSION="1.0"
LABEL DOCKERFILE_VERSION="1.1"
# Without this some of the package installs stop to try and ask questions...
ENV DEBIAN_FRONTEND=noninteractive
File diff suppressed because it is too large Load Diff
+91 -81
View File
@@ -13,95 +13,105 @@ library(gridExtra) # together.
suppressMessages(suppressWarnings(library(ggpubr))) # for ggtexttable.
suppressMessages(library(jsonlite)) # to load the data.
# A list of all the known results files we might find the information inside.
resultsfiles=c(
"k8s-parallel.json",
"k8s-scaling.json",
"k8s-rapid.json"
)
render_dut_details <- function()
{
# A list of all the known results files we might find the information inside.
resultsfiles=c(
"k8s-parallel.json",
"k8s-scaling.json",
"k8s-rapid.json"
)
data=c()
stats=c()
stats_names=c()
data=c()
stats=c()
stats_names=c()
# For each set of results
for (currentdir in resultdirs) {
count=1
dirstats=c()
for (resultsfile in resultsfiles) {
fname=paste(inputdir, currentdir, resultsfile, sep="/")
if ( !file.exists(fname)) {
#warning(paste("Skipping non-existent file: ", fname))
next
}
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Import the data
fdata=fromJSON(fname)
if (length(fdata$'kubectl-version') != 0 ) {
# We have kata-runtime data
dirstats=tibble("Client Ver"=as.character(fdata$'kubectl-version'$clientVersion$gitVersion))
dirstats=cbind(dirstats, "Server Ver"=as.character(fdata$'kubectl-version'$serverVersion$gitVersion))
numnodes= nrow(fdata$'kubectl-get-nodes'$items)
dirstats=cbind(dirstats, "No. nodes"=as.character(numnodes))
if (numnodes != 0) {
first_node=fdata$'kubectl-get-nodes'$items[1,]
dirstats=cbind(dirstats, "- Node0 name"=as.character(first_node$metadata$name))
havekata=first_node$metadata$labels$'katacontainers.io/kata-runtime'
if ( is.null(havekata) ) {
dirstats=cbind(dirstats, " Have Kata"=as.character('false'))
} else {
dirstats=cbind(dirstats, " Have Kata"=as.character(havekata))
}
dirstats=cbind(dirstats, " CPUs"=as.character(first_node$status$capacity$cpu))
dirstats=cbind(dirstats, " Memory"=as.character(first_node$status$capacity$memory))
dirstats=cbind(dirstats, " MaxPods"=as.character(first_node$status$capacity$pods))
dirstats=cbind(dirstats, " PodCIDR"=as.character(first_node$spec$podCIDR))
dirstats=cbind(dirstats, " runtime"=as.character(first_node$status$nodeInfo$containerRuntimeVersion))
dirstats=cbind(dirstats, " kernel"=as.character(first_node$status$nodeInfo$kernelVersion))
dirstats=cbind(dirstats, " kubeProxy"=as.character(first_node$status$nodeInfo$kubeProxyVersion))
dirstats=cbind(dirstats, " Kubelet"=as.character(first_node$status$nodeInfo$kubeletVersion))
dirstats=cbind(dirstats, " OS"=as.character(first_node$status$nodeInfo$osImage))
# For each set of results
for (currentdir in resultdirs) {
count=1
dirstats=c()
datasetname=c()
for (resultsfile in resultsfiles) {
fname=paste(inputdir, currentdir, resultsfile, sep="/")
if ( !file.exists(fname)) {
#warning(paste("Skipping non-existent file: ", fname))
next
}
break
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Import the data
fdata=fromJSON(fname)
if (length(fdata$'kubectl-version') != 0 ) {
# We have kata-runtime data
dirstats=tibble("Client Ver"=as.character(fdata$'kubectl-version'$clientVersion$gitVersion))
dirstats=cbind(dirstats, "Server Ver"=as.character(fdata$'kubectl-version'$serverVersion$gitVersion))
numnodes= nrow(fdata$'kubectl-get-nodes'$items)
dirstats=cbind(dirstats, "No. nodes"=as.character(numnodes))
if (numnodes != 0) {
first_node=fdata$'kubectl-get-nodes'$items[1,]
dirstats=cbind(dirstats, "- Node0 name"=as.character(first_node$metadata$name))
havekata=first_node$metadata$labels$'katacontainers.io/kata-runtime'
if ( is.null(havekata) ) {
dirstats=cbind(dirstats, " Have Kata"=as.character('false'))
} else {
dirstats=cbind(dirstats, " Have Kata"=as.character(havekata))
}
dirstats=cbind(dirstats, " CPUs"=as.character(first_node$status$capacity$cpu))
dirstats=cbind(dirstats, " Memory"=as.character(first_node$status$capacity$memory))
dirstats=cbind(dirstats, " MaxPods"=as.character(first_node$status$capacity$pods))
dirstats=cbind(dirstats, " PodCIDR"=as.character(first_node$spec$podCIDR))
dirstats=cbind(dirstats, " runtime"=as.character(first_node$status$nodeInfo$containerRuntimeVersion))
dirstats=cbind(dirstats, " kernel"=as.character(first_node$status$nodeInfo$kernelVersion))
dirstats=cbind(dirstats, " kubeProxy"=as.character(first_node$status$nodeInfo$kubeProxyVersion))
dirstats=cbind(dirstats, " Kubelet"=as.character(first_node$status$nodeInfo$kubeletVersion))
dirstats=cbind(dirstats, " OS"=as.character(first_node$status$nodeInfo$osImage))
}
break
}
}
if ( length(dirstats) == 0 ) {
cat(paste("No valid data found for directory ", currentdir, "\n\n"))
}
# use plyr rbind.fill so we can combine disparate version info frames
stats=rbind.fill(stats, dirstats)
stats_names=rbind(stats_names, datasetname)
}
if ( length(dirstats) == 0 ) {
warning(paste("No valid data found for directory ", currentdir))
if ( length(stats_names) == 0 ) {
cat("No system details found\n\n")
return()
}
# use plyr rbind.fill so we can combine disparate version info frames
stats=rbind.fill(stats, dirstats)
stats_names=rbind(stats_names, datasetname)
rownames(stats) = stats_names
# Rotate the tibble so we get data dirs as the columns
spun_stats = as_tibble(cbind(What=names(stats), t(stats)))
# Build us a text table of numerical results
# Set up as left hand justify, so the node data indent renders.
tablefontsize=8
tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize)
stats_plot = suppressWarnings(ggtexttable(data.frame(spun_stats, check.names=FALSE),
theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style),
rows=NULL
))
# It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and
# layout to match the other charts and tables in the report.
master_plot = grid.arrange(
stats_plot,
nrow=1,
ncol=1 )
}
rownames(stats) = stats_names
# Rotate the tibble so we get data dirs as the columns
spun_stats = as_tibble(cbind(What=names(stats), t(stats)))
# Build us a text table of numerical results
# Set up as left hand justify, so the node data indent renders.
tablefontsize=8
tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize)
stats_plot = suppressWarnings(ggtexttable(data.frame(spun_stats, check.names=FALSE),
theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style),
rows=NULL
))
# It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and
# layout to match the other charts and tables in the report.
master_plot = grid.arrange(
stats_plot,
nrow=1,
ncol=1 )
render_dut_details()
@@ -0,0 +1,26 @@
library('elasticsearchr')
for_scaling <- query('{
"bool": {
"must": [
{ "match":
{
"test.testname": "k8s scaling"
}
}
]
}
}')
these_fields <- select_fields('{
"includes": [
"date.Date",
"k8s-scaling.BootResults.launch_time.Result",
"k8s-scaling.BootResults.n_pods.Result"
]
}')
sort_by_date <- sort_on('[{"date.Date": {"order": "asc"}}]')
x=elastic("http://192.168.0.111:9200", "logtest") %search% (for_scaling + sort_by_date + these_fields)
@@ -32,7 +32,7 @@ This [test](https://github.com/clearlinux/cloud-native-setup/metrics/scaling/k8s
measures the time taken to launch and delete pods in parallel using a deployment. The times
are how long it takes for the whole deployment operation to complete.
```{r parallel, echo=FALSE, fig.cap="K8S parallel pods"}
```{r parallel, echo=FALSE, fig.cap="K8S parallel pods", results='asis'}
source('parallel.R')
```
@@ -57,7 +57,7 @@ This table describes the test system details, as derived from the information co
in the test results files.
```{r dut, echo=FALSE, fig.cap="System configuration details"}
```{r dut, echo=FALSE, fig.cap="System configuration details", results='asis'}
source('dut-details.R')
```
@@ -67,6 +67,6 @@ source('dut-details.R')
This table describes node details within the Kubernetes cluster that have been used for test.
```{r node, echo=FALSE, fig.cap="Node information within Kubernetes cluster"}
```{r node, echo=FALSE, fig.cap="Node information within Kubernetes cluster", results='asis'}
source('node-info.R')
```
+82 -69
View File
@@ -13,83 +13,96 @@ library(gridExtra) # together.
suppressMessages(suppressWarnings(library(ggpubr))) # for ggtexttable.
suppressMessages(library(jsonlite)) # to load the data.
# A list of all the known results files we might find the information inside.
resultsfiles=c(
"k8s-scaling.json"
)
render_node_info <- function()
{
# A list of all the known results files we might find the information inside.
resultsfiles=c(
"k8s-scaling.json"
)
stats=c()
stats_names=c()
max_char_name_node=18
stats=c()
stats_names=c()
datasetname=c()
complete_data=c()
max_char_name_node=18
# list for each dirstats
dirstats_list=list()
j=1
# list for each dirstats
dirstats_list=list()
j=1
# For each set of results
for (currentdir in resultdirs) {
dirstats=c()
for (resultsfile in resultsfiles) {
fname=paste(inputdir, currentdir, resultsfile, sep="/")
if ( !file.exists(fname)) {
next
}
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Import the data
fdata=fromJSON(fname)
if (length(fdata$'kubectl-version') != 0 ) {
numnodes= nrow(fdata$'kubectl-get-nodes'$items)
for (i in 1:numnodes) {
node_i=fdata$'kubectl-get-nodes'$items[i,]
node_info=fdata$'socketsPerNode'[i,]
# Substring node name so it fits properly into final table
node_name=node_i$metadata$name
if ( nchar(node_name) >= max_char_name_node) {
dirstats=tibble("Node \nname"=as.character(substring(node_name, 1, max_char_name_node)))
} else {
dirstats=tibble("Node \nname"=as.character(node_name))
}
dirstats=cbind(dirstats, "CPUs"=as.character(node_i$status$capacity$cpu))
dirstats=cbind(dirstats, "Memory"=as.character(node_i$status$capacity$memory))
dirstats=cbind(dirstats, "Max \nPods"=as.character(node_i$status$capacity$pods))
dirstats=cbind(dirstats, "Count \nsockets"=as.character(node_info$num_sockets))
dirstats=cbind(dirstats, "Have \nhypervisor"=as.character(node_info$hypervisor))
dirstats=cbind(dirstats, "kernel"=as.character(node_i$status$nodeInfo$kernelVersion))
dirstats=cbind(dirstats, "OS"=as.character(node_i$status$nodeInfo$osImage))
dirstats=cbind(dirstats, "Test"=as.character(datasetname))
dirstats_list[[j]]=dirstats
j=j+1
# For each set of results
for (currentdir in resultdirs) {
dirstats=c()
for (resultsfile in resultsfiles) {
fname=paste(inputdir, currentdir, resultsfile, sep="/")
if ( !file.exists(fname)) {
next
}
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Import the data
fdata=fromJSON(fname)
if (length(fdata$'kubectl-version') != 0 ) {
numnodes= nrow(fdata$'kubectl-get-nodes'$items)
for (i in 1:numnodes) {
node_i=fdata$'kubectl-get-nodes'$items[i,]
node_info=fdata$'socketsPerNode'[i,]
# Substring node name so it fits properly into final table
node_name=node_i$metadata$name
if ( nchar(node_name) >= max_char_name_node) {
dirstats=tibble("Node \nname"=as.character(substring(node_name, 1, max_char_name_node)))
} else {
dirstats=tibble("Node \nname"=as.character(node_name))
}
dirstats=cbind(dirstats, "CPUs"=as.character(node_i$status$capacity$cpu))
dirstats=cbind(dirstats, "Memory"=as.character(node_i$status$capacity$memory))
dirstats=cbind(dirstats, "Max \nPods"=as.character(node_i$status$capacity$pods))
dirstats=cbind(dirstats, "Count \nsockets"=as.character(node_info$num_sockets))
dirstats=cbind(dirstats, "Have \nhypervisor"=as.character(node_info$hypervisor))
dirstats=cbind(dirstats, "kernel"=as.character(node_i$status$nodeInfo$kernelVersion))
dirstats=cbind(dirstats, "OS"=as.character(node_i$status$nodeInfo$osImage))
dirstats=cbind(dirstats, "Test"=as.character(datasetname))
dirstats_list[[j]]=dirstats
j=j+1
}
complete_data = do.call(rbind, dirstats_list)
}
complete_data = do.call(rbind, dirstats_list)
}
if ( length(complete_data) == 0 ) {
cat(paste("No valid data found for directory ", currentdir, "\n\n"))
}
# use plyr rbind.fill so we can combine disparate version info frames
stats=rbind.fill(stats, complete_data)
stats_names=rbind(stats_names, datasetname)
}
if ( length(complete_data) == 0 ) {
warning(paste("No valid data found for directory ", currentdir))
if ( length(stats_names) == 0 ) {
cat("No node stats found\n\n");
return()
}
# use plyr rbind.fill so we can combine disparate version info frames
stats=rbind.fill(stats, complete_data)
stats_names=rbind(stats_names, datasetname)
# Build us a text table of numerical results
# Set up as left hand justify, so the node data indent renders.
tablefontsize=8
tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize)
stats_plot = suppressWarnings(ggtexttable(data.frame(complete_data, check.names=FALSE),
theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style),
rows=NULL))
# It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and
# layout to match the other charts and tables in the report.
master_plot = grid.arrange(stats_plot,
nrow=1,
ncol=1 )
}
# Build us a text table of numerical results
# Set up as left hand justify, so the node data indent renders.
tablefontsize=8
tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize)
stats_plot = suppressWarnings(ggtexttable(data.frame(complete_data, check.names=FALSE),
theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style),
rows=NULL))
# It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and
# layout to match the other charts and tables in the report.
master_plot = grid.arrange(stats_plot,
nrow=1,
ncol=1 )
render_node_info()
+106 -96
View File
@@ -13,113 +13,123 @@ suppressMessages(suppressWarnings(library(ggpubr))) # for ggtexttable.
suppressMessages(library(jsonlite)) # to load the data.
suppressMessages(library(scales)) # For de-science notation of axis
testnames=c(
"k8s-parallel*"
)
render_parallel <- function()
{
testnames=c(
"k8s-parallel*"
)
data=c()
stats=c()
rstats=c()
rstats_names=c()
cstats=c()
cstats_names=c()
data=c()
stats=c()
rstats=c()
rstats_names=c()
cstats=c()
cstats_names=c()
skip_points_enable_smooth=0 # Should we draw the points as well as lines on the graphs.
skip_points_enable_smooth=0 # Should we draw the points as well as lines on the graphs.
for (currentdir in resultdirs) {
dirstats=c()
for (testname in testnames) {
matchdir=paste(inputdir, currentdir, sep="")
matchfile=paste(testname, '\\.json', sep="")
files=list.files(matchdir, pattern=matchfile)
if ( length(files) == 0 ) {
#warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing"))
}
for (ffound in files) {
fname=paste(inputdir, currentdir, ffound, sep="")
if ( !file.exists(fname)) {
warning(paste("Skipping non-existent file: ", fname))
next
for (currentdir in resultdirs) {
dirstats=c()
for (testname in testnames) {
matchdir=paste(inputdir, currentdir, sep="")
matchfile=paste(testname, '\\.json', sep="")
files=list.files(matchdir, pattern=matchfile)
if ( length(files) == 0 ) {
#warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing"))
}
for (ffound in files) {
fname=paste(inputdir, currentdir, ffound, sep="")
if ( !file.exists(fname)) {
warning(paste("Skipping non-existent file: ", fname))
next
}
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Import the data
fdata=fromJSON(fname)
# De-nest the test name specific data
shortname=substr(ffound, 1, nchar(ffound)-nchar(".json"))
fdata=fdata[[shortname]]
# Import the data
fdata=fromJSON(fname)
# De-nest the test name specific data
shortname=substr(ffound, 1, nchar(ffound)-nchar(".json"))
fdata=fdata[[shortname]]
testname=datasetname
testname=datasetname
# convert ms to seconds
cdata=data.frame(boot_time=as.numeric(fdata$BootResults$launch_time$Result)/1000)
cdata=cbind(cdata, delete_time=as.numeric(fdata$BootResults$delete_time$Result)/1000)
cdata=cbind(cdata, npod=as.numeric(fdata$BootResults$n_pods$Result))
# convert ms to seconds
cdata=data.frame(boot_time=as.numeric(fdata$BootResults$launch_time$Result)/1000)
cdata=cbind(cdata, delete_time=as.numeric(fdata$BootResults$delete_time$Result)/1000)
cdata=cbind(cdata, npod=as.numeric(fdata$BootResults$n_pods$Result))
# If we have more than 20 items to draw, then do not draw the points on
# the graphs, as they are then too noisy to read.
# But, do draw the smoothed lines to help read the now dense and potentially
# noisy graphs.
if (length(cdata[, "boot_time"]) > 20) {
skip_points_enable_smooth=1
# If we have more than 20 items to draw, then do not draw the points on
# the graphs, as they are then too noisy to read.
# But, do draw the smoothed lines to help read the now dense and potentially
# noisy graphs.
if (length(cdata[, "boot_time"]) > 20) {
skip_points_enable_smooth=1
}
cdata=cbind(cdata, testname=rep(testname, length(cdata[, "boot_time"]) ))
cdata=cbind(cdata, dataset=rep(datasetname, length(cdata[, "boot_time"]) ))
# Store away as a single set
data=rbind(data, cdata)
}
cdata=cbind(cdata, testname=rep(testname, length(cdata[, "boot_time"]) ))
cdata=cbind(cdata, dataset=rep(datasetname, length(cdata[, "boot_time"]) ))
# Store away as a single set
data=rbind(data, cdata)
}
}
# If we found nothing to process, quit early and nicely
if ( length(data) == 0 ) {
cat("No results files found for parallel tests\n\n")
return()
}
# Show how boot time changed
boot_line_plot <- ggplot( data=data, aes(npod, boot_time, colour=testname, group=dataset)) +
geom_line( alpha=0.2) +
xlab("parallel pods") +
ylab("Boot time (s)") +
ggtitle("Deployment boot time (detail)") +
#ylim(0, NA) + # For big machines, better to not 0-index
theme(axis.text.x=element_text(angle=90))
if ( skip_points_enable_smooth == 0 ) {
boot_line_plot = boot_line_plot + geom_point(alpha=0.3)
} else {
boot_line_plot = bool_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3)
}
# And get a zero Y index plot.
boot_line_plot_zero = boot_line_plot + ylim(0, NA) +
ggtitle("Deployment boot time (0 index)")
# Show how boot time changed
delete_line_plot <- ggplot( data=data, aes(npod, delete_time, colour=testname, group=dataset)) +
geom_line(alpha=0.2) +
xlab("parallel pods") +
ylab("Delete time (s)") +
ggtitle("Deployment deletion time (detail)") +
#ylim(0, NA) + # For big machines, better to not 0-index
theme(axis.text.x=element_text(angle=90))
if ( skip_points_enable_smooth == 0 ) {
delete_line_plot = delete_line_plot + geom_point(alpha=0.3)
} else {
delete_line_plot = delete_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3)
}
# And get a 0 indexed Y axis plot
delete_line_plot_zero = delete_line_plot + ylim(0, NA) +
ggtitle("Deployment deletion time (0 index)")
# See https://www.r-bloggers.com/ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/ for
# excellent examples
master_plot = grid.arrange(
boot_line_plot_zero,
delete_line_plot_zero,
boot_line_plot,
delete_line_plot,
nrow=2,
ncol=2 )
}
# Show how boot time changed
boot_line_plot <- ggplot( data=data, aes(npod, boot_time, colour=testname, group=dataset)) +
geom_line( alpha=0.2) +
xlab("parallel pods") +
ylab("Boot time (s)") +
ggtitle("Deployment boot time (detail)") +
#ylim(0, NA) + # For big machines, better to not 0-index
theme(axis.text.x=element_text(angle=90))
if ( skip_points_enable_smooth == 0 ) {
boot_line_plot = boot_line_plot + geom_point(alpha=0.3)
} else {
boot_line_plot = bool_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3)
}
# And get a zero Y index plot.
boot_line_plot_zero = boot_line_plot + ylim(0, NA) +
ggtitle("Deployment boot time (0 index)")
# Show how boot time changed
delete_line_plot <- ggplot( data=data, aes(npod, delete_time, colour=testname, group=dataset)) +
geom_line(alpha=0.2) +
xlab("parallel pods") +
ylab("Delete time (s)") +
ggtitle("Deployment deletion time (detail)") +
#ylim(0, NA) + # For big machines, better to not 0-index
theme(axis.text.x=element_text(angle=90))
if ( skip_points_enable_smooth == 0 ) {
delete_line_plot = delete_line_plot + geom_point(alpha=0.3)
} else {
delete_line_plot = delete_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3)
}
# And get a 0 indexed Y axis plot
delete_line_plot_zero = delete_line_plot + ylim(0, NA) +
ggtitle("Deployment deletion time (0 index)")
# See https://www.r-bloggers.com/ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/ for
# excellent examples
master_plot = grid.arrange(
boot_line_plot_zero,
delete_line_plot_zero,
boot_line_plot,
delete_line_plot,
nrow=2,
ncol=2 )
render_parallel()
+4
View File
@@ -8,6 +8,10 @@ author: "Auto generated"
date: "`r format(Sys.time(), '%d %B, %Y')`"
output:
pdf_document:
# Shrink the page margins so we get bigger/better resolution on the graphs
# Keep the top and bottom margins reasonable, as we are really interested in
# gaining 'width', and if we trim the bottom too much, we lose the page numbers.
geometry: "left=1cm, right=1cm, top=2cm, bottom=2cm"
urlcolor: blue
---
+9
View File
@@ -0,0 +1,9 @@
suppressMessages(library(jsonlite)) # to load the data.
options(digits=22)
x=fromJSON('{"ns": 1567002188374607769}')
print(x)
print(fromJSON('{"ns": 1567002188374607769}'), digits=22)
+306 -290
View File
@@ -13,304 +13,320 @@ suppressMessages(library(jsonlite)) # to load the data.
suppressMessages(library(scales)) # For de-science notation of axis
library(tibble) # tibbles for tidy data
testnames=c(
"k8s-scaling.*"
)
render_tidy_scaling <- function()
{
testnames=c(
"k8s-scaling.*"
)
bootdata=c() # Track per-launch data
nodedata=c() # Track node status data
memstats=c() # Statistics for memory usage
cpustats=c() # Statistics for cpu usage
bootstats=c() # Statistics for boot (launch) times
inodestats=c() # Statistics for inode usage
bootdata=c() # Track per-launch data
nodedata=c() # Track node status data
memstats=c() # Statistics for memory usage
cpustats=c() # Statistics for cpu usage
bootstats=c() # Statistics for boot (launch) times
inodestats=c() # Statistics for inode usage
# iterate over every set of results (test run)
for (currentdir in resultdirs) {
# For every results file we are interested in evaluating
for (testname in testnames) {
matchdir=paste(inputdir, currentdir, sep="")
matchfile=paste(testname, '\\.json', sep="")
files=list.files(matchdir, pattern=matchfile)
if ( length(files) == 0 ) {
#warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing"))
}
# For every matching results file
for (ffound in files) {
fname=paste(inputdir, currentdir, ffound, sep="")
if ( !file.exists(fname)) {
warning(paste("Skipping non-existent file: ", fname))
next
# iterate over every set of results (test run)
for (currentdir in resultdirs) {
# For every results file we are interested in evaluating
for (testname in testnames) {
matchdir=paste(inputdir, currentdir, sep="")
matchfile=paste(testname, '\\.json', sep="")
files=list.files(matchdir, pattern=matchfile)
if ( length(files) == 0 ) {
#warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing"))
}
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Import the data
fdata=fromJSON(fname)
# De-nest the test name specific data
shortname=substr(ffound, 1, nchar(ffound)-nchar(".json"))
fdata=fdata[[shortname]]
testname=datasetname
# Most of the data we are looking for comes in BootResults, so pick it out to make
# referencing easier
br=fdata$BootResults
# The launched pods is a list of data frames when imported. It is much nicer
# for us to work with it as a single data frame, so convert it...
lp=do.call("rbind", br$launched_pods)
########################################################
#### Now extract all the pod launch boot data items ####
########################################################
local_bootdata=tibble(launch_time=br$launch_time$Result)
local_bootdata=cbind(local_bootdata, n_pods=br$n_pods$Result)
local_bootdata=cbind(local_bootdata, node=lp$node)
local_bootdata=cbind(local_bootdata, testname=rep(testname, length(local_bootdata$node)))
########################################################
#### Now extract all node performance information ######
########################################################
nu=br$node_util
# We need to associate a pod count with each result, but you
# get one result per-node, and the JSON does not carry the pod
# count in that table. Walk the node util structure, assigning the
# n_pods value from the boot results over to the list of node util
# entries associated with it - creating a new 'n_pods' field in the
# node util dataframe.
for (n in seq(length(br$n_pods$Result))) {
nu[[n]]$n_pods = br$n_pods$Result[[n]]
}
# node_util is a list of nested data frames. I'm sure there is some better R'ish
# way of extracting this data maybe with dplyr, map, select or melt, but I can't
# work it out right now, and at least this is semi-readable...
#
# Basically, we are de-listing and flattening the lists of dataframes into a
# singly 'tidy' dataframe...
nodes=do.call("rbind", lapply(nu, "[", "node"))
noschedule=do.call("rbind", lapply(nu, "[", "noschedule"))
n_pods=do.call("rbind", lapply(nu, "[", "n_pods"))
idle=lapply(nu, "[", "cpu_idle")
idle_df=do.call("rbind", lapply(idle, "[[", "cpu_idle"))
free=lapply(nu, "[", "mem_free")
free_df=do.call("rbind", lapply(free, "[[", "mem_free"))
used=lapply(nu, "[", "mem_used")
used_df=do.call("rbind", lapply(used, "[[", "mem_used"))
ifree=lapply(nu, "[", "inode_free")
ifree_df=do.call("rbind", lapply(ifree, "[[", "inode_free"))
iused=lapply(nu, "[", "inode_used")
iused_df=do.call("rbind", lapply(iused, "[[", "inode_used"))
# and build our rows
local_nodedata=tibble(node=nodes$node)
local_nodedata=cbind(local_nodedata, n_pods=n_pods)
local_nodedata=cbind(local_nodedata, noschedule=noschedule)
local_nodedata=cbind(local_nodedata, idle=idle_df$Result)
local_nodedata=cbind(local_nodedata, mem_free=free_df$Result)
local_nodedata=cbind(local_nodedata, mem_used=used_df$Result)
local_nodedata=cbind(local_nodedata, inode_free=ifree_df$Result)
local_nodedata=cbind(local_nodedata, inode_used=iused_df$Result)
local_nodedata=cbind(local_nodedata, testname=rep(testname, length(local_nodedata$node)))
# Now Calculate some stats. This gets more complicated as we may have n-nodes,
# and we want to show a 'pod average', so we try to assess for all nodes. If
# we have different 'size' nodes in a cluster, that could throw out the result,
# but the only other option would be to try and show every node separately in the
# table.
# Get a list of all the nodes
nodes=unique(local_nodedata$node)
memtotal=0
cputotal=0
inodetotal=0
# Calculate per-node totals, and tot them up to a global total.
for (n in nodes) {
# Make a frame with just that nodes data in
thisnode=subset(local_nodedata, node %in% c(n))
# Do not use the master (non-schedulable) nodes to calculate
# launched pod metrics
if(thisnode[1,]$noschedule == "true") {
# For every matching results file
for (ffound in files) {
fname=paste(inputdir, currentdir, ffound, sep="")
if ( !file.exists(fname)) {
warning(paste("Skipping non-existent file: ", fname))
next
}
memtotal = memtotal + thisnode[nrow(thisnode),]$mem_used
cpuused = thisnode[1,]$idle - thisnode[nrow(thisnode),]$idle
cputotal = cputotal + cpuused
inodetotal = inodetotal + thisnode[nrow(thisnode),]$inode_used
# Derive the name from the test result dirname
datasetname=basename(currentdir)
# Import the data
fdata=fromJSON(fname)
# De-nest the test name specific data
shortname=substr(ffound, 1, nchar(ffound)-nchar(".json"))
fdata=fdata[[shortname]]
testname=datasetname
# Most of the data we are looking for comes in BootResults, so pick it out to make
# referencing easier
br=fdata$BootResults
# The launched pods is a list of data frames when imported. It is much nicer
# for us to work with it as a single data frame, so convert it...
lp=do.call("rbind", br$launched_pods)
########################################################
#### Now extract all the pod launch boot data items ####
########################################################
local_bootdata=tibble(launch_time=br$launch_time$Result)
local_bootdata=cbind(local_bootdata, n_pods=br$n_pods$Result)
local_bootdata=cbind(local_bootdata, node=lp$node)
local_bootdata=cbind(local_bootdata, testname=rep(testname, length(local_bootdata$node)))
########################################################
#### Now extract all node performance information ######
########################################################
nu=br$node_util
# We need to associate a pod count with each result, but you
# get one result per-node, and the JSON does not carry the pod
# count in that table. Walk the node util structure, assigning the
# n_pods value from the boot results over to the list of node util
# entries associated with it - creating a new 'n_pods' field in the
# node util dataframe.
for (n in seq(length(br$n_pods$Result))) {
nu[[n]]$n_pods = br$n_pods$Result[[n]]
}
# node_util is a list of nested data frames. I'm sure there is some better R'ish
# way of extracting this data maybe with dplyr, map, select or melt, but I can't
# work it out right now, and at least this is semi-readable...
#
# Basically, we are de-listing and flattening the lists of dataframes into a
# singly 'tidy' dataframe...
nodes=do.call("rbind", lapply(nu, "[", "node"))
noschedule=do.call("rbind", lapply(nu, "[", "noschedule"))
n_pods=do.call("rbind", lapply(nu, "[", "n_pods"))
idle=lapply(nu, "[", "cpu_idle")
idle_df=do.call("rbind", lapply(idle, "[[", "cpu_idle"))
free=lapply(nu, "[", "mem_free")
free_df=do.call("rbind", lapply(free, "[[", "mem_free"))
used=lapply(nu, "[", "mem_used")
used_df=do.call("rbind", lapply(used, "[[", "mem_used"))
ifree=lapply(nu, "[", "inode_free")
ifree_df=do.call("rbind", lapply(ifree, "[[", "inode_free"))
iused=lapply(nu, "[", "inode_used")
iused_df=do.call("rbind", lapply(iused, "[[", "inode_used"))
# and build our rows
local_nodedata=tibble(node=nodes$node)
local_nodedata=cbind(local_nodedata, n_pods=n_pods)
local_nodedata=cbind(local_nodedata, noschedule=noschedule)
local_nodedata=cbind(local_nodedata, idle=idle_df$Result)
local_nodedata=cbind(local_nodedata, mem_free=free_df$Result)
local_nodedata=cbind(local_nodedata, mem_used=used_df$Result)
local_nodedata=cbind(local_nodedata, inode_free=ifree_df$Result)
local_nodedata=cbind(local_nodedata, inode_used=iused_df$Result)
local_nodedata=cbind(local_nodedata, testname=rep(testname, length(local_nodedata$node)))
# Now Calculate some stats. This gets more complicated as we may have n-nodes,
# and we want to show a 'pod average', so we try to assess for all nodes. If
# we have different 'size' nodes in a cluster, that could throw out the result,
# but the only other option would be to try and show every node separately in the
# table.
# Get a list of all the nodes
nodes=unique(local_nodedata$node)
memtotal=0
cputotal=0
inodetotal=0
# Calculate per-node totals, and tot them up to a global total.
for (n in nodes) {
# Make a frame with just that nodes data in
thisnode=subset(local_nodedata, node %in% c(n))
# Do not use the master (non-schedulable) nodes to calculate
# launched pod metrics
if(thisnode[1,]$noschedule == "true") {
next
}
memtotal = memtotal + thisnode[nrow(thisnode),]$mem_used
cpuused = thisnode[1,]$idle - thisnode[nrow(thisnode),]$idle
cputotal = cputotal + cpuused
inodetotal = inodetotal + thisnode[nrow(thisnode),]$inode_used
}
num_pods = local_bootdata$n_pods[length(local_bootdata$n_pods)]
# We get data in Kb, but want the graphs in Gb.
memtotal = memtotal / (1024*1024)
gb_per_pod = memtotal/num_pods
pod_per_gb = 1/gb_per_pod
# Memory usage stats.
local_mems = c(
"Test"=testname,
"n"=num_pods,
"Tot_Gb"=round(memtotal, 3),
"avg_Gb"=round(gb_per_pod, 4),
"n_per_Gb"=round(pod_per_gb, 2)
)
memstats=rbind(memstats, local_mems)
# cpu usage stats
local_cpus = c(
"Test"=testname,
"n"=num_pods,
"Tot_CPU"=round(cputotal, 3),
"avg_CPU"=round(cputotal/num_pods, 4)
)
cpustats=rbind(cpustats, local_cpus)
# launch (boot) stats
local_boots = c(
"Test"=testname,
"n"=num_pods,
"median"=median(na.omit(local_bootdata)$launch_time)/1000,
"min"=min(na.omit(local_bootdata)$launch_time)/1000,
"max"=max(na.omit(local_bootdata)$launch_time)/1000,
"sd"=round(sd(na.omit(local_bootdata)$launch_time)/1000, 4)
)
bootstats=rbind(bootstats, local_boots)
# inode stats
local_inodes = c(
"Test"=testname,
"n"=num_pods,
"Tot_inode"=round(inodetotal, 3),
"avg_inode"=round(inodetotal/num_pods, 4)
)
inodestats=rbind(inodestats, local_inodes)
# And collect up our rows into our global table of all results
# These two tables *should* be the source of all the data we need to
# process and plot (apart from the stats....)
bootdata=rbind(bootdata, local_bootdata, make.row.names=FALSE)
nodedata=rbind(nodedata, local_nodedata, make.row.names=FALSE)
}
num_pods = local_bootdata$n_pods[length(local_bootdata$n_pods)]
# We get data in Kb, but want the graphs in Gb.
memtotal = memtotal / (1024*1024)
gb_per_pod = memtotal/num_pods
pod_per_gb = 1/gb_per_pod
# Memory usage stats.
local_mems = c(
"Test"=testname,
"n"=num_pods,
"Tot_Gb"=round(memtotal, 3),
"avg_Gb"=round(gb_per_pod, 4),
"n_per_Gb"=round(pod_per_gb, 2)
)
memstats=rbind(memstats, local_mems)
# cpu usage stats
local_cpus = c(
"Test"=testname,
"n"=num_pods,
"Tot_CPU"=round(cputotal, 3),
"avg_CPU"=round(cputotal/num_pods, 4)
)
cpustats=rbind(cpustats, local_cpus)
# launch (boot) stats
local_boots = c(
"Test"=testname,
"n"=num_pods,
"median"=median(na.omit(local_bootdata)$launch_time)/1000,
"min"=min(na.omit(local_bootdata)$launch_time)/1000,
"max"=max(na.omit(local_bootdata)$launch_time)/1000,
"sd"=round(sd(na.omit(local_bootdata)$launch_time)/1000, 4)
)
bootstats=rbind(bootstats, local_boots)
# inode stats
local_inodes = c(
"Test"=testname,
"n"=num_pods,
"Tot_inode"=round(inodetotal, 3),
"avg_inode"=round(inodetotal/num_pods, 4)
)
inodestats=rbind(inodestats, local_inodes)
}
# And collect up our rows into our global table of all results
# These two tables *should* be the source of all the data we need to
# process and plot (apart from the stats....)
bootdata=rbind(bootdata, local_bootdata, make.row.names=FALSE)
nodedata=rbind(nodedata, local_nodedata, make.row.names=FALSE)
}
# Check if we got any stats at all by checking the memstats data. If we found no data,
# abort early and nicely
if ( length(memstats) == 0 ) {
cat("No results files found for scaling tests\n\n")
return()
}
# It's nice to show the graphs in Gb, at least for any decent sized test
# run, so make a new column with that pre-divided data in it for us to use.
nodedata$mem_free_gb = nodedata$mem_free/(1024*1024)
nodedata$mem_used_gb = nodedata$mem_used/(1024*1024)
# And show the boot times in seconds, not mS
bootdata$launch_time_s = bootdata$launch_time/1000
# The labels get messed up by us using an 'if' in the aes() - correct it by
# using the same 'if' to assign what we really want to use for the labels.
colour_label=(if(length(resultdirs)> 1) "testname" else "node")
########## Output memory page ##############
mem_stats_plot = suppressWarnings(ggtexttable(data.frame(memstats),
theme=ttheme(base_size=10),
rows=NULL
))
mem_line_plot <- ggplot(data=nodedata, aes(n_pods,
mem_free_gb,
colour=(if (length(resultdirs) > 1) testname else node),
group=interaction(testname, node))) +
labs(colour=colour_label) +
geom_line(alpha=0.2) +
geom_point(aes(shape=node), alpha=0.3, size=0.5) +
xlab("pods") +
ylab("System Avail (Gb)") +
scale_y_continuous(labels=comma) +
ggtitle("System Memory free") +
theme(legend.position="bottom") +
theme(axis.text.x=element_text(angle=90))
page1 = grid.arrange(
mem_line_plot,
mem_stats_plot,
ncol=1
)
# pagebreak, as the graphs overflow the page otherwise
cat("\n\n\\pagebreak\n")
########## Output cpu page ##############
cpu_stats_plot = suppressWarnings(ggtexttable(data.frame(cpustats),
theme=ttheme(base_size=10),
rows=NULL
))
cpu_line_plot <- ggplot(data=nodedata, aes(n_pods,
idle,
colour=(if (length(resultdirs) > 1) testname else node),
group=interaction(testname, node))) +
labs(colour=colour_label) +
geom_line(alpha=0.2) +
geom_point(aes(shape=node), alpha=0.3, size=0.5) +
xlab("pods") +
ylab("System CPU Idle (%)") +
ggtitle("System CPU usage") +
theme(legend.position="bottom") +
theme(axis.text.x=element_text(angle=90))
page2 = grid.arrange(
cpu_line_plot,
cpu_stats_plot,
ncol=1
)
# pagebreak, as the graphs overflow the page otherwise
cat("\n\n\\pagebreak\n")
########## Output boot page ##############
boot_stats_plot = suppressWarnings(ggtexttable(data.frame(bootstats),
theme=ttheme(base_size=10),
rows=NULL
))
boot_line_plot <- ggplot() +
geom_line( data=bootdata, aes(n_pods, launch_time_s, colour=testname, group=testname), alpha=0.2) +
geom_point( data=bootdata, aes(n_pods, launch_time_s, colour=interaction(testname, node), group=testname), alpha=0.6, size=0.6, stroke=0, shape=16) +
xlab("pods") +
ylab("Boot time (s)") +
ggtitle("Pod boot time") +
theme(legend.position="bottom") +
theme(axis.text.x=element_text(angle=90))
page3 = grid.arrange(
boot_line_plot,
boot_stats_plot,
ncol=1
)
# pagebreak, as the graphs overflow the page otherwise
cat("\n\n\\pagebreak\n")
########## Output inode page ##############
inode_stats_plot = suppressWarnings(ggtexttable(data.frame(inodestats),
theme=ttheme(base_size=10),
rows=NULL
))
inode_line_plot <- ggplot(data=nodedata, aes(n_pods,
inode_free,
colour=(if (length(resultdirs) > 1) testname else node),
group=interaction(testname, node))) +
labs(colour=colour_label) +
geom_line(alpha=0.2) +
geom_point(aes(shape=node), alpha=0.3, size=0.5) +
xlab("pods") +
ylab("inodes free") +
scale_y_continuous(labels=comma) +
ggtitle("inodes free") +
theme(legend.position="bottom") +
theme(axis.text.x=element_text(angle=90))
page4 = grid.arrange(
inode_line_plot,
inode_stats_plot,
ncol=1
)
}
# It's nice to show the graphs in Gb, at least for any decent sized test
# run, so make a new column with that pre-divided data in it for us to use.
nodedata$mem_free_gb = nodedata$mem_free/(1024*1024)
nodedata$mem_used_gb = nodedata$mem_used/(1024*1024)
# And show the boot times in seconds, not mS
bootdata$launch_time_s = bootdata$launch_time/1000
# The labels get messed up by us using an 'if' in the aes() - correct it by
# using the same 'if' to assign what we really want to use for the labels.
colour_label=(if(length(resultdirs)> 1) "testname" else "node")
########## Output memory page ##############
mem_stats_plot = suppressWarnings(ggtexttable(data.frame(memstats),
theme=ttheme(base_size=10),
rows=NULL
))
mem_line_plot <- ggplot(data=nodedata, aes(n_pods,
mem_free_gb,
colour=(if (length(resultdirs) > 1) testname else node),
group=interaction(testname, node))) +
labs(colour=colour_label) +
geom_line(alpha=0.2) +
geom_point(aes(shape=node), alpha=0.3, size=0.5) +
xlab("pods") +
ylab("System Avail (Gb)") +
scale_y_continuous(labels=comma) +
ggtitle("System Memory free") +
theme(axis.text.x=element_text(angle=90))
page1 = grid.arrange(
mem_line_plot,
mem_stats_plot,
ncol=1
)
# pagebreak, as the graphs overflow the page otherwise
cat("\n\n\\pagebreak\n")
########## Output cpu page ##############
cpu_stats_plot = suppressWarnings(ggtexttable(data.frame(cpustats),
theme=ttheme(base_size=10),
rows=NULL
))
cpu_line_plot <- ggplot(data=nodedata, aes(n_pods,
idle,
colour=(if (length(resultdirs) > 1) testname else node),
group=interaction(testname, node))) +
labs(colour=colour_label) +
geom_line(alpha=0.2) +
geom_point(aes(shape=node), alpha=0.3, size=0.5) +
xlab("pods") +
ylab("System CPU Idle (%)") +
ggtitle("System CPU usage") +
theme(axis.text.x=element_text(angle=90))
page2 = grid.arrange(
cpu_line_plot,
cpu_stats_plot,
ncol=1
)
# pagebreak, as the graphs overflow the page otherwise
cat("\n\n\\pagebreak\n")
########## Output boot page ##############
boot_stats_plot = suppressWarnings(ggtexttable(data.frame(bootstats),
theme=ttheme(base_size=10),
rows=NULL
))
boot_line_plot <- ggplot() +
geom_line( data=bootdata, aes(n_pods, launch_time_s, colour=testname, group=testname), alpha=0.2) +
geom_point( data=bootdata, aes(n_pods, launch_time_s, colour=interaction(testname, node), group=testname), alpha=0.6, size=0.6, stroke=0, shape=16) +
xlab("pods") +
ylab("Boot time (s)") +
ggtitle("Pod boot time") +
theme(axis.text.x=element_text(angle=90))
page3 = grid.arrange(
boot_line_plot,
boot_stats_plot,
ncol=1
)
# pagebreak, as the graphs overflow the page otherwise
cat("\n\n\\pagebreak\n")
########## Output inode page ##############
inode_stats_plot = suppressWarnings(ggtexttable(data.frame(inodestats),
theme=ttheme(base_size=10),
rows=NULL
))
inode_line_plot <- ggplot(data=nodedata, aes(n_pods,
inode_free,
colour=(if (length(resultdirs) > 1) testname else node),
group=interaction(testname, node))) +
labs(colour=colour_label) +
geom_line(alpha=0.2) +
geom_point(aes(shape=node), alpha=0.3, size=0.5) +
xlab("pods") +
ylab("inodes free") +
scale_y_continuous(labels=comma) +
ggtitle("inodes free") +
theme(axis.text.x=element_text(angle=90))
page4 = grid.arrange(
inode_line_plot,
inode_stats_plot,
ncol=1
)
render_tidy_scaling()
+118
View File
@@ -0,0 +1,118 @@
# Scaling metrics tests
This directory contains a number of scripts to perform a variety of system scaling tests.
The tests are described in their individual sections below.
Each test has a number of configurable options. Many of those options are common across all tests.
Those options are detailed in their own section below.
> **Note:** `k8s_scale_rapid.sh` is the most complete and upto date test. It is the only test to
> currently use the `collectd` data collection method. Other tests use a privileged container to
> gather statistics.
>
> If you find one of the other tests useful, please consider updating it and the corresponding report
> generation code to use the `collectd` method and send a Pull Request with your updates to this codebase.
## Global test configuration options
The following variables are settable for many of the tests. Check each individual tests help
for specifics and their individual default values.
| Variable | Default Value | Description |
| -------- | ------------- | ----------- |
| TEST_NAME | test dependant | Can be set to over-ride the default JSON results filename |
| NUM_PODS | 20 | Number of pods to launch |
| STEP | 1 | Number of pods to launch per cycle |
| wait_time | 30 | Seconds to wait for pods to become ready |
| delete_wait_time | 600 | Seconds to wait for all pods to be deleted |
| settle_time | 5 | Seconds to wait after pods ready before taking measurements |
| use_api | yes | specify yes or no to use the JSON API to launch pods (otherwise, launch via YAML) |
| grace | 30 | specify the grace period in seconds for workload pod termination |
| RUNTIME | unset | specify the `RuntimeClass` to use to launch the pods |
## k8s_parallel.sh
Measures pod create and delete times whilst increasing the number of pods launched in parallel.
The test works by creating and destroying deployments with the required number of replicas being scaled.
## k8s_scale_nc.sh
Measures pod response time using `nc` to test network connection response. Stores results as percentile
values. Is used to see if the response time latency and jitter is affected by scaling the number of pods.
## k8s_scale_net.sh
Measures pod response time to a `curl` HTTP get request from the K8S e2e `agnhost` image.
Used to measure if the 'ready to respond' time scales with the number of service ports in use.
## k8s_scale_rapid.sh
Measures how pod launch and the k8s system scales whilst launching more and more pods.
Uses the `collectd` method to gather a number of statistics, including:
- cpu usage
- memory usage
- network connections
- disk usage
- ipc stats
## k8s_scale.sh
The fore-runner to `k8s_scale_rapid.sh`, using the privileged pod method to gather statistics. It is recommended
to use `k8s_scale_rapid.sh` in preference if possible.
# Example
Below is a brief example of running the `k8s_scale_rapid.sh` test and generating a report from the results.
1. Run the test
The test will run against the default `kubectl` configured cluster.
```sh
$ ./scaling/k8s_scale.sh
```
Results are stored in the `results` directory. The results will comprise of one `JSON` file for the test, and
one `.tar.gz` file for each node found in the cluster.
> **Note:** Only the `collectd` based tests generate `.tar.gz` files. All other tests only generate a single
> `JSON` file for each run.
1. Move the results files
In order to generate the report, the results files should be moved into an appropriately named sub-directory.
The report generator can process and compare multiple sets of results. Each set of results should be placed
into its own sub-directory. The below example uses the name `run1` as an example:
```sh
$ cd results
$ mkdir run1
$ mv *.json run1
$ mv *.tar.gz run1
```
This sequence can be repeated to gather multiple test data sets. Place each data set in its own subdirectory.
The report generator will process and compare all data set subdirectories found in the `results` directory.
1. Generate the report
The report generator in the `report` subdirectory processes the sub-directories of the `results` directory
to produce a `PDF` report and individual `PNG` based graphs.. The report generator utilises `docker` to create
a docker image containing all the tooling necessary.
```sh
$ cd report
$ ./makereport.sh
...
$ tree output
output/
├── dut-1.png
├── metrics_report.pdf
├── scaling-1.png
├── scaling-2.png
├── scaling-3.png
└── scaling-4.png
```
+3
View File
@@ -13,6 +13,7 @@ deployment="busybox"
stats_pod="stats"
NUM_PODS=${NUM_PODS:-20}
NUM_DEPLOYMENTS=${NUM_DEPLOYMENTS:-20}
STEP=${STEP:-1}
LABEL=${LABEL:-magiclabel}
@@ -24,6 +25,8 @@ delete_wait_time=${delete_wait_time:-600}
settle_time=${settle_time:-5}
use_api=${use_api:-yes}
grace=${grace:-30}
proc_wait_time=${proc_wait_time:-20}
proc_sleep_time=2
declare -a new_pods
declare -A node_basemem
+2 -9
View File
@@ -126,14 +126,10 @@ init() {
# a nice way to do it (unless you want to parse 'descibe nodes')
# Have a read of https://github.com/kubernetes/kubernetes/issues/25353
k8s_api_init
framework_init
# Ensure we pre-cache the container image etc.
warmup
# And now we can set up our results storage then...
metrics_json_init "k8s"
save_config
}
save_config(){
@@ -218,11 +214,8 @@ cleanup() {
# First try to save any results we got
metrics_json_end_array "BootResults"
metrics_json_save
kill_deployment "${deployment}" "${LABEL}" "${LABELVALUE}" ${delete_wait_time}
k8s_api_shutdown
framework_shutdown
}
show_vars()
+3 -9
View File
@@ -169,7 +169,7 @@ EOF
if [ $n_pods -eq 0 ]; then
local pods_per_gb=0
else
local pods_per_gb=$(bc -l <<< "scale=2; ($total_mem_used/1024) / $n_pods")
local pods_per_gb=$(printf "%0f" $(bc -l <<< "scale=2; ($total_mem_used/1024) / $n_pods"))
fi
local mem_json="$(cat << EOF
"memory": {
@@ -209,7 +209,7 @@ init() {
# FIXME - check the node(s) can run enough pods - check 'max-pods' in the
# kubelet config - from 'kubectl describe node -o json' ?
k8s_api_init
framework_init
# Launch our stats gathering pod
kubectl apply -f ${SCRIPT_PATH}/${stats_pod}.yaml
@@ -218,10 +218,6 @@ init() {
# FIXME - we should probably 'warm up' the cluster with the container image(s) we will
# use for testing, otherwise the download time will likely be included in the first pod
# boot time.
# And now we can set up our results storage then...
metrics_json_init "k8s"
save_config
}
save_config(){
@@ -347,9 +343,7 @@ EOF
)"
metrics_json_add_fragment "$json"
metrics_json_save
k8s_api_shutdown
framework_shutdown
}
show_vars()
+3 -9
View File
@@ -195,7 +195,7 @@ EOF
if [ $n_pods -eq 0 ]; then
local pods_per_gb=0
else
local pods_per_gb=$(bc -l <<< "scale=2; ($total_mem_used/1024) / $n_pods")
local pods_per_gb=$(printf "%0f" $(bc -l <<< "scale=2; ($total_mem_used/1024) / $n_pods"))
fi
local mem_json="$(cat << EOF
"memory": {
@@ -235,7 +235,7 @@ init() {
# FIXME - check the node(s) can run enough pods - check 'max-pods' in the
# kubelet config - from 'kubectl describe node -o json' ?
k8s_api_init
framework_init
# Launch our stats gathering pod
kubectl apply -f ${SCRIPT_PATH}/${stats_pod}.yaml
@@ -244,10 +244,6 @@ init() {
# FIXME - we should probably 'warm up' the cluster with the container image(s) we will
# use for testing, otherwise the download time will likely be included in the first pod
# boot time.
# And now we can set up our results storage then...
metrics_json_init "k8s"
save_config
}
save_config(){
@@ -410,9 +406,7 @@ EOF
)"
metrics_json_add_fragment "$json"
metrics_json_save
k8s_api_shutdown
framework_shutdown
}
show_vars()
+285
View File
@@ -0,0 +1,285 @@
#!/bin/bash
# Copyright (c) 2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
set -e
# Pull in some common, useful, items
SCRIPT_PATH=$(dirname "$(readlink -f "$0")")
source "${SCRIPT_PATH}/../lib/common.bash"
source "${SCRIPT_PATH}/common.bash"
LABELVALUE=${LABELVALUE:-scale_net}
# Set some default metrics env vars
TEST_ARGS="runtime=${RUNTIME}"
TEST_NAME="k8s scaling net"
input_yaml="${SCRIPT_PATH}/net-serve.yaml.in"
input_json="${SCRIPT_PATH}/net-serve.json.in"
name_base_depl="net-serve"
# $1 is the launch time in seconds this pod/container took to start up.
# $2 is the number of pod/containers under test
# $3 is the time to pod network measure
grab_stats(){
local launch_time_ms=$1
local n_pods=$2
local net_time=$3
info "And grab some stats"
local date_json="$(cat << EOF
"date": {
"ns": $(date +%s%N),
"Date": "$(date -u +"%Y-%m-%dT%T.%3N")"
}
EOF
)"
metrics_json_add_array_fragment "$date_json"
local pods_json="$(cat << EOF
"n_pods": {
"Result": ${n_pods},
"Units" : "int"
}
EOF
)"
metrics_json_add_array_fragment "$pods_json"
local time_to_pod_net_json="$(cat << EOF
"time_to_pod_net": {
"Result": ${net_time},
"Units" : "ms"
}
EOF
)"
metrics_json_add_array_fragment "$time_to_pod_net_json"
local launch_json="$(cat << EOF
"launch_time": {
"Result": $launch_time_ms,
"Units" : "ms"
}
EOF
)"
metrics_json_add_array_fragment "$launch_json"
info "launch [$launch_time_ms]"
metrics_json_close_array_element
}
init() {
info "Initialising"
local cmds=("bc" "jq")
check_cmds "${cmds[@]}"
info "Checking Kubernetes accessible"
local worked=$( kubectl get nodes > /dev/null 2>&1 && echo $? || echo $? )
if [ "$worked" != 0 ]; then
die "kubectl failed to get nodes"
fi
info $(get_num_nodes) "Kubernetes nodes in 'Ready' state found"
framework_init
}
save_config() {
metrics_json_start_array
local json="$(cat << EOF
{
"testname": "${TEST_NAME}",
"NUM_DEPLOYMENTS": ${NUM_DEPLOYMENTS},
"STEP": ${STEP},
"wait_time": ${wait_time},
"delete_wait_time": ${delete_wait_time},
"settle_time": ${settle_time}
}
EOF
)"
metrics_json_add_array_element "$json"
metrics_json_end_array "Config"
}
run() {
info "Running test"
local header_post="Content-Type: application/json"
local base_curl=${API_ADDRESS}:${API_PORT}/apis/apps/v1/namespaces/default/deployments
trap cleanup EXIT QUIT KILL
metrics_json_start_array
for reqs in $(seq ${STEP} ${STEP} ${NUM_DEPLOYMENTS}); do
local deployment="${name_base_depl}${reqs}"
info "Testing replicas ${reqs} of ${NUM_DEPLOYMENTS}"
# Generate the next yaml file
local runtime_command
if [ -n "$RUNTIME" ]; then
runtime_command="s|@RUNTIMECLASS@|${RUNTIME}|g"
else
runtime_command="/@RUNTIMECLASS@/d"
fi
local input_template
local generated_file
if [ "$use_api" != "no" ]; then
input_template=$input_json
generated_file=$generated_json
else
input_template=$input_yaml
generated_file=$generated_yaml
fi
sed -e $runtime_command \
-e "s|@DEPLOYMENT@|${deployment}|g" \
-e "s|@LABEL@|${LABEL}|g" \
-e "s|@LABELVALUE@|${LABELVALUE}|g" \
-e "s|@GRACE@|${grace}|g" \
< ${input_template} > ${generated_file}
info "Applying changes"
local start_time=$(date +%s%N)
if [ "$use_api" != "no" ]; then
curl -s ${base_curl} -XPOST -H "${header_post}" -d@${generated_file} > /dev/null
else
kubectl apply -f ${generated_file}
fi
kubectl rollout status --timeout=${wait_time}s deployment/${deployment}
kubectl expose --port=8080 deployment $deployment
# Check service exposed
cmd="kubectl get services $deployment -n default --no-headers=true"
waitForProcess "$proc_wait_time" "$proc_sleep_time" "$cmd" "Waiting for service"
IP=$(kubectl get services $deployment -n default --no-headers=true | awk '{printf $3}')
end_net=$(date +%s%N)
info "IP: $IP"
# service health check
cmd="curl --noproxy \"*\" http://$IP:8080/healthz"
waitForProcess "$proc_wait_time" "$proc_sleep_time" "$cmd" "http server is not ready yet!!"
RESP=$(curl -s --noproxy "*" http://$IP:8080/echo?msg=curl%20request%20to%20$deployment)
local end_time=$(date +%s%N)
info "http reply: $RESP"
local total_milliseconds=$(( (end_time - start_time) / 1000000 ))
local net_diff=$(( (end_net - start_time) / 1000000 ))
info "Took $total_milliseconds ms ($end_time - $start_time)"
info "Net took $net_diff ms"
kubectl delete service $deployment
if [ $? -ne 0 ]; then
echo "kubectl delete service failed"
exit
fi
sleep ${settle_time}
grab_stats $total_milliseconds $reqs $net_diff
done
}
cleanup() {
info "Cleaning up"
# First try to save any results we got
metrics_json_end_array "BootResults"
local start_time=$(date +%s%N)
for reqs in $(seq ${STEP} ${STEP} ${NUM_DEPLOYMENTS}); do
local deployment="${name_base_depl}${reqs}"
kubectl delete deployment --wait=true --timeout=${delete_wait_time}s ${deployment} || true
done
for x in $(seq 1 ${delete_wait_time}); do
local npods=$(kubectl get pods -l=${LABEL}=${LABELVALUE} -o=name | wc -l)
if [ $npods -eq 0 ]; then
echo "All pods have terminated at cycle $x"
local alldied=true
break;
fi
sleep 1
done
if [ -z "$alldied" ]; then
echo "ERROR: Not all pods died!"
fi
local end_time=$(date +%s%N)
local total_milliseconds=$(( (end_time - start_time) / 1000000 ))
info "Delete Took $total_milliseconds ms ($end_time - $start_time)"
local json="$(cat << EOF
"Delete": {
"Result": ${total_milliseconds},
"Units" : "ms"
}
EOF
)"
metrics_json_add_fragment "$json"
framework_shutdown
}
show_vars() {
echo -e "\nEnvironment variables:"
echo -e "\tName (default)"
echo -e "\t\tDescription"
echo -e "\tNUM_DEPLOYMENTS (${NUM_DEPLOYMENTS})"
echo -e "\t\tNumber of deployments to launch"
echo -e "\tSTEP (${STEP})"
echo -e "\t\tNumber of pods to launch per cycle"
echo -e "\twait_time (${wait_time})"
echo -e "\t\tSeconds to wait for pods to become ready"
echo -e "\tproc_wait_time (${proc_wait_time})"
echo -e "\t\tSeconds to wait for net server process to become ready"
echo -e "\tdelete_wait_time (${delete_wait_time})"
echo -e "\t\tSeconds to wait for all pods to be deleted"
echo -e "\tsettle_time (${settle_time})"
echo -e "\t\tSeconds to wait after pods ready before taking measurements"
echo -e "\tuse_api (${use_api})"
echo -e "\t\tspecify yes or no to use the API to launch pods"
echo -e "\tgrace (${grace})"
echo -e "\t\tspecify the grace period in seconds for workload pod termination"
}
help() {
usage=$(cat << EOF
Usage: $0 [-h] [options]
Description:
Launch a series of workloads and take time to pod network metric measurements after
each launch.
Options:
-h, Help page.
EOF
)
echo "$usage"
show_vars
}
main() {
local OPTIND
while getopts "h" opt;do
case ${opt} in
h)
help
exit 0;
;;
esac
done
shift $((OPTIND-1))
init
run
}
main "$@"
+4 -26
View File
@@ -15,6 +15,8 @@ source "${SCRIPT_PATH}/../collectd/collectd.bash"
NUM_PODS=${NUM_PODS:-20}
STEP=${STEP:-1}
SMF_USE_COLLECTD=true
LABELVALUE=${LABELVALUE:-gandalf}
pod_command="[\"tail\", \"-f\", \"/dev/null\"]"
@@ -64,27 +66,7 @@ EOF
}
init() {
info "Initialising"
local cmds=("bc" "jq")
check_cmds "${cmds[@]}"
info "Checking k8s accessible"
local worked=$( kubectl get nodes > /dev/null 2>&1 && echo $? || echo $? )
if [ "$worked" != 0 ]; then
die "kubectl failed to get nodes"
fi
info $(get_num_nodes) "k8s nodes in 'Ready' state found"
k8s_api_init
# Launch our stats gathering pod
init_stats $wait_time
# And now we can set up our results storage then...
metrics_json_init "k8s"
save_config
framework_init
}
save_config(){
@@ -198,11 +180,7 @@ EOF
)"
metrics_json_add_fragment "$json"
metrics_json_save
cleanup_stats $delete_wait_time
k8s_api_shutdown
framework_shutdown
}
show_vars()
+40
View File
@@ -0,0 +1,40 @@
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"labels": {
"run": "net-serve"
},
"name": "@DEPLOYMENT@"
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"run": "net-serve"
}
},
"template": {
"metadata": {
"labels": {
"run": "net-serve",
"@LABEL@": "@LABELVALUE@"
}
},
"spec": {
"terminationGracePeriodSeconds": @GRACE@,
"runtimeClassName": "@RUNTIMECLASS@",
"automountServiceAccountToken": false,
"containers": [{
"name": "net-serve",
"image": "gcr.io/kubernetes-e2e-test-images/agnhost:2.8",
"imagePullPolicy": "IfNotPresent",
"args": [
"netexec"
]
}],
"restartPolicy": "Always"
}
}
}
}
+27
View File
@@ -0,0 +1,27 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: net-serve
name: @DEPLOYMENT@
spec:
replicas: 1
selector:
matchLabels:
run: net-serve
template:
metadata:
labels:
run: net-serve
@LABEL@: @LABELVALUE@
spec:
terminationGracePeriodSeconds: @GRACE@
runtimeClassName: @RUNTIMECLASS@
automountServiceAccountToken: false
containers:
- name: net-serve
image: gcr.io/kubernetes-e2e-test-images/agnhost:2.8
imagePullPolicy: IfNotPresent
args:
- netexec
restartPolicy: Always