Apakah lemah untuk mengangkat wadah sekecil itu? Membangun server HTTP dalam container 6kB

TL; DR   Saya memutuskan untuk membuat gambar kontainer terkecil yang Anda masih dapat melakukan sesuatu yang berguna. Memanfaatkan build multi-tahap, gambar dasar,  scratch



 dan server http kecil berdasarkan build ini, saya dapat memeras hasilnya hingga 6,32kB!











Jika Anda lebih suka video, inilah video YouTube untuk artikelnya!



Wadah kembung



Container sering disebut-sebut sebagai obat mujarab untuk menangani tantangan pemeliharaan perangkat lunak apa pun. Apalagi, karena saya suka container, dalam praktiknya saya sering menjumpai gambar container, terbebani berbagai masalah. Masalah umum adalah ukuran wadah; untuk beberapa gambar mencapai banyak gigabyte!  



Jadi saya memutuskan untuk menantang diri saya sendiri dan orang lain dan mencoba membuat gambar sekompak mungkin.



Sebuah tugas



Aturannya cukup sederhana:



  • Wadah harus menyajikan konten file melalui http ke port pilihan Anda 
  • Volume pemasangan tidak diperbolehkan (disebut "Aturan Marek")


Solusi yang disederhanakan



Untuk mengetahui ukuran gambar dasar, Anda dapat menggunakan node.js dan membuat server sederhana  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/`);
});

      
      





dan membuat gambar darinya dengan menjalankan gambar dasar resmi node:



FROM node:14
COPY . .
CMD ["node", "index.js"]

      
      





Yang ini bertahan  943MB



!



Gambar dasar dikurangi



Salah satu pendekatan taktis paling sederhana dan paling jelas untuk mengurangi ukuran kulit adalah dengan memilih kulit dasar yang lebih ramping. Gambar dasar resmi node ada dalam varian slim



 (masih berdasarkan debian, tetapi dengan dependensi pra-instal yang lebih sedikit) dan varian  alpine



 berdasarkan  Alpine Linux .



Menggunakan  node:14-slim



 dan  node:14-alpine



 sebagai dasar, adalah mungkin untuk mengurangi ukuran gambar untuk  167MB



 dan  116MB



 sesuai.



Karena gambar buruh pelabuhan bersifat aditif, dengan setiap lapisan dibangun di atas lapisan berikutnya, hampir tidak ada yang bisa dilakukan di sini untuk mengurangi solusi node.js.



Bahasa yang dikompilasi



Untuk membawa hal-hal ke level berikutnya, Anda dapat beralih ke bahasa terkompilasi yang memiliki dependensi run-time yang jauh lebih sedikit. Ada beberapa pilihan, tetapi golang sering digunakan untuk membuat layanan web .



Saya membuat server file paling sederhana  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)
	}
}

      
      





Dan saya membuatnya menjadi gambar kontainer menggunakan gambar dasar golang resmi:



FROM golang:1.14
COPY . .
RUN go build -o server .
CMD ["./server"]

      
      





Yang tergantung…  818MB



.



Ada masalah di sini: ada banyak dependensi yang terpasang di image golang dasar, yang berguna saat membuat program go, tetapi tidak diperlukan untuk menjalankan program. 



Majelis multi-tahap



Docker memiliki fitur yang disebut  build multistage , yang dengannya mudah untuk membuat kode di lingkungan yang berisi semua dependensi yang diperlukan, lalu menyalin file yang dapat dijalankan yang dihasilkan ke image lain.



Ini berguna karena beberapa alasan, tetapi salah satu yang paling jelas adalah ukuran gambarnya! Dengan melakukan refactoring dockerfile seperti ini:



###   ###
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"]

      
      





Ukuran gambar yang dihasilkan adalah segalanya  13.2MB



!



Kompilasi statis + Gambar awal



13 MB sama sekali tidak buruk, tetapi kami masih memiliki beberapa trik tersisa untuk membuatnya terlihat lebih ketat. 



Ada gambar dasar yang disebut  goresan , yang jelas kosong, ukurannya nol. Karena scratch



 tidak ada apa-apa di dalamnya , gambar apa pun yang dibangun di atasnya harus membawa semua dependensi yang diperlukan.



Untuk membuat ini mungkin berdasarkan server go kami, kami perlu menambahkan beberapa tanda pada waktu kompilasi untuk memastikan bahwa semua pustaka yang diperlukan ditautkan secara statis ke dalam file yang dapat dieksekusi:



###   ###
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"]

      
      





Secara khusus, kami menyetel external



 mode penautan dan meneruskan bendera ke  -static



 penaut eksternal.



Berkat dua perubahan ini, dimungkinkan untuk memperbesar ukuran gambar 8.65MB



 



ASM sebagai jaminan kemenangan!



Sebuah gambar berukuran kurang dari 10MB, ditulis dalam bahasa seperti Go, secara jelas dikecilkan untuk hampir semua keadaan ... tetapi Anda dapat membuatnya lebih kecil! Pengguna  nemasu telah  memposting server http lengkap yang ditulis dalam assembler di Github. Ini disebut assmttpd .



Yang diperlukan untuk membuat container adalah menginstal beberapa dependensi build ke dalam image dasar Ubuntu, sebelum menjalankan resep yang disediakan 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"] 

      
      





Eksekusi yang dihasilkan kemudian  asmttpd



 disalin ke gambar awal dan dipanggil melalui baris perintah. Ukuran gambar yang dihasilkan hanya 6.34kB!



All Articles