资讯详情

资讯详情

Docker的基础命令

一.镜像相关操作1.1 增加镜像1搜索镜像2拉取镜像Docker Hub上有大量的高质量的镜像可以用。从Docker镜像仓库获取镜像的命令是docker pull其命令格式为docker pull [选项] [Docker Registry地址[:端口号]/]仓库名[:标签]docker镜像仓库地址地址的格式一般是域名/IP:[端口号]不写的话默认地址是Docker Hub。仓库名仓库名是两段式名称即用户名/软件名。对于Docker Hub如果不给出用户名则默认为library也就是官方镜像。一个仓库会包含同一个软件不同版本的镜像 而标签就常用于对应该软件的各个版本。3导入镜像docker images load -i1.2 查看镜像1查看镜像信息2查看镜像的层数3标签为none的镜像如果查看镜像的显示结果中出现了没有仓库名也没有标签均为none的镜像可能是因为旧的镜像名被转移到了新下载的镜像身上而旧的镜像上的这个名称则被取消从而成为了none。除了 docker pull 可能导致这种情况 docker build 也同样可以导致这种现象。 由于新旧镜像同名 旧镜像名称被取消 从而出现仓库名、 标签均为 none 的镜像。 这类无标签镜像也被称为虚悬镜像 (dangling image) 可以用下面的命令专门显示这类镜像docker image ls -f danglingtrue一般来说 虚悬镜像已经失去了存在的价值 是可以随意删除的 可以用下面的命令删除docker image prune3查看镜像详细信息docker image inspect nginx:1.27.2-alpine1.3 修改镜像导出镜像1给镜像重新打标签2导出镜像为文件可以同时导出多个镜像为一个文件指定.tar.gz 可以导出并压缩。docker image save -o nginx-1.27.2.tar.gz nginx:1.27.21.4 删除镜像删除时的输出提示Untagged镜像的唯一标识是其 ID 和摘要 而一个镜像可以有多个标签首先需要做的是将满足我们要求的所有镜像标签都取消这就是我们看到的 Untagged 的信息。因为一个镜像可以对应多个标签 因此当我们删除了所指定的标签后 可能还有别的标签指向了这个镜像 如果是这种情况 那么 Delete 行为就不会发生。 所以并非所有的 docker image rm 都会产生删除镜像的行为 有可能仅仅是取消了某个标签而已。Deleted当该镜像所有的标签都被取消了 该镜像很可能会失去了存在的意义 因此会触发删除行为。镜像是多层存储结构 因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易 因此很有可能某个其它镜像正依赖于当前镜像的某一层。 这种情况 依旧不会触发删除该层的行为。 直到没有任何层依赖当前层时 才会真实的删除当前层。除了镜像依赖以外 还需要注意的是容器对镜像的依赖。 如果有用这个镜像启动的容器存在 即使容器没有运行 那么同样不可以删除这个镜像二.容器相关操作2.1 启动容器Docker运行容器前需要本地存在对应的镜像如果本地不存在该镜像Docker会从镜像仓库下载该镜像。容器内的第一个进程必须一直处于运行的状态否则这个容器就会处于退出状态1createstart运行容器docker create 镜像名 docker start 容器名 docker restart 容器名2run运行容器docker run [OPTIONS] IMAGE [COMMAND] [ARG...] docker exec [OPTIONS] CONTAINER COMMAND [ARG...]后台运行容器docker run --name web1 -d -p 8888:80 nginx:1.14-alpine交互式运行容器docker run --name busybox1 -it busybox:1.36 /bin/sh / # ls bin dev etc home lib lib64 proc root sys tmp usr var #退出容器 / # exit2.2 查看容器信息1查看容器信息#查看正在运行的容器 docker psdocker container ls #查看所有容器包括正在运行的和已经停止的 docker ps -a docker container ls -a #查看所有容器的id信息 docker ps -a -q #查看所有已退出容器的id信息 docker ps -aq -f statusexited2查看端口映射信息[rootlocalhost dockerimages]# docker port web1 80/tcp - 0.0.0.0:8888 80/tcp - [::]:88883查看容器详细信息[rootlocalhost dockerimages]# docker inspect web1 | grep -i ipaddress SecondaryIPAddresses: null, IPAddress: 172.17.0.3, IPAddress: 172.17.0.3,4查看容器日志5动态查看容器占用内存和cpu信息docker stats web16查看容器中的进程docker top web12.3 删除容器docker stop 关闭运行的容器 docker kill 杀死运行的容器 -s指定信号和kill 用法一样-9 强制停止容器 docker rm [-f] docker container prune #删除所有处于终止状态的容器#删除所有已经停止的容器 docker container prune2.4 管理容器中的数据如果正在运行中的容器生成了新的数据或者修改了现有的一个已经存在的文件内容那么新产生的数据将会被复制到读写层进行持久化保存这个读写层也就是容器的工作目录。这就是“写时复制COWcopy on write”机制。容器中修改的数据内容会随着容器的消亡而消失如果想要将容器中数据永久保存则需要将容器中的数据保存到宿主机的指定目录中可以使用如下方式持久化容器数据使用docker管理的数据卷数据卷的使用 类似于 Linux 下对目录或文件进行 mount 镜像中的被指定为挂载点的目录中的文件会隐藏掉 能看到的是挂载的数据卷里面的内容。使用宿主机本地的目录作为数据卷将宿主机的目录挂载至容器然后让其它容器通过该容器读写宿主机的数据数据卷的特点1、数据卷是宿主机的目录或者文件并且可以在多个容器之间共同使用。2、在宿主机对数据卷内容更新后会在所有容器里面立即更新。3、数据卷的数据可以持久保存即使删除使用该数据卷的容器也不影响。4、在容器里面写入的数据不会影响到镜像本身。1运行容器后数据默认存储位置运行一个容器LowerDirimage镜像层只读MergedDir容器的文件系统使用Union FS将lowerdir和upperdir合并给容器使用UpperDir容器的上层读写WorkDir容器在宿主机的工作目录用来存放临时文件进入容器生成数据容器数据文件随着容器消亡2使用数据卷持久化数据查看指定数据卷的信息启动一个挂载数据卷的容器在用docker run命令的时候 使用-v标记来将数据卷挂载到容器里。 在一次docker run中可以挂载多个数据卷 。创建一个名为 web2的容器 并加载一个数据卷到容器的 /usr/share/nginx/html/ 目录删除数据卷会提示不允许删除在宿主机修改数据卷中文件内容访问容器时信息会发生变化删除容器数据卷仍在数据卷是被设计用来持久化数据的 它的生命周期独立于容器 Docker不会在容器被删除后自动删除数据卷 。如果需要在删除容器后移除数据卷可以使用如下命令docker volume rm my-vol由于不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷无主的数据卷可能会占据很多空间 所以要清理请使用以下命令docker volume prune -af查看镜像、容器、数据卷所占用的空间4通过创建的数据卷容器来读写宿主机的数据创建数据卷容器创建容器使用数据卷容器的文件删除web2web2-client1仍然可以使用该卷三.镜像构建当我们从docker镜像仓库中下载的镜像不能满足我们的需求时我们可以通过以下两种方式对镜像进行更改1从已经创建的容器中更新镜像并且提交这个镜像2使用Dockerfile指令来创建一个新的镜像3.1基于容器制作镜像docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] Options参数 -a作者例如“along alongalong.com” -c修改Dockerfile指令应用于创建的镜像 -m提交的描述信息即记录本次修改的内容 -p在提交期间暂停容器默认为true① 先运行一个容器我们修改了容器的文件 也就是改动了容器的存储层。#查看宿主机的内核版本 [rootlocalhost ~]# uname -r 5.14.0-362.8.1.el9_3.x86_64 [rootlocalhost ~]# docker run --name b1 -it busybox:latest #查看容器的内核版本 / # uname -r 5.14.0-362.8.1.el9_3.x86_64 / # ls / bin etc lib proc sys usr dev home lib64 root tmp var / # mkdir -p /data/html / # echo busybox httpd server /data/html/index.html / # cat /data/html/index.html busybox httpd server② 不用退出这个容器另起终端在b1容器基础上制作新镜像定制好了变化 现在我们希望能将其保存下来形成镜像。要知道 当我们运行一个容器的时候 如果不使用卷的话 我们做的任何文件修改都会被记录于容器存储层里。 而 Docker 提供了一个 docker commit 命令 可以将容器的存储层保存下来成为镜像。换句话说 就是在原有镜像的基础上 再叠加上容器的存储层 并构成新的镜像。 以后我们运行这个新镜像的时候 就会拥有原有容器最后的文件变化。[rootlocalhost ~]# docker commit -p b1 sha256:eac3bfb08e59c4594bc3823fd1d4da77ac24c75c6d5a5cdc5123b468e7a4feac [rootlocalhost ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE none none eac3bfb08e59 6 seconds ago 4.26MB③ 给新制作的镜像打标签rootlocalhost ~]# docker tag eac3b httpd:v1.0 [rootlocalhost ~]# docker image ls httpd:v1.0 REPOSITORY TAG IMAGE ID CREATED SIZE httpd v1.0 eac3bfb08e59 2 minutes ago 4.26MB⑤ 删除同一镜像的标签只是把这个镜像的标签去掉直到删除这个镜像的最后一个标签此镜像才会被删除。⑥ 基于新的镜像运行一个容器验证是否是基于b1创建成功[rootlocalhost ~]# docker run --name b2 -it --rm httpd:v1.0 / # cat /data/html/index.html busybox httpd server注意使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念 但是实际环境中并不会这样使用。使用 docker commit 意味着所有对镜像的操作都是黑箱操作 生成的镜像也被称为黑箱镜像 换句话说 就是除了制作镜像的人知道执行过什么命令、 怎么生成的镜像 别人根本无从得知。而且 即使是这个制作镜像的人 过一段时间后也无法记清具体的操作。镜像所使用的是分层存储除当前层外之前的每一层都是不会发生改变的也就是说任何修改的结果仅仅是在当前层进行标记、添加、修改而不会改动上一层所以每一次修改都会让镜像更加臃肿一次。 比如 删除前一层文件的操作 实际不是真的删除前一层的文件 而是仅在当前层标记为该文件已删除。 在最终容器运行的时候 虽然不会看到这个文件 但是实际上该文件会一直跟随镜像。 因此 在构建镜像的时候 需要额外小心 每一层尽量只包含该层需要添加的东西 任何额外的东西应该在该层构建结束前清理掉。3.2基于dockerfile制作镜像DockerFile可以说是一种可以被Docker程序解释的脚本DockerFile是由一条条的命令组成的每条命令对应linux下面的一条命令Docker程序将这些DockerFile指令再翻译成真正的linux命令。dockerfile有自己的书写方式和支持的命令Docker程序读取DockerFile并根据指令生成Docker镜像相比手动制作镜像的方式DockerFile更能直观的展示镜像是怎么产生的有了写好的各种各样DockerFile文件当后期某个镜像有额外的需求时只要在之前的DockerFile添加或者修改相应的操作即可重新生成新的Docke镜像避免了重复手动制作镜像的麻烦具体如下FROM #FROM用来指定一个基础镜像并且FROM 指令必须是 Dockerfile 中非注释行的第一个指令docker build会在docker主机上查找指定的镜像文件不写镜像标签默认为latest。在其不存在时则会自动从 Docker 的公共库 pull 镜像下来。如果找不到指定的镜像文件docker build 会返回一个错误信息除了选择现有镜像为基础镜像外 Docker 还存在一个特殊的镜像 名为 scratch 。 这个镜像是虚拟的概念并不实际存在它表示一个空白的镜像。如果你以 scratch 为基础镜像的话 意味着你不以任何镜像为基础 接下来所写的指令将作为镜像第一层开始存在。 不以任何系统为基础直接将可执行文件复制进镜像的做法并不罕见 比如swarm 、coreos/etcd 。 LABEL #LABEL用来描述镜像的元数据信息的键值对。label可以用来给镜像添加一些自定义的属性比如镜像的版本、作者、描述等。 COPY #从上下文目录中复制文件或目录到容器里指定的路径要复制的源文件或目录可以是多个支持使用通配符例如COPY hom* hom?.txt /mydir/如果源是目录则会复制该目录下的所有文件不会复制目录本身想复制目录本身需要使用该方式COPY yum.repos.d /etc/yum.repos.d/即目标需要有个文件名和目录名同名如果目标文件不存在则会自动创建。 ADD #ADD 指令类似于COPY指令ADD支持使用TAR文件和URL路径如果是一个本地系统上的压缩格式的tar文件它将被展开为一个目录其行为类似于tar -x命令然而通过URL获取到的tar文件将不会自动展开。 ENV #定义所需的环境变量并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)所调用语法ENV key value一次设置一个变量 或 ENV keyvalue ... EXPOSE #当要将容器中的端口映射至宿主机上时使用-P选项会自动映射该端口例如EXPOSE 80/tcp USER #用于指定运行image时或执行Dockerfile中任何RUN、CMD或ENTRYPOINT指令指定的程序时的用户名或UID用户得事先存在。 VOLUME #用于在image中创建一个挂载点目录以挂载宿主机上的卷或其它容器上的卷。例如VOLUME /data这里的 /data 目录就会在运行时自动挂载为匿名卷任何向 /data 中写入的信息都不会记录进容器存储层从而保证了容器存储层的无状态化。可以使用docker run -v选项覆盖这个挂载设置。 WORKDIR #用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指令设定工作目录WORKDIR后面可以是绝对路径或者相对路径如该目录不存在 WORKDIR 会帮你建立目录WORKDIR也可调用由ENV指令定义的变量。语法 WORKDIR /etc RUN #用于指定docker build过程中运行的程序其可以是任何命令语法RUN command 或RUN [executable, param1, param2]第一种格式中通常是一个shell命令且以“/bin/sh -c”来运行它第二种语法格式中的参数是一个JSON格式的数组其中为要运行的命令的可执行文件后面的为传递给命令的选项或参数然而此种格式指定的命令不会以“/bin/sh -c”来发起因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行不过如果要运行的命令依赖于此shell特性的话可以将其替换为类似下面的格式RUN [/bin/bash, -c, , ] CMD #为启动的容器指定默认要运行的程序且其运行结束后容器也将终止不过CMD指定的命令可以被docker run的命令行选项所覆盖在Dockerfile中可以存在多个CMD指令但仅最后一个会生效。语法CMD command或者 CMD [executable,param1,param2] 或 CMD [param1,param2]此种写法主要是为ENTRYPOINT指令提供默认参数此处的引号需要使用双引号单引号会报错 ENTRYPOINT #类似CMD指令的功能用于为容器指定默认运行程序与CMD不同的是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖而且这些命令行参数会被当作参数传递给ENTRYPOINT指令指定的程序不过docker run命令的 --entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序当指定了 ENTRYPOINT 后CMD 的含义就发生了改变不再是直接的运行其命令而是将CMD 的内容作为参数传给 ENTRYPOINT 指令 换句话说实际执行时将变为 ENTRYPOINT CMD。语法ENTRYPOINT command 或 ENTRYPOINT [executable, param1, param2]说明1#号开头的行为dockerfile中的注释2使用以下命令构建的镜像中会发现找不到/app/world.txt 文件因为每一个 RUN 都是启动一个容器、 执行命令、 然后提交存储层文件变更。 第一层 RUN cd /app 的执行仅仅是当前进程的工作目录变更 一个内存上的变化而已 其结果不会造成任何文件变更。 而到第二层的时候 启动的是一个全新的容器 跟第一层的容器更完全没关系 自然不可能继承前一层构建过程中的内存变化。 因此如果需要改变以后各层的工作目录的位置 那么应该使用 WORKDIR 指令。基于dockerfile制作镜像命令docker build [OPTIONS] PATH | URL | - #选项-t指定要创建的目标镜像名1PATH一般用.当前工作目录作为构建镜像的上下文路径 Docker 在运行时分为 Docker 引擎 也就是服务端守护进程 和客户端工具。 Docker 的引擎提供了一组REST API 被称为 Docker Remote API 而如 docker 命令这样的客户端工具 则是通过这组 API与Docker 引擎交互 从而完成各种功能。 因此 虽然表面上我们好像是在本机执行各种 docker 功能 但实际上 一切都是使用的远程调用形式在服务端 Docker 引擎 完成。 也因为这种 C/S 设计让我们操作远程服务器的 Docker 引擎变得轻而易举。 当我们进行镜像构建的时候 并非所有定制都会通过 RUN 指令完成 经常会需要将一些本地文件复制进镜像 比如通过 COPY 指令、 ADD 指令等。 而 docker build 命令构建镜像 其实并非在本地构建 而是在服务端 也就是 Docker 引擎中构建的。 那么在这种客户端/服务端的架构中 如何才能让服务端获得本地文件呢 这就引入了上下文的概念。 当构建的时候 用户会指定构建镜像上下文的路径 docker build 命令得知这个路径后 会将路径下的所有内容打包 然后上传给 Docker 引擎。 这样Docker 引擎收到这个上下文包后 展开就会获得构建镜像所需的一切文件。 那么为什么会有人误以为 . 是指定 Dockerfile 所在目录呢 这是因为在默认情况下 如果不额外指定 Dockerfile 的话 会将上下文目录下的名为 Dockerfile 的文件作为Dockerfile。这只是默认行为 实际上 Dockerfile 的文件名并不要求必须为 Dockerfile 而且并不要求必须位于上下文目录中 比如可以用 -f ../Dockerfile.php 参数指定某个文件作为Dockerfile一般大家习惯性的会使用默认的文件名 Dockerfile 以及会将其置于镜像构建上下文目录中 。 2URL docker build还支持从URL构建docker build http://server/context.tar.gz 如果所给出的URL是个tar压缩包那么docker引擎会下载这个包并自动解压以其作为上下文开始构建。 3- docker build还支持从标准输入中读取Dockerfile进行构建 - docker build - Dockerfile 或者cat Dockerfile | docker build - 如果标准输入传入的是文本文件则将其视为Dockerfile并开始构建。这种形式由于直接从标准输入中读取Dockerfile的内容它没有上下文因此不可以像其他方法那样可以将本地文件COPY进镜像之类的事情。 - docker build - context.tar.gz 如果发现标准输入的文件格式是 gzip 、 bzip2 以及 xz 的话 将会使其为上下文压缩包直接将其展开 将里面视为上下文 并开始构建拉取基础镜像3.2.1 制作yum版本nginx镜像制作镜像4.2.2 优化nginx镜像选择最精简的基础镜像减少镜像的层数清理镜像构建的中间产物使用多阶段构建减小nginx镜像体积四.docker的网络模式4.1Docker的四种网络模式当你安装docker时它会自动创建三个网络可使用如下命令查看Bridge此模式会为每一个容器分配、设置IP等。使用--netbridge指定默认设置。host容器将不会虚拟出自己的网卡配置自己的IP等而是使用宿主机的IP和端口。使用--nethost指定。None该模式关闭了容器的网络功能。使用--netnone指定。Container创建的容器不会创建自己的网卡配置自己的IP而是和一个指定的容器共享IP、端口范围。使用--netcontainer:NAMEorID指定。4.2None网络模式使用none模式Docker容器拥有自己的Network Namespace但是并不为Docker容器进行任何网络配置。也就是说这个Docker容器没有网卡、IP、路由等信息只有lo 网络接口。需要我们自己为Docker容器添加网卡、配置IP等。不参与网络通信运行于此类容器中的进程仅能访问本地回环接口仅适用于进程无须网络通信的场景中例如备份、进程诊断及各种离线任务等。None模式示意图如下所示4.3Host网络模式如果启动容器的时候使用host模式那么这个容器将不会获得一个独立的Network Namespace而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡配置自己的IP等而是使用宿主机的IP和端口。但是容器的其他方面如文件系统、进程列表等还是和宿主机隔离的。Host模式示意图如下所示4.4container网络模式这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace而不是和宿主机共享。新创建的容器不会创建自己的网卡配置自己的 IP而是和一个指定的容器共享 IP、端口范围等。同样两个容器除了网络方面其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。Container模式示意图如下1先用bridge网络模式启动容器1[rootlocalhost ~]# docker run -d --name web4 nginx:1.27.2-alpine e5c5ae64f304fd37af4952659b4139f0d478bee732bc784ede8c36c93f638f76 [rootlocalhost ~]# docker inspect web4 | grep -i ipaddress SecondaryIPAddresses: null, IPAddress: 172.17.0.3, IPAddress: 172.17.0.3,2再使用Container 网络模式创建容器2[rootlocalhost ~]# docker run --name busybox --rm -it --network container:web4 busybox:latest / # ip a 1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 43: eth0if44: BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever #web1上启动的nginx服务在该容器中可以直接访问 / # wget -O - -q 172.17.0.3 !DOCTYPE html html head titleWelcome to nginx!/title style html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } /style /head body h1Welcome to nginx!/h1 pIf you see this page, the nginx web server is successfully installed and working. Further configuration is required./p pFor online documentation and support please refer to a hrefhttp://nginx.org/nginx.org/a.br/ Commercial support is available at a hrefhttp://nginx.com/nginx.com/a./p pemThank you for using nginx./em/p /body /html #注意文件系统并不共享 / # ls /usr/share ls: /usr/share: No such file or directory4.5Bridge网络模式安装docker时会自动创建一个docker0网桥运行容器时你可以使用docker run --networkNETWORK选项指定容器应连接到哪个网络否则Docker守护程序默认将容器连接到docker0虚拟网桥通过docker0网桥以及Iptables nat表配置与宿主机通信。Docker 随机分配一个本地未占用的私有网段 在 RFC1918 中定义 中的一个地址给docker0 接口。 此后启动的容器内的网卡也会自动分配一个同一网段的地址。docker0的IP地址则为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备Docker将veth pair设备的一端放在新创建的容器中并命名为eth0容器的网卡另一端放在主机中以vethxxx这样类似的名字命名并将这个网络设备加入到docker0网桥中bridge模式示意图如下图所示。可以通过brctl show命令查看。[rootlocalhost ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.02421e0f2f31 no [rootlocalhost ~]# ip a show docker0 3: docker0: NO-CARRIER,BROADCAST,MULTICAST,UP mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:1e:0f:2f:31 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:1eff:fe0f:2f31/64 scope link valid_lft forever preferred_lft forever通过这种方式 主机可以跟容器通信 容器之间也可以相互通信。 Docker 就创建了在主机和所有容器之间一个虚拟共享网络。注bridge模式是docker的默认网络模式不写--net参数就是bridge模式。使用docker run -p时docker实际是在iptables做了DNAT规则实现端口转发功能。可以使用iptables -t nat -vnL查看。4.6容器间的通信4.6.1同一台主机上的容器间的通信1容器都使用默认的Bridge网络模式两个容器使用ip地址互相访问容器间使用名字互相访问五.docker compose单机容器编排工具5.1compose的yml文件格式#用#表示注释 services: #服务字段必选项 web: #自定义服务名称唯一 image: nginx #镜像名称 build: ./dir #指定dockerfile所在路径 container_name: nginx #容器名称 restart: always #设定容器失败时总是自动重启 expose: #指定容器暴露哪些端口不会映射到主机的端口 - 80 ports: - 80:80 volumes: - ./conf.d/:/etc/nginx/conf.d/:ro #将本机的文件以只读方式映射至目标容器中 - db-data:/var/lib/backup/data network_mode: bridge #使用docker默认创建的bridge网络 volumes: db-data: networks: mynet1: driver: bridge mynet2: driver: bridge external: true #不创建新的网络而使用已经创建好的网络如果网络不存在会报错六.docker资源限制6.1容器的内存限制Docker可以强制执行硬性内存限制即只允许容器使用给定的内存大小。 Docker 也可以执行非硬性内存限制即容器可以使用尽可能多的内存除非内核检测到主机上的内存不够用了。如果容器没有做内存使用限制则该容器可以利用到系统内存最大空间默认创建的容器没有做内存资源限制。docker run参数说明-m或者--memory #表示容器可以使用的最大内存量硬限制--memory-reservation #指定小于--memory的软限制当docker检测到主机上的内存不足时会激活该限制该值不能超过。#运行容器未指定内存限制 [rootlocalhost stress-ng]# docker run --rm -it --name busybox busybox:latest /bin/sh/ # #在另外一个终端查看当前容器的内存限制可以看到未做任何限制和物理机可使用大小一致 [rootlocalhost ~]# docker stats busybox CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS cab5f5417a55 busybox 0.00% 892KiB / 3.532GiB 0.02% 2.42kB / 0B 0B / 0B 1 #使用-m选项设定硬限制 [rootlocalhost stress-ng]# docker run -m 256m --rm -it --name busybox busybox:latest /bin/sh / # #另一个终端查看当前容器的内存限制可以看到限制为256MB [rootlocalhost ~]# docker stats busybox CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS ed4193744afb busybox 0.00% 892KiB / 256MiB 0.34% 2.42kB / 0B 0B / 0B 1 #容器的硬限制不能比软限制小 [rootlocalhost stress-ng]# docker run -m 128m --memory-reservation 256m --rm -it --name busybox busybox:latest /bin/sh docker: Error response from daemon: Minimum memory limit can not be less than memory reservation limit, see usage. See docker run --help. [rootlocalhost stress-ng]# docker run -m 256m --memory-reservation 256m --rm -it --name busybox busybox:latest /bin/sh #查看容器内存限制 [rootlocalhost ~]# docker stats busybox CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 07a467461a60 busybox 0.00% 884KiB / 256MiB 0.34% 2.49kB / 0B 0B / 0B 1 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 07a467461a60 busybox 0.00% 884KiB / 256MiB 0.34% 2.49kB / 0B 0B / 0B 1 # --memory限制容器可用的物理内存为200MB--memory-swap限制内存 Swap交换分区的总使用量为 200MB这里等于禁用swap/dev/shm使用内存作为存储介质Docker 会默认分配64MB的共享内存空间可以使用 --shm-size自定义/dev/shm的大小 [rootdocker1 ~]# docker run -it --memory200M --memory-swap200M busybox:latest / # dd if/dev/zero of/dev/shm/test bs1M count200 dd: error writing /dev/shm/test: No space left on device / # dd if/dev/zero of/dev/shm/test bs1M count18 #指定--shm-size200M即/dev/shm使用所有的200M内存空间创建文件时无法创建该大小 [rootdocker1 ~]# docker run -it --memory200M --memory-swap200M --shm-size200M busybox:latest / # dd if/dev/zero of/dev/shm/test bs1M count200 Killed6.2容器的CPU限制一个宿主机有几十个核心的CPU但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的任务多进程共用一个CPU的核心依赖技术就是为可压缩资源即一个核心的CPU可以通过调度而运行多个进程但是同一个单位时间内只能有一个进程在CPU上运行那么这么多的进程怎么在CPU上执行和调度的呢 实时进程动态优先级为0-99的进程采用实时调度算法调度。 普通进程动态优先级为100-139的进程采用完全公平调度算法调度CFSCompletely Fair Scheduler。 nice值-20-19用于调整普通进程优先级的参数对应100-139的进程优先级。 默认情况下每个容器对主机CPU周期的访问权限是不受限制的但是我们可以设置各种约束来限制给定容器访问主机的CPU周期大多数用户使用的是默认的CFS调度方式在Docker1.13及更高版本中还可以配置实时优先级。docker run参数说明--cpus#指定容器可以使用多少可用CPU资源最大不能超过宿主机的核心数。如果主机有两个CPU并且设置了--cpus1.5那么该容器将保证最多可以访问1.5个CPU如果是4核CPU那么可以每个核心上用一点总计还是1.5核心使用--cpu-period 和 --cpu-quota参数 #这两个参数用于更精细的 CPU 资源控制。--cpu-period 设置评估周期单位为微秒范围在10001毫秒到10000001秒之间--cpu-quota 设置在这个评估周期内的 CPU 配额单位也为微秒。cpu-quota/cpu-period 的结果即为实际分配给容器的 CPU 量如果是小数表示分配的 CPU 量不足一个 vCPU如果大于1则表示分配的 CPU 量超过一个 vCPU。--cpuset-cpus参数通过该参数可以指定容器能够运行在哪些 CPU 核心上。参数值可以是一个逗号分隔的 CPU 编号列表或者是一个范围如0-3表示第0、1、2和3核心。设置 CPU 权重--cpu-shares参数该参数用于设置容器使用 CPU 的相对权重默认值为1024。当多个容器竞争 CPU 资源时权重较高的容器会获得更多的 CPU 时间。但只有在 CPU 资源紧张的情况下这种按权重分配 CPU 的方式才会生效。#限制容器使用cpu数量 [rootlocalhost stress-ng]# docker run -it --cpus 2 --rm --name stress-ng stress-ng:v1 /bin/sh / # stress-ng --cpu 2 stress-ng: info: [8] defaulting to a 1 day run per stressor stress-ng: info: [8] dispatching hogs: 2 cpu #设定使用0号cpu [rootlocalhost stress-ng]# docker run -it --cpuset-cpus 0 --rm --name stress-ng stress-ng:v1 /bin/sh6.3限制docker磁盘IO#指定容器使用磁盘io的速率为30M [rootdocker1 ~]# docker run -it --rm --device-write-bps /dev/nvme0n1:30M busybox / # dd if/dev/zero of/file bs1M count200 #开启容器后会发现速度和设定不匹配是因为系统的缓存机制 2000 records in 2000 records out 209715200 bytes (200.0MB) copied, 0.044561 seconds, 4.4GB/s / # dd if/dev/zero of/file bs1M count200 oflagdirect #设定dd命令直接写入磁盘 2000 records in 2000 records out 209715200 bytes (200.0MB) copied, 6.652777 seconds, 30.1MB/s6.4解决docker的默认资源隔离LXCFS 是一个为 LXCLinux Containers容器提供增强文件系统功能的工具。主要功能资源可见性LXCFS 可以使容器内的进程看到准确的 CPU、内存和磁盘 I/O 等资源使用信息。在没有 LXCFS 时容器内看到的资源信息可能不准确这会影响到在容器内运行的应用程序对资源的评估和管理。性能监控方便对容器内的资源使用情况进行监控和性能分析。通过提供准确的资源信息管理员和开发人员可以更好地了解容器化应用的性能瓶颈并进行相应的优化。[rootdocker1 ~]# cat /etc/yum.repos.d/epel.repo [epel] nameepel baseurlhttps://mirrors.aliyun.com/epel/9/Everything/x86_64/ gpgcheck0 [rootdocker1 ~]# systemctl start lxcfs [rootdocker1 ~]# ps -ef | grep lxc root 5454 1 0 12:55 ? 00:00:00 /usr/bin/lxcfs /var/lib/lxcfs 运行lxcfs并解决容器隔离性 [rootdocker1 docker]# docker run -it -m 256m \ -v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \ -v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \ -v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \ -v /var/lib/lxcfs/proc/stat:/proc/stat:rw \ -v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \ -v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \ ubuntu rootcdf22cfd7002:/# free -m total used free shared buff/cache available Mem: 256 1 254 0 0 254 Swap: 0 0 06.5 提升容器权限[rootdocker1 ~]# docker run -it --rm busybox:latest / # ip a 1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0if52: BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN mtu 1500 qdisc noqueue link/ether 6e:6b:09:38:31:ba brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever / # ip a a 192.168.168.168/24 dev eth0 ip: RTNETLINK answers: Operation not permitted这是因为容器使用的很多资源都是和系统真实主机公用的如果允许容器修改这些重要资源系统的稳定性会变的非常差但是由于某些需要求容器需要控制一些默认控制不了的资源如何解决此问题这时我们就要设置容器特权--privilegedtrue 的权限非常大接近于宿主机的权限为了防止用户的滥用需要增加限制只提供给容器必须的权限。此时Docker 提供了权限白名单的机制使用--cap-add添加必要的权限rootdocker1 ~]# docker run -it --rm --cap-add NET_ADMIN busybox:latest #可以设定网络 / # ip a a 192.168.168.168/24 dev eth0 / # ip a 1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0if60: BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN mtu 1500 qdisc noqueue link/ether 02:8d:5e:43:7f:f9 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever inet 192.168.168.168/24 scope global eth0 valid_lft forever preferred_lft forever #无法查看磁盘 / # fdisk -l
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →