在现代的B/S架构应用中,我们会做前后端分离,某些前端Web服务会将编译完成的静态文件放到一个web服务器进行部署。例如,我的博客也是基于Hugo编译的静态文件来进行部署的。

那在容器化部署模式下,我们需要基于一个web服务的基础容器(镜像)将静态文件构建成站点或者Web服务的容器镜像来进行部署。在Docker开发最佳实践中,我们应该尽量保持镜像足够小(Size大小)。因此,我们应该尽量选择满足我们需求的web服务基础镜像足够小。

static-webserver-container

大部分情况下,我们会选择Nginx作为我们的web服务器,一开始我也是这么选择的,因为社区在Docker Hub上为我们提供了开箱即用的容器镜像,下面来看看我用来构建静态web服务的过程。

Nginx On Alpine

我们知道在容器构建的实践中,我们可以选择基于AlpineLinux为分发系统的镜像,其比其他(例如 ubuntu, centos等)的镜像会小很多。因此一开始我们也是选择基于Alpine的nginx镜像,例如 nginx:1.22-alpine

$ docker image pull nginx:1.22-alpine

$ docker image ls | grep nginx
nginx    1.22-alpine    23.5MB

可以看到其大小为 23.5MB

基于该惊醒构建我的博客的发布镜像

FROM mengzyou/hugo:0.106 AS builder
COPY --chown=hugo:hugo . /home/hugo/app
RUN hugo

FROM nginx:1.22-alpine
COPY --from=builder /home/hugo/app/public/ /usr/share/nginx/html
$ docker build -t myblog:nginx .

$ docker image ls --format "{{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep myblog
myblog  nginx   29MB

构建出来而最终交付镜像的大小为 29MB

Easyhttpd On Alpine

后来,我发现了一个用GoLang编写的轻量级web服务器 - easyhttpd,于是我Fork了该项目,编写了一个Dockerfile来构建该web服务器的镜像,具体可查看该文件内容。

镜像我已发布在mengzyou/easyhttpd

$ docker image ls --format "{{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep easyhttpd
mengzyou/easyhttpd      v0.0.1  13.7MB

镜像大小为 13.7MB,比 nginx:alpine 的镜像小了十几MB。使用该镜像构建来构建我的博客站点

...
FROM mengzyou/easyhttpd:v0.0.1
COPY --from=builder --chown=http:www /home/hugo/app/public/ /srv/www
$ docker image ls --format "{{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep myblog
myblog  nginx   29MB
myblog  ehttpd  19.1MB

得到的应用镜像大小为 19.1MB ,进一步减少了应用的镜像大小。

BusyBox Httpd

最近看到了一个国外的博客文章,可以构建一个只有 ~155KB 大小的web服务器镜像,我非常好奇,向着是否可以进一步减少我的静态站点的镜像大小。

是使用了BusyBox内置的httpd来静态文件提供web服务。于是我也学习该作者创建了一个基于busybox - httpd的web服务器镜像,将其命名为 bbhttpd,具体的构建内容请参考Github仓库 - docker-bbhttpd

构建的镜像我也发布到Docker Hub - mengzyou/bbhttpd

$ docker image ls --format "{{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep bbhttpd
mengzyou/bbhttpd        1.35    155kB

镜像大小确实只有 155KB,是不是挺惊人的?使用该镜像来构建我的站点

...
FROM mengzyou/bbhttpd:1.35
COPY --from=builder --chown=www:www /home/hugo/app/public/ /home/www/html
docker image ls --format "{{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep myblog
myblog  nginx   29MB
myblog  ehttpd  19.1MB
myblog  bbhttpd 5.64MB

最终的交付镜像大小只有 1.64MB,几乎也就是web服务静态文件的大小。

总结

按照Docker容器镜像构建的最佳实践,我们应该尽量保持最小的经销大小,而减少镜像大小的一个方法就是选择足够小的基础镜像。因此我们在构建静态Web服务的时候,可以通过自己构建基础镜像的方式,大大减少最终的镜像大小。

基础镜像nginx:1.22-alpnemengzyou/easyhttpd:v0.0.1mengzyou/bbhttpd:1.35
Size23.5MB13.7MB155KB