Skip to content

Instantly share code, notes, and snippets.

@andrius
Last active May 21, 2023 12:27
Show Gist options
  • Save andrius/1bc9f3255f436c482db110b731cf1332 to your computer and use it in GitHub Desktop.
Save andrius/1bc9f3255f436c482db110b731cf1332 to your computer and use it in GitHub Desktop.
Running Crystal-lang on aarch64 Linux with Docker

Running Crystal-lang on aarch64 Linux with Docker

Motivation: Oracle Cloud offers an "always-free" tier with Ampere CPU (aarch64 architecture). You can run the latest Ubuntu on it, but unfortunately, Crystal-lang is not available on aarch64.

To run Crystal-lang, I used docker and qemu. NOTE: it is very slow!

First, install docker, docker-compose, and qemu. For qemu, I used the following command to install everything:

sudo apt-get install \
  qemu \
  qemu-user-static \
  qemu-user \
  qemu-system-x86 \
  qemu-system-arm \
  qemu-system-mips \
  qemu-system-ppc \
  qemu-system-sparc \
  qemu-system-m68k \
  qemu-system-microblaze \
  qemu-system-microblazeel \
  qemu-system-sh4 \
  qemu-system-sh4eb \
  qemu-system-s390x \
  qemu-system-riscv64 \
  qemu-system-riscv32

Then, test that docker can run foreign platform images:

uname -m
docker run --rm -ti --platform linux/amd64 alpine:latest uname -m

Now it's time to build a Docker image and run it. Everything has been wrapped in the docker-compose.yml file. You should use the docker compose or docker-compose command, depending on your environment.

Thanks to build args, it's possible to solve user permission issues. Docker image runs as a host user. To build the image, use the following command:

docker compose build \
  --build-arg DOCKER_USER=$(whoami) \
  --build-arg DOCKER_USER_ID=$(id --user) \
  --build-arg DOCKER_GROUP_ID=$(id --group)

And run it:

docker compose run -i crystal crystal ./hello-world.cr

Result:

Hello from x86_64

Finally, note that Crystal is available in both Alpine and Ubuntu flavors. You can check the Dockerfile for the FROM instruction.

version: '3.8'
services:
crystal:
# Build it this way:
# ```
# docker compose build --build-arg USER=$(whoami) --build-arg USER_ID=$(id --user) --build-arg GROUP_ID=$(id --group)
# ```
#
# and run
# ```
# docker compose run -i crystal bash
# ```
build:
context: .
args:
USER: ${USER:-app}
USER_ID: ${USER_ID:-1000}
GROUP_ID: ${USER_ID:-1000}
platform: linux/amd64
# command: ["echo", "Hello, world!"]
# environment:
# USER: $(whoami)
# USER_ID: $(id --user)
# GROUP_ID: $(id --group)
volumes:
- .:/app/
FROM crystallang/crystal:1.8
# FROM crystallang/crystal:1.8-alpine
ARG USER=user
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN sh <<'EOF'
if [ "$(awk -F= '/^ID=/{print $2}' /etc/os-release)" = "alpine" ]; then
apk add bash curl
addgroup -g $GROUP_ID $USER
adduser -u $USER_ID -G $USER -s /bin/bash -D $USER
else
apt-get -q update
apt-get -yqq install bash curl
groupadd -g $GROUP_ID $USER
useradd -u $USER_ID -g $GROUP_ID -ms /bin/bash $USER
fi
EOF
USER $USER:$USER
WORKDIR /app
SHELL ["/bin/bash", "-c"]
arch = %x(uname -m).chomp
puts "Hello from #{arch}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment