Skip to content
This repository has been archived by the owner on Oct 24, 2023. It is now read-only.

feat: modify container runtime data dir #3072

Merged
merged 1 commit into from Apr 21, 2020

Conversation

alexeldeib
Copy link
Contributor

@alexeldeib alexeldeib commented Apr 15, 2020

Reason for Change:
Allow customization of the container runtime data directory.

The location of this directory will be subject to heavy disk IO and offloading it from the main OS disk can significantly stabilize the system. Solutions already exist to hack this onto nodes, but we'd like to enable it at provision time. This PR does that.

Issue Fixed:
n/a

Requirements:

Notes:

I haven't made API changes to AKS engine in a while, I wasn't sure what all tests I should add. Do we want an e2e covering this? What's the best way to test the full template generation, cmd/generate_test?

@@ -282,6 +282,9 @@ installContainerd() {
ensureContainerd() {
wait_for_file 1200 1 /etc/systemd/system/containerd.service.d/exec_start.conf || exit {{GetCSEErrorCode "ERR_FILE_WATCH_TIMEOUT"}}
wait_for_file 1200 1 /etc/containerd/config.toml || exit {{GetCSEErrorCode "ERR_FILE_WATCH_TIMEOUT"}}
{{- if HasContainerDataDir}}
echo -e "root = \"{{GetContainerDataDir}}\"\n$(cat /etc/containerd/config.toml)" > /etc/containerd/config.toml
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this is ugly. An alternative is doing it in cloud-init for non-vhd installs and doing it in the VHD otherwise. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we don't want to just get the whole generic key/val implementation over with first of all? See the way we use the GetKubeletConfigKeyVals go template layer func in cloud-init (parts/k8s/cloud-init/masternodecustomdata.yml and parts/k8s/cloud-init/nodecustomdata.yml) to accommodate generic key/val injection at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The part I wasn't sure about was how much to generalize this for docker vs containerd because the config files are totally different.

Are you thinking we could do a func like for kubelet, 1 each for docker and containerd, and have those functions accept the raw map[string]string and spit out the raw CRI-specific JSON/TOML? Then we can avoid any conditionals in the bash

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly 💯

Not trying to introduce scope creep, but I think we'll thank ourselves later for doing it the generic way. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really like to avoid "containerdConfig" and "dockerConfig", but we're kind of hiding it no matter what

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm on board with that, will update

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can stick with a single containerRuntimeConfig, and then have per-runtime go template helper funcs. As long as we can assume that key/val strings are common across all runtimes (I think we can), then we can use a single interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a good way to handle errors in the template funcs? I want to do something like this, but struggling to find a good place to drop the Marshal to catch the error. I could do raw string manipulation, but that feels error prone (pun intended) and I wouldn't be guaranteed to construct valid json anyway.

// GetDockerConfig returns the full docker daemon configuration including user overrides.
func (k *KubernetesConfig) GetDockerConfig() (string, error) {
	config := map[string]interface{}{
		"live-restore": true,
		"log-driver": "json-file",
		"log-opts": {
			"max-size": "50m",
			"max-file": "5",
		},
	}

	dataDir, ok := k.ContainerRuntimeConfig[ContainerDataDirKey]
	if ok {
		config["data-root"] = dataDir
	}

	b, err := json.MarshalIndent(config, "", "    ")
	return string(b), err
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and containerd uses toml, even more fun

@codecov
Copy link

codecov bot commented Apr 15, 2020

Codecov Report

Merging #3072 into master will increase coverage by 0.02%.
The diff coverage is 75.83%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3072      +/-   ##
==========================================
+ Coverage   71.03%   71.05%   +0.02%     
==========================================
  Files         147      147              
  Lines       25553    25671     +118     
==========================================
+ Hits        18152    18241      +89     
- Misses       6271     6294      +23     
- Partials     1130     1136       +6     
Impacted Files Coverage Δ
pkg/api/types.go 94.45% <ø> (ø)
pkg/api/vlabs/types.go 73.30% <ø> (ø)
pkg/engine/templates_generated.go 37.76% <ø> (ø)
pkg/api/convertertoapi.go 93.42% <60.00%> (-0.28%) ⬇️
pkg/engine/template_generator.go 81.70% <65.62%> (-0.81%) ⬇️
pkg/api/vlabs/validate.go 79.27% <73.33%> (-0.09%) ⬇️
pkg/api/common/helper.go 75.61% <78.94%> (+0.83%) ⬆️
pkg/api/common/const.go 40.00% <100.00%> (+40.00%) ⬆️
pkg/api/converterfromapi.go 93.94% <100.00%> (+0.05%) ⬆️
pkg/api/defaults.go 92.44% <100.00%> (+0.02%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 22c5fb6...d2ba320. Read the comment docs.

@jackfrancis
Copy link
Member

Thank you @alexeldeib!

@alexeldeib alexeldeib force-pushed the ace/root branch 3 times, most recently from 5424520 to e2ae09a Compare April 17, 2020 11:59
@alexeldeib
Copy link
Contributor Author

alexeldeib commented Apr 17, 2020

@jackfrancis how do you feel about this? Can get some more test coverage if it's agreeable. Curious what you think about adding types like this for containerd/docker config and stashing it internally in common.

@alexeldeib alexeldeib force-pushed the ace/root branch 2 times, most recently from 76453bd to 22fe938 Compare April 17, 2020 17:09
if k.ContainerRuntime == Containerd {
_, err := common.GetContainerdConfig(k.ContainerRuntimeConfig, nil)
if err != nil {
return err
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized the way I'm doing this now, these errors are basically guaranteed not to get hit...they're reducing coverage in the diff but I think keeping them is correct for future sanity

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yay for functional options

}

// GetContainerdConfig transforms the default containerd config with overrides. Overrides may be nil.
func GetContainerdConfig(opts map[string]string, overrides []func(*ContainerdConfig) error) (string, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I came up with the functional setters to work around the fact that there are templated values in the docker/containerd config normally, but we don't want that logic in the helper functions (for testing purposes and circular imports).

I realize now that having both map[string]string and []func(*ContainerdConfig) error) is a bit redundant, for example we could have a separate helper with signature func help(map[string]string) []func(*ContainerdConfig) error) for containerd/docker each and use only functional setters to construct the final object

DefaultContainerdConfig = ContainerdConfig{
Version: 2,
// should this be true? seems maybe not https://github.com/containerd/containerd/blob/master/docs/ops.md#base-configuration
Subreaper: false,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought recommended docs say to set this to true? Is false a kube thing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpuguy83 should be able to answer

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the setting. It is not part of the config anymore and never really made sense I don't think.
The config was changed to no_subreaper in containerd/containerd#1822 and later removed altogether as far as I can tell (can't find where it was removed, but the config doesn't exist).

@@ -282,6 +282,9 @@ installContainerd() {
ensureContainerd() {
wait_for_file 1200 1 /etc/systemd/system/containerd.service.d/exec_start.conf || exit {{GetCSEErrorCode "ERR_FILE_WATCH_TIMEOUT"}}
wait_for_file 1200 1 /etc/containerd/config.toml || exit {{GetCSEErrorCode "ERR_FILE_WATCH_TIMEOUT"}}
{{- if HasValidContainerdConfig}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should do this in the cloud-init file spec directly, rather than as a post-cloud-init shell command on top of the cloud-init-paved file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you happen to know if apt will avoid overwriting the old daemon config if it already exists? That was my only concern, other than some confusion about VHD vs non-VHD.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also do you think it's reasonable to remove the validity check and write whatever we have once we've passed validation, or should we attempt to provide a sane default if we know it's invalid but still got here somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of course doing that dropped my PR coverage again :P

}

// ContainerdSandboxImageOverrider produces a function to transform containerd config by setting the SandboxImage.
func ContainerdSandboxImageOverrider(image string) func(*ContainerdConfig) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an Overrider rather than just an Override func so we can use "curried" funcs with an image arg baked in elsewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my thinking, the semantics are already fairly clear so I'm happy to keep the naming consistent (Override) if you think it makes sense

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.untrusted]
{{/* note: runc really should not be used for untrusted workloads... should we remove this? This is here because it was here before */}}
runtime_type = "io.containerd.runc.v2"
#EOF
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure we keep these #EOF sentinel chars at the end of the data stream, which the CSE scripts use to determine that cloud-init has paved the entire file.

@alexeldeib
Copy link
Contributor Author

felt like it was time for some squash

@alexeldeib alexeldeib force-pushed the ace/root branch 3 times, most recently from 0b037ec to bc21273 Compare April 18, 2020 18:34
@alexeldeib
Copy link
Contributor Author

alexeldeib commented Apr 20, 2020

@jackfrancis @mboersma friendly ping for another review, think this is good to go and squashed

@jackfrancis
Copy link
Member

@alexeldeib, will do, could you add example usage of this new configuration in one of the agent pools, and the masterProfile, to: test/e2e/test_cluster_configs/everything.json. 👈 We use that via jenkins to regularly E2E test cluster config functionality.

Thanks again for this!

jackfrancis
jackfrancis previously approved these changes Apr 20, 2020
Copy link
Member

@jackfrancis jackfrancis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm pending E2E test config intregation of this foo + successful run

type ContainerdConfig struct {
OomScore int `toml:"oom_score,omitempty"`
Root string `toml:"root,omitempty"`
Subreaper bool `toml:"subreaper"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this, it's not a real config. I removed it from the containerd docs: containerd/containerd#4194

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for the actual generated toml.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gah, thought I pulled this out. will remove

@alexeldeib
Copy link
Contributor Author

@jackfrancis this would be cluster-wide since it's in KubernetesConfig like ContainerRuntime (although it definitely could be per agent pool). Would you like me to add a new file/test case with this, or update the everything cluster to use this by default?

@jackfrancis
Copy link
Member

@alexeldeib Right, sorry, that's obviously evident in the changeset, thanks for the clarification. :)

So yeah, let's put it into KubernetesConfig of the everything cluster (unless that doesn't work w/ a windows pool, which that config includes?). We can consider making this config a per-pool config if we ever get to the larger (and more meaningful) refactor work of actually making the container runtime per-pool configurable.

"GetDockerConfig": func() string {
var overrides []func(*common.DockerConfig) error

if cs.Properties.HasNSeriesSKU() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already squashed but L704-709 were not present which failed N series VMs with GPUs before this

}
}{{end}}
}
{{IndentString (GetDockerConfig (IsNSeriesSKU .VMSize)) 4}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're teaching me new things about go template syntax

Copy link
Member

@jackfrancis jackfrancis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@jackfrancis jackfrancis merged commit b6ea0dc into Azure:master Apr 21, 2020
alexeldeib added a commit to alexeldeib/aks-engine that referenced this pull request May 1, 2020
xuto2 added a commit that referenced this pull request May 15, 2020
xuto2 added a commit that referenced this pull request May 15, 2020
xuto2 added a commit that referenced this pull request May 16, 2020
alexeldeib added a commit to alexeldeib/aks-engine that referenced this pull request Jun 3, 2020
xuto2 pushed a commit that referenced this pull request Jun 5, 2020
xuto2 added a commit that referenced this pull request Jun 5, 2020
xuto2 added a commit that referenced this pull request Jun 5, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants