Skip to content

Commit

Permalink
Add GraalVM native image based docker image. (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
Robothy authored Feb 7, 2023
1 parent 1b0e6d7 commit c458f42
Show file tree
Hide file tree
Showing 13 changed files with 392 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build
name: Main Branch Build
on:
push:
branches:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class InMemoryLocalS3Manager implements LocalS3Manager {
* @param initialDataPath initial data path.
*/
InMemoryLocalS3Manager(Path initialDataPath, boolean enableInitialDataCache) {
if (Objects.isNull(initialDataPath)) {
if (Objects.isNull(initialDataPath) || !Files.exists(initialDataPath)) {
this.storage = Storage.createInMemory();
this.s3Metadata = new LocalS3Metadata();
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"interfaces":["com.robothy.s3.core.service.BucketService"]
},
{
"interfaces":["com.robothy.s3.core.service.ObjectService"]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ void testDeleteObjectsFromUnVersionedBucket(BucketService bucketService, ObjectS

objectService.putObject(bucketName, "a.txt", PutObjectOptions.builder()
.content(new ByteArrayInputStream("Hello".getBytes()))
.contentType("plain.text")
.size(5)
.contentType("plain.text")
.size(5)
.build());
List<Object> results1 = objectService.deleteObjects(bucketName, new DeleteObjectsRequest(List.of(
new ObjectIdentifier("a.txt", null)
Expand Down
11 changes: 11 additions & 0 deletions local-s3-docker/GraalVMNativeImage.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM frolvlad/alpine-glibc:glibc-2.34

MAINTAINER Fuxiang Luo <robothyluo@gmail.com>

WORKDIR /app

COPY build/bin/s3 /app/s3

EXPOSE 80

CMD exec ./s3
54 changes: 54 additions & 0 deletions local-s3-docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
## LocalS3 Docker Image

LocalS3 provides two types of Docker images: `local-s3` and `local-s3-native`. The executable in `local-s3` is a Java application that runs on Java 17,
while the executable in `local-s3-native` is build using GraalVM. The `local-s3-native` image is much smaller than the `local-s3` image.

### Gradle tasks

This module contains several Gradle tasks to build and publish the Docker images.

### Collect reachability metadata

+ `buildCollectReachabilityMetadataImage` - Builds the Docker image for collecting reachability metadata.
+ `collectReachabilityMetadata` - Collects reachability metadata for building the GraalVM native image.

Above tasks are not integrated in the CI pipeline. We need to manually run the `collectReachabilityMetadata` task.
Collected reachability metadata files in `build/graalvm-native-image/reachability-metadata` are used to build the GraalVM native image.

```mermaid
graph BT;
collectReachabilityMetadata --> jar;
buildJava17BasedDockerImage --> jar;
subgraph build Docker images
buildGraalVMNativeImage --> collectReachabilityMetadata;
buildDockerImages --> buildJava17BasedDockerImage
buildGraalVMNativeBasedDockerImage --> buildGraalVMNativeImage
buildDockerImages --> buildGraalVMNativeBasedDockerImage
end
test --> buildDockerImages
pushJava17BasedDockerImage --> test
pushGraalVMNativeBasedDockerImage --> test
subgraph test and publish Docker images
pushLatestDockerImage --> pushJava17BasedDockerImage
pushDockerImages --> pushLatestDockerImage
pushDockerImages --> pushGraalVMNativeBasedDockerImage
end
release --> pushDockerImages
```


`collectReachbilityMetadata` - Collects reachability metadata for building the GraalVM native image.

```mermaid
graph TD;
```
98 changes: 93 additions & 5 deletions local-s3-docker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,113 @@ jar {
def os = DefaultNativePlatform.getCurrentOperatingSystem()
def executor = os.isWindows() ? Arrays.asList("cmd", "/c") : Arrays.asList("sh", "-c")

task dockerBuild(type: Exec, dependsOn: jar) {
tasks.register('collectReachabilityMetadata', JavaExec) {
group('native-image')
dependsOn('jar')

outputs.cacheIf { true }

inputs.file("${buildDir}/libs/s3.jar")
.withPropertyName("jar")
.withPathSensitivity(PathSensitivity.RELATIVE)

outputs.dir(file("${buildDir}/reachability-metadata"))
.withPropertyName("reachability-metadata")

mainClass.set('com.robothy.s3.docker.ReachabilityMetadataGenerator')
classpath = sourceSets.test.runtimeClasspath + sourceSets.main.runtimeClasspath
}

tasks.register('buildGraalVMNativeImage', Exec) {
group('native-image')
dependsOn('collectReachabilityMetadata')

outputs.cacheIf { true }
inputs.dir(file("${buildDir}/reachability-metadata"))
.withPropertyName("reachability-metadata")
.withPathSensitivity(PathSensitivity.RELATIVE)
inputs.file("${buildDir}/libs/s3.jar")
.withPropertyName("jar")
.withPathSensitivity(PathSensitivity.RELATIVE)
outputs.file("${buildDir}/bin/s3")
.withPropertyName("s3-native-image")

doFirst {
var cmd = new ArrayList(executor)
var buildCmd = """docker run --rm \
-v ${project.projectDir}:/project \
-v ${project.buildDir}/bin:/app \
ghcr.io/graalvm/native-image:ol9-java17-22.3.0 \
-jar /project/build/libs/s3.jar \
--initialize-at-build-time=ch.qos.logback,org.slf4j \
--install-exit-handlers \
--class-path /project/build/reachability-metadata
"""
cmd.add(buildCmd)
commandLine(cmd)
}
}

tasks.register('buildGraalVMNativeBasedDockerImage', Exec) {
group("docker-image")
dependsOn('buildGraalVMNativeImage')

outputs.cacheIf { true }

inputs.file("${buildDir}/bin/s3")
.withPropertyName("s3-native-image")
.withPathSensitivity(PathSensitivity.RELATIVE)

doFirst {
var cmd = new ArrayList(executor)
cmd.add("docker build -t luofuxiang/local-s3:native-${project.version} -f GraalVMNativeImage.dockerfile .")
commandLine(cmd)
}
}

tasks.register('buildJava17BasedDockerImage', Exec) {
group("docker-image")
dependsOn('jar')

doFirst {
var cmd = new ArrayList(executor)
cmd.add("docker build -t luofuxiang/local-s3:latest -t luofuxiang/local-s3:${project.version} .")
commandLine(cmd)
}
}

tasks.test.dependsOn(dockerBuild)

task dockerPush(type: Exec, dependsOn: 'test') {
tasks.register('buildDockerImages') {
group("docker-image")
dependsOn ('buildGraalVMNativeBasedDockerImage', 'buildJava17BasedDockerImage')
description = "Builds the docker images"
}

tasks.test.dependsOn(buildDockerImages)

tasks.register('pushJava17BasedDockerImage', Exec) {
dependsOn 'test'
group("docker-image")
doFirst {
var cmd = new ArrayList(executor)
cmd.add("docker push luofuxiang/local-s3:${project.version}")
commandLine(cmd)
}
}

task dockerPushLatest(type: Exec, dependsOn: 'test') {
tasks.register('pushGraalVMNativeBasedDockerImage', Exec) {
dependsOn 'test'
group("docker-image")
doFirst {
var cmd = new ArrayList(executor)
cmd.add("docker push luofuxiang/local-s3:native-${project.version}")
commandLine(cmd)
}
}

tasks.register('pushLatestImage', Exec) {
dependsOn 'test'
group("docker-image")
doFirst {
var cmd = new ArrayList(executor)
cmd.add("docker push luofuxiang/local-s3:latest")
Expand All @@ -53,4 +141,4 @@ task dockerPushLatest(type: Exec, dependsOn: 'test') {
}


rootProject.tasks.release.dependsOn(dockerPush, dockerPushLatest)
rootProject.tasks.release.dependsOn(pushLatestImage, pushJava17BasedDockerImage, pushGraalVMNativeBasedDockerImage)
9 changes: 6 additions & 3 deletions local-s3-docker/src/main/java/com/robothy/s3/docker/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.robothy.s3.rest.LocalS3;
import com.robothy.s3.rest.bootstrap.LocalS3Mode;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -13,11 +12,11 @@ public class App {
private static final String MODE = "MODE";

public static void main(String[] args) {
if (System.getenv(MODE) == null) {
if (getProperty(MODE) == null) {
log.info("\"MODE\" is not specified; use the default value \"PERSISTENCE\"");
}

final String mode = Optional.ofNullable(System.getenv(MODE)).orElse(LocalS3Mode.PERSISTENCE.name());
final String mode = Optional.ofNullable(getProperty(MODE)).orElse(LocalS3Mode.PERSISTENCE.name());
if (Arrays.stream(LocalS3Mode.values()).noneMatch(m -> m.name().equalsIgnoreCase(mode))) {
log.error("\"{}\" is not a valid mode. Valid values are {}", mode, LocalS3Mode.values());
System.exit(1);
Expand All @@ -33,4 +32,8 @@ public static void main(String[] args) {
.start();
}

private static String getProperty(String name) {
return Optional.ofNullable(System.getenv(name)).orElse(System.getProperty(name));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ public class InMemoryModeTest {
.withMode(LocalS3Container.Mode.IN_MEMORY)
.withRandomHttpPort();


@Test
void testInMemoryMode() {
assertTrue(container.isRunning());
// assertTrue(container.isHealthy());
int port = container.getPort();
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.enablePathStyleAccess()
Expand Down
Loading

0 comments on commit c458f42

Please sign in to comment.