[Настройка Linux, Node.JS, Серверное администрирование, Go] Слабо поднять такой крошечный контейнер? Создаем контейнеризованный HTTP-сервер на 6kB (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
TL;DR я решил создать самый маленький образ контейнера, при помощи которого все-таки можно сделать что-нибудь полезное. Опираясь на преимущества многоступенчатых сборок, базового образа scratch и крошечного http-сервера на основе этой сборки, я смог ужать результат до 6.32kB!
Извините, данный ресурс не поддреживается. :(
Если предпочитаете видео, вот ролик по статье, выложенный на YouTube!
Раздутые контейнеры
Контейнеры часто превозносятся как панацея, позволяющая справиться с любыми вызовами, связанными с эксплуатацией ПО. Притом, как мне нравятся контейнеры, на практике мне часто попадаются контейнерные образы, отягощенные разнообразными проблемами. Распространенная беда – размер контейнера; у некоторых образов он достигает многих гигабайт!
Поэтому я решил устроить челлендж себе и всем желающим и попытаться создать настолько компактный образ, насколько возможно.
Задача
Правила довольно просты:
- Контейнер должен выдавать содержимое файла по http на выбранный вами порт
- Монтирование томов не допускается (так называемое «Правило Марека» )
Упрощенное решение
Чтобы узнать размер базового образа, можно воспользоваться node.js и создать простой сервер index.js:
const fs = require("fs");
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'content-type': 'text/html' })
fs.createReadStream('index.html').pipe(res)
})
server.listen(port, hostname, () => {
console.log(`Server: http://0.0.0.0:8080/`);
});
и сделать из него образ, запуская официальный базовый образ node:
FROM node:14
COPY . .
CMD ["node", "index.js"]
Этот завесил на 943MB!
Уменьшенный базовый образ
Один из простейших и наиболее очевидных тактических подходов к уменьшению размера образа – выбрать более компактный базовый образ. Официальный базовый образ node существует в варианте slim (по-прежнему на основе debian, но с меньшим количеством предустановленных зависимостей) и вариант alpine на основе Alpine Linux.
С применением node:14-slim и node:14-alpine в качестве базового удается уменьшить размер образа до 167MB и 116MB соответственно.
Поскольку образы docker аддитивны, где каждый уровень надстраивается над следующим, здесь уже практически нечего сделать, чтобы еще сильнее уменьшить решение с node.js.
Скомпилированные языки
Чтобы вывести ситуацию на новый уровень, можно перейти к компилируемому языку, где гораздо меньше зависимостей времени исполнения. Есть ряд вариантов, но для создания веб-сервисов часто применяется golang.
Я создал простейший файловый сервер server.go:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
fileServer := http.FileServer(http.Dir("./"))
http.Handle("/", fileServer)
fmt.Printf("Starting server at port 8080\n")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
И встроил его в контейнерный образ, воспользовавшись официальным базовым образом golang:
FROM golang:1.14
COPY . .
RUN go build -o server .
CMD ["./server"]
Который завесил на… 818MB.
Здесь есть проблема: в базовом образе golang установлено много зависимостей, которые полезны при создании программ на go, но не нужны для запуска программ.
Многоступенчатые сборки
В Docker есть возможность под названием многоступенчатые сборки, с которыми просто собирать код в среде, содержащей все необходимые зависимости, а потом скопировать полученный исполняемый файл в иной образ.
Это полезно по нескольким причинам, но одна из наиболее очевидных – размер образа! Выполнив рефакторинг dockerfile вот так:
### этап сборки ###
FROM golang:1.14-alpine AS builder
COPY . .
RUN go build -o server .
### этап запуска ###
FROM alpine:3.12
COPY --from=builder /go/server ./server
COPY index.html index.html
CMD ["./server"]
Размер полученного образа – всего 13.2MB!
Статическая компиляция + образ Scratch
13 MB – совсем неплохо, но у нас в запасе осталась еще пара трюков, позволяющих еще сильнее ужать этот образ.
Есть базовый образ под названием scratch, который однозначно пуст, его размер равен нулю. Поскольку внутри scratch ничего нет, любой образ, построенный на его основе, должен нести в себе все необходимые зависимости.
Чтобы это было возможно на основе нашего сервера на go, нужно добавить несколько флагов на этапе компиляции, чтобы обеспечить статическую линковку всех необходимых библиотек в исполняемый файл:
### этап сборки ###
FROM golang:1.14 as builder
COPY . .
RUN go build \
-ldflags "-linkmode external -extldflags -static" \
-a server.go
### этап запуска ###
FROM scratch
COPY --from=builder /go/server ./server
COPY index.html index.html
CMD ["./server"]
В частности, мы задаем external в качестве режима линковки и передаем флаг -static внешнему линковщику.
Благодаря двум этим изменениям удается довести размер образа до 8.65MB
ASM как залог победы!
Образ размером менее 10MB, написанный на языке вроде Go, отчетливо миниатюрен почти для любых обстоятельств… но можно сделать еще меньше! Пользователь nemasu выложил на Github полноценный http-сервер, написанный на ассемблере. Он называется assmttpd.
Все, что потребовалось для его контейнеризации – установить несколько зависимостей сборки в базовый образ Ubuntu, прежде, чем запустить предоставленный рецепт make release:
### этап сборки ###
FROM ubuntu:18.04 as builder
RUN apt update
RUN apt install -y make yasm as31 nasm binutils
COPY . .
RUN make release
### этап запуска ###
FROM scratch
COPY --from=builder /asmttpd /asmttpd
COPY /web_root/index.html /web_root/index.html
CMD ["/asmttpd", "/web_root", "8080"]
Затем полученный в результате исполняемый файл asmttpd копируется в scratch-образ и вызывается через командную строку. Размер полученного образа – всего 6,34kB!
===========
Источник:
habr.com
===========
===========
Автор оригинала: © DevOps Directive 2020
===========Похожие новости:
- [Децентрализованные сети, Open source, Go, Распределённые системы, IPFS] Азбука libp2p от Textile, часть 2 (перевод)
- [Системное администрирование, IT-инфраструктура, Cisco, Сетевые технологии] Сброс пароля и базовая настройка Cisco 1941
- [Программирование, Node.JS] Релиз Node.js 16: обзор лучшего
- [PHP, Программирование] От версии 8 к 8.1: новый виток развития PHP (перевод)
- [Управление проектами, Медийная реклама, Управление продуктом, Управление медиа, Финансы в IT] Российские власти обсуждают запрет закупки рекламы в зарубежных сервисах для госкомпаний или юрлиц
- [Облачные вычисления, Node.JS, Amazon Web Services, Тестирование мобильных приложений, Serverless] CloudWatch и Lambda, или Как я перестал бояться и полюбил AWS
- [Тестирование IT-систем, Go, Тестирование веб-сервисов] Подсказки по написанию тестов в приложениях на Go
- [Управление разработкой, Управление проектами] Как работать в команде, которая пишет на 5 языках
- [Настройка Linux, Open source, Софт] Пссст, %username%, Ubuntu 21.04 уже здесь
- [Карьера в IT-индустрии, Лайфхаки для гиков] Манифест: почему я не делаю свой пет-проджект постоянной работой (перевод)
Теги для поиска: #_nastrojka_linux (Настройка Linux), #_node.js, #_servernoe_administrirovanie (Серверное администрирование), #_go, #_node.js, #_kontejnery (контейнеры), #_blog_kompanii_maklaud (
Блог компании Маклауд
), #_nastrojka_linux (
Настройка Linux
), #_node.js, #_servernoe_administrirovanie (
Серверное администрирование
), #_go
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:18
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
TL;DR я решил создать самый маленький образ контейнера, при помощи которого все-таки можно сделать что-нибудь полезное. Опираясь на преимущества многоступенчатых сборок, базового образа scratch и крошечного http-сервера на основе этой сборки, я смог ужать результат до 6.32kB! Извините, данный ресурс не поддреживается. :( Если предпочитаете видео, вот ролик по статье, выложенный на YouTube! Раздутые контейнеры Контейнеры часто превозносятся как панацея, позволяющая справиться с любыми вызовами, связанными с эксплуатацией ПО. Притом, как мне нравятся контейнеры, на практике мне часто попадаются контейнерные образы, отягощенные разнообразными проблемами. Распространенная беда – размер контейнера; у некоторых образов он достигает многих гигабайт! Поэтому я решил устроить челлендж себе и всем желающим и попытаться создать настолько компактный образ, насколько возможно. Задача Правила довольно просты:
Упрощенное решение Чтобы узнать размер базового образа, можно воспользоваться node.js и создать простой сервер index.js: const fs = require("fs");
const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'content-type': 'text/html' }) fs.createReadStream('index.html').pipe(res) }) server.listen(port, hostname, () => { console.log(`Server: http://0.0.0.0:8080/`); }); и сделать из него образ, запуская официальный базовый образ node: FROM node:14
COPY . . CMD ["node", "index.js"] Этот завесил на 943MB! Уменьшенный базовый образ Один из простейших и наиболее очевидных тактических подходов к уменьшению размера образа – выбрать более компактный базовый образ. Официальный базовый образ node существует в варианте slim (по-прежнему на основе debian, но с меньшим количеством предустановленных зависимостей) и вариант alpine на основе Alpine Linux. С применением node:14-slim и node:14-alpine в качестве базового удается уменьшить размер образа до 167MB и 116MB соответственно. Поскольку образы docker аддитивны, где каждый уровень надстраивается над следующим, здесь уже практически нечего сделать, чтобы еще сильнее уменьшить решение с node.js. Скомпилированные языки Чтобы вывести ситуацию на новый уровень, можно перейти к компилируемому языку, где гораздо меньше зависимостей времени исполнения. Есть ряд вариантов, но для создания веб-сервисов часто применяется golang. Я создал простейший файловый сервер server.go: package main
import ( "fmt" "log" "net/http" ) func main() { fileServer := http.FileServer(http.Dir("./")) http.Handle("/", fileServer) fmt.Printf("Starting server at port 8080\n") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } } И встроил его в контейнерный образ, воспользовавшись официальным базовым образом golang: FROM golang:1.14
COPY . . RUN go build -o server . CMD ["./server"] Который завесил на… 818MB. Здесь есть проблема: в базовом образе golang установлено много зависимостей, которые полезны при создании программ на go, но не нужны для запуска программ. Многоступенчатые сборки В Docker есть возможность под названием многоступенчатые сборки, с которыми просто собирать код в среде, содержащей все необходимые зависимости, а потом скопировать полученный исполняемый файл в иной образ. Это полезно по нескольким причинам, но одна из наиболее очевидных – размер образа! Выполнив рефакторинг dockerfile вот так: ### этап сборки ###
FROM golang:1.14-alpine AS builder COPY . . RUN go build -o server . ### этап запуска ### FROM alpine:3.12 COPY --from=builder /go/server ./server COPY index.html index.html CMD ["./server"] Размер полученного образа – всего 13.2MB! Статическая компиляция + образ Scratch 13 MB – совсем неплохо, но у нас в запасе осталась еще пара трюков, позволяющих еще сильнее ужать этот образ. Есть базовый образ под названием scratch, который однозначно пуст, его размер равен нулю. Поскольку внутри scratch ничего нет, любой образ, построенный на его основе, должен нести в себе все необходимые зависимости. Чтобы это было возможно на основе нашего сервера на go, нужно добавить несколько флагов на этапе компиляции, чтобы обеспечить статическую линковку всех необходимых библиотек в исполняемый файл: ### этап сборки ###
FROM golang:1.14 as builder COPY . . RUN go build \ -ldflags "-linkmode external -extldflags -static" \ -a server.go ### этап запуска ### FROM scratch COPY --from=builder /go/server ./server COPY index.html index.html CMD ["./server"] В частности, мы задаем external в качестве режима линковки и передаем флаг -static внешнему линковщику. Благодаря двум этим изменениям удается довести размер образа до 8.65MB ASM как залог победы! Образ размером менее 10MB, написанный на языке вроде Go, отчетливо миниатюрен почти для любых обстоятельств… но можно сделать еще меньше! Пользователь nemasu выложил на Github полноценный http-сервер, написанный на ассемблере. Он называется assmttpd. Все, что потребовалось для его контейнеризации – установить несколько зависимостей сборки в базовый образ Ubuntu, прежде, чем запустить предоставленный рецепт make release: ### этап сборки ###
FROM ubuntu:18.04 as builder RUN apt update RUN apt install -y make yasm as31 nasm binutils COPY . . RUN make release ### этап запуска ### FROM scratch COPY --from=builder /asmttpd /asmttpd COPY /web_root/index.html /web_root/index.html CMD ["/asmttpd", "/web_root", "8080"] Затем полученный в результате исполняемый файл asmttpd копируется в scratch-образ и вызывается через командную строку. Размер полученного образа – всего 6,34kB! =========== Источник: habr.com =========== =========== Автор оригинала: © DevOps Directive 2020 ===========Похожие новости:
Блог компании Маклауд ), #_nastrojka_linux ( Настройка Linux ), #_node.js, #_servernoe_administrirovanie ( Серверное администрирование ), #_go |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:18
Часовой пояс: UTC + 5