Можно заменить distroless на Alpine-образ.
Alpine Linux — это ориентированный на безопасность, легкий дистрибутив на основе musl libc и busybox. Но не будем верить на слово, а лучше проверим.
Запускаем Dockerfile с использованием node:8-alpine:
FROM node:8 as buildСоздаем образ:
WORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM node:8-alpine
COPY --from=build /app /
EXPOSE 3000
CMD ["npm", "start"]
$ docker build -t node-alpine .Проверяем размер:
$ docker images | grep node-alpineНа выходе имеем 69.7MB — это даже меньше, чем distroless-образ.
node-alpine aa1f85f8e724 69.7MB
Проверим, можно ли подключиться к работающему контейнеру (в случае с distrolles-образом мы не могли этого сделать).
Запускаем контейнер:
$ docker run -p 3000:3000 -ti --rm --init node-alpineИ подключаемся:
Example app listening on port 3000!
$ docker exec -ti 9d8e97e307d7 bashНеудачно. Но, возможно, у контейнера есть sh'ell…:
OCI runtime exec failed: exec failed: container_linux.go:296:
starting container process caused "exec: \"bash\":
executable file not found in $PATH": unknown
$ docker exec -ti 9d8e97e307d7 sh / #Отлично! У нас получилось подключиться к контейнеру, и при этом его образ имеет ещё и меньший размер. Но и тут не обошлось без нюансов.
Alpine-образы основаны на muslc — альтернативной стандартной библиотеке для C. В то время, как большинство Linux-дистрибутивов, таких как Ubuntu, Debian и CentOS, основаны на glibc. Считается, что обе эти библиотеки предоставляют одинаковый интерфейс для работы с ядром.
Однако у них разные цели: glibc является наиболее распространенной и быстрой, muslc же занимает меньше места и написана с уклоном в безопасность. Когда приложение компилируется, как правило, оно компилируется под какую-то определенную библиотеку C. Если потребуется использовать его с другой библиотекой, придется перекомпилировать.
Другими словами, сборка контейнеров на Alpine-образах может привести к неожиданному развитию событий, поскольку используемая в ней стандартная библиотека C отличается. Разница будет заметна при работе с прекомпилируемыми бинарниками, такими как расширения Node.js для C++.
Например, готовый пакет PhantomJS не работает на Alpine.
Так какой же базовый образ выбрать?
Alpine, distroless или ванильный образ — решать, конечно, лучше по ситуации.
Если имеем дело с prod-ом и важна безопасность, возможно, наиболее уместным будет distroless.
Каждый бинарник, добавленный к Docker-образу, добавляет определенный риск к стабильности всего приложения. Этот риск можно уменьшить, имея только один бинарник, установленный в контейнере.
Например, если злоумышленник смог найти уязвимость в приложении, запущенном на базе distroless-образа, он не сможет запустить в контейнере shell, потому что его там нет!
Если же по каким-то причинам размер docker-образа для вас крайне важен, определенно стоит присмотреться к образам на основе Alpine.
Они реально маленькие, но, правда, ценой совместимости. Alpine использует немного другую стандартную библиотеку C — muslc, так что иногда будут всплывать какие-то проблемы. С примерами можно ознакомиться по ссылкам: https://github.com/grpc/grpc/issues/8528 и https://github.com/grpc/grpc/issues/6126.
Ванильные же образы идеально подойдут для тестирования и разработки.
Да, они большие, но зато максимально похожи на полноценную машину с установленной Ubuntu. Кроме того, доступны все бинарники в ОС.
#лонгрид