Docker

Docker 是一个开源的应用容器引擎,基于Go 语言 并遵从 Apache2.0 协议开源。

Docker 官网https://www.docker.com

Github Docker 源码:https://github.com/docker/docker-ce

概念

  1. 镜像 Image

      镜像类似于虚拟机镜像,面向docker的只读模板,包含了文件系统
    
  2. 容器 Container

       容器类似一个轻量级的沙箱,docker利用它运行隔离应用。容器是从镜像创建的应用实例,容器之间相互隔离。镜像本身是只读的,容器从镜像启动时,docker会在镜像最上层创建一个可写层,镜像保持不变。
    
  3. 仓库 Repository

        类似于代码仓库,存放镜像文件的敌方。注册服务器(Registry)是存放仓库的地方。创建仓库后可以通过push命令上传到公有或私有仓库。
    

安装

CentOS 7

yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache
yum -y install docker-ce docker-ce-cli containerd.io
sudo systemctl restart docker
docker run hello-world

CentOS 8

dnf install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
dnf makecache
dnf -y install docker-ce docker-ce-cli containerd.io
sudo systemctl restart docker
docker run hello-world

官方自动安装脚本

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
sudo systemctl restart docker
docker run hello-world

常见安装错误

- problem with installed package podman

sudo dnf remove -y podman # 卸载podman

- problem with installed package buildah

yum install --allowerasing docker-ce

国内镜像加速

获取镜像加速地址

配置镜像加速地址

mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["xxxxxxxxxxx"]
}
EOF
sudo systemctl daemon-reload && sudo systemctl restart docker

注: 请使用镜像地址替换上文xxxxxxxxxxx

镜像命令

获取镜像 pull

docker pull NAME[:TAG]  
docker pull registry.hub.docker.com/NAME:TAG  # 上面是这条命令的缩写

如果不指定TAG,默认选择latest 标签。镜像文件由很多层组成。下载记录中,每行的开头代表了各层的ID。层是AUFS(Advanced Union File System)中的概念,是实现增量保存与更新的基础。

查看信息 images

docker images # 列出本机已有的镜像
# REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
#来自于那个仓库   标签信息    镜像的ID号        创建时间       镜像大小
docker tag NAME[:TAG]  NEWNAME[:NEWTAG] # 创建别名
docker inspect <IMAGE ID> # 镜像详细信息
docker inspect -f  {{".Architecture"}} <IMAGE ID> # 删选部分信息
docker history [OPTIONS] IMAGE    # 查看指定镜像的创建历史。
    # --no-trunc : 显示完整的提交记录
    # -H :以可读的格式打印镜像大小和日期

查找镜像 search

docker search [--automated=false] [--no-trunc=false] [-s, --starts=0]  KEYWORD
#               仅显示自动创建的镜像     输出信息不截断显示    仅显示评价星级以上的

删除镜像 rmi

docker rmi [-f] IMAGE [IMAGE...]  

当一个镜像有多个标签,rmi命令只是删除了指定的标签,不影响镜像文件。rmi命令删除镜像时,会先删除所有指向这个镜像的标签,然后才会删除镜像文件本身,注意这个命令必须是 docker rmi <IMAGE ID>,而不是指定名字。当镜像被容器依赖时,不能被删除,除非加了 -f,但是可能造成遗留问题。

创建镜像

基于已有镜像容器创建

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
# OPTIONS 
#    -a, --author=""   作者信息
#    -m, --message=""  提交信息
#    -p, --pause=true  提交时暂停容器运行

流程:

  1. 启动一个镜像
  2. 进行一些修改后退出
  3. 使用commit提交
docker run -ti centos:latest /bin/bash
touch testfile
exit
docker commit <之前的容器ID> # 这条命令会返回新的镜像ID
docker images

基于本地模板导入

  1. openvz下载一个模板 下载地址
  2. 使用命令 cat 下载的模板.tar.gz | docker import - <自定义名字>:<TAG>

基于Dockerfile创建

注意:Docker最多有127层Layer

Dockerfile有四部分

  1. 基础镜像信息
  2. 维护者信息
  3. 镜像操作指令
  4. 容器启动时执行指令
FROM ubuntu #指定基于什么的基础镜像,第一条指令必须是FROM指令
FROM <IMAGE> 
FROM <IMAGE>:<TAG>
# FROM 可以有多个,用以在一个 Dockerfile文件中创建多个镜像

MAINTAINER m_docker_user [email protected] #维护者信息
MAINTAINER <NAME> # 指定维护者信息

# 镜像的操作指令
RUN echo "deb ..."
RUN apt-get update
RUN <COMMAND>
RUN ["EXECUTABLE","PARAM1","PARAM2"]
# 每运行一条run指令,镜像添加新的一层,并提交


CMD /bin/mysql #镜像启动时执行的指令,指定的是运行容器时的操作命令
CMD ["executable","param1","param2"]
CMD <COMMAND> <PARAM1> <PARAM1>
# CMD只能有一条,多条CMD只有最后一条会被执行
# 如果用户指定了运行时的命令,会覆盖掉 CMD 指定的指令

EXPOSE 22 80 443
EXPOSE <PORT> [<PORT>...]
# 指定的暴露的端口号,启动时需要用过 -P ,docker会自动分配一个端口转发到指定端口
# 使用 -p 可以具体指定那个本地端口映射过来

ENV PATH /usr/local/:$PATH
ENV <KEY> <VALUE>  #指定一个环境变量,会被后续RUN使用,并在容器运行时保持
# 也可以通过在docker run ‐‐env key=value 时设置或修改环境变量

ADD <SRC> <DEST>
# 复制 <SRC> 到容器的 <DEST>。
# src可以是Dockerfile所在目录的一个相对路径,也可以是URL,也可以是tar文件(自动解压为目录)

COPY <SRC> <DEST>
# 和ADD命令一样,ADD支持 1.远程服务器通过URL获取资源 2.可以读取tar并自动解压
# COPY更建议用在本地

##---
# COPY指令和ADD指令的唯一区别在于是否支持从远程URL获取资源。
# COPY指令只能从执行docker build所在的主机上读取资源并复制到镜像中。
# ADD指令还支持通过URL从远程服务器读取资源并复制到镜像中。
# 大多数时间应该使用 COPY
##---

ENTRYPOINT ["executable","param1","param2"]
ENTRYPOINT command param1 param2 (shell执行)
# 配置容器启动后执行的命令,只有最后一个可以生效。
# ENTRYPOINT指令指定的命令不能被覆盖,而是将docker run指定的参数当做ENTRYPOINT指定命令的参数。
# CMD指令可以为ENTRYPOINT指令设置默认参数,而且可以被docker run指定的参数覆盖;

VOLUME ["/data"]
# 创建一个可以从本地主机或其他容器挂载的挂载点。
# 共享VOLUME :docker run ‐itd ‐volumes‐from container1 newimage /bin/bash

USER daemon
# 指定运行容器时的用户名或UID,后续的RUN也会使用指定用户

WORKDIR /path/workdir
# 为RUN,CMD,ENTRYPOINY指令配置工作目录
# 可以使用多个改命令,后续参数如果为相对路径,则会基于之前命令给予的路径

ONBUILD [INSTRUCTION]
#此镜像作为其他新创建镜像的基础镜像时执行的操作命令。

编写完Dockerfile之后,通过 docker build 创建镜像

使用 .dockerignore文件让Docker忽略路径下的目录和文件

docker build [OPTIONS] PATH | URL | -

# --squash : 将 Dockerfile 中所有的操作压缩为一层。
# --tag, -t : 镜像的名字及标签,通常 name:tag 或者 name 格式
# -h : 打印帮助信息

存储和载入镜像 save load

存储镜像

docker save -o xxxx.tar <NAME>:<TAG>

载入镜像

docker load不能对载入的镜像重命名

docker load --input xxxx.tar
docker load << xxxx.tar

上传镜像 push

# 给镜像打上 Tag
docker tag test:latest user/test:latest

# 推送镜像
docker push user/test:latest

容器命令

更新容器配置 update

docker update [OPTIONS] CONTAINER [CONTAINER...]
选项描述
--blkio-weight阻塞IO (相对权重),介于10到1000之间,0表示禁用(默认禁止)
--cpu-period限制CPU CFS(完全公平的调度程序)期限
--cpu-quota限制CPU CFS(完全公平的调度程序)配额
--cpu-rt-period将CPU实时时间限制为微秒
--cpu-rt-runtime将CPU实时运行时间限制为微秒
--cpu-shares, -cCPU份额(相对权重)
--cpusCPU数量
--cpuset-cpus允许执行的CPU(0-3,0,1)
--cpuset-mem允许执行的MEM(0-3,0,1)
--kernel-memory内核内存限制
--memory-swap交换限制等于内存加交换,“-1”以启用无限交换
--memory-reservatio内存软限制
--memory, -m内存限制
--pids-limit调节容器pids限制(-1表示无限制)
--restart容器退出时重新启动策略以应用

列出容器 ps

docker ps
  # -a, --all          显示所有的容器,默认只显示运行中的容器
  # -f, --filter filter   Filter output based on conditions provided
  #     --format string   Pretty-print containers using a Go template
  # -n, --last int    Show n last created containers (includes all states) (default -1)
  # -l, --latest      只列出最后创建的容器(包括所有状态)
  # --no-trunc        不截断输出
  # -q, --quiet       只输出容器ID
  # -s, --size        显示文件使用大小

创建容器 create run

新创建的容器默认处于停止状态。

docker create -it <NAME>:<TAG> OR <IMAGE ID>

run命令可以新建一盒容器并启动,或重新启动一个终止状态的容器。

docker run -it <镜像名> /bin/bash 
    # -i: 使容器的标准输入保持打开。
    # -t: 分配一个伪终端。并绑定到容器的标准输入上 /bin/bash:命令
    # --name <NAME> 指定别名
    # -d 守护态运行
    # --rm 容器停止后立即删除,不能与 -d 同用
    # -b BRIDGE OR --BRIDGE=BRIDGE 指定使用的网桥
    # --restart=always Docker重启时,容器自动启动
    # ‐‐network=网络名  将容器连接到网络 

使用run命令时,docker的操作包括

  1. 检查本地是否有镜像,否则就下载。
  2. 利用镜像创建容器
  3. 分配一个文件系统,在只读镜像外挂在一层可读写层
  4. 从宿主机网桥接口桥接一个虚拟接口到容器
  5. 从地址池配置一个IP给容器
  6. 执行用户指定的应用程序
  7. 执行完毕后容器被终止

启动容器 start

docker start <容器 ID>  #启动一个停止的容器,包含文件系统挂载的启动。

停止容器 stop

docker stop <容器 ID>  [-t|--time[=10]]  #包含文件系统卸载的停止
# 首先会发送SIGTERM信号,等待时间后发送SIGKILL终止容器。

docker kill CONTAINER
# 直接发送SIGKILL来终止容器

重启容器 restart

docker restart [-t|--time[=10]] <容器 ID> 
# 重启一个正在运行的容器,不包含文件系统的操作
# 如果容器在运行,会将其终止后重新启动。

进入容器 attach exec

docker attach <容器 ID> 
# 如果从这个容器退出,会导致容器停止

docker exec -it <容器 ID> /bin/bash
# exec是直接在容器里运行命令
# 如果从这个容器退出,容器不会停止
# -d, --detach 分离模式,在后台运行命令
# -u, --user   指定用户 格式: <name|uid>[:<group|gid>]

PID=$(docker inspect --format "{{.State.Pid}}" <container>) 
nsenter --target $PID --mount --uts --ipc --net --pid
# 类似于docker exec,但是更底层

删除容器 rm

docker rm [-f , --force=false] [-l ,--link=false] [-v --volumes=false] <容器 ID>
#    强制终止并删除一个运行中的容器  删除容器的连接,但保留容器  删除容器挂载的数据卷

导入导出容器 export import

docker export <容器 ID>   >  file.tar  #导出
cat file.tar | docker import - <镜像名>:[TAG]  #导入成为镜像
# docker import 丢弃所有历史记录和元数据信息
#                 仅保存容器当时的快照状态,比较小,且导入时可重新指定标签等

# docker load 保存完整记录,体积也比较大
DOCKER
save保存的是镜像(image)
export保存的是容器(container),导出的只是一个linux系统的文件目录
load载入镜像包,必须是一个分层文件系统,必须是是save的包;恢复为镜像
import载入容器包,恢复为镜像

网络管理 -P -p

docker create -it -P <NAME>:<TAG> OR <IMAGE ID>
docker create -it -p 5000:5000 <NAME>:<TAG> OR <IMAGE ID>
docker create -it -p 127.0.0.1:5000:80 <NAME>:<TAG> OR <IMAGE ID>
docker create -it -p 127.0.0.1::5000 <NAME>:<TAG> OR <IMAGE ID>

# 在创建的时候使用  `-P` 命令,将随机映射一个 49000-49900 的端口至容器内部开放的网络端口

# 在创建的时候使用 `-p 本地端口:容器端口 / IP:本地端口:容器端口    / ip::容器端口 ` 映射指定端口
#                映射所有接口地址     / 映射到指定地址的指定端口 /  映射到指定地址的任意端口 

docker port <容器 ID>   # 查看端口映射的配置

其他命令

system

docker system df [-v, --verbose] # 显示docker的磁盘占用
docker system events #  查看实时事件(例如容器创建,删除等)
docker system info # 查看docker 系统信息(同docker info)
docker system prune # 清理(清理停止的容器,没用容器使用的网络,镜像,缓存)

文件系统

​ 联合文件系统(Union File System,Unionfs)是一种分层的轻量级文件系统,它可以把多个目录内容联合挂载到同一目录下,从而形成一个单一的文件系统,这种特性可以让使用者像是使用一个目录一样使用联合文件系统。默认情况下,只有第一层(第一个目录)是可写的,其余层是只读的。

​ OverlayFS 的发展分为两个阶段。2014 年,OverlayFS 第一个版本被合并到 Linux 内核 3.18 版本中,此时的 OverlayFS 在 Docker 中被称为overlay文件驱动。由于第一版的overlay文件系统存在很多弊端(例如运行一段时间后Docker 会报 "too many links problem" 的错误), Linux 内核在 4.0 版本对overlay做了很多必要的改进,此时的 OverlayFS 被称之为overlay2。

​ 在目录/var/lib/docker/overlay2下存存放着本机可写的层。

读取文件:

​ 容器内进程读取文件分为以下三种情况。

  • 文件在容器层中存在:当文件存在于容器层并且不存在于镜像层时,直接从容器层读取文件;
  • 当文件在容器层中不存在:当容器中的进程需要读取某个文件时,如果容器层中不存在该文件,则从镜像层查找该文件,然后读取文件内容;
  • 文件既存在于镜像层,又存在于容器层:当我们读取的文件既存在于镜像层,又存在于容器层时,将会从容器层读取该文件。

修改文件或目录

​ overlay2 对文件的修改采用的是写时复制的工作机制,这种工作机制可以最大程度节省存储空间。 具体的文件操作机制如下。

  • 第一次修改文件:当我们第一次在容器中修改某个文件时,overlay2 会触发写时复制操作,overlay2 首先从镜像层复制文件到容器层,然后在容器层执行对应的文件修改操作。因此,只要有对只读层中的文件做修改,不管修改数据的量的多少,在第一次修改时,文件都会被拷贝到可写层然后再被修改
  • 删除文件或目录:当文件或目录被删除时,overlay2 并不会真正从镜像中删除它,因为镜像层是只读的,overlay2 会创建一个特殊的文件或目录, 使用 whiteout 机制,它的实现是通过在上层的可写的目录下建立对应的whiteout隐藏文件来实现的。这种特殊的文件或目录会阻止容器的访问。

文件查找

​ 查找性能在层数非常多时会出现下降,层数越多,查找性能越低,因此,在制作 Docker 镜像时要注意层数不要太多。

增加文件

​ 默认情况下,新增的文件都会被放在最上面的可写层中。

Docker 网络

基础概念

Docker启动的时候会创建一个虚拟网桥docker0,会根据RFC1918规定的私有网络地址分配获取一组地址。

典型的是172.17.42.1/16,此后启动的容器内的网口也会分配统一网段 172.17.0.0/16。容器和主机之间通过一对veth pair接口进行通信。一端在docker0上,以veth开头。另一端则挂载在容器内。

Docker中默认的三种网络分别为bridgehostnone,其中名为bridge的网络就是默认的bridge驱动网络,也是容器创建时默认的网络管理方式,配置后可以与宿主机通信从而实现与互联网通信功能,而hostnone属于无网络,容器添加到这两个网络时不能与外界网络通信。

网络类型

  • Bridge networks(桥接网络)

​ 为了保证容器的安全性,我们可以使用基于bridge的驱动创建新的bridge网络,这种基于bridge驱动的自定义网络可以较好的实现容器隔离。需要说明的是,这种用户自定义的基于bridge驱动的网络对于单主机的小型网络环境管理是一个不错的选择,但是对于大型的网络环境管理(如集群)就需要考虑使用自定义overlay集群网络。

  • Overlay network in swarm mode(Swarm集群中的覆盖网络)

Docker Swarm集群环境下可以创建基于overlay驱动的自定义网络。为了保证安全性,Swarm集群使自定义的overlay网络只适用于需要服务的群集中的节点,而不会对外部其他服务或者Docker主机开放 。

  • Custom network plugins(定制网络插件)

如果前面几种自定义网络都无法满足需求时,就可以使用Docker提供的插件来自定义网络驱动插件。自定义网络插件会在Docker进程所在主机上作为另一个运行的进程。自定义网络驱动插件与其他插件遵循相同的限制和安装规则,所有插件都使用Docker提供的插件API,并且有一个包含安装、启动、停止和激活的生命周期。由于自定义网络插件使用较少,所以只需了解即可。

默认网络

docker会自动创建三个网络 Host Bridge None

模式说明
Host容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。在host模式下,容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。使用 --network=host 指定。
Bridge此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。除非特殊指定,每个docker都会默认连接到此网络。使用 --network=bridge 指定
None该模式关闭了容器的网络功能。使用 --network=none 指定。
Container创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。在这个模式下,新创建的容器和已经存在的一个容器共享一个Network Namespace,两个容器的进程可以通过lo网卡设备通信。使用 --network=container:NAME_or_ID 指定。

DNS 和 主机名

  • /etc/resolv.conf - 在容器创建的时候,默认与宿主机保持一致
  • /etc/hostname - 记录了容器自身的一些地址和名称
  • /etc/hosts - 记录了容器的主机名

1.2.0 开始支持在容器里直接编辑以上文件,不过修改是临时的,只在运行的容器中保留,终止或重启后会重置,且不会docker commit提交。

-h HOSTNAME or --hostname=HOSTNAME 设定容器的主机名,但只有容器内能看到,外部和docker ps以及其他docker /etc/hosts都看不到。

--link=CONTAINER_NAME:ALIAS 使用这个选项创建容器,可以添加一个所连容器的主机名到容器内的/etc/hosts中,这样新创建的容器可以直接使用主机名与连接的容器通信。

--dns=IP_ADDRESS 添加DNS到容器的/etc/resolv.conf

--dns-search=DOMAIN 指定DNS搜索域。

访问控制

访问外部网络

在启动Docker服务的时候设定 --ip-forward=trueDocker服务会自动打开宿主机系统的转发服务。

或打开转发开关

net.ipv4.ip_forward = 1

网络命令

列出网络

 docker network ls

创建网络

docker network create [OPTIONS] <NAME>
    # -d, --driver string     管理网络的驱动,默认是bridge

连接与断开网络

docker network connect <网络名> 容器
docker network disconnect <网络名> 容器

移除网络

docker network rm <网络名>
其他内容见高级用法