Docker 学习笔记

主要是 gitbook 开源书籍 Docker — 从入门到实践 笔记,也包含了自己的一些见解和坑点记录。

文章目录

    • 学习目标
    • 基础概念
      • 镜像(Image)
      • 容器(Container)
      • 仓库(Register)
        • 仓库服务
    • 安装 Docker
      • 镜像加速器
        • Ubuntu 16.04+、Debian 8+、CentOS 7
          • 检查是否生效
    • 使用镜像
      • 获取镜像
      • 查找镜像
      • 查看镜像
      • 删除镜像
        • Untagged 和 Deleted
        • 配合 docker image ls 命令删除
      • commit 保存镜像
      • 使用 Dockerfile 定制镜像
        • 什么是 Dockerfile
        • FROM 指定基础镜像
        • RUN 执行命令
        • 构建镜像
      • Dockerfile 指令详解
        • COPY 复制文件
        • ADD 更高级的复制文件
        • CMD 容器启动命令
          • 容器和虚拟机的差异
        • ENTRYPOINT 入口点
        • ENV 设置环境变量
        • ARG 构建参数
        • VOLUME 定义匿名卷
        • EXPOSE 声明端口
        • WORKDIR 指定工作目录
        • USER 指定当前用户
        • HEALTHCHECK 健康检查
        • ONBUILD 为他人做嫁衣裳
    • 操作容器
      • 基本概念
      • 启动容器
        • 新建并启动
        • 启动已终止容器
      • 后台运行
      • 终止容器
      • 进入容器
      • 导出和导入容器
        • 导出
        • 导入
      • 删除容器
      • 清理所有处于终止状态的容器
    • 实际利用
      • 需求分析
      • Dockerfile 编写
      • 坑点记录
        • docker 下 mysql 安装顺序
        • Dockerfile CMD 深刻理解

学习目标

这次学习主要达到能用 docker 快速复现赛题和 cve 环境,docker-compose 的坑就只有以后再来填了

  1. 了解 docker 工作流程及主要的命令
  2. 能够编写 dockerfile 快速复现环境

基础概念

镜像(Image)

操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root
文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系
统。

简而言之,可以把镜像理解为虚拟机的快照文件

因为体积大小,镜像并不是像 iso 那样的打包文件,采用了分层存储,前一层是后一层的基础。需要注意的是,删除前一层文件的操作,实际不是真的删除
前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看
到这个文件,但是实际上该文件会一直跟随镜像。

容器(Container)

类似类和实例,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。

仓库(Register)

包含多个仓库( 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

Docker 分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。

Docker CE 分为 stable, test, 和 nightly 三个更新频道。每六个月发布一个 stable 版本
(18.09, 19.03, 19.09…)。

官网安装指南

镜像加速器

  • Docker 官方提供的中国mirror:https://registry.docker-cn.com
  • 七牛云加速:https://kirk-enterprise.github.io/hub-docs/#/user-guide/mirror

Ubuntu 16.04+、Debian 8+、CentOS 7

/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

Untagged 只是取消标签,Deleted 才是真正的删除。

因为容器和镜像存在依赖关系,所以需要先删除容器再删除镜像。

配合 docker image ls 命令删除

比如,我们需要删除所有仓库名为 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 保存镜像

commit 命令可以将正在运行的容器保存为镜像

docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

不推荐用 commit 来定制镜像,commit 意味着对镜像的操作都是黑箱操作,除了制作人,不知道执行了哪些命令。同时因为镜像分层存储的机制,上一层删除的东西并不会丢失,导致整个镜像臃肿。

适用于被入侵后保存环境。

使用 Dockerfile 定制镜像

什么是 Dockerfile

存储每一层修改、安装、构建、操作命令的脚本。

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

FROM 指定基础镜像

指定以哪个镜像为基础,必须为第一条指令。

官方镜像大致分为三类:

  1. 直接拿来使用的服务类的镜像,如nginx 、 redis 、 mongo 、 mysql 、 httpd 、 php 、 tomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node 、 openjdk 、 python 、 ruby 、 golang 等。
  2. 基础的操作系统镜像,如ubuntu 、 debian 、 centos 、 fedora 、 alpine 等
  3. 除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

RUN 执行命令

有两种格式:

  1. shell 格式: RUN <命令>,就像直接在命令行中输入的命令一样。

RUN echo '

Hello, Docker!

' > /usr/share/nginx/html/index.html

  1. exec 格式: RUN [“可执行文件”, “参数1”, “参数2”] ,这更像是函数调用中的格式。

注意 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 和 压缩包进行构建

Dockerfile 指令详解

COPY 复制文件

格式:

  • COPY <源路径>... <目标路径>
  • COPY ["<源路径1>",... "<目标路径>"]

RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。

将主机/www/runoob目录拷贝到容器96f7f14e99ab中,目录重命名为www。

docker cp /www/runoob 96f7f14e99ab:/www

注意 COPY 只复制目录内的内容而不复制目录本身

ADD 更高级的复制文件

效果同 copy,原路径可以是 url,但不支持解压,并不常用

CMD 容器启动命令

指定默认的容器主进程的启动命令的

格式:

  • shell 格式: CMD <命令>
  • exec 格式: CMD [“可执行文件”, “参数1”, “参数2”…]
  • 参数列表格式: CMD [“参数1”, “参数2”…] 。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
容器和虚拟机的差异

对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。

所以将 cmd 写为:CMD service nginx start,容器执行后就立即退出了

正确的写法,直接执行 nginx 可执行文件,并要求以前台形式运行:

CMD ["nginx", "-g", "daemon off;"]

ENTRYPOINT 入口点

运行结果同 cmd 。

在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。

ENV 设置环境变量

格式:

  • ENV
  • ENV = =…

ARG 构建参数

格式:ARG <参数名>[=<默认值>]

构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。

VOLUME 定义匿名卷

格式:

  • VOLUME ["<路径1>", "<路径2>"...]
  • VOLUME <路径>

将数据库文件保存于卷中,保证容器存储层的无状态化

EXPOSE 声明端口

格式:EXPOSE <端口1> [<端口2>...]

指明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务

WORKDIR 指定工作目录

格式:WORKDIR <工作目录路径>

改变以后各层工作目录的位置,分层存储特性

USER 指定当前用户

格式:USER <用户名>

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。 WORKDIR 是改变工作目录, USER 则是改变之后层的执行 RUN , CMD 以及 ENTRYPOINT 这类命令的身份。

HEALTHCHECK 健康检查

  • HEALTHCHECK [选项] CMD <命令> :设置检查容器健康状况的命令
  • HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

ONBUILD 为他人做嫁衣裳

格式: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 attachdocker 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 环境

Dockerfile 编写

已经打包放到 gayhub:https://github.com/jlkl/CTF_Challenge/tree/master/anheng_sep_web2

坑点记录

docker 下 mysql 安装顺序

首次编写的 Dockerfile 先安装 mysql 再安装 php,这样的话会卡在设置 mysql 数据库密码,没有交互环境。参考柠檬师傅的 docker,先安装 php 再安装 mysql 就可以了,我也不清楚是怎么回事 TAT

Dockerfile CMD 深刻理解

写了一个 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 解决。

你可能感兴趣的:(Web)