Вопрос или проблема
Предыстория
Я пишу клиент open-source GTK go app subsonic API, который:
- нацелен в первую очередь на мобильные Linux системы, такие как postmarketOS (alpine linux), Mobian (debian)
- в будущем будет также расширен на настольные Linux, Windows и Mac OS, но не Android или iOS, так как у них уже есть клиенты.
- должен быть доступен по крайней мере на
aarch64
иx86_64
- зависит от нативных библиотек, таких как
portaudio
,libasound
,libopus
- поэтому требуется включить
CGO
Я пока не опубликовал приложение, так как размещаю свой git самостоятельно и мне нужно сделать дополнительную настройку. Оно не будет доступно до конца октября 2021 года.
Приложение успешно компилируется (на хосте) и запускается на моем основном ПК с x86_64 fedora.
Приложение успешно компилируется (в docker) и запускается на aarch64 дистрибутивах с использующей библиотекой glibc, таких как Mobian
Вот Dockerfile:
FROM golang:1.17-bullseye
LABEL os=linux
LABEL arch=arm64
ENV GOOS=linux
ENV GOARCH=arm64
ENV CGO_ENABLED=1
ENV CC=aarch64-linux-gnu-gcc
ENV PATH="/go/bin/${GOOS}_${GOARCH}:${PATH}"
ENV PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
# установка зависимостей для сборки и выполнения
RUN dpkg --add-architecture arm64
RUN apt update && apt upgrade -y
RUN apt install -y --no-install-recommends \
protobuf-compiler \
upx \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
pkg-config \
libasound2-dev:arm64 \
libgtk-3-dev:arm64 \
libcairo2-dev:arm64 \
libglib2.0-dev:arm64 \
libgdk-pixbuf2.0-dev:arm64 \
libsamplerate0:arm64 \
libsamplerate0-dev:arm64 \
libopusfile0:arm64 \
libopusfile-dev:arm64 \
libopus0:arm64 \
libopus-dev:arm64 \
libportaudio2:arm64 \
portaudio19-dev:arm64
# установка зависимостей для сборки (генераторы кода)
RUN go get github.com/hajimehoshi/oto \
&& go get github.com/faiface/beep \
&& go get github.com/faiface/beep/flac \
&& go get github.com/faiface/beep/speaker \
&& go get github.com/faiface/beep/mp3 \
&& go get github.com/faiface/beep/vorbis \
&& go get github.com/faiface/beep/wav\
&& go get github.com/gotk3/gotk3 \
&& go get github.com/delucks/go-subsonic \
&& go get github.com/hashicorp/go-retryablehttp \
&& go get github.com/zalando/go-keyring \
&& go get github.com/emirpasic/gods/lists/ \
&& go get github.com/emirpasic/gods/lists/arraylist \
Проблемы
- Я не могу запустить рабочий бинарный файл Mobian на postmarketOS, так как alpine linux использует musl вместо этого. Вывод
file
:ELF 64-битное LSB исполняемое, ARM aarch64, версия 1 (SYSV), динамически связанное, интерпретатор /lib/ld-linux-aarch64.so.1, (...) для GNU/Linux 3.7.0. не обрезано
. - Мне не удалось использовать инструментарий musl в docker debian, так как это 32-битный исполняемый файл, а образ 64-битный.
- Я не смог найти информацию о multiarch в alpine, поэтому, вероятно, я не могу использовать образ golang:1.17-alpine на x86_64 (тогда мне также нужно найти нативные библиотеки в пакетах)
Я надеюсь, что это всего лишь проблема с моей конфигурацией, если это возможно, я хотел бы решение с docker, так как в будущем хотелось бы использовать CI/CD.
Ресурсы, идеи, обходные пути
- У меня есть raspberry pi с установленным debian и некоторыми сервисами. Я мог бы использовать нативный инструментарий musl и docker-контейнер debian aarch64, но это неудобно в производственном цикле. К тому же, я еще не пробовал это.
Рекомендуется прочитать
У меня была аналогичная проблема, когда мне нужно было создать статический Go бинарный файл с cgo, который в итоге будет работать в контейнере alpine с архитектурой arm64, но должен быть собран в контейнере golang:alpine с архитектурой x86_64 (я не контролировал архитектуру CI/CD runner).
Мне удалось это сделать, загрузив кросс-компилятор (CC) и указав, что он должен использоваться во время сборки Go.
Вот пример загрузки/распаковки CC в домашнем каталоге пользователя контейнера и его использования в сборке go из /usr/src/example
, которая производит исполняемый файл с именем exampleApp.
wget -P ~ https://musl.cc/aarch64-linux-musl-cross.tgz
tar -xvf ~/aarch64-linux-musl-cross.tgz -C ~
cd /usr/src/example
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=~/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc go build -o exampleApp -a -ldflags=-extldflags=-static .
Запуск file
на выходе exe
подтвердил, что он был статически связан для aarch64. Затем я смог запустить его в контейнере arm64 alpine в другом месте.
Ответ или решение
Кросс-компиляция приложений Go с поддержкой CGO для Alpine Linux (musl) и архитектуры aarch64 может быть вызовом, особенно в случае использования Docker. В данной задаче мы имеем дело с приложением, которое зависит от нескольких нативных библиотек, таких как portaudio
, libasound
и libopus
. Эти зависимости требуют включения CGO, что добавляет сложности при работе в среде, отличной от целевой.
Шаги для кросс-компиляции с использованием Docker
1. Использование musl Toolchain
Вам понадобится musl-cross заголовочный файл и соответствующий компилятор, чтобы скомпилировать приложение для musl libc в Alpine Linux. Даже если вы начинаете с образа на основе Debian, вы сможете скачать и установить musl toolchain:
wget -P ~/ https://musl.cc/aarch64-linux-musl-cross.tgz
tar -xvf ~/aarch64-linux-musl-cross.tgz -C ~
2. Настройка среды Docker
Ваш Dockerfile
должен быть изменен, чтобы использовать musl cross-compiler для аrch64. Это можно сделать, установив переменные окружения и модифицируя команды сборки именно так:
FROM golang:1.17-alpine
RUN apk add --no-cache \
wget \
build-base \
musl-dev \
git
# Установка musl toolchain
RUN wget -P ~/ https://musl.cc/aarch64-linux-musl-cross.tgz && \
tar -xvf ~/aarch64-linux-musl-cross.tgz -C ~ && \
rm ~/aarch64-linux-musl-cross.tgz
ENV GOOS=linux
ENV GOARCH=arm64
ENV CGO_ENABLED=1
ENV CC=/root/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc
# Установка зависимостей через go get
RUN go get -v github.com/hajimehoshi/oto \
&& go get -v github.com/faiface/beep \
&& go get -v github.com/faiface/beep/flac \
&& go get -v github.com/faiface/beep/speaker \
&& go get -v github.com/faiface/beep/mp3 \
&& go get -v github.com/faiface/beep/vorbis \
&& go get -v github.com/faiface/beep/wav \
&& go get -v github.com/gotk3/gotk3 \
&& go get -v github.com/delucks/go-subsonic \
&& go get -v github.com/hashicorp/go-retryablehttp \
&& go get -v github.com/zalando/go-keyring \
&& go get -v github.com/emirpasic/gods/lists/arraylist
# Копирование исходных кодов и сборка приложения
WORKDIR /usr/src/yourapp
COPY . .
# Сборка приложения
RUN go build -o yourAppName -a -ldflags=-extldflags=-static .
3. Верификация и запуск
После сборки используйте file yourAppName
, чтобы убедиться, что бинарный файл скомпилирован корректно как статически связанный ELF для aarch64:
file yourAppName
# ожидаемый вывод: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), ...
Заключение
Этот процесс позволяет создать статически связанный бинарный файл, совместимый с musl, который может быть развёрнут и выполнен в Alpine Linux на архитектуре arm64. Это решение интегрирует использование Docker для автоматизации сборки, что идеально подходит для будущего применения в CI/CD. Этот процесс позволяет использовать кросс-компиляцию и CGO с Alpine, предоставляя универсальный подход для разработки и развертывания приложений Go.