构建最小Docker Image运行网站程序并部署到DaoCloud中

[编者的话] 当文字偶遇代码,当程序插上了翅膀,让分享成为我们彼此沟通的语言。我们期待可以构建这样一个平台让开发者们看到你们的智慧,挖掘你们的才华,让彼此在开源的路上不再孤独。“DaoCloud分享写作计划”已全面启动,欢迎投稿。


作者:李建赢


意义:

阅读“创建尽可能小的Docker容器中译本)”后,经过对比我们可以发现 Adriaan de Jonge的工作方式是通过CGO实现Golang的静态编译以达到目的。我们认为这种方式虽然很棒,但是操作起来并不容易,而且在很多项目中编译起来颇为麻烦。

为了精益求精,我们在极致精简与正常使用操作系统(比如说基于ubuntu14.04)做工程角度的平衡,我们还是基于Scratch空Docker image进行构建,使用busybox重新构建了一个大小为5MB左右裁剪过的Linux作为golang运行环境,该系统可以直接使用普通golang编译器输出amd64架构的程序,整个image构建完成之后大小为11.75mb。在DaoCloud中可以达到秒级构建,经过4次测试3次在一分钟以下最短45秒。

Dockerfile以及环境参考https://github.com/lijianying10/DaoCloudStaticBlog

golang HTTP 主程序

package main

import "net/http"  
import "fmt"

func main() {  
    fmt.Println("Server start") //最简单的日志提示已经开始工作
    http.Handle("/",http.FileServer(http.Dir("/public/"))) // 设置静态文件路由
    http.ListenAndServe(":8080", nil)// 开始http server
}

Golang给出了足够多的Web开发工具包,可以编写简短的代码写出高性能web静态服务器。

Docker image中底层系统的构建

如果方便使用 ubuntu 14.04 amd64 的情况下:

apt-get install busybox-static #安装busybox  
mkdir /rootfs #新建根  
mkdir bin etc dev dev/pts lib proc sys tmp #根据根的格式创建目录  
touch etc/resolv.conf # DNS服务器地址  
cp /etc/nsswitch.conf etc/nsswitch.conf #名字解析配置  
echo root:x:0:0:root:/:/bin/sh > etc/passwd # 新建用户  
echo root:x:0: > etc/group #新建用户组  
ln -s lib lib64 # 创建软连接  
ln -s bin sbin  
cp /bin/busybox bin # 复制bin文件,不然没有命令用  
$(which busybox) --install -s bin #同上两步操作,用which是因为需要绝对路径
bash -c "cp /lib/x86_64-linux-gnu/lib{c,m,dl,crypt,util,rt,nsl,nss_*,pthread,resolv}.so.* lib" # 这一步很重要,如果使用了Go调用外部动态库这里需要复制进去,  
bash -c "cp /lib/x86_64-linux-gnu/ld* lib" #同上  
cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 lib #同上  
tar cf /rootfs.tar . #根目录打包  
for X in console null ptmx random stdin stdout stderr tty urandom zero ; do tar uf /rootfs.tar -C/ ./dev/$X ; done #复制设备,注意自己的程序调用的设备,比如说random,顺便吐槽一下等待cpu周期太长  

构建调试过程中的注意事项:

  1. 在复制lib文件夹的底层动态库的时候需要不断调试动态库的依赖。
  2. 因为容器中要运行不同的项目,有可能对设备与权限上有不同的需求,需要注意定制脚本及时调整。
  3. 整个打包过程每次项目几乎只需要一次,调整项目大部分情况都是增量打包因此精简系统文件尽量仔细做。

boot2Docker 跨平台解决方案的Dockerfile(仿造上面来):

FROM ubuntu:14.04  
RUN apt-get update -q  
RUN apt-get install -qy busybox-static  
RUN mkdir /rootfs  
WORKDIR /rootfs  
RUN mkdir bin etc dev dev/pts lib proc sys tmp  
RUN touch etc/resolv.conf  
RUN cp /etc/nsswitch.conf etc/nsswitch.conf  
RUN echo root:x:0:0:root:/:/bin/sh > etc/passwd  
RUN echo root:x:0: > etc/group  
RUN ln -s lib lib64  
RUN ln -s bin sbin  
RUN cp /bin/busybox bin  
RUN $(which busybox) --install -s bin  
RUN bash -c "cp /lib/x86_64-linux-gnu/lib{c,m,dl,crypt,util,rt,nsl,nss_*,pthread,resolv}.so.* lib"  
RUN bash -c "cp /lib/x86_64-linux-gnu/ld* lib"  
RUN cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 lib  
RUN tar cf /rootfs.tar .  
RUN for X in console null ptmx random stdin stdout stderr tty urandom zero ; do tar uf /rootfs.tar -C/ ./dev/$X ; done  

非安全快速解决方案

我们这里的安全性是指,我提供的tar根目录中也许包含了老版本的代码漏洞,或者各种技术债务。如果不是特殊场景下不需要自己手动构建Linux根。

具体解决方案如下:

  1. 直接在Dockerhub上使用progrium/busybox
  2. 直接使用我们在github上共享的rootfs.tar

方式1不是很推荐,因为构建的适合需要从网上下载依赖,可能会降低构建速度。

非安全情况下推荐方式2构建所需要的docker image

方式1构建的Dockerfile:

FROM progrium/busybox  
MAINTAINER Jianying Li <lijianying12@gmail.com>

RUN mkdir -p /app  
WORKDIR /app  
COPY ./static /app/  
COPY ./public /public  
EXPOSE 80

CMD ["/app/static"]  

方式2构建使用的Dockerfile:

FROM scratch  
MAINTAINER Jianying Li <lijianying12@gmail.com>

ADD ./rootfs.tar /  
RUN mkdir -p /app  
WORKDIR /app  
COPY ./static /app/  
COPY ./public /public  
EXPOSE 80

CMD ["/app/static"]  

Tips

  • static为第一节编译好的golang静态程序,public为静态网站根目录

  • 在构建时请不要使用sudo docker build -< somefile 要使用sudo docker build .

  • 方式2是我在Github中提供的方式

跨平台支持

在本方案中跨平台的支持主要有两个方面:

  1. golang源程序的跨平台编译:GOX
  2. busybox 系统根(方案二中的Dockerfile中的文件rootfs.tar)的编译可以通过boot2docker来实现。

DaoCloud部署

DaoCloud给我们提供了一个非常好的测试平台,验证性能工作方式等等,不需要电脑中有Docker环境也可非常方便测试。

在这里我们介绍一下上面提供的github仓库的使用方法。

  1. Fork我们提供的代码仓库到您的github账号中。
  2. 登陆到DaoCloud.io使用代码构建功能
  3. 给项目取名字
  4. 登陆到github中系统会列出您在Github中的所有项目。并选中DaoCloudStaticBlog项目点击开始创建

在这之后DaoCloud会帮您创建目标项目的DockerImage。

在镜像仓库中找到项目点击部署给容器起名选择环境即可运行。

注意事项

  1. Golang程序中的端口一定要与Dockerfile中的EXPOSE的号码对应,不然外网无法从DaoCloud访问您的容器。
  2. CMD中注意要直接运行网站。
  3. 如果条件允许请在本地调试运行之后再部署到DaoCloud中。

总结

在本次工作中我们使用了BusyBox来替代原来CGO的实施方案,有效的解决了编译时的麻烦。建立了底层依赖后直接复制应用程序进入Docker image完成在DaoCloud上的部署。在Dockerfile的依赖上我们直接使用了scratch没有任何依赖,加快了构建的速度达到秒级构建。


本文来自“DaoCloud分享写作计划”,这项计划旨在为开发者提供一个平台,分享使用Docker的心得体会和技术经验。DaoCloud将为文章作者提供一定的物质奖励,具体方式请访问:DaoCloud写作分享计划 ,欢迎Docker爱好者和DaoCloud用户踊跃投稿。