Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,33 @@ jobs:
distribution: 'graalvm'
cache: maven
- run: ./mvnw clean verify
# TODO: check native-image can build ice

native-build:
name: Build and test native image (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
profile: native
artifact: ice-native-amd64-dynamic
- arch: arm64
runner: ubuntu-24.04-arm
profile: native-arm64
artifact: ice-native-arm64-dynamic
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'graalvm'
cache: maven
- name: Build native image (dynamic linking - no musl required)
run: ./mvnw -P${{ matrix.profile }} -pl ice clean package -Dmaven.test.skip=true
- name: Upload native binary as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: ice/target/ice
if-no-files-found: error
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea/
target
/demo/data
/demo/clickhouse-udfs.xml
Expand Down
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ Create/delete tables, insert data with `ice insert -p ns1.table1 file://example.

## Installation

Pre-built binaries\* (+ links to Docker images for [ice](https://hub.docker.com/r/altinity/ice) and [ice-rest-catalog](https://hub.docker.com/r/altinity/ice-rest-catalog)) are available form [GitHub Releases](https://github.com/Altinity/ice/releases) page.
> \* currently require `java` 21+ to run (available [here](https://adoptium.net/installation/)).
Pre-built binaries (+ links to Docker images for [ice](https://hub.docker.com/r/altinity/ice) and [ice-rest-catalog](https://hub.docker.com/r/altinity/ice-rest-catalog)) are available from [GitHub Releases](https://github.com/Altinity/ice/releases) page.

**Two types of binaries are available:**

1. **Java-based binaries** - Require Java 21+ to run (available [here](https://adoptium.net/installation/))
2. **Native binaries** - Standalone executables with no Java dependency
- `ice-native-amd64` - Static binary (musl) for x86_64 Linux (no dependencies)
- `ice-native-arm64` - Dynamic binary for ARM64 Linux (requires glibc)

## Usage

See [examples/](examples/).

## Development

### Standard Build (Java-based)

Install [sdkman](https://sdkman.io/install), then

```shell
Expand All @@ -35,6 +43,24 @@ sdk env
./mvnw
```

### Native Image Build (Standalone)

Build standalone native binaries with no Java dependency:

```shell
# Install prerequisites
sdk env # or ensure Java 21+ and GraalVM are available

# For amd64 (static with musl - no dependencies)
mvn -Pnative-amd64-static -pl ice clean package -Dmaven.test.skip=true
For arm64
mvn -Pnative-arm64 -pl ice clean package -Dmaven.test.skip=true

# Docker builds
docker build -f ice/Dockerfile.native-amd64-static -t ice-native:amd64 .

docker build -f ice/Dockerfile.native-arm64 -t ice-native:arm64 .

## License

Copyright (c) 2025, Altinity Inc and/or its affiliates. All rights reserved.
Expand Down
33 changes: 33 additions & 0 deletions ice/Dockerfile.native-amd64-static
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dockerfile for building ICE native image (amd64, static with musl)
# Usage: docker build -f ice/Dockerfile.native-amd64-static -t ice-native:amd64 .

FROM ghcr.io/graalvm/native-image-community:21-muslib AS builder

WORKDIR /workspace

# Copy license header file (required by license-maven-plugin)
COPY apache-header.txt .

# Copy Maven files for dependency resolution
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY ice/pom.xml ice/
COPY ice-rest-catalog/pom.xml ice-rest-catalog/

# Download dependencies (cached layer)
RUN ./mvnw dependency:go-offline -pl ice || true

# Copy source code
COPY ice/src ice/src

# Build static native image with musl
RUN ./mvnw -Pnative-amd64-static -pl ice clean package -Dmaven.test.skip=true

# ============================================================================
# Final stage: minimal scratch image (truly standalone binary)
# ============================================================================
FROM scratch
COPY --from=builder /workspace/ice/target/ice /ice
ENTRYPOINT ["/ice"]

33 changes: 33 additions & 0 deletions ice/Dockerfile.native-arm64
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dockerfile for building ICE native image (arm64, dynamic linking)
# Usage: docker build -f ice/Dockerfile.native-arm64 -t ice-native:arm64 .

FROM ghcr.io/graalvm/native-image-community:21 AS builder

WORKDIR /workspace

# Copy license header file (required by license-maven-plugin)
COPY apache-header.txt .

# Copy Maven files for dependency resolution
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY ice/pom.xml ice/
COPY ice-rest-catalog/pom.xml ice-rest-catalog/

# Download dependencies (cached layer)
RUN ./mvnw dependency:go-offline -pl ice || true

# Copy source code
COPY ice/src ice/src

# Build dynamic native image
RUN ./mvnw -Pnative-arm64 -pl ice clean package -Dmaven.test.skip=true

# ============================================================================
# Final stage: distroless base (includes minimal glibc runtime)
# ============================================================================
FROM gcr.io/distroless/base-debian12:nonroot
COPY --from=builder /workspace/ice/target/ice /ice
ENTRYPOINT ["/ice"]

106 changes: 106 additions & 0 deletions ice/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -618,5 +618,111 @@
</plugins>
</build>
</profile>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native.maven.plugin.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<imageName>ice</imageName>
<mainClass>com.altinity.ice.cli.Main</mainClass>
<buildArgs>
<buildArg>-H:ReflectionConfigurationFiles=${project.basedir}/src/main/resources/reflection-config.json</buildArg>
<buildArg>--initialize-at-run-time=io.netty</buildArg>
<buildArg>--initialize-at-run-time=ch.qos.logback</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
</buildArgs>

</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>native-amd64-static</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native.maven.plugin.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<imageName>ice</imageName>
<mainClass>com.altinity.ice.cli.Main</mainClass>
<buildArgs>
<!-- Base configuration -->
<buildArg>-H:ReflectionConfigurationFiles=${project.basedir}/src/main/resources/reflection-config.json</buildArg>
<buildArg>--initialize-at-run-time=io.netty</buildArg>
<buildArg>--initialize-at-run-time=ch.qos.logback</buildArg>
<!-- Static linking with musl for truly standalone binary (amd64 only) -->
<buildArg>--static</buildArg>
<buildArg>--libc=musl</buildArg>
<!-- Additional optimizations -->
<buildArg>-H:+RemoveUnusedSymbols</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>native-arm64</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native.maven.plugin.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<imageName>ice</imageName>
<mainClass>com.altinity.ice.cli.Main</mainClass>
<buildArgs>
<!-- Base configuration -->
<buildArg>-H:ReflectionConfigurationFiles=${project.basedir}/src/main/resources/reflection-config.json</buildArg>
<buildArg>--initialize-at-run-time=io.netty</buildArg>
<buildArg>--initialize-at-run-time=ch.qos.logback</buildArg>
<!-- ARM64 optimizations (no static musl support yet) -->
<buildArg>-H:+RemoveUnusedSymbols</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
112 changes: 112 additions & 0 deletions ice/src/main/resources/reflection-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
[
{
"name": "ch.qos.logback.classic.AsyncAppender",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.classic.encoder.PatternLayoutEncoder",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.classic.pattern.DateConverter",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.classic.pattern.LevelConverter",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.classic.pattern.LineSeparatorConverter",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.classic.pattern.LoggerConverter",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.classic.pattern.MessageConverter",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.classic.pattern.ThreadConverter",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.core.ConsoleAppender",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
},
{
"name": "ch.qos.logback.core.FileAppender",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredClasses": true,
"allPublicClasses": true
}
]