Почему не запускается Go бинарники или "libc not found"
Я, как ни в чём не бывало, скомпилировал сервис на свежеустановленном линуксовом Go, загрузил на сервер с CentOS, и неожиданно столкнулся со следующей ошибкой:
/lib64/libc.so.6: version 'GLIBC_2.34' not found (required by /opt/my-super-program)
Я сначала не понял, да и потом не понял. Машинально полез искать, как обновить этот glibc, но на самом деле он статически линкуется с операционной системой и “обновить” его не получится. Однако я нашёл решение через другой подход.
В моём случае проблема заключалась в том, что Go по умолчанию почему-то решил, что я хочу использовать CGO. CGO - это когда вы можете интегрировать C/C++ код прямо в ваш проект на Go. Но у меня не было такой связки.
Вероятнее всего раньше я просто не сталкивался с поведением по умолчанию, потому что всегда компилировал гошный код под Linux на Windows или macOS. То есть всегда была кросс-компиляция. А при кросс-компиляции CGO по умолчанию выключен. Сейчас же я компилировал бинарник на Linux для Linux, и поэтому и столкнулся с по умолчанию включённым CGO.
Как получилось решить проблему?
Я просто запустил:
go env -w CGO_ENABLED=0
И у меня скомпилировался обычный бинарь без CGO.
Кстати, попутно нашёл интересную статью от 2016 года о недостатках CGO .
Но что делать, если вам нужен CGO?
В статье выше можно найти ссылку на инструкцию по статической линковке альтернативы glibc: musl. musl - легковесная реализация стандартной библиотеки C.
Я попробовал скомпилировать свой кейс с использованием musl, и да, оно работает! Были опасения о том что значительно вырастет размер приложения, но он даже уменьшился:
37756810 app-cgo
37688214 app-nocgo
36910704 app-musl
С точки зрения настройки всё оказалось предельно просто:
wget http://www.musl-libc.org/releases/musl-1.2.3.tar.gz
tar -xvf musl-1.2.3.tar.gz
cd musl-1.2.3/
./configure
make
sudo make install
И скомпилировать потом вот так:
CC=/usr/local/musl/bin/musl-gcc go build --ldflags '-linkmode external -extldflags "-static"' -o bin/my-app ./cmd/my-app/
И на выходе получится статически слинкованный бинарник:
$ file app-musl
app-musl: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=123, not stripped
Тут стоит понимать, что скорость компиляции всё равно пострадает, да и musl может иметь свои собственные баги.
Для себя я оставил CGO выключенным до тех пор, пока явно не понадобится какая-нибудь библиотека с зависимостью от него. Однако, если в вашем случае такая уже есть, то вот возможное решение 🙂