DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Ways To Reduce JVM Docker Image Size
  • How To Create a Stub in 5 Minutes
  • Smart Deployment Strategies for Modern Applications
  • Solving the Mystery: Why Java RSS Grows in Docker on M1 Macs

Trending

  • Bringing Intelligence Closer to the Source: Why Real-Time Processing is the Heart of Edge AI
  • Observability in Spring Boot 4
  • Product-Led Software Delivery: Intelligent Platforms for DevOps at Scale
  • Genkit Middleware: Intercept, Extend, and Harden your Gen AI Pipelines
  1. DZone
  2. Software Design and Architecture
  3. Microservices
  4. Containerizing Ballerina GraalVM Executable

Containerizing Ballerina GraalVM Executable

This article is written using Ballerina Swan Lake Update 7(2201.7.0). We will explore how to containerize a GraalVM native executable for a ballerina application.

By 
Tharmigan Krishnananthalingam user avatar
Tharmigan Krishnananthalingam
·
Nov. 08, 23 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
5.0K Views

Join the DZone community and get the full member experience.

Join For Free

Welcome back to the series exploring the synergy between Ballerina and GraalVM. In the previous article, ‘Ballerina Code to GraalVM Executable,’ we delved into the seamless integration of Ballerina and GraalVM, witnessing how Ballerina applications can build GraalVM native executable and achieve improved performance and reduced memory consumption. In this continuation, we take the next step in our journey, exploring how to containerize a Ballerina GraalVM executable. If you have not read the previous article, I recommend you do so before continuing with this one.

We will use the same Conference Service application to build a Docker image containing the GraalVM executable. The code for this application can be found in the below link:

GitHub — TharmiganK/conference-service-ballerina: A RESTful conference service written in…
A RESTful conference service which is written in Ballerina. — GitHub — TharmiganK/conference-service-ballerina: A RESTful…github.com

We will be looking into the following ways to create the Docker image.

  1. Using a custom Docker file.
  2. Using the Ballerina Code to Cloud feature.

Using a Custom Docker File

As we know already, the GraalVM native executable is platform-dependent. If you are a Linux user, then you can build the GraalVM executable locally and pass it to a Docker with the simplest slim container. If you are using macOS or Windows to build a Docker image containing the GraalVM executable, then you have to build the executable in a Docker container. 

In this post, I am using a macOS, so I need to build the executable in a Docker container that has the GraalVM native image tool. The GraalVM community already has container images with the native-image tool. The images can be found on the GraalVM container page. Since Ballerina Swan Lake Update 7 works with Java11, I have chosen this image: ghcr.io/graalvm/native-image:ol8-java11–22.3.3.

Let’s start by building the application and obtaining the JAR file.

$ bal build

Compiling source
        tharmigan/conference_service:1.0.0

Generating executable
        target/bin/conference_service.jar


Use the following Docker file to build the GraalVM executable in the graalvm/native-image container and run the executable in adebian:stable-slim container.

Dockerfile
 
FROM ghcr.io/graalvm/native-image:ol8-java11-22.3.3 as build

WORKDIR /app/build
COPY target/bin/conference_service.jar .

RUN native-image -jar conference_service.jar --no-fallback


FROM debian:stable-slim

WORKDIR /home/ballerina
COPY --from=build /app/build/conference_service .

CMD echo "time = $(date +"%Y-%m-%dT%H:%M:%S.%3NZ") level = INFO module = tharmigan/conference_service message = Executing the Ballerina application" && "./conference_service"

Build the Docker image.
$ docker build . -t ktharmi176/conference-service:1.0.0


Use the following Docker compose file to run the conference_service and the mock country_service in the host network.

YAML
 

version: '2'

services:
  conference-service:
    image: 'ktharmi176/conference-service:1.0.0'
    ports:
        - '8102:8102'
    volumes:
        - ./Config.toml:/home/ballerina/Config.toml
    depends_on: 
      country-service: 
          condition: service_started
    network_mode: "host"

  country-service:
    image: 'ktharmi176/country-service:latest'
    hostname: country-service
    container_name: country-service
    ports:
        - '9000:9000'
    network_mode: "host"


Check the image names and run the following command:

$ docker compose up


Now, the two services have been started. Test the service using the request.http file.

Using the Ballerina Code to Cloud Feature

Default Mode

The Code to Cloud feature in Ballerina enables developers to quickly deploy their Ballerina applications to cloud platforms without the need for extensive configuration or manual setup. It aims to reduce the complexity of cloud-native development and streamline the deployment process.

We can simply run the following command to build the GraalVM executable in a Docker container.

$ bal build --graalvm --cloud=docker

Compiling source
        tharmigan/conference_service:1.0.0


Generating artifacts

Building the native image. This may take a while

[+] Building 331.1s (13/13) FINISHED                                                                                                                                              docker:default
 => [internal] load build definition from Dockerfile                                                                                                                                        0.0s
 => => transferring dockerfile: 439B                                                                                                                                                        0.0s
 => [internal] load .dockerignore                                                                                                                                                           0.0s
 => => transferring context: 2B                                                                                                                                                             0.0s
 => [internal] load metadata for gcr.io/distroless/base:latest                                                                                                                              3.0s
 => [internal] load metadata for ghcr.io/graalvm/native-image:ol8-java11-22.3.3                                                                                                             3.7s
 => [build 1/4] FROM ghcr.io/graalvm/native-image:ol8-java11-22.3.3@sha256:c0b4d9c31013d4fd91c4dec25f8772602e851ee67b8510d21bfdab532da4c17c                                                 0.0s
 => [stage-1 1/3] FROM gcr.io/distroless/base@sha256:73deaaf6a207c1a33850257ba74e0f196bc418636cada9943a03d7abea980d6d                                                                       0.0s
 => [internal] load build context                                                                                                                                                           0.4s
 => => transferring context: 42.65MB                                                                                                                                                        0.4s
 => CACHED [stage-1 2/3] WORKDIR /home/ballerina                                                                                                                                            0.0s
 => CACHED [build 2/4] WORKDIR /app/build                                                                                                                                                   0.0s
 => [build 3/4] COPY conference_service.jar .                                                                                                                                               0.1s
 => [build 4/4] RUN native-image -jar conference_service.jar -H:Name=conference_service --no-fallback -H:+StaticExecutableWithDynamicLibC                                                 326.3s
 => [stage-1 3/3] COPY --from=build /app/build/conference_service .                                                                                                                         0.3s
 => exporting to image                                                                                                                                                                      0.3s 
 => => exporting layers                                                                                                                                                                     0.3s 
 => => writing image sha256:4a6e1223a8d5a0446b688b110522bdc796027bfc1bc4fe533c62be649900ee05                                                                                                0.0s 
 => => naming to docker.io/library/conference_service:latest                                                                                                                                0.0s 
                                                                                                                                                                                                 
Execute the below command to run the generated Docker image: 
        docker run -d conference_service:latest


The auto-generated Docker file can be found in the following path: target/docker/conference_service 

Dockerfile
 
# Auto Generated Dockerfile
FROM ghcr.io/graalvm/native-image:ol8-java11-22.3.3 as build

WORKDIR /app/build

COPY conference_service.jar .

RUN native-image -jar conference_service.jar -H:Name=conference_service --no-fallback -H:+StaticExecutableWithDynamicLibC

FROM gcr.io/distroless/base

WORKDIR /home/ballerina


COPY --from=build /app/build/conference_service .

CMD ["./conference_service"]

Note: By default, Ballerina builds a mostly-static native-image and packs it in a distorless container. For more information on GraalVM mostly-static images, see Static and Mostly Static Images.

Now, let’s run docker-compose after changing the image name of the conference_service.

YAML
 
version: '2'

services:
  conference-service:
    image: 'conference_service:latest'
    ports:
        - '8102:8102'
    volumes:
        - ./Config.toml:/home/ballerina/Config.toml
    depends_on: 
      country-service: 
          condition: service_started
    network_mode: "host"

  country-service:
    image: 'ktharmi176/country-service:latest'
    hostname: country-service
    container_name: country-service
    ports:
        - '9000:9000'
    network_mode: "host"

Test the service using the request.http file.

Configure Mode

The Code to Cloud feature supports overriding the default mode where we configure the following:

  • The GraalVM build-image
  • The native-image build command
  • The base image for the deployment

This can be achieved by providing the configurations via Cloud.toml file. The following shows an example to provide the same configurations used for the custom Docker file.

TOML
 
[container.image]
name = "conference-service"
repository = "ktharmi176"
tag = "1.0.0"
base = "debian:stable-slim"

[graalvm.builder]
base = "ghcr.io/graalvm/native-image:ol8-java11-22.3.3"
buildCmd = "native-image -jar conference_service.jar --no-fallback"

Run the following command to build the Docker image with the above configurations.
$ bal build --graalvm --cloud=docker


Compiling source
        tharmigan/conference_service:1.0.0


Generating artifacts

Building the native image. This may take a while

[+] Building 310.0s (14/14) FINISHED                                                                                           docker:default
 => [internal] load build definition from Dockerfile                                                                                     0.0s
 => => transferring dockerfile: 372B                                                                                                     0.0s
 => [internal] load .dockerignore                                                                                                        0.0s
 => => transferring context: 2B                                                                                                          0.0s
 => [internal] load metadata for docker.io/library/debian:stable-slim                                                                    3.9s
 => [internal] load metadata for ghcr.io/graalvm/native-image:ol8-java11-22.3.3                                                          2.5s
 => [auth] library/debian:pull token for registry-1.docker.io                                                                            0.0s
 => [build 1/4] FROM ghcr.io/graalvm/native-image:ol8-java11-22.3.3@sha256:c0b4d9c31013d4fd91c4dec25f8772602e851ee67b8510d21bfdab532da4  0.0s
 => [stage-1 1/3] FROM docker.io/library/debian:stable-slim@sha256:6fe30b9cb71d604a872557be086c74f95451fecd939d72afe3cffca3d9e60607      0.0s
 => [internal] load build context                                                                                                        0.3s
 => => transferring context: 42.65MB                                                                                                     0.3s
 => CACHED [stage-1 2/3] WORKDIR /home/ballerina                                                                                         0.0s
 => CACHED [build 2/4] WORKDIR /app/build                                                                                                0.0s
 => [build 3/4] COPY conference_service.jar .                                                                                            0.1s
 => [build 4/4] RUN native-image -jar conference_service.jar --no-fallback                                                             305.1s
 => [stage-1 3/3] COPY --from=build /app/build/conference_service .                                                                      0.2s 
 => exporting to image                                                                                                                   0.3s 
 => => exporting layers                                                                                                                  0.3s 
 => => writing image sha256:1f5b5b30653a48a6d27258f785d93a1654dde25d2e70899e14f2b61996e01996                                             0.0s 
 => => naming to docker.io/ktharmi176/conference-service:1.0.0                                                                           0.0s 
                                                                                                                                              
Execute the below command to run the generated Docker image: 
        docker run -d ktharmi176/conference-service:1.0.0


The auto-generated Docker file with the above configurations will look like this.

Dockerfile
 
# Auto Generated Dockerfile
FROM ghcr.io/graalvm/native-image:ol8-java11-22.3.3 as build

WORKDIR /app/build

COPY conference_service.jar .

RUN native-image -jar conference_service.jar --no-fallback

FROM debian:stable-slim

WORKDIR /home/ballerina


COPY --from=build /app/build/conference_service .

CMD ["./conference_service"]


This is the same as the one we wrote manually. Run Docker-compose and check the functionality using the request.http file.

In conclusion, we have built a GraalVM executable for a Ballerina application and containerized it in Docker. GraalVM and Ballerina with the Code to Cloud feature simplify the experience of developing and deploying the Ballerina GraalVM native image in the cloud. It also enables the use of cloud-native technologies with GraalVM easily without in-depth knowledge.

Executable GraalVM Ballerina (programming language) Docker (software) JAR (file format)

Published at DZone with permission of Tharmigan Krishnananthalingam. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Ways To Reduce JVM Docker Image Size
  • How To Create a Stub in 5 Minutes
  • Smart Deployment Strategies for Modern Applications
  • Solving the Mystery: Why Java RSS Grows in Docker on M1 Macs

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook