構(gòu)建一個(gè)世界上最小的 Redis 鏡像

好吧,我承認(rèn)這么說可能違反了廣告法,但是……它確實(shí)挺小的。

可以對(duì)比一組數(shù)據(jù):
官方的 Redis 鏡像:105MB 官方的基于 alpine 的 Redis 鏡像:32.3MB 在 ubuntu 下面用默認(rèn)配置 Build 出來的 redis-server binary:13M 一個(gè)什么都沒有的 alpine:latest docker 鏡像:5.6MB 上圖中的 Redis 鏡像:1.69MB
所以……可以說,確實(shí)挺小了。
當(dāng)然生產(chǎn)環(huán)境肯定不差 100M 這點(diǎn)空間,還是帶上一些常用的工具在生產(chǎn)環(huán)境跑比較好。本文只是在玩 Nix 時(shí)候的自?shī)首詷?,沒有什么實(shí)際意義。
這個(gè)鏡像是用 Nix build 的,實(shí)際上,就是玩了一下 Nix 網(wǎng)站上的 Cover Demo[1]。用到的手段有:
使用 Nix 來 build 這個(gè) image; 編譯的時(shí)候關(guān)閉了 systemd 的支持,Docker 里面不需要這種東西; 使用 musl 靜態(tài)鏈接編譯; 把除了 redis-server 之外的東西刪除掉; 編譯完成使用 strip -s對(duì)最終的 binary 再次做刪減。
具體的編譯方法
首先在 Nix 創(chuàng)建一個(gè)文件,來編譯 Redis,這里實(shí)際上使用的 Nix 打包好的 Redis,我只是對(duì)其通過 preBuild 和 postInstall 做了一些操作,替換 musl 和 strip 之類的。
[nix-shell:~]$?cat?redis-minimal.nix
{?pkgs???import??{}
}:
pkgs.redis.overrideAttrs?(old:?{
??#?no?need?for?systemd?support?in?our?docker?image
??makeFlags?=?old.makeFlags?++?["USE_SYSTEMD=no"];
??#?build?static?binary?with?musl
??preBuild?=?''
????makeFlagsArray=(PREFIX="$out"
????????????????????CC="${pkgs.musl.dev}/bin/musl-gcc?-static"
????????????????????CFLAGS="-I${pkgs.musl.dev}/include"
????????????????????LDFLAGS="-L${pkgs.musl.dev}/lib")
??'';
??#?Let's?remove?some?binaries?which?we?don't?need
??postInstall?=?''
?????rm?-f?$out/bin/redis-{benchmark,check-*,cli,sentinel}
?????strip?-s?$out/bin/redis-server
??'';
})
然后再需要一個(gè)文件描述如何 build docker image:
[nix-shell:~]$?cat?docker-redis-minimal.nix
{?pkgs???import??{?system?=?"x86_64-linux";}
}:???????????????????????????????????#?nixpkgs?package?set
let
??redisMinimal?=?import?./redis-minimal.nix?{?inherit?pkgs;?};
in
pkgs.dockerTools.buildLayeredImage?{?#?helper?to?build?docker?image
??name?=?"nix-redis-minimal";????????#?give?docker?image?a?name
??tag?=?"latest";????????????????????#?provide?a?tag
??contents?=?[?redisMinimal?];???????#?use?redisMinimal?package
}
非常簡(jiǎn)單,以至于不需要解釋。
然后運(yùn)行下面這個(gè)命令,build 就可以了。因?yàn)槲乙呀?jīng) build 好了,所以 Nix 不會(huì)再出現(xiàn) build 的日志。
[nix-shell:~]$?nix-build?docker-redis-minimal.nix?-o?./result
/nix/store/42frnm57499800z3mpwf05ab37mam1r0-docker-image-nix-redis-minimal.tar.gz
在 Docker (容器) 的原理[2] 曾經(jīng)解釋過,Docker image 本質(zhì)上就是一個(gè) tar 包。我們使用 docker load -i ./result 可以 load 這個(gè) image。然后就可以運(yùn)行了:
[nix-shell:~]$?docker?load?-i?result
Loaded?image:?nix-redis-minimal:latest
[nix-shell:~/export-docker-image]$?docker?images
REPOSITORY????????????????TAG?????????????????IMAGE?ID????????????CREATED?????????????SIZE
nix-redis-minimal?????????latest??????????????433cad4ad790????????51?years?ago????????1.69MB
[nix-shell:~]$?docker?run?nix-redis-minimal:latest?redis-server
1:C?19?Jul?2021?16:37:09.847?#?oO0OoO0OoO0Oo?Redis?is?starting?oO0OoO0OoO0Oo
1:C?19?Jul?2021?16:37:09.847?#?Redis?version=5.0.7,?bits=64,?commit=00000000,?modified=0,?pid=1,?just?started
1:C?19?Jul?2021?16:37:09.847?#?Warning:?no?config?file?specified,?using?the?default?config.?In?order?to?specify?a?config?file?use?redis-server?/path/to/redis.conf
1:M?19?Jul?2021?16:37:09.848?*?Running?mode=standalone,?port=6379.
1:M?19?Jul?2021?16:37:09.848?#?Server?initialized
1:M?19?Jul?2021?16:37:09.848?#?WARNING?overcommit_memory?is?set?to?0!?Background?save?may?fail?under?low?memory?condition.?To?fix?this?issue?add?'vm.overcommit_memory?=?1'?to?/etc/sysctl.conf?and?then?reboot?or?run?the?command?'sysctl?vm.overcommit_memory=1'?for?this?to?take?effect.
1:M?19?Jul?2021?16:37:09.848?#?WARNING?you?have?Transparent?Huge?Pages?(THP)?support?enabled?in?your?kernel.?This?will?create?latency?and?memory?usage?issues?with?Redis.?To?fix?this?issue?run?the?command?'echo?never?>?/sys/kernel/mm/transparent_hugepage/enabled'?as?root,?and?add?it?to?your?/etc/rc.local?in?order?to?retain?the?setting?after?a?reboot.?Redis?must?be?restarted?after?THP?is?disabled.
可能你發(fā)現(xiàn)了這個(gè) image 有一些奇怪:Created 51 years ago. 其實(shí)這是對(duì)的。因?yàn)?Nix 號(hào)稱是完全 reproducible 的,但是 image 如果有一個(gè)創(chuàng)建時(shí)間的話,那么每次 build 出來的產(chǎn)物都會(huì)因?yàn)檫@個(gè)創(chuàng)建時(shí)間,而導(dǎo)致每次的產(chǎn)物 hash 都不一樣。所以 Nix 將 Docker image 產(chǎn)物的 Created 時(shí)間設(shè)置成 0 了。即 timestamp = 0.
看看這個(gè)鏡像里面都有什么?
讀者可以在 Docker hub[3] 上下載這個(gè)鏡像,然后使用 docker save 將它保存成 tar 再解壓,看看里面都有什么。我這里直接去解壓 Nix build 好的 image,每一層 layer 下面的 layer.tar 也都解壓到對(duì)應(yīng)的 layer 下面了。
可以看到,里面一層只有一個(gè) redis-server 的 binary,上面一層是一個(gè) bianry 的符號(hào)鏈接。符號(hào)鏈接是 Nix 的邏輯,符號(hào)鏈接很小,就懶得去刪除了。
[nix-shell:~/export-docker-image]$?tree
.
├──?42frnm57499800z3mpwf05ab37mam1r0-docker-image-nix-redis-minimal.tar.gz
├──?433cad4ad790679879357e81194fa7502f7688edfe5b7ff64a4f59edfed3f84d.json
├──?83d967dde8785f9b8efc694109992e72f3a9ab6a1b953519e4b892633fd01b1d
│???├──?bin
│???│???└──?redis-server?->?/nix/store/h25xxpbif767cwhiqxk9z3gp1rbbx6fr-redis-5.0.7/bin/redis-server
│???├──?json
│???├──?layer.tar
│???└──?VERSION
├──?8f7098124197b3823e91dd99eb3cd89853d3ec03448689f2a3f283b261551734
│???├──?json
│???├──?layer.tar
│???├──?nix
│???│???└──?store
│???│???????└──?h25xxpbif767cwhiqxk9z3gp1rbbx6fr-redis-5.0.7
│???│???????????└──?bin
│???│???????????????└──?redis-server
│???└──?VERSION
├──?manifest.json
└──?repositories
7?directories,?12?files
體驗(yàn)這個(gè)鏡像
我把這個(gè)鏡像放到了 Docker hub[4] 上??梢灾苯舆\(yùn)行 docker run laixintao/redis-minimal:v1 redis-server 來體驗(yàn)一下。
Docker 在 NixOS 里面的安裝可以參考這里[5]。
引用鏈接
Cover Demo: https://nixos.org/#asciinema-demo-cover
[2]Docker (容器) 的原理: https://www.kawabangga.com/posts/4224
[3]Docker hub: https://hub.docker.com/repository/docker/laixintao/redis-minimal
[4]Docker hub: https://hub.docker.com/repository/docker/laixintao/redis-minimal
[5]這里: https://nixos.wiki/wiki/Docker
原文鏈接:https://www.kawabangga.com/posts/4411


你可能還喜歡
點(diǎn)擊下方圖片即可閱讀

云原生是一種信仰???
關(guān)注公眾號(hào)
后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!


點(diǎn)擊?"閱讀原文"?獲取更好的閱讀體驗(yàn)!
發(fā)現(xiàn)朋友圈變“安靜”了嗎?


