本文最后更新于:星期二, 六月 16日 2020, 2:35 下午

Docker是个好东西,希望大家都能会

0x00 前言

最近由于队伍要招新,需要准备一些练习题放到平台上,队里大哥都跟我说搞web题放自己公网服务器上最好还是整个Docker,防止被什么意料之外的人整个梭穿。以前都没有整过,还是得好好学习一下。

学习第一步肯定是得找找相关资料,这里找到一个Wiki,内容蛮充分的,整挺好

https://github.com/yeasy/docker_practice/blob/master/SUMMARY.md

怎么说,Docker其实就是一个容器,类似于虚拟机,你可以在里面运行一个小型的Linux系统,它的各种功能没有什么影响,但是是一个沙箱,与外部完全隔离。

这种东西比较适合拿来出题,因为一般一个服务器上要放很多道题目,这样可以防止一道题被打穿导致其他的题目都被影响。

0x01 安装

安装还是按官方文档来,安装手册给的蛮全,我这里用的是ubuntu16.04

  1. 首先还是升级一下apt,保证它是最新的,国内可以把apt源换一下,阿里云亲测还是丝滑一些
$ cd /etc/apt/
$ cp sources.list sources.list.bak   #备份
$ sudo vim sources.list

然后把下面的阿里云源粘贴到sources.list里去

#deb包

deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse  
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse  
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse  
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse 
 
##测试版源  

deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse  

# 源码  

deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse  
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse  
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse  
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse 
 
##测试版源  

deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse  

# Canonical 合作伙伴和附加

最后

$ apt-get update
$ apt-get upgrade
  1. 然后我们就可以安装Docker了,先安装Docker需要的依赖环境,首先要让apt可以通过HTTPS来使用仓库
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
  1. 添加Docker的offcial GPG key
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  1. 添加Docker repository
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
  1. 安装Docker Engine(Community)
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
  1. 然后就可以选择想要安装的版本了
$ apt-cache madison docker-ce    #列出Docker版本
$ sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io

也可以直接选择安装最新版

$ sudo apt-get install docker-ce
  1. 最后查看docker版本
$ docker -v

0x02 拉取镜像

由于从DockerHub上拉取镜像特别慢,所以我们要配置一下国内的镜像源,这样会快很多

$ vim /etc/docker/daemon.json

在里面写入以下内容

{
  "registry-mirrors": [
                "https://afkemp44.mirror.aliyuncs.com",
                "https://dockerhub.azk8s.cn",
                "https://reg-mirror.qiniu.com"
]
}

之后重启服务

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

因为我想给服务器搭一个lamp的环境,所以我准备拉一个ubuntu16.04的镜像下来

$ docker pull ubuntu:16.04

这样便成功拉取了一个镜像,如果想要搜索一些镜像,可以使用如下命令

$ docker search [关键词]

0x02 常用操作

拉取完镜像之后,就要将它安装在本机上并让他运行:

docker run --name 容器名 -it -d -p 主机端口:容器端口 -v 主机目录:容器目录:ro 镜像TD或镜像名:TAG

# --name 指定容器名,可自定义,不指定自动命名
# -i 以交互模式运行容器
# -t 分配一个伪终端,即命令行,通常组合来使用
# -p 指定映射端口,将主机端口映射到容器内的端口
# -d 后台运行容器
# -v 指定挂载主机目录到容器目录,默认为rw读写模式,ro表示只读

查看正在运行的容器

docker ps -a -q

# -a 查看所有容器(运行中、未运行)
# -q 只查看容器的ID

启动和停止容器

docker start 容器ID或容器名    # 启动容器

docker stop 容器ID或容器名    #停止容器

删除容器

docker rm -f 容器ID或容器名

# -f 表示强制删除

进入正在运行的容器

docker exec -it 容器ID或者容器名 /bin/bash

# 进入正在运行的容器并且开启交互模式终端

# /bin/bash是固有写法,作用是因为docker后台必须运行一个进程,否则容器就会退出,在这里表示启动容器后启动bash。

# 也可以用docker exec在运行中的容器执行命令

拷贝文件

docker cp 主机文件路径 容器ID或容器名:容器路径 # 主机中文件拷贝到容器中

docker cp 容器ID或容器名:容器路径 主机文件路径 # 容器中文件拷贝到主机中

导出容器

docker export $container_id > 容器快照名

导入容器

# docker import命令可以导入容器
cat 容器快照名 | docker import - ctf:1amp5.6    # 导入容器作为镜像

列出镜像

docker images

docker image ls [关键词]    # 列出已有镜像中与关键词相关的镜像

镜像保存/载入

docker load
docker save

#将一个镜像导出为文件,再使用docker load命令将文件导入为一个镜像,会保存该镜像的的所有历史记录。比docker export命令导出的文件大,很好理解,因为会保存镜像的所有历史记录。

将容器直接保存为镜像

docker commit \
    --author [作者名] \
    --message [备注] \
    webserver \   # 容器名
    nginx:v2      # 仓库:tag

删除镜像

docker rmi [仓库:tag]	# 注意如果有容器基于这个镜像,需要先删除容器才能删除镜像

容器与本机的文件传输

docker cp <path> <containerid>:<path>	# 从本机复制文件到容器
docker cp flag e4c6:/	# 从本机复制到容器根目录
docker cp <containerid>:<path> <path> # 从容器复制文件到本机

0x03 使用Dockerfile

使用Dockerfile构建镜像

对于Docker的应用来说,Dockerfile还是一个比较重要且实用的东西了,他可以很方便的共享镜像(主要是网上师傅们的环境都是做成Dockerfile的,不会搞有点难顶)

Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

使用Dockerfile相对来说比较简单,只需要用到一条命令:docker build

docker build有许多种用法:

docker build -t [仓库/tag] .    # 这一命令将当前目录下名为Dockerfile的文件build,并以 . 作为上下文路径

关于上下文(Context)可以参考Wiki里的这一部分

docker build -f [Dockerfile路径] -t [仓库:tag] .
# -f用于指定Dockerfile的路径,默认为当前目录下的Dockerfile

使用Git repo进行构建:

docker build https://github.com/twang2218/gitlab-ce-zh.git#:11.1
#这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /11.1/,然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。

使用给定的tar压缩包构建:

docker build http://server/context.tar.gz
# 如果所给出的 URL 不是个 Git repo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。

从标准输入中读取Dockerfile进行构建:

docker build - < Dockerfile
cat Dockerfile | docker build -
# 如果标准输入传入的是文本文件,则将其视为 Dockerfile,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件 COPY 进镜像之类的事情。

从标准输入中读取上下文压缩包进行构建:

docker build - < context.tar.gz
# 如果发现标准输入的文件格式是 gzip、bzip2 以及 xz 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建

编写Dockerfile

编写Dockerfile有几个常用的命令,我这里主要也只说说这几个

FROM

FROM是一定要的,并且一定要放在第一句,它指定了使用哪个镜像作为最基本的容器使用,例如

FROM ubuntu:16.04

如果本地没有这个镜像它就会去服务器上拉取

RUN

RUN用来执行命令行命令,所以说一个Dockerfile中大部分都是RUN,RUN的格式有两种,一种是shell格式:RUN <command>,一种是exec格式:RUN ["可执行文件", "参数1", "参数2"]

RUN echo "hello, world!"	# 这种是shell格式
RUN ["ls", "-a"]	# 这种是exec格式

但是这里有一个误区,并不是每一个RUN只能对应一条命令行命令来执行,这样反而是不符合规范的,这里贴上Wiki给的解释

之前说过,Dockerfile 中每一个指令都会建立一层,RUN 也不例外。每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。

而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 这是很多初学 Docker 的人常犯的一个错误。

Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。

所以Dockerfile中,RUN的正确写法应该是类似这样

RUN apt-get update \
    && apt-get upgrade -y \
    && apt install apache2 -y \
    && apt install php7.0-mysql php7.0-curl php7.0-json php7.0-cgi php7.0 libapache2-mod-php7.0 -y

COPY

COPY命令用于将本机的文件复制到镜像中,这对一些配置文件很有用,这样你就不需要在建立镜像之后再去一个个改了(特别是在搭CTF环境的时候,把php.inihttpd.conf.htaccess这种文件复制一份会方便很多)

COPY命令的格式为:COPY [--chown=<user>:<group>] <源路径>... <目标路径>,例如

COPY php.ini /etc/php/7.0/cgi	# 这时候php.ini的路径即为build命令中指定的上下文路径

源路径中可以使用通配符,比如

COPY *.php /var/www/html/

还可以使用--chown=<user>:<group>来改变文件所属的用户和所属组

COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/

使用Docker-compose

Docker Compose是个好东西,有时候需要搭建多个容器协同组成的服务的时候,一个docker-compose.yml,搭建起服务来那是又快又省心,官方对Docker Compose的定义是这样的:

Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。

安装

Linux直接从官方Release网站下载即可,或者使用网站上给出的方式下载并安装,比如:

curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

这样也可以安装docker-compose,安装完毕后,运行

docker-compose --version

有回显即安装成功

常用命令

  • -f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。
  • -p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。
  • --x-networking 使用 Docker 的可拔插网络后端特性
  • --x-network-driver DRIVER 指定网络后端的驱动,默认为 bridge
  • --verbose 输出更多调试信息。
  • -v, --version 打印版本并退出。

命令使用说明

build

格式为 docker-compose build [options] [SERVICE...]

构建(重新构建)项目中的服务容器。

服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db。

可以随时在项目目录下运行 docker-compose build 来重新构建服务。

选项包括:

  • --force-rm 删除构建过程中的临时容器。
  • --no-cache 构建镜像过程中不使用 cache(这将加长构建过程)。
  • --pull 始终尝试通过 pull 来获取更新版本的镜像。

config

验证 Compose 文件格式是否正确,若正确则显示配置,若格式错误显示错误原因。

down

此命令将会停止 up 命令所启动的容器,并移除网络

exec

进入指定的容器。

help

获得一个命令的帮助。

images

列出 Compose 文件中包含的镜像。

kill

格式为 docker-compose kill [options] [SERVICE...]

通过发送 SIGKILL 信号来强制停止服务容器。

支持通过 -s 参数来指定发送的信号,例如通过如下指令发送 SIGINT 信号。

$ docker-compose kill -s SIGINT

logs

格式为 docker-compose logs [options] [SERVICE...]

查看服务容器的输出。默认情况下,docker-compose 将对不同的服务输出使用不同的颜色来区分。可以通过 --no-color 来关闭颜色。

该命令在调试问题的时候十分有用。

pause

格式为 docker-compose pause [SERVICE...]

暂停一个服务容器。

port

格式为 docker-compose port [options] SERVICE PRIVATE_PORT

打印某个容器端口所映射的公共端口。

选项:

  • --protocol=proto 指定端口协议,tcp(默认值)或者 udp。
  • --index=index 如果同一服务存在多个容器,指定命令对象容器的序号(默认为 1)。

ps

格式为 docker-compose ps [options] [SERVICE...]

列出项目中目前的所有容器。

选项:

  • -q 只打印容器的 ID 信息。

pull

格式为 docker-compose pull [options] [SERVICE...]

拉取服务依赖的镜像。

选项:

  • --ignore-pull-failures 忽略拉取镜像过程中的错误。

push

推送服务依赖的镜像到 Docker 镜像仓库。

restart

格式为 docker-compose restart [options] [SERVICE...]

重启项目中的服务。

选项:

  • -t, --timeout TIMEOUT 指定重启前停止容器的超时(默认为 10 秒)。

rm

格式为 docker-compose rm [options] [SERVICE...]

删除所有(停止状态的)服务容器。推荐先执行 docker-compose stop 命令来停止容器。

选项:

  • -f, --force 强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。
  • -v 删除容器所挂载的数据卷。

run

格式为 docker-compose run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

在指定服务上执行一个命令。

例如:

$ docker-compose run ubuntu ping docker.com

将会启动一个 ubuntu 服务容器,并执行 ping docker.com 命令。

默认情况下,如果存在关联,则所有关联的服务将会自动被启动,除非这些服务已经在运行中。

该命令类似启动容器后运行指定的命令,相关卷、链接等等都将会按照配置自动创建。

两个不同点:

  • 给定命令将会覆盖原有的自动运行命令;
  • 不会自动创建端口,以避免冲突。

如果不希望自动启动关联的容器,可以使用 --no-deps 选项,例如

$ docker-compose run --no-deps web python manage.py shell

将不会启动 web 容器所关联的其它容器。

选项:

  • -d 后台运行容器。
  • --name NAME 为容器指定一个名字。
  • --entrypoint CMD 覆盖默认的容器启动指令。
  • -e KEY=VAL 设置环境变量值,可多次使用选项来设置多个环境变量。
  • -u, --user="" 指定运行容器的用户名或者 uid。
  • --no-deps 不自动启动关联的服务容器。
  • --rm 运行命令后自动删除容器,d 模式下将忽略。
  • -p, --publish=[] 映射容器端口到本地主机。
  • --service-ports 配置服务端口并映射到本地主机。
  • -T 不分配伪 tty,意味着依赖 tty 的指令将无法运行。

scale

格式为 docker-compose scale [options] [SERVICE=NUM...]

设置指定服务运行的容器个数。

通过 service=num 的参数来设置数量。例如:

$ docker-compose scale web=3 db=2

将启动 3 个容器运行 web 服务,2 个容器运行 db 服务。

一般的,当指定数目多于该服务当前实际运行容器,将新创建并启动容器;反之,将停止容器。

选项:

  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

start

格式为 docker-compose start [SERVICE...]

启动已经存在的服务容器。

stop

格式为 docker-compose stop [options] [SERVICE...]

停止已经处于运行状态的容器,但不删除它。通过 docker-compose start 可以再次启动这些容器。

选项:

  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

top

查看各个服务容器内运行的进程。

unpause

格式为 docker-compose unpause [SERVICE...]

恢复处于暂停状态中的服务。

up

格式为 docker-compose up [options] [SERVICE...]

该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

链接的服务都将会被自动启动,除非已经处于运行状态。

可以说,大部分时候都可以直接通过该命令来启动一个项目。

默认情况,docker-compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。

当通过 Ctrl-C 停止命令时,所有容器将会停止。

如果使用 docker-compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。

默认情况,如果服务容器已经存在,docker-compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker-compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker-compose up --no-deps -d 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。

选项:

  • -d 在后台运行服务容器。
  • --no-color 不使用颜色来区分不同的服务的控制台输出。
  • --no-deps 不启动服务所链接的容器。
  • --force-recreate 强制重新创建容器,不能与 --no-recreate 同时使用。
  • --no-recreate 如果容器已经存在了,则不重新创建,不能与 --force-recreate 同时使用。
  • --no-build 不自动构建缺失的服务镜像。
  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

version

格式为 docker-compose version

打印版本信息。

0x04 Windows10下使用Docker

官网下载Docker,注意要求Window是professional,即专业版。

下载完之后直接安装,Docker的GUI上没有选择安装路径的选项,只能默认安装在C盘,安装完毕之后重启电脑就可以使用Docker了。Docker For Windows也可以在命令行操作,操作方式基本上是一致的。

使用阿里云镜像加速

Docker默认镜像源在国外,拉取比较慢,可以使用阿里云镜像加速。登录阿里云后,搜索“容器镜像服务”,就可以找到对应的服务支持,点击进入后在“镜像加速器”一栏可以看到自己专属的加速地址。

右键点击右下角docker图标 -> setting -> Docker Engine 中在registry-mirrors中添加镜像源即可

修改镜像存储位置

因为C盘实在空间不多,所以要修改一下 docker pull 镜像的存储位置,否则C盘顶不住

右键点击右下角docker图标 -> setting -> Resources,在右边页面找到 Disk image location,直接修改位置即可


本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

HacktheBox实战笔记(一) 上一篇
PE文件格式分析 下一篇