Я, как ни в чём не бывало, скомпилировал сервис на свежеустановленном линуксовом 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: muslmusl - легковесная реализация стандартной библиотеки 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 выключенным до тех пор, пока явно не понадобится какая-нибудь библиотека с зависимостью от него. Однако, если в вашем случае такая уже есть, то вот возможное решение 🙂