Docker从0到1

0x00 写在前面

安装docker
安装docker-compose
docker简介

0x01 Docker常用命令

以下命令均在root下
启动docker

1
service docker start

查看当前镜像列表

1
docker images

导入镜像

1
2
3
4
docker import [options] file|URL|- [REPOSITORY[:TAG]]
例:
docker import music.tar music:centos
导入以前导出的用Dockerfile生成的镜像,然后修改它的REPOSITORY为music,TAG为centos

导出镜像

1
2
3
docker export [OPTIONS] CONTAINER
例:
docker export -o test-mysql.tar <容器 ID>

进入镜像的终端

1
2
3
4
5
6
7
8
docker run -t -i music:centos /bin/bash

退出: exit
参数说明:
-i: 交互式操作
-t: 终端
music:centos: 镜像
/bin/bash: 交互式shell

网络端口映射

1
2
3
4
docker run -d -p 1001:80 music:centos /run.sh

-d: 后台运行
-p: 指定端口 宿主机端口:容器端口

删除容器进程

1
docker rm -f ID

查看所有容器

1
docker ps -a

查看正在运行的容器

1
docker ps

docker停止一个容器

1
docker stop 容器ID

开启以关闭容器

1
docker start 容器ID

删除镜像

1
docker rmi 镜像ID

使用dockerfile

1
2
3
4
docker build . -t test-mysql
docker build为创建镜像命令
名称为test-mysql
'.'表示当前目录即Dockerfile文件所在的目录

进入一个以开启的容器

1
docker exec -it 容器ID /bin/bash

从宿主机复制文件到容器

1
docker cp 本地文件 容器ID:路径
1
2
3
4
5
6
7
8
9
10
11
docker search [keywords] 搜寻与keywords相关的镜像
docker images 查看本地的image镜像
docker pull [image] 拉取镜像
docker run -d -p [host port]:[docker port] [image] 新建一个docker容器,并映射端口号
docker ps -a 查看运行中的docker容器
docker exec -it [container id] bash 进入一个docker容器
docker cp [本地路径] [container id]:[container 路径] 拷贝本地文件到docker
docker start [container id] 启动一个docker容器
docker stop [container id] 停止一个docker容器
docker rm [container id] 删除一个docker容器(需要先停止容器才能删除容器)
docker rmi [image id] 删除一个docker镜像(需要删除镜像对应的容器才能删除镜像)

0x02 实例

下面以一道ctf为例
搜索环境镜像

1
docker search lamp

拉取环境需要的镜像

1
docker pull tutum/lamp

用镜像创建容器

1
2
3
root@test:~# docker run -d -p 8888:80 tutum/lamp
cb0bc63b1ebbec1258c9eb0c97415a0ccf6a323a60cddcc361006f59f77a3c2e
这一串数字为容器ID

上传本地文件到容器

1
2
docker cp /home/test/2019JNUCTF/Web/easyupload/ cb0bc63b1ebbec1258c9eb:/var/www/html
容器ID可以不用写全,只写前面几位也是可以的

访问 ip:8888/easyupload 成功。

0x03 Dockerfile

我们可以把构建一道题目的过程分为以下具体三步。

  1. 指定具体要使用的镜像
  2. 启动镜像,构建一个容器
  3. 移入相关的源码,构建容器里面的环境配置
    在上面的实例中,我们第三步里面需要进行的操作只有把源码移入/var/www/html文件夹里面而已,但如果环境配置较为复杂,比如需要构建数据库,安装各种插件等,第三步需要的时间就太长了。如果我们改变下上面的步骤。变成:
  4. 指定使用的镜像
  5. 配置相关的环境,移入相关的代码
  6. 根据第二步的内容,把这些操作以类似于代码,程序的模式写入一个模板,让Docker根据这个模板来生成新的镜像
  7. 根据这个新的镜像来生成新的容器
    如果是这么操作的话,带来的好处就是可以方便的构造出一个针对性的镜像。配置题目的时候,我们只需要根据这个我们创作的模板生成特制的镜像,直接按照这个镜像就可以直接生成环境了。这个需要的模板就是Dockerfile
    Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。
    docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile
    Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,# 为 Dockerfile 中的注释。
    Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUNCMDFROMEXPOSEENV等指令。

常用指令
FROM: 指定基础镜像,必须为第一个命令

1
2
3
4
5
6
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
例:
FROM php:5.6
tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像

MAINTAINER: 维护者信息

1
2
3
4
5
MAINTAINER <name>

例:
MAINTAINER zhangsan
MAINTAINER 123@163.com

RUN: 构建镜像时执行的命令

1
2
3
4
5
6
RUN用于在镜像容器中执行命令,其有以下两种命令执行方式:
shell执行
RUN <command>
exec执行
RUN ["executable", "paraml", "param2"]
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache

ADD: 将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget

1
2
3
4
5
6
ADD <src> ... <dest>
ADD ["<src>", ... "<dest>"] 用于支持包含空格的路径
例:
ADD hom* /mydir/ # 添加所有以 hom 开头的文件
ADD hom?.txt /mydir/ # ?替代一个单字符,例如,home.txt
ADD test mydir/ # 添加test到mydir

COPY: 功能类似ADD,但是不会自动解压文件,也不能访问网络资源
CMD: 构建容器后调用,也就是在容器启动时才进行调用

1
2
3
4
5
6
7
8
CMD ["executable","param1","param2"] # 执行可执行文件,优先
CMD ["param1","param2"] # 设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数
CMD command param1 param2 # 执行shell内部命令

CMD echo "This is a test." | wc -
CMD ["/usr/bin/wc","--help"]

CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。

ENTRYPOINT: 配置容器,使其可执行化。配合CMD可省去 application,只使用参数

1
2
3
ENTRYPOINT ["executable","param1","param2"]  # 可执行文件,优先
ENTRYPOINT command param1 param2 # shell内部命令
ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。

LABEL: 用于为镜像添加元数据

1
2
3
LABEL <key>=<value> <key>=<value> ...
LABEL version="1.0" description="this is a test" by="ca5tle"
使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。

ENV: 设置环境变量

1
2
ENV <key> <value>  # <key>之后的所有内容均会被视为其<value>的组成部分,因此一次只能设置一个变量
ENV <key>=<value> ... # 可以设置多个变量,每个变量为一个<key>=<value>的键值对,如果<key>中包含空格,可以使用 \ 来进行转义,也可以用""表示,另外 \ 也可以用于续行

EXPOSE: 指定外界交互的端口

1
2
3
EXPOSE <port> [<port>...]

EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来映射端口

VOLUME: 用于指定持久化目录

1
2
3
4
5
6
7
VOLUME ["/path/to/dir"]
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
1. 卷可以容器间共享和重用
2. 容器并不一定要和其他容器共享卷
3. 修改卷后会立即生效
4. 对卷的修改不会对镜像产生影响
5. 卷会一直存在,直到没有任何容器在使用它

USER: 指定运行容器时的用户名或UID,后续的RUN也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或者是两者组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户

1
2
3
4
5
6
7
USER user
USER user:group
USER uidU
SER uid:gid
USER user:gid
USER uid:group
使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。

ARG: 用于指定传递给构建运行时的变量

1
ARG <name>

ONBUILD: 用于设置镜像触发器

1
2
3
4
ONBUILD [INSTRUCTION]
例:
ONBUILD ADD . /app/src
当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发