docker总结

什么是docker

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroupnamespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 版本开始,则进一步演进为使用 runCcontainerd

cgroup用来资源管控

  • linux Namespace 是linux kernel 提供的资源隔离方案
    • 系统可以为进程提供不同的Namespace
    • 保证不同的Namespace资源独立分配、进程彼此隔离

隔离性:

  • pid namespace
    • 不同的用户进程通过pid namespace 隔离开,且不同的namespace可以有相同的pid
    • 有了pid namespace,每个namespace中的pid都能相互隔离
  • net namespace
    • 网络隔离是通过net namespace 实现的,每个net namespace 有独立的 network address、IP address、routing tables、/proc/net 目录
    • Docker 默认使用 veth的·1方式将container 中的虚拟网卡同host上的一个dockers bridge: docker() 连接在一起
  • ipc namespace
    • container中的进程交互还是采用linux中常见的 进程交互方式(interprocess communication -IPC)。包括常见的信号量、消息队列和共享内存
    • container的进程间交互实际上还是host上具有相同Pid namespace 中的进程交互,因此需要在IPC资源申请时加入namespace 的信息 - 每个IPC子啊云有一个唯一的32位 ID

容器镜像本身是一个tar包

基本概念

  • 镜像 image

Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。

镜像构建时,会一层一层的构建,前一层是后一层的基础。

  • 容器 container

镜像(image)和容器(container)的关系,就像是面向对象程序设计中的 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

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

因此,容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间、甚至自己的用户ID空间。

使用镜像

获取镜像

从docker中获取镜像的命令是 docker pull

1
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

eg:

1
docker pull ubuntu:18.04

运行

1
docker run -it ubuntu:18.04 bash
  • it :这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。

退出

1
exit

列举镜像

1
docker image ls

删除镜像

1
docker image rm [选项] <镜像1> [<镜像2> ...] (ID 或者是镜像名)
1
2
3
4
5
6
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 0584b3d2cf6d 3 weeks ago 196.5 MB
redis alpine 501ad78535f0 3 weeks ago 21.03 MB
docker latest cf693ec9b5c7 3 weeks ago 105.1 MB
nginx latest e43d811ce2f4 5 weeks ago 181.5 MB

我们可以使用长 ID 来删除,但也可也使用短 ID 来删除,一般来说只需要输入前三位即可

1
2
3
4
5
6
7
8
9
$ docker image rm 501
Untagged: redis:alpine
Untagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86d
Deleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7
Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b
Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23
Deleted: sha256:127227698ad74a5846ff5153475e03439d96d4b1c7f2a449c7a826ef74a2d2fa
Deleted: sha256:1333ecc582459bac54e1437335c0816bc17634e131ea0cc48daa27d32c75eab3
Deleted: sha256:4fc455b921edf9c4aea207c51ab39b10b06540c8b4825ba57b3feed1668fa7c7
1
docker run --name bb -d busybox

使用dockerfile 定制镜像

  • 使用 FROM 指定基础镜像

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。

Docker Hub 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginxredismongomysqlhttpdphptomcat 等。如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntudebiancentosfedoraalpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

1
FROM scratch
  • 使用RUN 执行命令

RUN指令在定制镜像是是最常用的指令之一,其格式有两种

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

    1
    RUN echo '<h1>hello,Docker!<h1>' > a.log
  2. exec格式,RUN ["可执行文件","arg-1","arg-2"],这会比较像函数调用的格式

注意: Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。所以,要注意RUN的使用,写的时候尽量写一行,之间用 && 连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM debian:stretch

RUN set -x; buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.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

构建镜像

1
docker build [选项] <上下文路径/URL/->

在Dockerfile 执行

1
2
3
4
5
6
7
8
9
 $ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 9cdc27646c7b
---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c

其中的 . 表示当前目录

1
COPY ./package.json /app/

这表示 复制上下文目录下的 package.json

实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用 -f ../Dockerfile.php 参数指定某个文件作为 Dockerfile

add | copy

copy 用于将上下文目录中的源文件/目录复制到新一层的镜像的目标位置中

1
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
1
2
3
4
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/

ADD 可以自动解压缩 压缩文件,压缩的格式为 gzip,bzip2以及XZ。ADD指令会自动解压缩这个压缩文件到目标目录中

1
2
FROM scratch
ADD buntu-xenial-core-cloudimg-amd64-root.tar.gz /

在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。

建议:

  • 单纯的复制场景,使用 COPY
  • 需要自动解压缩的场景,使用 ADD
1
2
3
4
ADD --chown=55:mygroup files* /mydir/
ADD --chown=bin files* /mydir/
ADD --chown=1 files* /mydir/
ADD --chown=10:11 files* /mydir/

其他docker build的用法

直接使用git rep
1
2
3
4
5
# $env:DOCKER_BUILDKIT=0
# export DOCKER_BUILDKIT=0

docker build -t hello-world https://github.com/docker-library/hello-world.git#master:amd64/hello-world
....
使用指定的压缩包
1
docker build http://server/context.tar.gz

Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。

从标准输入中读取 Dockerfile | 压缩包 进行构建
1
cat Dockerfile | docker build -
1
docker build - < context.tar.gz

CMD容器启动

之前有介绍过,docker不是虚拟机,容器就是进程,既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD指令就是用于指定默认的容器主进程启动的命令

CMD 指令的格式和 RUN 相似,也是两种格式:

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

错误的写法

1
CMD service nginx start

然后发现容器执行后就立即退出了。甚至在容器内去使用 systemctl 命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。

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

而使用 service nginx start 命令,则是希望 upstart 来以后台守护进程形式启动 nginx 服务。而刚才说了 CMD service nginx start 会被理解为 CMD [ "sh", "-c", "service nginx start"],因此主进程实际上是 sh。那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。

正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行。比如:

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

ENTRYPOINT 入口点

ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。

ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:

1
<ENTRYPOINT> "<CMD>"

场景一:让镜像变成像变量的命令一样使用

1
2
3
4
5
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ]
1
2
3
docker run -t myip

docker run myip -i 等价于 docker run myip curl -s "http://myip.ipip.net" -i
1
2
3
4
5
6
7
8
9
10
11
12
13
docker run myip -i
HTTP/1.1 200 OK
Connection: close
Content-Length: 68
Accept-Ranges: bytes
Content-Type: text/plain; charset=utf-8
Date: Mon, 20 Nov 2023 13:57:24 GMT
Server: WAF
X-Cache: BYPASS
X-Request-Id: 3c3017cc86f1ca3499256cb036918c3b

当前 IP:59.46.213.219 来自于:中国 辽宁 大连 电信

ENV 设置环境变量

格式有两种:

  • ENV <key> <value>
  • ENV <key1>=<value1> <key2>=<value2>...
1
2
3
ENV VERSION=1.0

ENV TYPE String
1
RUN set -x;ehco ${TYPE}&& ehco${VERSION}

ARG 构建参数

构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。

Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。

灵活的使用 ARG 指令,能够在不修改 Dockerfile 的情况下,构建出不同的镜像。

ARG 指令有生效范围,如果在 FROM 指令之前指定,那么只能用于 FROM 指令中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ARG DOCKER_USERNAME=library

FROM ${DOCKER_USERNAME}/alpine

# 在FROM 之后使用变量,必须在每个阶段分别指定
ARG DOCKER_USERNAME=library

RUN set -x ; echo ${DOCKER_USERNAME}

FROM ${DOCKER_USERNAME}/alpine

# 在FROM 之后使用变量,必须在每个阶段分别指定
ARG DOCKER_USERNAME=library

RUN set -x ; echo ${DOCKER_USERNAME}

VOLUME 设置匿名卷

EXPOSE暴露端口

1
EXPOSE <hostPort>:<containerPort>

expose用来声明容器运行时提供服务的端口,这只是一个声明,在容器运行时,不会因为这个声明而去开启这个端口的服务。

用处

  • 便于镜像的使用者理解这个服务镜像的守护端口号是什么
  • 使用 docker run -p 时,会自动随机映射EXPOSE的端口号

WORKDIR 指定工作目录

1
2
3
4
5
WORKDIR /a
WORKDIR b
WORKDIR c

RUN pwd

/a/b/c

HEALTHCHECK健康检查

1
2
3
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
1
docker build -t healthcheck:1 .
1
docker run --name web -p 80:80 -d  healthcheck:1 

ONBUILD 为他人作嫁衣裳

构建基础镜像的时候,这ONBUILD三行并不会被执行

1
2
3
4
5
6
7
FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]
1
docker build -t my-node .

前基础镜像的那三行 ONBUILD 会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 npm install,生成应用镜像。

1
FROM my-node

其他

set -eux

RUN set -eux; 是一种在 Dockerfile 中常见的用法,用于设置一些 shell 的行为,尤其是在构建过程中执行的命令。让我们逐步解释这个命令:

  • set -e:这个部分的含义是设置 shell 的执行模式为 “exit immediately if a simple command exits with a non-zero status”,也就是说,如果任何一个命令返回非零状态,那么整个构建过程就会立即失败,终止构建流程。这对于构建过程的可靠性和调试是很有用的,因为它能够确保任何构建阶段的错误都会导致构建过程终止。

  • set -u:这个部分启用了 shell 的 “uninitialized variable check” 模式。如果你尝试使用未定义的变量,shell 会立即退出并报告错误。这可以帮助捕获一些潜在的编程错误,确保你的脚本中没有使用未赋值的变量。

  • set -x:这个部分启用了 shell 的 “verbose mode”,也就是在执行每个命令之前,将该命令及其参数打印到标准错误输出。这对于调试和了解构建过程中究竟执行了哪些命令是非常有用的。

将这三个选项结合在一起,set -eux 提供了一种强大的工具,能够使得构建过程更加健壮,并且在构建过程中发生错误时提供详细的信息以便于调试。当构建过程中的某个步骤失败时,容器构建过程会立即停止,并且输出的信息会显示详细的执行步骤,有助于快速定位问题。

整个命令的样式通常是在 Dockerfile 中的 RUN 指令的开始处使用,以确保在构建过程中的每个命令都遵循这些行为设置。

数据管理

数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UnionFS,可以提供很多有用的特性:

-

`数据卷` 可以在容器之间共享和重用

-

对 `数据卷` 的修改会立马生效

-

对 `数据卷` 的更新,不会影响镜像

-

`数据卷` 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。

  • 创建数据卷
1
$ docker volume create my-vol
  • 查看数据卷
1
2
3
4
$ docker volume ls

DRIVER VOLUME NAME
local my-vol
  • 查看卷的信息
1
2
3
4
5
6
7
8
9
10
11
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
  • 启动一个挂载数据卷的容器
1
2
3
4
5
$ docker run -d -P \
--name web \
# -v my-vol:/usr/share/nginx/html \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx:alpine
  • 删除数据卷的信息
1
$ docker volume rm my-vol

无主的数据卷可能会占据很多空间,要清理请使用以下命令

1
$ docker volume prune

挂载主机目录

挂载一个主机目录作为数据卷
1
docker run -d --name web --mount type=bind,source=D:\dockerVol,target=/usr/shared/nginx/html nginx:alpine
1
docker inspect web

image-20231121104215352

挂载一个主机文件作为数据卷
1
docker run  --rm -it --mount type=bind,source=$home/.bash_history,target=/root/.bash_history ubuntu:18.04 bash

使用网络

Docker:网络模式详解 - Gringer - 博客园 (cnblogs.com)

高级网络配置


docker总结
http://example.com/2023/11/20/docker总结/
作者
Forrest
发布于
2023年11月20日
许可协议