Skip to content

Produce distroless minimized container images #312

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

matt-domsch-sp
Copy link

@matt-domsch-sp matt-domsch-sp commented Apr 22, 2025

In support of feature request #311

This change produces a minimal container image (~25MB) containing only the entrypoint bash executable, the split-sync or split-proxy executable, and the minimal Debian files needed for these to execute.

The dependency on the tr application was removed from functions.sh by using bash variable parameter expansion instead.

split-sync and split-proxy are dynamically linked with libc because the go net library requires it. Therefore, libc is copied into the final image too. bash needs libtinfo6 so we include that.

ca-certificates are copied into the image so TLS connections to split.io can be validated.

A healthcheck.sh script is added for use by container runtimes such as AWS ECS, which avoids needing to include curl or wget in the image - bash can do it for us.

The last build stage is used to produce a single-layer final container.

@matt-domsch-sp
Copy link
Author

This is a draft as I don't have a way to test it at present, aside from starting the container to see that the application code is indeed started properly. I don't know that this includes all the Debian files that the executable actually requires. I know ca-certificates is needed so that's included. If there are others, please advise. I also don't have a way to test against a split.io test tenant to ensure it works as expected.

@matt-domsch-sp
Copy link
Author

I have been able to execute this against one of our development environments. It appears to be working as expected with no failures seen so far. It does connect to the split.io servers, does receive updated feature flags as soon as they are changed, and does accurately update them in Redis.

@matt-domsch-sp matt-domsch-sp marked this pull request as ready for review April 23, 2025 01:20
@matt-domsch-sp matt-domsch-sp requested a review from a team as a code owner April 23, 2025 01:20
@matt-domsch-sp
Copy link
Author

Startup has the messages:

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

Should the Dockerfiles be setting GIN_MODE=release?

This change produces a minimal container image (~25MB) containing only
the entrypoint bash (static) executable, the split-sync or split-proxy
executable, and the minimal Debian files needed for these to execute.

The dependency on the `tr` application was removed from functions.sh
by using bash variable parameter expansion instead.

We use a static bash executable to minimize libraries needed to
include in the image.  Bash executes only briefly in the entrypoint
which then exec's into the application.

split-sync and split-proxy are dynamically linked with libc because
the go net library requires it.  Therefore, libc is copied into the
final image too.

ca-certificates are copied into the image so TLS connections to
split.io can be validated.

The last build stage is used to produce a single-layer final
container.
@matt-domsch-sp
Copy link
Author

I'd also recommend replacing ENTRYPOINT with CMD, leaving ENTRYPOINT for any security tools that may be needed to wrap the running application.

split-synchronizer doesn’t come with a health check program in the
container callable by an ECS Task Definition.  We can add one using
bash only, which is already present in the image, so we don't have to
add something large such as curl.  The executable includes a GET
/health/application endpoint on port 3010 which returns HTTP 200 OK on
healthy, HTTP 500 on unhealthy.

This script performs the minimal HTTP protocol call, placing the GET
call and reading the first returned line for the status code, ignoring
all other headers and body.
Since we already need glibc dynamically linked into the go
applications, and bash only needs glibc plus one more small library,
we can continue using the dynamically linked bash executable as long
as we add libtinfo6 to the image as well.  This reduces the size of
the image by about 1MB and still works.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant