Skip to content
This repository has been archived by the owner on Mar 19, 2024. It is now read-only.

Support distroless (and distroful) Envoy images #391

Merged
merged 23 commits into from
Oct 3, 2022

Conversation

nathancoleman
Copy link
Member

@nathancoleman nathancoleman commented Sep 22, 2022

Changes proposed in this PR:

Remove dependency on shell by:

  1. Creating a Secret containing the Consul CA cert for each gateway deployment
    1. Note Creating this secret per-gateway is important because the Secret must reside in the same namespace as the Pod that we will be mounting the volume into, and Gateways are not restricted to a single namespace.

    2. Note Creating/updating this Secret requires updating RBAC for the controller over in consul-k8s. I considered using a ConfigMap instead of a Secret - which we already conveniently have permissions to create/update - but consul-k8s uses Secrets for the ca cert and it feels better to respect that precedent.

  2. Mounting a volume on each gateway deployment sourcing the CA cert from the Secret
  3. Configuring an envar on each gateway deployment w/ the location of the CA cert in the mounted volume
  4. Run consul-api-gateway binary directly without the export or exec that we previously depended on

How I've tested this PR:

  1. Deploy consul-k8s with the API gateway controller enabled (apiGateway.enabled: true) and a build of this branch
  2. Modify the consul-api-gateway GatewayClassConfig to use envoyproxy/envoy-distroless instead of envoyproxy/envoy
  3. Create a Gateway and verify that it works correctly

How I expect reviewers to test this PR:

See above

Checklist:

  • Tests added
  • CHANGELOG entry added

    Run make changelog-entry for guidance in authoring a changelog entry, and
    commit the resulting file, which should have a name matching your PR number.
    Entries should use imperative present tense (e.g. Add support for...)

Secret is assumed to exist at this point. Future commits will create the secret.

Co-Authored-By: sarahalsmiller <[email protected]>
This allows our Gateway image to run on envoy-distroless 🎉

Co-Authored-By: sarahalsmiller <[email protected]>
We create a secret per gateway since the secret must be in the same namespace as the gateway pod in order to reference it in a volume mount
@nathancoleman nathancoleman changed the title Mount CA cert from secret into gateway pod Support distroless Envoy images Sep 22, 2022
@nathancoleman nathancoleman added pr/conformance Run conformance tests from kubernetes-sigs/gateway-api and removed do not merge pr/run-conformance labels Sep 28, 2022
@nathancoleman nathancoleman marked this pull request as ready for review September 28, 2022 19:50
@nathancoleman nathancoleman removed the pr/conformance Run conformance tests from kubernetes-sigs/gateway-api label Sep 28, 2022
This branch will not pass conformance tests until we can pin to consul-k8s v0.49.0, which hasn't been released yet. This is because of a dependency on RBAC changes.
Copy link
Member Author

@nathancoleman nathancoleman left a comment

Choose a reason for hiding this comment

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

Personal review

Note Conformance tests won't pass until consul-k8s v0.49.0, which contains the necessary RBAC changes for create/update Secret, is released and consumed

@@ -94,8 +94,10 @@ rules:
resources:
- secrets
verbs:
- create
Copy link
Member Author

Choose a reason for hiding this comment

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

The controller needs to be able to create Secrets to hold the Consul CA cert for each gateway Deployment. I used a Secret to be consistent w/ consul-k8s which uses a Secret for the same value.

Comment on lines 256 to 257
Name: api.HTTPCAFile,
Value: consulCALocalFile,
Copy link
Member Author

Choose a reason for hiding this comment

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

This adds the path to the CA cert in the pod's environment. This was previously done inside the shell script below as

export CONSUL_CACERT={{ .ConsulCAFile }}

Comment on lines 260 to 261
Command: []string{"/bootstrap/consul-api-gateway", "exec"},
Args: b.execArgs(),
Copy link
Member Author

Choose a reason for hiding this comment

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

Instead of running the shell with one giant argument that was our startup script, we're now running consul-api-gateway exec with many small arguments/flags

Comment on lines +333 to +343
Secret: &corev1.SecretVolumeSource{
SecretName: b.gateway.Name,
Items: []corev1.KeyToPath{
{
Key: "consul-ca-cert",
Path: consulCAFilename,
},
},
DefaultMode: nil,
Optional: pointer.Bool(false),
},
Copy link
Member Author

Choose a reason for hiding this comment

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

Instead of creating an empty volume and then having our shell script insert the CA cert, we now source the volume's contents from the Secret containing the CA cert, which is created further down in internal/k8s/reconciler/deployer.go

Comment on lines +394 to +397
// gwContainerArgsTpl is the template for the command arguments executed in the Envoy container.
// The resulting args are split on \n to obtain a []string for the pod spec's args.
// Note: Make sure not to leave whitespace at the beginning or end of any line.
const gwContainerArgsTpl = `
Copy link
Member Author

Choose a reason for hiding this comment

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

Left the godoc here to explain since the template looks kinda weird now.

Basically, the flag name and its value are expected to be two separate items in the args array for the pod template. It was still easier to compile the list of args using a template like we were before since the other options is a big string builder with a bunch of conditionally-included fmt.Sprintfs.

@@ -388,15 +389,19 @@ func (g *gatewayClient) Update(ctx context.Context, obj client.Object) error {
return nil
}

func (g *gatewayClient) CreateOrUpdateDeployment(ctx context.Context, deployment *apps.Deployment, mutators ...func() error) (bool, error) {
operation, err := controllerutil.CreateOrUpdate(ctx, g.Client, deployment, func() error {
func multiMutatorFn(mutators []func() error) func() error {
Copy link
Member Author

Choose a reason for hiding this comment

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

abstracting this out into a function wrapper since we needed the same logic in one more place now for CreateOrUpdateSecret

@@ -52,6 +54,10 @@ func (d *GatewayDeployer) Deploy(ctx context.Context, gateway *K8sGateway) error
return err
}

if err := d.ensureSecret(ctx, gateway.Gateway); err != nil {
Copy link
Member Author

Choose a reason for hiding this comment

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

You can see here that we create the Secret containing the CA Cert before we create the Deployment that needs to reference the Secret for the contents of one of its volumes.

// MergeService merges a gateway service a onto b and returns b, overriding all of
// the fields that we'd normally set for a service deployment. It does not attempt
// to change the service type
func MergeService(a, b *corev1.Service) *corev1.Service {
if !compareServices(a, b) {
a.Annotations = b.Annotations
b.Annotations = a.Annotations
Copy link
Member Author

Choose a reason for hiding this comment

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

This seemed like an oversight. Not sure if it's had any material impact on our software or not up until now

@@ -177,12 +177,39 @@ type MeshServiceList struct {
Items []MeshService `json:"items"`
}

func MergeSecret(a, b *corev1.Secret) *corev1.Secret {
Copy link
Member Author

Choose a reason for hiding this comment

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

Ripped off from the MergeService logic below. Just need to determine what the final state of the Secret should be if we're modifying one that already exists.

@nathancoleman nathancoleman changed the title Support distroless Envoy images Support distroless (and distroful) Envoy images Sep 28, 2022
Comment on lines -47 to -50
export CONSUL_CACERT=/consul/tls/ca.pem
cat <<EOF >/consul/tls/ca.pem
CONSUL_CA_MOCKED
EOF
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks a little different from other tests, should any of this be reflected in the new CA cert logic?

Copy link
Member Author

Choose a reason for hiding this comment

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

The CONSUL_CACERT envvar is now added to the pod spec below in this case, and the cert content ("CONSUL_CA_MOCKED" here) would now be deployed into a secret that the volume mount below references.

I did refine the code a bit just now to only include the envvar and create the secret when the CA is actually required, so now it's clearer that the resulting deployment for this test case which does require the CA is different from the others which don't require the CA.

@mikemorris mikemorris added the pr/conformance Run conformance tests from kubernetes-sigs/gateway-api label Sep 29, 2022
Copy link
Contributor

@mikemorris mikemorris left a comment

Choose a reason for hiding this comment

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

Holding approval until conformance tests are updated to consul-k8s v0.49.0 and passing (added pr/conformance label so it's clear this is currently blocked on failing tests), but approach LGTM.

@mikemorris
Copy link
Contributor

Should this update or add a CI test with distroless images as #152 did?

@nathancoleman
Copy link
Member Author

@mikemorris that gets difficult because consul-k8s doesn't support distroless yet, so we can't just set imageEnvoy in our values.yaml file. I figured it'd be easier to wait until they do and then add to our matrix. Thoughts?

@nathancoleman nathancoleman merged commit 5e992cd into main Oct 3, 2022
@nathancoleman nathancoleman deleted the support-distroless branch October 3, 2022 17:53
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
pr/conformance Run conformance tests from kubernetes-sigs/gateway-api
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants