主要是 gitbook 开源书籍 Docker — 从入门到实践 笔记,也包含了自己的一些见解和坑点记录。
这次学习主要达到能用 docker 快速复现赛题和 cve 环境,docker-compose 的坑就只有以后再来填了
操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root
文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系
统。
简而言之,可以把镜像理解为虚拟机的快照文件
因为体积大小,镜像并不是像 iso 那样的打包文件,采用了分层存储,前一层是后一层的基础。需要注意的是,删除前一层文件的操作,实际不是真的删除
前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看
到这个文件,但是实际上该文件会一直跟随镜像。
类似类和实例,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。
包含多个仓库( Repository );每个仓库可以包含多个标签( Tag );每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。以 Ubuntu 镜像 为例, ubuntu 是仓库的名字,其内包含有不同的版本标签,如, 14.04 ,16.04 。我们可以通过 ubuntu:14.04 ,或者 ubuntu:16.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu ,那将视为 ubuntu:latest 。
官方仓库:Docker Hub
仓库镜像(Register Mirror),被称为加速器:阿里云加速,DaoCloud 加速器。
类似 Docker Hub: 时速云镜像仓库、网易云
镜像服务、DaoCloud 镜像市场、阿里云镜像库等。
Docker 分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。
Docker CE 分为 stable, test, 和 nightly 三个更新频道。每六个月发布一个 stable 版本
(18.09, 19.03, 19.09…)。
官网安装指南
/etc/docker/daemon.json
{
"registry-mirrors": [
"https://registry.docker-cn.com"
]
}
之后重新启动服务
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
在命令行执行 ocker info
,如果从结果中看到了如下内容,说明配置成功。
Registry Mirrors:
https://registry.docker-cn.com/
pull命令
docker pull [选项] [Docker Registry地址[:端口号]/]仓库名[:标签]
docker search
docker image ls
列表包含了 仓库名 、 标签 、 镜像 ID 、创建时间 以及 所占用的空间 。
镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签。
$ docker image rm [选项] <镜像1> [<镜像2> ...]
<镜像> 可以是 镜像短 ID 、 镜像长 ID 、 镜像名 或者 镜像摘要 。
Untagged 只是取消标签,Deleted 才是真正的删除。
因为容器和镜像存在依赖关系,所以需要先删除容器再删除镜像。
比如,我们需要删除所有仓库名为 redis 的镜像:
$ docker image rm $(docker image ls -q redis)
或者删除所有在 mongo:3.2 之前的镜像:
$ docker image rm $(docker image ls -q -f before=mongo:3.2)
commit 命令可以将正在运行的容器保存为镜像
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
不推荐用 commit 来定制镜像,commit 意味着对镜像的操作都是黑箱操作,除了制作人,不知道执行了哪些命令。同时因为镜像分层存储的机制,上一层删除的东西并不会丢失,导致整个镜像臃肿。
适用于被入侵后保存环境。
存储每一层修改、安装、构建、操作命令的脚本。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
指定以哪个镜像为基础,必须为第一条指令。
官方镜像大致分为三类:
有两种格式:
RUN echo '
Hello, Docker!
' > /usr/share/nginx/html/index.html
注意 Dockerfile 中每一个指令会创建一层,有最大层数的限制。所以正确写法应该是:
FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
同时注意最后最好清理工作。
还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。
根据 Dockerfile 构建镜像,使用 docker build
docker build [选项] <上下文路径/URL/->
例如:$ docker build -t nginx:v3 .
注意最后这个点 .
是上下文路径,指定上传到 docker 引擎的文件路径。
docker run
还支持从 git repo 和 压缩包进行构建
格式:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
和 RUN
指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。
将主机/www/runoob目录拷贝到容器96f7f14e99ab中,目录重命名为www。
docker cp /www/runoob 96f7f14e99ab:/www
注意 COPY
只复制目录内的内容而不复制目录本身
效果同 copy
,原路径可以是 url,但不支持解压,并不常用
指定默认的容器主进程的启动命令的
格式:
对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
所以将 cmd 写为:CMD service nginx start
,容器执行后就立即退出了
正确的写法,直接执行 nginx 可执行文件,并要求以前台形式运行:
CMD ["nginx", "-g", "daemon off;"]
运行结果同 cmd 。
在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
格式:
格式:ARG <参数名>[=<默认值>]
构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
将数据库文件保存于卷中,保证容器存储层的无状态化
格式:EXPOSE <端口1> [<端口2>...]
指明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务
格式:WORKDIR <工作目录路径>
改变以后各层工作目录的位置,分层存储特性
格式:USER <用户名>
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。 WORKDIR 是改变工作目录, USER 则是改变之后层的执行 RUN , CMD 以及 ENTRYPOINT 这类命令的身份。
格式:ONBUILD <其它指令>
ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN , COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态( stopped )的容器重新启动。
docker run
列如:$ docker run -t -i ubuntu:14.04 /bin/bash
其中, -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。
docker container start [container ID or NAMES]
添加参数 -d
让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下
使用 docker container stop
来终止一个运行中的容器
此外,当 Docker 容器中指定的应用终结时,容器也自动终止.
查看终止状态的容器:docker container ls -a
docker attach
和 docker exec
,推荐使用 docker exec
,在 stdin 中 exit,不会导致容器停止。
如果要导出本地某个容器,可以使用 docker export 命令
可以使用 docker import 从容器快照文件中再导入为镜像。
用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 dockerimport 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。
docker container rm
例如:
$ docker container rm trusting_newton
trusting_newton
如果要删除一个运行中的容器,可以添加 -f
参数。Docker 会发送 SIGKILL 信号给容器。
$ docker container prune
复现 2018 安恒杯 8 月赛 Web2
一个能运行 php 的 linux 环境
已经打包放到 gayhub:https://github.com/jlkl/CTF_Challenge/tree/master/anheng_sep_web2
首次编写的 Dockerfile 先安装 mysql 再安装 php,这样的话会卡在设置 mysql 数据库密码,没有交互环境。参考柠檬师傅的 docker,先安装 php 再安装 mysql 就可以了,我也不清楚是怎么回事 TAT
写了一个 shell 脚本 start.sh
,环境依赖安装好之后用于启动服务,但是启动容器的时候发现这个脚本一直没有运行。后来发现是因为启动容器的时候设置了执行的进程为 /bi/bash
,启动命令:$ sudo docker run -it -p 23333:80 anheng_sep_web2:v3 /bin/bash
。仔细查看对 CMD 的解释,是指定容器启动时的命令,这里设置为/bin/bash
,自然无法运行脚本了。
另外,编写 shell 脚本最好在 linux 环境下,windows 编写会导致格式变成 dos
,报错。可以在 vi 下使用 :set ff=unix
解决。