Is it possible to customize docker image generated with Spring Native (with buildpack)
Asked Answered
S

3

6

I'm currently developping a Spring Native application, it's building using paketo buildpack and generating a Docker image. I was wondering if it's possible to customize the generated Docker image by adding third party tools (like a Datadog agent for example).

Also, for now the generated container image is installed locally, is it possible to send it directly in another Docker repo ?

Snorkel answered 5/10, 2021 at 8:54 Comment(1)
This question is very similar to #63789755, and its answer https://mcmap.net/q/1008998/-install-package-in-docker-image-created-by-spring-boot-maven-plugin can be applied here as well.Stinger
O
12

I'm currently developping a Spring Native application, it's building using paketo buildpack and generating a Docker image. I was wondering if it's possible to customize the generated Docker image by adding third party tools (like a Datadog agent for example).

This applies to Spring Boot apps, but really also any other app you can build with buildpacks.

There are a couple of options:

  1. You can customize the base image that you use (called a stack).
  2. You can add additional buildpacks which will perform more customizations during the build.

#2 is obviously easier if there is a buildpack that provides the functionality that you require. In regards to Datadog specifically, the Paketo buildpack now has a Datadog Buildpack you can use with Java and Node.js apps.

It's more work, but you can also create a buildpack if you are looking to add specific functionality. I wouldn't recommend this if you have one application that needs the functionality, but if you have lots of applications it can be worth the effort.

A colleague of mine put this basic sample buildpack together, which installs and configures a fictitious APM agent. It is a pretty concise example of this scenario.

#1 is also possible. You can create your own base image and stack. The process isn't that hard, especially if you base it on a well-known and trusted image that is getting regular updates. The Paketo team also has the jam create-stack command which you can use to streamline the process.

What's more difficult with both options is that you need to keep them up-to-date. That requires some CI to watch for software updates & publish new versions of your buildpack or stack. If you cannot commit to this, then both are a bad idea because your customization will get out of date and potentially cause security problems down the road.

UPDATE

  1. You can bundle dependencies with your application. This option works well if you have static binaries you need to include, perhaps a cli you call to from your application.

    In this case, you'd just create a folder in your project called binaries/ (or whatever you want) and place the static binaries in there (make sure to download versions compatible with the container image you're using, Paketo is Ubuntu Bionic at the time I write this). Then when you call the cli commands from your application, simply use the full path to them. That would be /workspace/binaries or /workspace/<path to binaries in your project>.

  2. You can use the apt buildpack to install packages with apt. This is a generic buildpack that you provide a list of apt packages to and it will install them.

    This can work in some cases, but the main drawback is that buildpacks don't run as root, so this buildpack cannot install these packages into their standard locations. It attempts to work around this by setting env variables like PATH, LD_LIBRARY_PATH, etc to help other applications find the packages that have been installed.

    This works ok most of the time, but you may encounter situations where an application is not able to locate something that you install with the apt buildpack. Worth noting if you see problems when trying this approach.

END OF UPDATE

For what it's worth, this is a common scenario that is a bit painful to work through. Fortunately, there is an RFC that should make the process easier in the future.

Also, for now the generated container image is installed locally, is it possible to send it directly in another Docker repo ?

You can docker push it or you can add the --publish flag to pack build and it will send the image to whatever registry you tell it to use.

https://mcmap.net/q/63622/-how-to-push-a-docker-image-to-a-private-repository

The publish flag works the same way, you need to name your image [REGISTRYHOST/][USERNAME/]NAME[:TAG].

Overtask answered 6/10, 2021 at 13:20 Comment(4)
Thanks a lot for your answer. Option #2 seems to be the easiest indeed but it's good to know all these customization optionsFelonry
FYI, I added a couple more options that came to mind.Overtask
stumbled across the same issue today, wanting to add newrelic-agent.jar to the resulting docker image. how did you solve this? Option #3 does not work for me (just putting the jar into a /binaries folder)Sisile
Option #3 works best with statically compiled binaries. If you shell out and run them. For shared libraries, you'd need to set LD_LIBRARY_PATH so that they are found from your custom location. For JAR files, you'd need to do the same but set CLASSPATH. NewRelic might also require you to adjust command line arguments, as I think it needs to be invoked as a Java JVMTI agent. In short, just putting files into the image is only part of the solution. You need to invoke or ensure they are enabled as well.Overtask
C
1

For me what worked was in my build.gradle file (I'm using kotlin) I added this:

bootBuildImage {
    val ecrRepository: String? by project
    buildpacks = listOf("urn:cnb:builder:paketo-buildpacks/java", "urn:cnb:builder:paketo-buildpacks/datadog")
    imageName = "$ecrRepository:${project.version}"
    environment = mapOf("BP_JVM_VERSION" to "17.*", "BP_DATADOG_ENABLED" to "true")
    isPublish = true

    docker {
      val ecrPassword: String? by project
      publishRegistry {
        url = ecrRepository
        username = "AWS"
        password = ecrPassword
      }
    }
}

notice the buildpacks part where I added first the base default oci and then the datadog oci. I also added on the environment the BP_DATADOG_ENABLED to true, so that it adds the agent.

Card answered 15/11, 2022 at 12:34 Comment(0)
T
0

Simply setting the environment variable "BP_DATADOG_ENABLED" to truthy made it work for me. You don't have to set "buildpacks" environment variable and append "paketo-buildpacks/datadog" build pack to an existing list.

I followed this link from paketo on app monitoring:

Below is the snippet from the Kotlin gradle file I modified to add this flag.

tasks.named("bootBuildImage") { environment.set(environment.get() + mapOf("BP_DATADOG_ENABLED" to "true")) }

Thee answered 23/10, 2023 at 21:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.