-
Notifications
You must be signed in to change notification settings - Fork 63
Install RT kernel from RPMs included in RHCOS #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,55 +1,68 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| ###################################################################################### | ||
| ## NOTE: ## | ||
| ## THIS IS A TEMPORARY WORKAROUND UNTIL THIS FEATURE IS AVAILABLE VIA MACHINECONFIG ## | ||
| ## SEE MCO WIP PR: https://github.com/openshift/machine-config-operator/pull/1330 ## | ||
| ## IT ONLY WORKS ON OCP 4.4 AND WITH KERNEL VERSIONS 4.* ## | ||
| ###################################################################################### | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| REPO_DIR="/etc/yum.repos.d" | ||
| RT_REPO="${REPO_DIR}/rt-kernel.repo" | ||
| # What we are doing here: | ||
| # | ||
| # - The installer's bootstrap node installs an "old" os bootimage on cluster nodes, which is just new enough to be able | ||
| # to do everything which is needed on first boot | ||
| # - Then the MCO installs a newer version of the os ("early pivot"). That one is delivered via container image. See the | ||
| # "machine-os-content" image of OCP | ||
| # - With RHCOS version 44 (so OCP 4.4, what the operator is aiming at) the image contains the RT kernel RPMs. They were | ||
| # just put into the root directory of the image | ||
| # - So what we do here (borrowed from MCO code): mount the image (which is not possible directly, so create (but don't | ||
| # run) a container from it) which contains the os version which is currently booted, and install the RPMs from the | ||
| # mount path. | ||
|
|
||
| # Enable yum repo | ||
| if [[ -f $RT_REPO ]] | ||
| then | ||
| # The env var might have been changed, so always create new rt repo | ||
| rm $RT_REPO | ||
| fi | ||
| echo "Mounting OS image" | ||
|
|
||
| mkdir -p $REPO_DIR | ||
| cat > $RT_REPO <<EOF | ||
| [rt] | ||
| baseurl=${RT_REPO_URL} | ||
| gpgcheck=0 | ||
| EOF | ||
|
|
||
| # update cache | ||
| rpm-ostree refresh-md -f | ||
|
|
||
| exit_handler () { | ||
| exit_code=$? | ||
| if [[ ${exit_code} -eq 77 ]]; then | ||
| echo "No update available, nothing to do"; | ||
| exit 0; | ||
| elif [[ ${exit_code} -eq 100 ]]; then | ||
| echo "Initiate reboot, touch /var/reboot" | ||
| touch /var/reboot | ||
| exit 0; | ||
| else | ||
| exit ${exit_code} | ||
| fi | ||
| } | ||
| # remove old container | ||
| osContainerName="osContainer" | ||
| set +e | ||
| podman rm -f "$osContainerName" >/dev/null 2>&1 | ||
| set -e | ||
|
|
||
| # find booted image sha | ||
| sha=$(rpm-ostree status --json | jq -r '.deployments[] | select(.booted == true) | .["custom-origin"][0]' | sed -E "s|.*@(.*)|\1|") | ||
| imageID=$(podman images --digests | grep $sha | awk '{print $4}') | ||
|
|
||
| trap exit_handler EXIT | ||
| # create and mount new container | ||
| podman create --net=none --name "$osContainerName" "$imageID" > /dev/null | ||
| kernelDir=$(podman mount "$osContainerName") | ||
|
|
||
| # Swap to RT kernel | ||
| # Swap to or update RT kernel | ||
| kernel=$(uname -a) | ||
| if [[ $kernel =~ "PREEMPT RT" ]] | ||
| if [[ "$kernel" =~ "PREEMPT RT" ]] | ||
| then | ||
| echo "RT kernel already installed, checking for updates" | ||
| # if no upgrade is available the script will exit with code 77, and we will trap it | ||
| rpm-ostree upgrade --unchanged-exit-77 | ||
| echo "RT kernel updated" | ||
| exit 100 | ||
|
|
||
| installedVersion=$(rpm -qa | grep kernel-rt-core) | ||
| # filename without rpm suffix is available version | ||
| availableVersion=$(ls ${kernelDir}/kernel-rt-core-4*.rpm | xargs basename | xargs -i bash -c 'f="{}" && echo "${f%.*}"') | ||
|
|
||
| if [[ "$installedVersion" == "$availableVersion" ]] | ||
| then | ||
| echo "No update available, nothing to do"; | ||
| exit 0 | ||
| else | ||
| echo "Updating RT kernel" | ||
| rpm-ostree override replace ${kernelDir}/kernel-rt-core-4*.rpm ${kernelDir}/kernel-rt-modules-4*.rpm ${kernelDir}/kernel-rt-modules-extra-4*.rpm | ||
| echo "RT kernel updated, trigger reboot by touching /var/reboot" | ||
| touch /var/reboot | ||
| exit 0 | ||
| fi | ||
|
|
||
| else | ||
| echo "Installing RT kernel" | ||
| rpm-ostree override remove kernel{,-core,-modules,-modules-extra} --install kernel-rt-core --install kernel-rt-modules --install kernel-rt-modules-extra | ||
| echo "RT kernel installed" | ||
| exit 100 | ||
| rpm-ostree override remove kernel{,-core,-modules,-modules-extra} --install ${kernelDir}/kernel-rt-core-4*.rpm --install ${kernelDir}/kernel-rt-modules-4*.rpm --install ${kernelDir}/kernel-rt-modules-extra-4*.rpm | ||
| echo "RT kernel installed, trigger reboot by touching /var/reboot" | ||
| touch /var/reboot | ||
| exit 0 | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package performance | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "time" | ||
|
|
||
| . "github.com/onsi/ginkgo" | ||
| . "github.com/onsi/gomega" | ||
|
|
||
| testutils "github.com/openshift-kni/performance-addon-operators/functests/utils" | ||
| testclient "github.com/openshift-kni/performance-addon-operators/functests/utils/client" | ||
| "github.com/openshift-kni/performance-addon-operators/functests/utils/pods" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| ) | ||
|
|
||
| var _ = Describe("[performance]RT Kernel", func() { | ||
| var testpod *corev1.Pod | ||
|
|
||
| AfterEach(func() { | ||
| if err := testclient.Client.Pods(testutils.NamespaceTesting).Delete(testpod.Name, &metav1.DeleteOptions{}); err == nil { | ||
| pods.WaitForDeletion(testclient.Client, testpod, 60*time.Second) | ||
| } | ||
| }) | ||
|
|
||
| It("should have RT kernel enabled", func() { | ||
|
|
||
| Eventually(func() string { | ||
|
|
||
| // run uname -a in a busybox pod and get logs | ||
| testpod = pods.GetBusybox() | ||
| testpod.Spec.Containers[0].Command = []string{"uname", "-a"} | ||
| testpod.Spec.RestartPolicy = corev1.RestartPolicyNever | ||
| testpod.Spec.NodeSelector = map[string]string{ | ||
| fmt.Sprintf("%s/%s", testutils.LabelRole, testutils.RoleWorkerRT): "", | ||
| } | ||
|
|
||
| var err error | ||
| testpod, err = testclient.Client.Pods(testutils.NamespaceTesting).Create(testpod) | ||
| if err != nil { | ||
| return "" | ||
| } | ||
|
|
||
| err = pods.WaitForPhase(testclient.Client, testpod, corev1.PodSucceeded, 60*time.Second) | ||
| if err != nil { | ||
| return "" | ||
| } | ||
|
|
||
| logs, err := pods.GetLogs(testclient.Client, testpod) | ||
| if err != nil { | ||
| return "" | ||
| } | ||
|
|
||
| return logs | ||
|
|
||
| }, 15*time.Minute, 30*time.Second).Should(ContainSubstring("PREEMPT RT")) | ||
|
|
||
| }) | ||
|
|
||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #!/bin/bash | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. who calls this? only humans or also the operator proper? I guess the former, asking to be sure.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we use a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh sorry, just noticed I was looking at the wrong script |
||
|
|
||
| set -e | ||
|
|
||
| # expect oc to be in PATH by default | ||
| OC_TOOL="${OC_TOOL:-oc}" | ||
|
|
||
| # Label 1 worker node | ||
| echo "[INFO]:labeling 1 worker node with worker-rt" | ||
| node=$(${OC_TOOL} get nodes --selector='node-role.kubernetes.io/worker' -o name | head -1) | ||
| ${OC_TOOL} label $node node-role.kubernetes.io/worker-rt="" | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,7 +52,6 @@ const ( | |
| ) | ||
|
|
||
| const ( | ||
| environmentRTRepoURL = "RT_REPO_URL" | ||
| environmentNonIsolatedCpus = "NON_ISOLATED_CPUS" | ||
| ) | ||
|
|
||
|
|
@@ -73,7 +72,7 @@ func New(assetsDir string, profile *performancev1alpha1.PerformanceProfile) (*ma | |
| Spec: machineconfigv1.MachineConfigSpec{}, | ||
| } | ||
|
|
||
| ignitionConfig, err := getIgnitionConfig(assetsDir, *profile.Spec.RealTimeKernel.RepoURL, profile.Spec.CPU.NonIsolated) | ||
| ignitionConfig, err := getIgnitionConfig(assetsDir, profile) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
@@ -118,7 +117,8 @@ func getKernelArgs(hugePages *performancev1alpha1.HugePages, isolatedCPUs *perfo | |
| return kargs | ||
| } | ||
|
|
||
| func getIgnitionConfig(assetsDir string, rtRepoURL string, nonIsolatedCpus *performancev1alpha1.CPUSet) (*igntypes.Config, error) { | ||
| func getIgnitionConfig(assetsDir string, profile *performancev1alpha1.PerformanceProfile) (*igntypes.Config, error) { | ||
|
|
||
| mode := 0700 | ||
| ignitionConfig := &igntypes.Config{ | ||
| Ignition: igntypes.Ignition{ | ||
|
|
@@ -149,11 +149,12 @@ func getIgnitionConfig(assetsDir string, rtRepoURL string, nonIsolatedCpus *perf | |
| }) | ||
| } | ||
|
|
||
| rtKernelService, err := getSystemdContent(getRTKernelUnitOptions(rtRepoURL)) | ||
| rtKernelService, err := getSystemdContent(getRTKernelUnitOptions()) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| nonIsolatedCpus := profile.Spec.CPU.NonIsolated | ||
| preBootTuningService, err := getSystemdContent( | ||
| getPreBootTuningUnitOptions(string(*nonIsolatedCpus)), | ||
| ) | ||
|
|
@@ -166,11 +167,15 @@ func getIgnitionConfig(assetsDir string, rtRepoURL string, nonIsolatedCpus *perf | |
| return nil, err | ||
| } | ||
|
|
||
| enableRTKernel := profile.Spec.RealTimeKernel != nil && | ||
| profile.Spec.RealTimeKernel.Enabled != nil && | ||
| *profile.Spec.RealTimeKernel.Enabled | ||
|
|
||
| ignitionConfig.Systemd = igntypes.Systemd{ | ||
| Units: []igntypes.Unit{ | ||
| { | ||
| Contents: rtKernelService, | ||
| Enabled: pointer.BoolPtr(true), | ||
| Enabled: &enableRTKernel, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we create at all RT kernel unit if the RT disabled, also pre-tunning also depends on the RT kernel unit, so I am interesting if it will work as expected?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah yes, good point
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. other than that, pre-tuning uses
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK it is correct, if an unit |
||
| Name: getSystemdService(rtKernel), | ||
| }, | ||
| { | ||
|
|
@@ -209,7 +214,7 @@ func getSystemdContent(options []*unit.UnitOption) (string, error) { | |
| return string(outBytes), nil | ||
| } | ||
|
|
||
| func getRTKernelUnitOptions(rtRepoURL string) []*unit.UnitOption { | ||
| func getRTKernelUnitOptions() []*unit.UnitOption { | ||
| return []*unit.UnitOption{ | ||
| // [Unit] | ||
| // Description | ||
|
|
@@ -222,8 +227,6 @@ func getRTKernelUnitOptions(rtRepoURL string) []*unit.UnitOption { | |
| unit.NewUnitOption(systemdSectionUnit, systemdBefore, systemdServiceKubelet), | ||
| unit.NewUnitOption(systemdSectionUnit, systemdBefore, getSystemdService(preBootTuning)), | ||
| // [Service] | ||
| // Environment | ||
| unit.NewUnitOption(systemdSectionService, systemdEnvironment, getSystemdEnvironment(environmentRTRepoURL, rtRepoURL)), | ||
| // Type | ||
| unit.NewUnitOption(systemdSectionService, systemdType, systemdServiceTypeOneshot), | ||
| // RemainAfterExit | ||
|
|
@@ -264,7 +267,7 @@ func getPreBootTuningUnitOptions(nonIsolatedCpus string) []*unit.UnitOption { | |
| return []*unit.UnitOption{ | ||
| // [Unit] | ||
| // Description | ||
| unit.NewUnitOption(systemdSectionUnit, systemdDescription, "Reboot initiated by rt-kernel and pre-boot-tuning"), | ||
| unit.NewUnitOption(systemdSectionUnit, systemdDescription, "Preboot tuning patch"), | ||
| // Wants | ||
| unit.NewUnitOption(systemdSectionUnit, systemdWants, getSystemdService(rtKernel)), | ||
| // After | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow, so we're delivering kernel pkgs in containers? interesting.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIU this is used to access the packages already shipped in the system image (so we are not shipping anything anymore). But I may have out of date information.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is complicated, let me try to summarize:
machine-os-contentimage: that one contains the oslonger version: https://github.com/openshift/machine-config-operator/blob/master/docs/OSUpgrades.md and others
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @slintes great summary. I think it would be great to record this as comment in the
rt-kernel.shscript itself. WDYT?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought REPO_URL was a workaround in 4.3 until we have machineconfig in 4.4 and I see you have a note here as "TEMPORARY WORKAROUND".
Are we having 2 workarounds now for 4.3 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The operator is for 4.4 only. There we have the RT kernel RPMs already available in the RHCOS image, so no need for a yum repo anymore, but the MCO is not ready to use it yet, see WIP PR [0]. So this is a new temporary workaround for 4.4, unrelated to 4.3.
[0] openshift/machine-config-operator#1330
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fromanirh ah missed your comment
will do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fromanirh done