Skip to content
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

feat(k8s): flexible buildKit cache configuration with mode=max support #3239

Merged
merged 32 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
96680b5
feat: simple mode=max support with a list of not supported registries
stefreak Sep 20, 2022
4a77d8a
chore: rearrange code
stefreak Sep 20, 2022
07f55be
feat: flexible cache configuration
stefreak Sep 21, 2022
cdece13
fix: ECR and GCR only work with inline, so adding inline support
stefreak Sep 21, 2022
1e47691
chore: remove local cache
stefreak Sep 21, 2022
273f56d
fix: use deploymentImageId
stefreak Sep 21, 2022
ac8399d
docs: document the buildkit cache options
stefreak Sep 21, 2022
c1eb23f
docs: add comment for subtle buildkit --import-cache behaviour
stefreak Sep 22, 2022
5aa714c
docs: add workaround for #3245
stefreak Sep 22, 2022
4f6ff7c
fix(lint): remove unused import
stefreak Sep 22, 2022
9c5a1da
feat(k8s): add support for cache registries in addition to deployment…
stefreak Sep 22, 2022
131d7b3
docs: regenerate
stefreak Sep 22, 2022
0ccd98a
chore: implement tests and insecure registry support
stefreak Sep 22, 2022
24d0ca7
chore: update docs and fix linter error
stefreak Sep 22, 2022
a4f4456
fix: integration tests
stefreak Sep 22, 2022
d218fe1
docs: regenerate
stefreak Sep 22, 2022
4d2bf94
Update core/src/plugins/container/config.ts
stefreak Sep 23, 2022
24efab7
Update core/src/plugins/kubernetes/container/build/buildkit.ts
stefreak Sep 23, 2022
2f6d778
Update core/src/plugins/kubernetes/config.ts
stefreak Sep 23, 2022
3095389
Update core/src/plugins/kubernetes/config.ts
stefreak Sep 23, 2022
0f2869a
Update core/src/plugins/kubernetes/config.ts
stefreak Sep 23, 2022
deb3a18
Update core/src/plugins/kubernetes/config.ts
stefreak Sep 23, 2022
fe385e8
Update core/src/plugins/kubernetes/config.ts
stefreak Sep 23, 2022
e3212fe
docs: uses deploymentRegistry if registry is not specified.
stefreak Sep 23, 2022
9cdf515
docs: address Jons comment and use `valid` instead of `allow`.
stefreak Sep 23, 2022
94179bb
docs: document the importance of import order
stefreak Sep 23, 2022
5a9ccce
docs: regenerate
stefreak Sep 23, 2022
aac809d
Update core/src/plugins/kubernetes/config.ts
stefreak Sep 23, 2022
edbb9ef
tests: restructured
stefreak Sep 23, 2022
68f4a4c
Add default cache config to buildkit integ test
stefreak Sep 23, 2022
0f5932e
docs: add `mode=max` to in-cluster building docs
stefreak Sep 23, 2022
da1a0e8
fix: remove unused import
stefreak Sep 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions core/src/plugins/container/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,7 @@ export interface ContainerRegistryConfig {
hostname: string
port?: number
namespace: string
insecure: boolean
}

export const containerRegistryConfigSchema = () =>
Expand All @@ -735,13 +736,15 @@ export const containerRegistryConfigSchema = () =>
namespace: joi
.string()
.default("_")
.description("The namespace in the registry where images should be pushed.")
.description(
"The registry namespace. Will be placed between hostname and image name, like so: <hostname>/<namespace>/<image name>"
)
.example("my-project"),
}).description(dedent`
The registry where built containers should be pushed to, and then pulled to the cluster when deploying services.

Important: If you specify this in combination with in-cluster building, you must make sure \`imagePullSecrets\` includes authentication with the specified deployment registry, that has the appropriate write privileges (usually full write access to the configured \`deploymentRegistry.namespace\`).
`)
insecure: joi
.boolean()
.default(false)
.description("Set to true to allow insecure connections to the registry (without SSL)."),
})

export interface ContainerService extends GardenService<ContainerModule> {}

Expand Down
158 changes: 154 additions & 4 deletions core/src/plugins/kubernetes/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,18 @@ export interface NamespaceConfig {
labels?: StringMap
}

export interface ClusterBuildkitCacheConfig {
type: "registry"
mode: "min" | "max" | "inline" | "auto"
tag: string
export: boolean
registry?: ContainerRegistryConfig
}

export interface KubernetesConfig extends BaseProviderConfig {
buildMode: ContainerBuildMode
clusterBuildkit?: {
cache: ClusterBuildkitCacheConfig[]
rootless?: boolean
nodeSelector?: StringMap
}
Expand Down Expand Up @@ -447,11 +456,72 @@ const tlsCertificateSchema = () =>
.example("cert-manager"),
})

const buildkitCacheConfigurationSchema = () =>
joi.object().keys({
type: joi
.string()
.valid("registry")
.required()
.description(
dedent`
Use the Docker registry configured at \`deploymentRegistry\` to retrieve and store buildkit cache information.

See also the [buildkit registry cache documentation](https://github.com/moby/buildkit#registry-push-image-and-cache-separately)
`
),
registry: containerRegistryConfigSchema().description(
dedent`
The registry from which the cache should be imported from, or which it should be exported to.

If not specified, use the configured \`deploymentRegistry\` in your kubernetes provider config, or the internal in-cluster registry in case \`deploymentRegistry\` is not set.

Important: You must make sure \`imagePullSecrets\` includes authentication with the specified cache registry, that has the appropriate write privileges (usually full write access to the configured \`namespace\`).
`
),
mode: joi
.string()
.valid("auto", "min", "max", "inline")
.default("auto")
.description(
dedent`
This is the buildkit cache mode to be used.

The value \`inline\` ensures that garden is using the buildkit option \`--export-cache inline\`. Cache information will be inlined and co-located with the Docker image itself.

The values \`min\` and \`max\` ensure that garden passes the \`mode=max\` or \`mode=min\` modifiers to the buildkit \`--export-cache\` option. Cache manifests will only be
stored stored in the configured \`tag\`.

\`auto\` is the same as \`max\` for most registries. Some popular registries do not support \`max\` and garden will fall back to \`inline\` for them.
See the [clusterBuildkit cache option](#providers-.clusterbuildkit.cache) for a description of the detection mechanism.

See also the [buildkit export cache documentation](https://github.com/moby/buildkit#export-cache)
`
),
tag: joi
.string()
.default("_buildcache")
.description(
dedent`
This is the Docker registry tag name buildkit should use for the registry build cache. Default is \`_buildcache\`

**NOTE**: \`tag\` can only be used together with the \`registry\` cache type
`
),
export: joi
.boolean()
.default(true)
.description(
dedent`
If this is false, only pass the \`--import-cache\` option to buildkit, and not the \`--export-cache\` option. Defaults to true.
`
),
})

export const kubernetesConfigBase = () =>
providerConfigBaseSchema().keys({
buildMode: joi
.string()
.allow("local-docker", "cluster-docker", "kaniko", "cluster-buildkit")
.valid("local-docker", "cluster-docker", "kaniko", "cluster-buildkit")
.default("local-docker")
.description(
dedent`
Expand All @@ -465,6 +535,78 @@ export const kubernetesConfigBase = () =>
clusterBuildkit: joi
.object()
.keys({
cache: joi
.array()
.items(buildkitCacheConfigurationSchema())
.default([{ type: "registry", mode: "auto", tag: "_buildcache", export: true }])
.description(
dedent`
Use the \`cache\` configuration to customize the default cluster-buildkit cache behaviour.

The default value is:
\`\`\`yaml
clusterBuildkit:
cache:
- type: registry
mode: auto
\`\`\`

For every build, this will
- import cached layers from a docker image tag named \`_buildcache\`
- when the build is finished, upload cache information to \`_buildcache\`

For registries that support it, \`mode: auto\` (the default) will enable the buildkit \`mode=max\`
option.

Some registries are known not to support the cache manifests needed for the \`mode=max\` option, so
we will avoid using \`mode=max\` with them.

See the following table for details on our detection mechanism:

| Registry Name | Detection string | Assumed \`mode=max\` support |
|---------------------------------|------------------|------------------------------|
| AWS Elastic Container Registry | \`.dkr.ecr.\` | No |
| Google Cloud Container Registry | \`gcr.io\` | No |
| Any other registry | - | Yes |

In case you need to override the defaults for your registry, you can do it like so:

\`\`\`yaml
clusterBuildkit:
cache:
- type: registry
mode: inline
\`\`\`

When you add multiple caches, we will make sure to pass the \`--import-cache\` options to buildkit in the same
order as provided in the cache configuration. This is because buildkit will not actually use all imported caches
for every build, but it will stick with the first cache that yields a cache hit for all the following layers.

An example for this is the following:

\`\`\`yaml
clusterBuildkit:
cache:
- type: registry
tag: _buildcache-\${slice(kebabCase(git.branch), "0", "30")}
- type: registry
tag: _buildcache-main
export: false
\`\`\`

Using this cache configuration, every build will first look for a cache specific to your feature branch.
If it does not exist yet, it will import caches from the main branch builds (\`_buildcache-main\`).
When the build is finished, it will only export caches to your feature branch, and avoid polluting the \`main\` branch caches.
A configuration like that may improve your cache hit rate and thus save time.

If you need to disable caches completely you can achieve that with the following configuration:

\`\`\`yaml
clusterBuildkit:
cache: []
\`\`\`
`
),
rootless: joi
.boolean()
.default(false)
Expand All @@ -485,7 +627,7 @@ export const kubernetesConfigBase = () =>
.example({ disktype: "ssd" })
.default(() => ({})),
})
.default(() => {})
.default(() => ({}))
.description("Configuration options for the `cluster-buildkit` build mode."),
clusterDocker: joi
.object()
Expand All @@ -501,7 +643,7 @@ export const kubernetesConfigBase = () =>
)
.meta({ deprecated: true }),
})
.default(() => {})
.default(() => ({}))
.description("Configuration options for the `cluster-docker` build mode.")
.meta({ deprecated: "The cluster-docker build mode has been deprecated." }),
jib: joi
Expand Down Expand Up @@ -814,7 +956,15 @@ export const configSchema = () =>
.keys({
name: joiProviderName("kubernetes"),
context: k8sContextSchema().required(),
deploymentRegistry: containerRegistryConfigSchema().allow(null),
deploymentRegistry: containerRegistryConfigSchema()
.description(
dedent`
The registry where built containers should be pushed to, and then pulled to the cluster when deploying services.

Important: If you specify this in combination with in-cluster building, you must make sure \`imagePullSecrets\` includes authentication with the specified deployment registry, that has the appropriate write privileges (usually full write access to the configured \`deploymentRegistry.namespace\`).
`
)
.allow(null),
ingressClass: joi.string().description(dedent`
The ingress class to use on configured Ingresses (via the \`kubernetes.io/ingress.class\` annotation)
when deploying \`container\` services. Use this if you have multiple ingress controllers in your cluster.
Expand Down
Loading