如何创建一个安全的Docker基础镜像

这里写图片描述

背景

我最初使用Docker的时候,每个人都在说它用起来有多简单方便,它内部的机制是有多么好,它为我们节省了多少时间。但是当我一使用它就发现,几乎所有镜像都是臃肿而且不安全的(没有使用包签名,盲目相信上游的镜像库以curl | sh的方式安装),而且也没有一个镜像能实现Docker的初衷:隔离,单进程,容易分发,简洁。

Docker镜像本来不是为了取代复杂的虚拟机而设计的,后者有完整的日志、监控、警报和资源管理模块。而Docker则倾向于利用内核的cgroupsnamespaces特性进行封装组合,这就好像:

在物理机器环境下,一旦内核完成了初始化,init进程就起来了。

这也是为什么当你在Dockerfile的CMD指令启动的进程PID是1,这是与Unix中的进程机制类似的。

现在请查看一下你的进程列表,使用top或者ps,你会看到init进程占用的也是这个PID,这是每个类Unix系统的核心进程,所有进程的父进程,一旦你理解这个概念:在类Unix系统上每个进程都是init进程的子进程,你会理解Docker容器里不应该有无关的修饰文件,它应该是刚好满足进程运行需要。

如何开始

现在的应用多数是大型复杂的系统,通常都需要很多依赖库,例如有调度,编译和很多其他相关工具类应用,它们的架构通常封装性良好,通过一层层的抽象和接口把底层细节隐藏了,从某种程度上说,这也算是一种容器,但是从系统架构视角看,我们需要一种比以往虚拟环境更简单的方案了。

以Java为例

从零开始,思考你要构建一个最通用的基础容器,想想你的应用本身,它运行需要什么?

可能性有很多,如果你要运行Java应用,它需要Java运行时;如果运行Rails应用,它需要Ruby解释器,对Python应用也一样。Go和其他一些编译型语言有些许不同,我以下会提到。

在Java例子中,下一步要想的是:JRE需要什么依赖才能运行?因为它是让应用能运行的最重要的组件,所以很自然的下一步就是要想清楚JRE运行依赖于什么。

而实际上JRE并没太多依赖,它本来就是作为操作系统的抽象层,使代码不依赖于宿主系统运行,因此安装好JRE就基本准备就绪了。

(实际上,对操作系统的独立性并不是理所当然的事,有非常多的系统特有API和专有的系统扩展,但是便于举例,我们把注意力放在简单的情况下)

在Linux上,JVM主要是调用系统的C语言库,Oracle的官方JRE,使用的是libc,也就是glibc,这意味着你要运行任何Java程序,都需要先装好glibc。另外你可能需要某种shell来管理环境,还有一个与外部通讯的接口,例如网络和资源的接口。

我们总结一下Java应用示例需要的最低配置是:

  • JRE,在例子中我们使用Oracle JRE
  • glibc,JRE的依赖
  • 一个基础环境(包含网络、内存、文件系统等资源管理工具)

走进Alpine Linux

Alpine Linux最近得到很多关注,主要是因为它打包了一系列的经过验签的可信任的依赖,并且还保持体积在2MB!而在本文发布时,其他的一些镜像分发版如下:

  • ubuntu:latest: 66MB (已经瘦身了非常多了,以前有些版本超过600MB)
  • debian:latest: 55MB (同上,一开始是200MB以上的)
  • arch:latest: 145MB
  • busybox:latest: 676KB (是的!KB,我稍后会讨论它)
  • alpine:latest: 2MB (2MB,包含一个包管理工具的Linux系统)

Busybox是最小的竞争者?

从上边的对比中你可以看到,在体积上唯一能打败Alpine Linux的是Busybox,所以现在几乎所有嵌入式系统都是使用它,它被应用在路由器,交换机,ATM,或者你的吐司机上。它作为一个最最基础的环境,但是又提供了足够容易维护的shell接口。

在网上有很多文章解释了为什么人们会选择Alpine Linux而不是Busybox,我在这总结一下:

  • 开放活跃的软件包仓库:Alpine Linux使用apk包管理工具,它集成在Docker镜像中,而Busybox你需要另外安装一个包管理器,例如opkg,更甚者,你需要寻找一个稳定的包仓库源(这几乎没有),Alpine的包仓库中提供了大量常用的依赖包,例如,如果你仍然需要在容器中编译NodeJS或Ruby之类的代码,你可以直接运行apk来添加nodejs和ruby,这在几秒内便可以完成。
  • 体积确实重要,但是当你在功能性,灵活性,易用性和1.5MB之间衡量,体积就不那么重要了,Alpine上添加的包使这些方面都大大增强了。
  • 广泛的支持:Docker公司已经聘请了Alpine Linux的作者来维护它,所有官方镜像,在以后都将基于Alpine Linux来构建。没有比这个更有说服力的理由去让你在自己的容器中使用它了吧。
  • 希云cSphere很早就意识到镜像越来越庞大的问题,因此在去年推出微镜像,也是引导大家如何更好地构建和理解镜像,镜像只是一种软件包格式而已。

构建一个Java环境基镜像

正如我刚解释的,Alpine Linux是一个构建自有镜像时不错的选择,因此,我们在此将使用它来构建简洁高效的Docker镜像,我们开始吧!

组合:Alpine + bash

每个Dockerfile第一个指令都是指定它的父级容器,通常是用于继承,在我们的例子中是alpine:latest:

FROM alpine:latest
MAINTAINER cSphere <docker@csphere.cn>

我们同时声明了谁为这个镜像负责,这个信息对上传到Docker Hub的镜像是必要的。

就这样,你就有了往下操作的基础,接下来安装我们选好的shell,把下边的命令加上:

RUN apk add --no-cache --update-cache bash
CMD ["/bin/bash"]

最终的Dockerfile是这样:

FROM alpine:latest
MAINTAINER cSphere <docker@csphere.cn>

RUN apk add --no-cache --update-cache bash
CMD ["/bin/bash"]

好了,现在我们构建容器:

$ docker build -t my-java-base-image .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM alpine:latest
 ---> 2314ad3eeb90
Step 2 : MAINTAINER cSphere <docker@csphere.cn>
 ---> Running in 63433312d77e
 ---> bfe94713797a
Removing intermediate container 63433312d77e
... 省略若干行
Step 4 : CMD /bin/bash
 ---> Running in d2291684b797
 ---> ecc443d68f27
Removing intermediate container d2291684b797
Successfully built ecc443d68f27

并且运行它:

$ docker run --rm -ti my-java
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
创建一个Docker镜像可以通过以下步骤完成: 1. 编写Dockerfile:Dockerfile是一个文本文件,用于定义镜像的构建过程。在Dockerfile中,你可以指定基础镜像、安装软件、复制文件等操作。 2. 构建镜像:使用Docker命令行工具,通过执行`docker build`命令来构建镜像。该命令会根据Dockerfile的定义,自动执行构建过程。 3. 运行容器:构建完成后,你可以使用`docker run`命令来创建并运行一个容器,该容器是基于你所构建的镜像创建的。 下面是一个简单的示例Dockerfile,用于创建一个基于Ubuntu的镜像,并安装了Nginx服务器: ``` # 使用Ubuntu作为基础镜像 FROM ubuntu:latest # 更新软件包列表并安装Nginx RUN apt-get update && apt-get install -y nginx # 复制Nginx配置文件到容器中 COPY nginx.conf /etc/nginx/nginx.conf # 暴露80端口 EXPOSE 80 # 启动Nginx服务 CMD ["nginx", "-g", "daemon off;"] ``` 在上述示例中,首先指定了基础镜像为最新版的Ubuntu。然后通过`RUN`命令更新软件包列表并安装Nginx。接着使用`COPY`命令将本地的nginx.conf文件复制到容器中的/etc/nginx/nginx.conf路径下。最后使用`EXPOSE`命令暴露容器的80端口,并使用`CMD`命令启动Nginx服务。 你可以将上述Dockerfile保存为一个文件,然后在该文件所在的目录下执行以下命令来构建镜像: ``` docker build -t my-nginx-image . ``` 其中,`-t`参数用于指定镜像的名称和标签,`.`表示Dockerfile所在的当前目录。 构建完成后,你可以使用以下命令来运行容器: ``` docker run -d -p 8080:80 my-nginx-image ``` 其中,`-d`参数表示以后台模式运行容器,`-p`参数用于将容器的80端口映射到主机的8080端口。 希望以上信息对你有所帮助!如果你还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值