Get Started with Docker
我都不喜欢照搬官网教程,就从一些示例来说,这样比较快速了解docker
从nginx的官网docker镜像入门,理解docker
Nginx-Docker镜像地址
能看到,有很多tag的镜像版本:
- 镜像标注了nginx的版本:mainline和stable,分别表示开发版(主力版)和稳定版(生产环境建议版本)
- 标注了基础镜像版本:stretch(debain)和alpine
主要讲一下基础镜像,所有的dockerfile第一段都是用FROM开头,标明这个dockerfile是在什么镜像的基础上编写的
作为最底层的基础镜像,就像虚拟机里面的操作系统
Alpine:优点 小,快,安全
- 一个alpine的基础镜像只有不到4M,debain差不多120+M
- alpine的速度快,拉取依赖的速度快
- 因为小,默认不安装bash解释器,漏洞少
使用Alpine作为基础镜像需要注意的点参考博客
- 库问题
- DNS问题
alpine仓库中很多已经编译好的二进制服务的软件包是依赖于alpine的musl libc库
安装这些服务包只需要apk add xxx
直接安装
仓库中没有的服务包,同时这些服务包依赖于glibc的软件,则需要提前在alpine基础镜像中安装glibc库 gs
RUN apk --no-cache add ca-certificates && \
wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub && \
wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.27-r0/glibc-2.27-r0.apk && \
apk add glibc-2.27-r0.apk
Dns问题官方给出的解释还是和musl libc库有关系
总结下来就是musl实现的DNS服务不会使用resolv.conf文件中的search和domain两个配置
github官方解释
- 一是在使用dns作为服务发现时
- 二是在使用并行的dns服务器时,不能确保前一个一直作为默认的dns服务器 –dns xxx1 –dns xxx2
推荐使用dnsmasq,运行时使用–server /consul/10.0.0.1这个参数
FROM alpine:edge
RUN apk --no-cache add dnsmasq
EXPOSE 53 53/udp
ENTRYPOINT ["dnsmasq", "-k"]
从Nginx的dockerfile来理解docker
FROM alpine:3.8
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
ENV NGINX_VERSION 1.14.2
RUN GPG_KEYS=B0F4253373F8F6F510D42178520A9993A1C052F8 \
***
&& ln -sf /dev/stderr /var/log/nginx/error.log
COPY nginx.conf /etc/nginx/nginx.conf
COPY nginx.vh.default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
STOPSIGNAL SIGTERM
CMD ["nginx", "-g", "daemon off;"]
上文dockerfile,RUN部分的代码省略了,下面做命令的简单解释:
RUN命令用\ &&
连接很多命令,因为docker是层的概念,每增加一个指令相当于在镜像上添加一层,所以用连接符将多个命令连接成一个RUN
- FROM: 前面已经说了,声明基础镜像,声明这个dockerfile是基于哪个镜像来生成的
- LABEL: 没啥作用,标注作者用的
- ENV: 环境变量声明,类似语言中申明变量一样
- RUN: 运行命令,一般用户构建过程中的指令,RUN执行的命令是在构建镜像过程中,在基于FROM的基础镜像的系统上执行的命令,所以RUN里面的命令风格要和FROM指定的基础镜像一致,比如上面基于alpine的nginx安装时,RUN时安装包则用apk add
- CP: 将宿主机的文件拷贝到镜像内
- EXPOSE: 指定镜像端口
- CMD: 最后运行指定,多个时以最后一个为准,CMD只会执行最后一条
dockerfile打包的规则是打出尽量小的镜像,多指令通过连接符、–no-cache,删除过程文件均是为了最小化镜像
在docker17.05之后,docker支持了多阶段构建的-multi-stage build
通过多阶段构建,解决依赖构建过程,造成镜像包大的问题,因为安装的构建服务只会使用一次
使用也很简单
FROM golang:1.7.3
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/sparkdevo/href-counter/app .
CMD ["./app"]
可以看到多阶段构建
第一阶段即进行依赖构建,将构建好的依赖包拷贝出来
第二阶段将打好包的依赖CP进去,启动服务
这样最终生成的镜像仅包含第二阶段的内容,而没有仅仅打包时需要安装的一些库。从而更加精简了docker镜像
日常使用下来,真正需要手动编写的dockerfile很少,大部分是基于docker官方镜像仓库编好的镜像进行改写
比如在编写好的nginx增加一个插件,FROM nginx:xxx 然后安装插件,即可