目录

Docker概述Docker运行体系架构Docker核心技术栈1.命名空间(Namespace)2.控制组(Cgroups)3.联合文件系统(Union FS)

Docker核心对象1.Dockerfile2.镜像(Image)3.容器(Container)

Docker工作流镜像制作1.通过Dockerfile制作镜像2.通过容器制作镜像3.通过文件系统导入镜像

容器管理仓库管理

以企业级应用为背景,总结Docker架构/技术栈/核心对象/Dockerfile多阶段构建/镜像分层/工作流等使用经验。 文章内容较多,建议根据需要查看对应章节。

Docker概述

Docker是用Go编程语言的,遵从Apache2.0协议开源。

Docker是Client/Server架构,用户在客户端输入指令,客户端将指令转换为DockerAPI调用,Docker守护进程侦听DockerAPI请求并管理Docker对象(如:镜像、容器、网络、卷、仓库等)。

Docker运行体系架构

Docker是一种轻量化的虚拟技术,将应用软件及其依赖的环境整体打包成镜像,可以移植到任何Docker环境上运行,而无需担心环境依赖问题。

Docker容器之间相互独立,可以灵活组装,互不影响,如果我们把Docker守护进程理解成货轮,则容器就是货轮上的集装箱。Docker容器共享操作系统内核,可以秒级启动。

架构对比:

图(a)是linux体系架构图,应用软件直接运行在宿主机上,没有进行隔离,资源的使用没有限制。 图(b)是Docker体系架构图,应用软件运行在容器中,容器是一个被隔离的拥有独立的命名空间和文件系统的进程。Docker Engine也是一个进程,负责容器的管理。这些进程都共享宿主机内核。 图(c)是VM体系架构图,应用软件运行在虚拟机中,每一个VM都拥有一个完成的GuestOS。

Docker核心技术栈

Docker是通过命名空间(Namespace)、控制组(Control Group)、联合文件系统(Union File System)三大技术来实现容器隔离的。

1.命名空间(Namespace)

命名空间是Linux内核的一个核心功能,是容器实现隔离的基础,Docker为每个容器创建不同的命名空间,每个命名空间拥有独立的隔离环境。

pid 命名空间 隔离进程。 Docker为容器进程创建一个独立的pid命名空间,在同一pid命名空间中只能看到当前命名空间的进程。两个不同的命名空间可以拥有相同的pid,互不影响。 容器内的所有进程的父进程为Docker进程,Docker进程由Docker守护进程创建,并通过pip命名空间进行进程隔离,最后交由linux内核进行管理。net 命名空间 隔离网络(端口/设备)。 Docker为容器进程创建一个独立的net命名空间,每个net命名空间拥有独立的虚拟网络设备(如:网卡), 路由表等。 Docker默认采用veth的方式,将容器中的虚拟网卡同host上的Docker虚拟网桥docker0连接在一起。user 命名空间 隔离用户和用户组。 Docker为容器进程创建一个独立的user命名空间,每个容器拥有独立的用户和用户组。 每个容器内都可以通过useradd/groupadd命令创建用户和组,不同的容器中创建的用户名和UID可以相同,也可以不同。mount 命名空间 隔离挂载点。 Docker为容器进程创建一个独立的mount命名空间,每个容器拥有独立的文件系统和mount挂载配置。 可以为每个容器挂载不同的文件目录,每个容器没只能看到为它挂载的文件目录。在容器中执行df -h即可查看容器的mount。uts 命名空间 隔离域名。 Docker为容器进程创建一个独立的uts命名空间,每个容器拥有独立的hostname和domain name。会被当作网络上的一个独立的节点。ipc 命名空间 隔离进程间通信。 只有运行在同一个容器内的进程才可以进行通信。

2.控制组(Cgroups)

控制组是Linux内核的一个功能,用来限制、控制与分离一个进程组(如CPU、内存、磁盘输入输出等)。

资源限制:进程组可以使用的资源数量。可以设置进程组可使用的内存限制,包括虚拟内存。 比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会触发OOM(out of memory)。优先级:进程组的优先级控制。进程组可以优化得到大量的CPU或磁盘IO吞吐量。 比如:可以使用cpu子系统为某个进程组分配特定cpu share。结算:记录进程组使用的资源数量。用来衡量系统资源实际使用情况。 比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间。进程组隔离。 比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。

Docker相关命令选项

控制CPU使用份额

–cpu-shares或-c 设置每个容器占用cpu的时间比例(权重,相对限制)。 示例: docker run --name “C1” --cpu-shares=10 ubuntu; docker run --name “C2” --cpu-shares=30 ubuntu; 容器“C1”和容器“C2”对cpu时间的占用就是1:3 控制CPU核的使用

–cpus 限制容器运行的核数。 示例: docker run --name “C3” --cpus=2 ubuntu; 容器“C3”的进程只能使用主机上的2个cpu–cpuset-cpus 限制容器运行在哪个CPU核心上。 示例: docker run --name “C4” --cpuset-cpus=“0-1” ubuntu; 容器“C4”的进程只可在 CPU-0、CPU-1上执行。 控制CPU使用周期

–cpu-period 设置每个容器进程的调度周期(单位:us)。–cpu-quota 设置在每个调度周期内。容器能使用的CPU时间(单位:us)。 示例1: docker run --name “C5” --cpu-period=100000 --cpu-quota=50000 ubuntu; 每100ms的周期内,容器的CPU配额为50ms(不限制cpu核),换成百分比:每周期50%的cpu使用时间。 示例2: docker run --name “C6” --cpu-period=100000 --cpu-quota=200000 ubuntu; 每100ms的周期内,容器的CPU配额为200ms(不限制cpu核),换成百分比:每周期100%使用两个cpu核。 控制内存使用

-m或–memory 设置每个容器进程的内存使用额度(默认值:-1,表示无限制,–memory-swap是-m的两倍)。–memory-swap 设置“内存+交换分区”的使用额度。 示例: docker run --name “C7” -m 500M --memory-swap=700M ubuntu; 容器“C7”的进程,最多只能使用500M内存和200M交换内存swap。 控制磁盘IO 默认情况下,所有容器能平等地读写磁盘,可通过设置权重、bps和iops来限制磁盘读写的带宽。 bps(byte per second):表示每秒读写的数据量。 iops (io per second):表示每秒的输入输出量(读写次数)。

–blkio-weight:权重,默认值500。–device-read-bps和–device-write-bps:限制读写某个设备的bps。–device-read-iops和–device-write-iops:限制读写某个设备的iops。

3.联合文件系统(Union FS)

我们都知道,linux上“一切皆文件”,Docker容器能够运行,就需要一套可运行的文件系统。 通常来说,Docker镜像都是多层的,每一层都是在上一层做的差异修改,那么每层都会有很多文件目录,如何将多层的文件系统整合为一个可用的文件系统? Docker使用了“Union FS”技术,顾名思义,就是将镜像的各个层进行计算,将各个层目录内容联合挂载到同一个目录下,最后生成一个文件系统。这就是联合挂载。 联合文件系统是一种分层、轻量级并且高性能的文件系统,它把对文件系统的修改作为一次提交commit,一层一层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。镜像可以通过分层来进行继承,基于基础镜像,可以制作各种具体的应用镜像。 比如,一个拥有3层的镜像。最底层创建/home/test1.txt文件。第二层创建/home/test2.txt,那么第二层只有test2.txt文件。第三层在/home/test1.txt中加了一句“CSDN”,那么第三层只有/home/test1.txt这个文件。在创建容器的时候,Docker会采用UnionFS技术,在“容器层”将test1.txt和test2.txt都挂载到/home目录下,并且test1.txt也根据修改记录进行整合(文件中有“CSDN”)。

Docker核心对象

1.Dockerfile

Dockerfile是一个文本文档,可以在其中定义生成镜像的步骤。编写Dockerfile的语言,称为Dockerfile语法。

Dockerfile构建命令

语法: docker build [OPTIONS] PATH|URL|- 每个部分的含义:docker build:Docker 命令,用于构建镜像。OPTIONS :可选的命令参数, 如: --build-arg =:给ARG定义的变量传参。 -t ::定义构建镜像名称和标签。PATH|URL|-: 三种Dockerfile读取方式。 PATH表示包含Dockerfile的目录。 URL 表示Git仓库的URL。 -表示从标准输入流中读取Dockerfile内容。示例: docker build --build-arg USER_NAME=my_app --build-arg PRODUCT_VERSION=6.3.0 . 推荐使用这个方式。 docker build --build-arg USER_NAME=my_app - < Dockerfile

Dockerfile语法

首行注释:

格式:# syntax=docker/dockerfile:1作用:定义Dockerfile解释器。可选性。

1. FROM

格式:FROM [--platform=] [:] [AS ]作用:指定基础镜像,后面的所有指令都要基于此指令。 每个构建阶段都是从FROM开始。以CMD或者ENTRYPIONT结束。每个Dockerfile可以有多个FROM,称为“多阶段构建”。

2. LABEL

格式:LABEL = = ...作用:以键值对的形式给镜像添加元数据信息。可选项。

3. MAINTAINER

格式:MAINTAINER 作用:指定镜像的作者。可选项。

4. RUN

格式:RUN ["executable", "param1", "param2"]作用:定义执行命令。说明:每条RUN指令都将在当前镜像的基础上执行指定命令,并提交为新的镜像。也就是说,每执行一次RUN,提交一层镜像。每个Dockerfile可以有多个RUN指令。

5. CMD

格式1:CMD ["executable","param1","param2"] 作用:定义容器启动docker run时,运行的命令。 说明:如果docker run后面定义了运行的命令参数,则会覆盖CMD定义的命令参数。格式2:CMD ["param1","param2"] 作用:作为ENTRYPOINT的参数 说明:每个Dockerfile只能有一条CMD命令,若指定了多条,只有最后一条生效。

6. ENTRYPOINT

格式:ENTRYPOINT ["executable", "param1", "param2"]作用:定义容器启动docker run时,运行的命令。说明:不会被docker run提供的参数覆盖,会把docker run的参数传递给ENTRYPOINT。 每个Dockerfile只能有一条ENTRYPOINT命令,若指定了多条,只有最后一条生效。

7. CMD 和 ENTRYPIONT 异同点

相同点:两者都可以单独在Dockerfile中定义,而且功能相同。差异点:CMD定义的命令参数可以被docker run参数覆盖。ENTRYPIONT会接收docker run的参数作为自己的参数。若Dockerfile中同时定义了CMD和ENTRYPIONT,则CMD定义的内容将作为ENTRYPIONT的参数。docker run参数会覆盖CMD定义的参数。 推荐使用ENTRYPIONT定义容器执行的第一条命令(主进程)。

8. ENV

格式:ENV = ...作用:定义环境变量值。说明:自定义后生效,可在Dockerfile中引用(RUN USER COPY等),也会被保存到镜像中,成为容器的环境变量。可在运行容器时,通过–env修改或者定义这些环境变量docker run --env =

9. ARG

格式:ARG [=]作用:定义Dockerfile变量。可以通过外部参数传值。说明:仅在构建阶段有效(从定义开始,到CMD/ENTRYPIONT结束),即docker build时生效。 可通过docker build --build-arg =给Dockerfile中定义的键name传值value。

10. ARG和ENV结合使用

相同点:都是定义变量值。都可以定义多个变量。都可在Dockerfile指令中被引用。不同点: ARG定义的是Dockerfile的变量,仅在Dockerfile构建阶段有效,不会保存到镜像中。可以通过外部参数传值。 ENV定义的是容器内的环境变量,即可在Dockerfile构建阶段被Dockerfile指令引用,也会被保存到镜像中,待容器创建时,作为容器的环境变量。不能通过外部参数传值,只能在Dockerfile中定义好。 ENV定义的环境变量会覆盖ARG定义的同名变量。应用:在定义需要灵活传参的环境变量时,我们可以通过ARG定义变量,然后赋值给ENV同名环境变量。 Dockerfile构建镜像时直接使用宿主机的环境变量: 构建时,docker build --build-arg USER_NAME=csdn .,给USER_NAME传值csdn。

11. WORKDIR

格式:WORKDIR /path/to/workdir作用:可以设置当前工作目录为任何路径。如果目录不存,则被创建。 如果Dockerfile中的第一个WORKDIR定义的是相对路径,则是基于Dockerfile位置的相对路径。 如果之后的WORKDIR定义的也是相对路径,则基于上一个WORKDIR指定的路径。 如,依次定义WORKDIR a \n WORKDIR b \n WORKDIR c,则当前的工作路径依次为./a ./a/b ./a/b/c,最终工作路径./a/b/c

12. COPY

格式:COPY [OPTIONS] ... 或COPY [OPTIONS] ["", ... ""]作用:复制本地主机的到镜像中的:Dockerfile所在目录的相对路径。:可以是绝对路径也可以是相对路径。相对路径是相对当前工作路径而言。路径可以使用匹配字符串,*匹配所有,?匹配单个字符。如,COPY test_*.txt relativeDir/,将test_*.txt复制到镜像/relativeDir/目录下。OPTIONS:

--chown和 - --chmod 格式:COPY [--chown=:] [--chmod= ...] ... 作用:复制到镜像中的目录文件,默认都是UID和GID为0创建的。可在COPY时指定用户和组。 如,COPY --chown=myuser:mygroup --chmod=640 files* /somedir/ 将files*复制到镜像中的/somedir/,并设置其属性myuser:mygroup,权限640。--from 格式:--from=或--from= 作用:指定源镜像名称或者编号。在Dockerfile多阶段(多个FROM)构建的时候使用。 表示镜像名称或者别名。表示Dockerfile中的镜像编号,0表示第一阶段的镜像。--link--parents--exclude

13. ADD

格式:ADD [OPTIONS] ... 或ADD [OPTIONS] ["", ... ""]作用:将外部资源添加到镜像中。常用于拉取远程git仓代码。:可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个tar文件(自动解压为目录)。:同COPY。 当为本地目录时,推荐使用COPY。

14. USER

格式:USER [:]或USER UID[:GID]作用:指定运行容器时的用户名和组,后续的RUN|ENTRYPOINT|CMD也会使用指定用户执行。用户和组必须已经存在。组:默认为root,当指定group时,用户将只有指定组的成员身份,其他已配置的组成员身份会被删除。应用:当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r docker_group && useradd -r -g docker_group docker。要临时获取管理员权限可以使用gosu,而不推荐sudo。

15. SHELL

格式:SHELL ["executable", "parameters"]作用:定义容器执行的shell解释器。应用:SHELL ["/bin/bash", "-c"]

16. EXPOSE

格式:EXPOSE [/...]作用:定义容器监听的端口(默认为tcp,如:10328/tcp)。说明:当启动容器时,Docker会将该端口映射到宿主机端口上。示例1: docker run -p 80:10328/tcp 根据-p定义的映射关系,Docker将宿主机端口80映射到容器监听端口10328/tcp。外界就可以通过 “宿主机IP:80” 地址访问容器了。示例2:docker run -P -P表示Docker主机会自动随机分配一个主机端口,映射到容器监听端口10328/tcp。

17. VOLUME

格式:VOLUME ["/data"]作用:在容器中创建一个挂载点,一般用来存放数据库和需要保存的数据等。应用: 不指定宿主机目录时docker run,在宿主机上随机创建一个容器卷path/_data目录,挂载到容器中的/data挂载点。 指定宿主机目录时docker run -v /home/data:/data,根据指定的目录进行挂载。

Dockerfile与镜像层对应关系

在每个构建阶段FROM中,每次对镜像的修改,都会新建一层,层层叠加组成镜像的文件系统,涉及到的Dockerfile命令有COPY|RUN|ADD等。

Dockerfile多阶段构建

每一条FROM指令都是一个构建阶段,多条FROM就是多阶段构建,最后生成的镜像仍是最后一个构建阶段的结果。我们可以利用多阶段构建,将前置阶段中的文件拷贝到后面的阶段中,从而减少镜像的大小和层数。

示例1:利用多阶段构建,分离编译环境和运行环境。 # syntax=docker/dockerfile:1

# 第一构建阶段:编译环境

FROM golang:1.16-alpine AS builder

WORKDIR /home/app

# 从git仓上拉去代码,并保留项目目录mygo

ADD --keep-git-dir=true https://github.com/mygo.git#v1.10.1 .

# 进入mygo项目目录下

WORKDIR /home/app/mygo

# 编译生成自己的app

RUN go build -o myserver

# 第二阶段:运行环境

# scratch镜像是一个精简的基础镜像,它仅包含Docker容器运行所需的最小文件系统和执行环境。

FROM scratch

WORDDIR /home/app

# 在基础镜像基础上,创建一层。若基础镜像为1层,则该阶段制作的镜像为2层。

COPY --from=builder /home/app/mygo/myserver .

# 定义容器启动时运行的命令

ENTRYPOINT ["/home/app/myserver"]

示例1构建过程:

首先使用golang:1.16-alpine镜像作为编译环境,编译构建我们的应用程序myserver。然后,我们在第二构建阶段,使用scratch作为运行环境,并将应用程序myserver复制到其中。第一阶段使用的golang:1.16-alpine镜像包含了所有的go库和编译工具,特别庞大,运行程序的时候又不需要这些编译工具。为了降低镜像的体积,在第二阶段我们以scratch为基础镜像,添加myserver应用程序。

Docker Scratch镜像具有以下优点:

引用方便:Docker镜像不基于任何其他镜像构建,只需要在Dockerfile中指定FROM scratch即可极小的镜像大小:Scratch镜像非常小,因为它仅包含Docker容器运行所需的最小文件系统和执行环境。这使得它非常适合用于构建轻量级容器化应用程序,减小了镜像大小和传输时间。更好的安全性:由于Scratch镜像非常精简,因此它具有更少的漏洞和攻击面,提高了容器的安全性。此外,由于Scratch镜像不包含任何额外的组件或库,因此它可以防止不必要的攻击。更好的可移植性:由于Scratch镜像非常小,因此它非常适合构建微服务应用程序。每个微服务都可以使用自己的Scratch镜像作为基础镜像,从而获得更好的隔离和可移植性。

示例2:利用多阶段构建,减少镜像制作过程中产生的中间层。 # syntax=docker/dockerfile:1

# 第一构建阶段

ARG BASE_IMAGE

ENV BASE_IMAGE=${BASE_IMAGE:-ubuntu:22.04}

# 基础镜像:我们在此镜像基础之上,制作自己的镜像。

FROM ${BASE_IMAGE} as b_image

# 定义变量,从外部传参。

ARG USER_NAME

ARG PRODUCT_VERSION

# 将ARG变量值复制给ENV,如果USER_NAME为空,则取默认值docker。

ENV USER_NAME=${USER_NAME:-docker}

ENV PRODUCT_VERSION=${PRODUCT_VERSION:-6.3.0}

# 维护者:Specify the author of an image

MAINTAINER docker_user author_name@email.com

# 镜像操作指令:Commands to update the image

# 定义容器内的工作目录为/home/${USER_NAME}。跟宿主机目录无关。

WORKDIR /home/${USER_NAME}

# 复制本地主机的install.sh到容器中的/home/${USER_NAME}/install/目录下。并将属主设置为9079

COPY --chown=9079 --chmod=750 install.sh install/

# 更新软件软并安装nginx软件。

RUN apt-get upgrate && apt-get install bc ksh

# 定义工作目录为/home/${USER_NAME}/install/

WORKDIR install

# 将宿主机上的软件安装包service_pkg.tar.gz复制到容器中的/home/docker/install/目录下。

COPY --chown=9079 --chmod=640 service_pkg.tar.gz .

# 将软件包安装到镜像中。

RUN sh install.sh ${USER_NAME} && rm -rf service_pkg.tar.gz install.sh

# 定义容器内的工作目录为/home/${USER_NAME}。

WORKDIR /home/${USER_NAME}

# 将启动脚本复制到/home/${USER_NAME}

COPY --chown=9079 --chmod=640 start_up.sh .

# 第二构建阶段

FROM ${BASE_IMAGE}

ARG USER_NAME

ENV USER_NAME=${USER_NAME:-docker}

WORKDIR /home/${USER_NAME}

# 把第一个段构建的镜像中文件,拷贝到当前构建阶段。

# COPY --from= <当前镜像的路径>

COPY --from=b_image /home/${USER_NAME} .

# 定义ENTRYPIONT默认参数

CMD ["${USER_NAME}", "${PRODUCT_VERSION}"]

# 定义start_up.sh为容器启动时执行的命令。

ENTRYPOINT [ "/bin/bash", "-c", "start_up.sh"]

示例2构建过程:

首先使用ubuntu:22.04镜像作为基础镜像,安装我们的应用程序软件包service_pkg.tar.gz。然后,我们在第二构建阶段,我们同样使用ubuntu:22.04作为运行环境,并将应用程序myserver复制到其中。第一阶段执行了特别多的COPY|RUN命令,创建了很多中间层。这些中间层没有实际的业务需要,不利于镜像层的管理。为了屏蔽掉这些中间层,在第二阶段,我们以ubuntu:22.04镜像重新制作镜像,并将第一阶段安装的应用程序添加到当前镜像中,该阶段只有一个COPY创建了1层,加上基础镜像的1层,总共只有两层。

2.镜像(Image)

UnionFS是镜像分层的基础。 镜像实际上由一层层的文件系统组成,这些层都是只读的(不会变,无状态)。镜像分层可以共享资源,方便复制迁移,可以被复用。 比如,基于基础镜像构建了很多应用镜像,那么,Docker只需加载一份基础镜像,就可以为所有容器服务。而且镜像的每一层都会被共享,不需要重复加载。复制迁移时,也只需要迁移一份基础镜像即可。

镜像文件系统

镜像的文件系统是容器运行的基础,须包含运行应用程序所需的一切(所有依赖项、配置、脚本等),还应包含容器运行的文件系统,如linux文件系统,内核等。

bootfs (boot file system)。 镜像的最底层是文件系统bootfs,这层与典型的Linux系统一样,包含bootloader和kernel。rootfs (root file system) 在bootfs的上层是rootfs,就是各种文件系统(如:ext2/3/4、XFS、ZFS等),包含:/dev,/bin,/etc等标准目录和文件。业务层 就是我们在Dockerfile构建时,修改文件系统时创建的新层,每个镜像有很多业务层。每层都有目录文件。容器层 此层不是镜像文件的一部分,是Docker创建容器时,基于镜像的多层文件系统,利用UnionFS联合挂载技术,创建的一个可写层,存在于容器中。 就文件系统而言,容器与镜像的区别就在于容器层。

镜像加载机制(基于UnionFS)

容器刚启动时,Docker引擎会加载bootfs文件系统的bootloader引导程序,然后bootloader引导加载kernel。当bootfs文件系统加载完成之后,容器的VFS已经被创建成功(根挂载点已就绪),整个内核也被加载到内存中了。此时内存的使用权已由bootfs转交给系统内核,系统内核会将bootfs卸载掉。在bootfs文件系统加载完成后,内核会加载rootfs系统,并将其挂载到容器VFS的根挂载点上。在rootfs加载完成后,系统内核会依次加载这些业务层,利用UnionFS联合挂载技术,将所有层的文件系统整合,在“容器层”创建一个完整的linux文件系统(如:ext4)。此时,再将启动参数配置到“容器层”的文件系统中,一个运行的容器就加载完成了。此外,镜像被加载到内存后,会生成一个64位十六进制的字符串(镜像ID)。通常,前12位ID对镜像进行标识。

镜像分层

新镜像是从基础镜像一层层叠加生成的,每对镜像做一次修改,就在现有镜像基础上加一层。

镜像存储 Docker默认安装的情况下,将/var/lib/docker作为存储目录,存放镜像、容器、网络和挂载卷等。执行docker info可查看Docker安装信息。

master:~ # docker info

...

Storage Driver: overlay2 # 存储驱动,常见的存储驱动:devicemapper、btrfs、zfs。

Docker Root Dir: /var/lib/docker # 存储根目录

...

/var/lib/docker目录结构

master:/var/lib/docker # ls -g

total 48

drwx--x--x 4 root 4096 Aug 14 2023 buildkit

drwx--x--x 3 root 4096 Aug 14 2023 containerd

drwx--x--- 2 root 4096 Aug 14 2023 containers

-rw------- 1 root 36 Aug 14 2023 engine-id

drwx------ 3 root 4096 Aug 14 2023 image

drwxr-x--- 3 root 4096 Aug 14 2023 network

drwx--x--- 3 root 4096 Mar 15 13:50 overlay2 # 根据驱动类型创建的目录。

drwx------ 4 root 4096 Aug 14 2023 plugins

drwx------ 2 root 4096 Mar 15 13:50 runtimes

drwx------ 2 root 4096 Aug 14 2023 swarm

drwx------ 2 root 4096 Mar 15 13:50 tmp

drwx-----x 2 root 4096 Mar 15 13:50 volumes

可以通过修改配置文件/etc/docker/daemon.json,更改Docker Root Dir和 Storage Driver。

vi /etc/docker/daemon.json

{

"storage-driver": "devicemapper", # 修改默认的存储驱动

"data-root": "/opt/paaslib/docker" # 修改默认存储路径

}

sudo systemctl stop docker.service # 重启Docker服务

镜像仓库 我们都知道Git有远程仓库和本地仓库的概念,当我们使用git将远程仓库的代码拉取git pull到本机时,会在本机创建一个关联分支,通过这个关联分支,我们可以便捷的把commit的代码推送到远程仓库。通常把我们本机存储地址看做本地仓库。 类似的,Docker也有远程仓库,本机存储镜像的地址称为本地仓库。Docker把镜像拉取docker pull到本机后,可以进行修改和提交docker commit,再把commit后的镜像通过docker tag创建与远程仓库关联的关联镜像,然后将其推送docker push到远程仓库上。

3.容器(Container)

容器是镜像的运行实例,是一组通过Namespace/Cgroups/UnionFS技术隔离的进程。镜像为容器提供隔离的文件系统。就像运行的应用程序和其静态的源代码之间的关系。

容器拥有独立的命名空间和文件系统,他们之前相互独立,互不影响。容器归根结底也是宿主机上运行的一个进程,他们共享操作系统内核。

当创建container时,存储驱动程序会在原始的只读镜像上创建一个可写层(称为容器层),所有的变更都发生顶层的容器层。 因为每个容器都会创建自己的容器层,并且所有更改都是发生在容器层中。所以,一个镜像可以被多个容器共享,容器在共享镜像基础上拥有自己的数据状态。当容器被删除时,对应的容器层也会被删除,不会被持久化。

Docker工作流

Docker工作流程汇总图:

1.查看容器ID:docker ps。 2.重启/启动/停止容器:docker restart/start/sotp/stats。 3.运行容器:docker run/exec/attach。 4.容器与宿主机间拷贝文件:docker cp 。 5.删除容器:docker rm 容器ID。

6.查看镜像ID:docker images 7.查看镜像原数据:docker inspect 镜像ID 8.删除镜像:docker rmi 镜像ID。 命令参考:https://docs.docker.com/reference/cli/docker/

镜像制作

1.通过Dockerfile制作镜像

构建流程:

# -t:定义新建镜像的标签。

$ sudo docker build -t mycontainer/ubuntu1:22.04 - << EOF

# Dockerfile

FROM ubuntu:14.04

MAINTAINER rufei@LLM

RUN apt-get update

RUN pip install numpy

RUN pip install flask

ENTRYPIONT ["/bin/bash","-c","python"]

EOF

...

Successfully built 324104cde6ad

# 查看新创建的镜像

$ sudo docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE

mycontainer/ubuntu1 22.04 324104cde6ad 1 hours ago 478.5 MB

ubuntu 22.04 3c59e02ddd1a 10 hours ago 446.7 MB

# 导出镜像到本地文件myubuntu1_22.04.tar.gz

$ sudo docker save -o myubuntu1_22.04.tar.gz mycontainer/ubuntu1:22.04

# 将归档的镜像文件myubuntu_22.04.tar.gz迁移到生产节点中, 并载入到本地镜像库。

$ sudo docker load --input myubuntu1_22.04.tar.gz

2.通过容器制作镜像

构建流程:

# 宿主机运行容器。

$ sudo docker run -it ubuntu:22.04 /bin/bash

# 进入到fe7fc4bd8fc9容器内, 安装python包。

root@fe7fc4bd8fc9:/$ pip install flask

# 退出容器fe7fc4bd8fc9

root@fe7fc4bd8fc9: exit

# 此时的容器已经被修改了, 使用docker commit命令来提交更新后的副本。-m提交信息。

$ sudo docker commit -m "install flask" ubuntu:22.04 mycontainer/ubuntu2:22.04

# 查看新创建的镜像

$ sudo docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE

mycontainer/ubuntu2 22.04 5bc342fa0b91 1 hours ago 458.5 MB

mycontainer/ubuntu1 22.04 324104cde6ad 1 hours ago 478.5 MB

ubuntu 22.04 3c59e02ddd1a 10 hours ago 446.7 MB

# 导出镜像到本地文件myubuntu_22.04.tar.gz

$ sudo docker save -o myubuntu2_22.04.tar.gz mycontainer/ubuntu2:22.04

# 将归档的镜像文件myubuntu2_22.04.tar.gz迁移到生产节点中, 并载入到本地镜像库。

$ sudo docker load --input myubuntu2_22.04.tar.gz

3.通过文件系统导入镜像

上面讲述了使用Dockerifle和本地容器来创建镜像的方法,除此之外,还可以根据一个文件系统来创建一个简单的镜像。常用于将本地文件系统快照转换为Docker镜像。不常用,了解即可。 构建流程:

命令格式:docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]] 各部分含义:

file|URL:系统文件来源,可以是本地目录|文件 URL资源 STDIN标准输入流,资源类型必须是.tar/.tar.gz/.tgz/.bzip/.tar.xz/.txz。本地目录需包含完整的文件系统,如/。OPTIONS: -c, --change:将Dockerfile指令应用到创建的镜像。如--change "ENV DEBUG=true"。 -m, --message:设置提交消息。如--message "new image"。[REPOSITORY[:TAG]]:给新制作的镜像设置标签。如exmpimage1:new。

# 通过URL资源创建镜像

$ docker import https://example.com/exampleimage.tgz exmpimage1:new

# 通过本地文件创建镜像 - STDIN

$ cat exampleimage.tgz | docker import --message "new image" - exmpimage2:new

# 通过本地文件创建镜像 - archive

$ docker import /path/exampleimage.tgz --change "ENV USER_NAME=docker" exmpimage3:new

# 通过本地文件创建镜像 - directory

$ sudo tar -c . | docker import --change "ENV USER_NAME=docker" - fsdir exmpimage4:new

docker load:目标对象是Docker镜像归档文件。 docker import:的目标对象是文件系统资源。

容器管理

新建并启动

docker container run [OPTIONS] IMAGE [COMMAND] [ARG…] docker run 可选项 镜像 启动容器命令 命令参数 docker run --name mycontainer01 ubuntu:22.04 echo param1 param2 命令参考:https://docs.docker.com/reference/cli/docker/container/run/

运行容器

docker container exec [OPTIONS] CONTAINER COMMAND [ARG…] docker exec 可选项 容器 启动容器命令 命令参数 docker exec -it mycontainer01 sh -c “echo a && echo b” 命令参考:https://docs.docker.com/reference/cli/docker/container/exec/

仓库管理

Docker镜像远程仓库是集中存放镜像的地方。以公共仓库Docker Hub为例。

注册Docker账号。 登陆和退出

docker login [OPTIONS] [SERVER] docker login -u 用户名 -p 密码 仓库名称

$ docker login --username=mydocker registry.cn-hangzhou.aliyuncs.com

Password:

$ docker logout registry.cn-hangzhou.aliyuncs.com

拉取镜像 # 在镜像仓中查找镜像

$ docker search ubuntu

# 拉取镜像

$ docker pull ubuntu:22.04

推送镜像 # 打个tag标签

$ docker tag ubuntu:22.04 registry.cn-hangzhou.aliyuncs.com/ubuntu:22.04

# 推送到远程仓库

$ docker push registry.cn-hangzhou.aliyuncs.com/ubuntu:22.04

参考: Docker官方文档:https://docs.docker.com/get-started/overview/ Docker中文文档:http://www.dockerinfo.net/document

好文推荐

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: