docker learn 1 镜像

docker相关概念

镜像 image

对于linux系统来讲,在启动的时候会挂在root文件系统(filesystem)为用户空间提供支持,docker image就相当于一个root文件系统,docker没有自己的硬件虚拟化。严格来讲,镜像不是一个具体的文件(like iso文件),而是由多层文件系统联合。

镜像在构建(build)的时候,会一层一层的进行构建,(通俗来讲,每一个命令即是一层),前一层是后一层的基础,每一层的改变会永远跟随着镜像,就像git一样。因此,在构建镜像的时候要做到每一层只包含该层所需要的东西,而不要加入一些额外的东西,任何额外的东西需要在构建结束时进行清理。

容器 container

镜像和容器的关系就好像时类和实例一样,在构建完成一个镜像时,就好像写完了一个类,而后还需要进行实例化出一个容器来。每一个容器拥有自己独立的文件系统,互相隔离,就好像是一个独立的操作系统,但是容器的实质是一个进程。每一个容器运行时以其镜像作为基础层,并在其上创建自己的存储层。

docker不是虚拟机,容器就是一个进程,所以容器内的所有应用全部应在前台执行。

docker 安装(ubuntu 20.04)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#1. 卸载旧的版本(如果有的话)
apt-get remove docker docker-engine docker.io containerd runc
#2. 设置apt的库
apt-get update
apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
#3. 安装docker
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io
#4. 验证安装成功
docker run hello-world

使用镜像

获取镜像

获取镜像的命令格式为:

1
2
3
pull [option] [address:port/]仓库名[:tag]
#eg:
docker pull ubuntu:20.04

默认地址为docker hub,在默认为docker hub时,默认仓库为library即官方的仓库。

列出镜像

1
docker image ls

保存镜像

1
docker save ubuntu:20.04 | gzip > ubuntu-latest.tar.gz

加载镜像

1
docker load -i ubuntu-latest.tar.gz

删除镜像

1
docker image rm [选项] <镜像1> [<镜像2> ...]

运行镜像

1
2
3
4
5
6
7
8
#在某一个镜像中执行某个命令
docker run [option] image [command] [agrs]
#eg 在ubuntu:20.04这个镜像中执行 bash命令, it指明进行交互 rm指明退出容器时销毁他,即终止命令
docker run -it --rm ubuntu:20.04 bash
#eg
docker run --name webserver -d -p 80:80 ubuntu:20.04
#下面为docker run的一些参数
--name

镜像构建 build

镜像构建是指从零或者一个镜像开始通过不同的命令构建出一个自定义的镜像。

镜像构建有两种方式,docker commit 和 docker build,前者由于其混乱性不推荐使用,这里只说docker build

docker build 依赖于Dockerfile,当然可以使用 -f 命令指定文件。镜像的构建就是在定制每一层所需要添加的配置和文件,Dockerfile表明了每一层构建的具体步骤,使得构建变得透明化,轻量化,其内包含一条一套的指令,每一条指令构建一层,所以一条指令描述该层该如何构建,

Dockerfile 语法

  • FROM

    1
    2
    3
    #表明从哪一个镜像作为基础进行构建
    FROM [image]
    #scratch,一个特殊的image,空白image。
  • RUN

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #执行一条命令。命令可以当作shell编程来写。
    RUN <command>
    #镜像在构建的时候,每一个RUN语句都会构建一层,所以RUN命令的一个准则就是将所有<command>写为一行并负责清楚构建中产生的多余的东西,如下示例。
    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
    #编写Dockerfile的时候要记住是在描述一层,所表达的信息不要多也不要少。
    #docker在执行Dockerfile脚本的时候,每一层都会以上一层实例化一个容器temp,在此容器temp中执行RUN内容,之后将执行完成的结构保存,删除容器temp。
  • COPY

docker在运行的时候分为docker引擎和docker客户端,就好像时C/S一样,我们使用docker执行各种命令其结果就好像时对一个动态网页进行操作一样,都会将操作数据,操作本身传递到服务器端,docker引擎获得客户端的数据按照客户端的指令进行构建等操作,这就要求docker引擎需要获得客户端的文件,这就引出了上下文的概念.上下文只认可相对路径。该相对路径不是相对于Dockerfile,因为Dockerfile可以通过 -f 进行指定,该相对路径就是当前路径。

COPY命令即将上下文的数据(本物理机的)交给docker引擎进行构建所用(复制到正在构建层的目标位置).COPY保留源文件的源信息(权限,时间等信息)

1
2
3
#source为相对于上下文路径的相对路径,des可以是绝对路径,或者相对于WORKDIR的相对目录
COPY <source> <des>
COPY ["<source1>","<source2>",........"<des>"]
  • ADD

ADD相对于COPY:

  1. ADD会更改权限为600
  2. ADD会自动解压文件
  3. 一般不使用ADD
  • CMD

相较于RUN,CMD是用于指定默认的容器主进程的启动命令。docker run命令中,CMD在image之后,其省缺值为bash

1
2
3
4
5
6
7
8
9
CMD <command>
CMD ["可执行文件", "参数1", "参数2"...]
#在指定了 ENTRYPOINT指令之后。用CMD指定参数
CMD ["参数1","参数2".....]

#错误示例 docker不是虚拟机,容器相当于一个前台进程,CMD需要指定一个进程在前台执行。
CMD service nginx start
#更正
CMD ["nginx", "-g", "daemon off;"]
  • ENTRYPOINT

在指定了ENTRYPOINT之后,CMD指令中的内容作为ENTRYPONIT指令的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#比如一个Dockerfile这么写
FROM ubuntu:20.04
RUN apt-get update && apt-get -y install inetutils-ping && rm -rf /var/lib/apt/lists/*
CMD ["ping", "cuimouren.cn"]
#构建镜像
docker build -t myping .
#执行镜像 会一直执行ping命令
docker run myping
#如果想要使用 -c 指定次数的话你可能想要这样。下面这条命令是错误的,因为镜像后面的指令会当作在镜像中执行的命令,-c 10显然不是命令
docker run myping -c 10
#需要使用ENPRYPOINT来重写
FROM ubuntu:20.04
RUN apt-get update && apt-get -y install inetutils-ping && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["ping", "cuimouren.cn"]
#然后, 下面这条命令中的 -c 1当作ENTRYPOINT的参数来执行
docker run myping -c 1
#现在就可以更加理解docker容器是一个进程的概念了
  • ENV

设置环境变量,该环境变量可以给RUN指令使用,也可以给运行时的应用使用。

1
2
3
4
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>
#eg:
ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"
  • ARG

和ENV一样,区别如下:

  1. ARG可以在docker build 时通过 –build-arg <参数名>=<值> 来指定
  2. ARG指定的环境变量在容器运行时时不存在的,即不可用。
  • VOLUME

定义匿名卷,容器运行时应尽量保持不对容器的存储层进行写操作。对于数据库这些西药保存动态数据的应用,其数据库文件应该保存在卷中。使用VOLUME将某些目录挂载为匿名卷。

1
2
3
4
5
6
COLUME <路径>
COLUME ["<路径1>","<路径2>"......]
#eg: 将data挂载为匿名卷 任何想容器中 /data写入的操作都将直接作用于宿主机的该匿名卷
VOLUME /data
#也可以在docker run命令中指定挂载卷
docker run -d -v mydata:/data #将 、/data 目录挂载到mydata下。
  • EXPOSE

生命容器打算使用的端口号

1
2
EXPOSE <端口号>
EXPOSE [<端口号1> <端口号2> ......]
  • WORKDIR

指定各层的当前工作目录

1
2
#指定目录不存在的话会自动创建
WORKDIR <工作目录路径>
  • USER

指定用户

1
2
##指定用户名不存在的话 不 会自动创建
USER <用户名>
  • HEALTHCHECK

健康检查,用于检查容器的运行情况。和 CMD , ENTRYPOINT 一样, HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。

1
2
3
4
5
6
7
8
9
10
11
12
HEALTHCHECK [选项] CMD <命令>
#如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK NONE
#选项
--interval=<间隔> :两次健康检查的间隔,默认为 30 秒;
--timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
--retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3 次。
#示例 在 HEALTHCHECK [选项] CMD 后面的命令,格式和 ENTRYPOINT 一样
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
  • ONBUILD

它后面跟的是其它指令,比如 RUN , COPY 等,而这些指令, 在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

1
ONBUILD <其它指令>