拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Docker 容器与网络

Docker学习笔记容器与网络本文是 Docker 学习笔记的“容器”“网络”的整理包含总结和详细。适合正在学习 Docker 容器操作、资源限制、底层技术以及容器网络通信的同学参考。实验环境CentOS Stream 8 Docker 26.1.3。文章目录Docker学习笔记容器与网络[toc]前言容器01 如何运行容器02 两种进入容器的方法03 运行容器的最佳实践04 容器常用操作05 容器状态机06 限制容器对内存的使用07 限制容器对CPU的使用08 export和import容器09 容器底层实现技术容器常用操作命令小结网络01 none和host网络的适用场景02 学容器必须懂bridge网络03 自定义网络04 理解容器之间的连通性05 容器间通信的三种方式06 容器如何访问外部世界07 外部世界如何访问容器总结前言最近系统学习了 Docker 的容器和网络部分。容器部分涉及生命周期管理、资源限制、底层 cgroup 和 namespace网络部分涉及 none/host/bridge 三种原生网络、自定义网络、容器间通信、NAT 和端口映射。学完之后对“容器到底是什么”“容器怎么通信”有了更清晰的认识。容器01 如何运行容器docker run是启动容器的方法它等价于docker createdocker start。容器启动时执行的命令可以通过三种方式指定Dockerfile 中的CMD指令Dockerfile 中的ENTRYPOINT指令在docker run命令行中指定。容器的生命周期依赖于启动时执行的命令只要该命令不结束容器就不会退出。使用-d参数可以让容器以后台方式运行。可以通过--name为容器命名后续操作可使用短ID、长ID或名称来指定容器。# 使用 ubuntu 镜像创建容器此时容器处于 Created 状态dockercreate ubuntu# 查看所有容器包括未启动的dockerps-a# 启动刚创建的容器dockerstart e1aa0ca86b2# 再次查看状态发现容器已退出Exited因为 /bin/bash 执行完就结束了dockerps-a# 直接运行并执行 pwd 命令容器执行完立即退出dockerrun ubuntupwd# 输出/# 让容器长期运行执行一个死循环dockerrun ubuntu /bin/bash-cwhile true; do sleep 1; echo hahaha; done# 此时容器保持运行但占用当前终端# 使用 -d 后台运行dockerrun-dubuntu /bin/bash-cwhile true; do sleep 1; echo hahaha; done# 返回容器长ID# 查看运行中的容器dockerps# 可以看到容器状态为 Up# 停止容器dockerstop 081be2bc2e1d# 通过 --name 指定容器名dockerrun--namemy_http_server-dhttpddockerps# 可以看到 NAMES 列显示 my_http_server注意docker run如果指定了命令容器执行完该命令就会退出。想要保持运行需要让命令一直不结束比如while true或运行一个守护进程。02 两种进入容器的方法进入容器有两种常用方法docker attachattach 到容器启动命令的终端不会启动新的进程。退出方式为Ctrlp然后Ctrlq。docker exec在容器中打开新的终端并且可以启动新的进程。常用docker exec -it container bash|sh。如果想直接查看启动命令的输出用attach其他情况使用exec。查看容器日志推荐使用docker logs -f。# 启动一个持续输出日志的容器dockerrun-dubuntu /bin/bash-cwhile true; do sleep 1; echo I_am_in_container; done# 假设返回长ID dc508b94447f...# 使用 attach 进入dockerattach dc508b94447f# 会看到每隔一秒打印 I_am_in_container# 按 Ctrlp 再按 Ctrlq 退出 attach容器继续运行# 使用 exec 进入容器dockerexec-itdc508b94447fbash# 进入后可以执行命令ps-elf# 可以看到容器启动进程 while 以及当前 bash 进程exit# 使用 docker logs -f 查看日志dockerlogs-fdc508b94447f# 持续打印输出类似 tail -f踩坑docker attach退出时如果直接按CtrlC可能会把容器主进程杀掉导致容器停止。推荐用CtrlpCtrlq组合键退出。03 运行容器的最佳实践按用途容器可分为两类服务类容器以 daemon 形式运行对外提供服务如 web server、数据库。使用-d后台启动排查问题用exec -it进入。工具类容器提供临时工作环境通常以run -it方式运行如 busybox。执行exit退出终端同时容器停止。容器运行相关当CMD、ENTRYPOINT或docker run命令行指定的命令运行结束时容器停止。通过-d参数在后台启动容器。通过exec -it可进入容器并执行命令。指定容器的三种方法短ID、长ID、容器名称。可通过--name为容器命名docker rename重命名容器。# 服务类容器示例dockerrun--namemy_http_server-dhttpddockerps# 容器后台运行COMMAND 为 httpd-foreground# 工具类容器示例dockerrun-itbusybox# 进入交互式终端wgetwww.baidu.com# 可以访问互联网exit# 退出后容器停止# 重命名容器dockerrenamemy_http_server web_serverdockerps# NAMES 变为 web_server04 容器常用操作stop/start/restartdocker stop向容器进程发送 SIGTERM 信号停止容器。docker kill发送 SIGKILL 信号快速停止容器。docker start重新启动处于停止状态的容器保留第一次启动时的所有参数。docker restart依次执行docker stop和docker start。自动重启--restartalways无论容器因何种原因退出不包括docker stop立即重启。--restarton-failure:3如果启动进程退出代码非0则重启容器最多重启3次。pause/unpausedocker pause暂停容器处于暂停状态的容器不会占用 CPU 资源。docker unpause恢复运行。删除容器docker rm删除容器可一次指定多个。批量删除已退出容器docker rm -f $(docker ps -aq -f statusexited)删除所有容器慎用docker rm -f $(docker ps -aq)# 启动一个 httpd 容器dockerrun-d--namehttpd_test httpddockerps# 停止容器dockerstop httpd_testdockerps-a# 状态变为 Exited# 重新启动dockerstart httpd_testdockerps# 状态变为 Up# 重启容器dockerrestart httpd_test# 测试 --restartalwaysdockerrun-d--restartalways--namehttpd11 httpddockerattach httpd11# 按 CtrlC 终止容器进程触发重启dockerps# 发现 httpd11 已自动重启# 管理员停止容器不会自动重启dockerstop httpd11dockerps-a# 状态为 Exited未重启# 暂停和恢复dockerrun-d--namehttpd_pause httpddockerpause httpd_pausedockerps# 状态显示 Pauseddockerunpause httpd_pausedockerps# 状态恢复 Up# 删除容器dockerrm35af7150bd17 9769bb915803# 批量删除已退出容器dockerrm-f$(dockerps-aq-fstatusexited)注意docker rm -f $(docker ps -aq)会删除所有容器生产环境慎用05 容器状态机容器状态转换图描述了容器各种状态之间的转换关系。主要状态包括Created容器已创建但未启动。Running容器正在运行。Stopped容器已停止。Paused容器已暂停。Deleted容器已删除。docker create创建的容器处于 Created 状态docker start将以后台方式启动容器docker run命令实际上是docker create和docker start的组合。--restart生效条件只有当容器的启动进程退出时正常退出或非正常退出如 OOM--restart才生效。如果容器是因为执行docker stop或docker kill退出则不会自动重启。# 创建容器状态为 Createddockercreate httpddockerps-a# STATUS 显示 Created# 启动容器dockerstart dcc931f8d444dockerps# STATUS 显示 Up# 测试 --restartalways 在进程退出时生效dockerrun-d--restartalways--namehttpd11 httpddockerattach httpd11# 按 CtrlC 触发进程退出dockerps-a# 发现 httpd11 已自动重启状态为 Up# 管理员停止容器不会触发重启dockerstop httpd11dockerps-a# 状态为 Exited未重启06 限制容器对内存的使用Docker 通过 cgroup 来控制容器使用的资源配额包括 CPU、内存、磁盘三大方面。cgroup 是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源的机制。cgroup 子系统包括blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns、perf_event 等。内存限额相关参数-m或--memory设置内存的使用限额例如 100M、2G。--memory-swap设置内存 swap 的使用限额。默认情况下如果--memory-swap设置为 0 或不设置则容器可以使用的 swap 大小为-m值的两倍。如果--memory-swap的值和-m值相同则容器不能使用 swap。如果--memory-swap值为 -1表示容器程序使用的内存受限而可以使用的 swap 空间不受限制。# 构建 stress 工具镜像vimDockerfile# 内容# FROM ubuntu# MAINTAINER xianyu xianyuqq.com# RUN apt-get -y update apt-get -y install stress# ENTRYPOINT [/usr/bin/stress]dockerbuild-tubuntu-with-stress.# 运行容器限制内存 200Mswap 300M分配 280M 内存dockerrun-it-m200M --memory-swap300M ubuntu-with-stress--vm1--vm-bytes 280M-v# 输出显示 stress 正常工作分配和释放内存# 分配超过 300M 内存dockerrun-it-m200M --memory-swap300M ubuntu-with-stress--vm1--vm-bytes 310M# stress 报错容器退出# 输出stress: FAIL: [1] (416) -- worker 7 got signal 9# 只指定 -m不指定 --memory-swapdockerrun-it-m200M ubuntu-with-stress# 容器最多使用 200M 物理内存和 200M swap07 限制容器对CPU的使用默认设置下所有容器可以平等地使用 host CPU 资源并且没有限制。Docker 可以通过-c或--cpu-shares设置容器使用 CPU 的权重。如果不指定默认值为 1024。--cpu-shares的值不能保证可以获得 1 个 vCPU 或者多少 GHz 的 CPU 资源仅仅只是一个弹性的加权值。只有在同时运行多个容器时容器的 CPU 加权的效果才能体现出来。这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下。如果某个容器处于空闲状态其他容器也可以分配到全部可用的 CPU。# 启动 container_Acpu share 为 1024dockerrun--namecontainer_A-it-c1024ubuntu-with-stress--cpu4-v# 再开一个窗口启动 container_Bcpu share 为 512dockerrun--namecontainer_B-it-c512ubuntu-with-stress--cpu4-v# 在 host 中执行 top查看 CPU 使用情况top# 可以看到两个容器的 CPU 占用比例约为 2:1# 如果 container_A 空闲container_B 可以用满整颗 CPU08 export和import容器docker export将容器导出为一个 tar 包不管容器是否处于运行状态。导出文件会丢失所有元数据和历史记录仅保存容器当时的状态。docker import将 export 导出的 tar 包导入成为镜像可指定 repository 和 tag。docker save将镜像保存为文件save 会保存该镜像的所有元数据和历史记录。对比docker export导出的文件不能使用docker load导入docker save导出的文件不能使用docker import导入会报错。# 创建容器dockerrun-d--namehttpd1 httpddockerps-a# 导出容器为 tar 包dockerexporthttpd1-omyhttpd.tarls# 导入为镜像dockerimportmyhttpd.tar myweb:v1dockerimages# 可以看到 myweb:v1但丢失了历史记录# 尝试用 docker load 导入 export 文件会报错dockerload-imyhttpd.tar# 输出open /var/lib/docker/tmp/docker-import-.../boot/json: no such file or directory09 容器底层实现技术容器底层最重要的两种技术是cgroup和namespacecgroup实现资源限额可以设置进程使用 CPU、内存和 IO 资源的限额。namespace实现资源隔离包括UTS namespace让容器有自己的 hostname。IPC namespace让容器拥有自己的共享内存和信号量。PID namespace让容器拥有独立的进程空间。Network namespace让容器拥有自己独立的网卡、IP、路由等资源。User namespace让容器能够管理自己的用户host 不能看到容器中创建的用户。# cgroup 示例设置 --cpu-shares512dockerrun-it--cpu-shares512ubuntu-with-stress-c1-v# 查看容器IDdockerps# 在 /sys/fs/cgroup/cpu/docker/容器长ID/cpu.shares 中可以看到 512# UTS namespace设置 hostnamedockerrun-hmyhost-itubuntu rootmyhost:/# hostnamemyhost# User namespace在容器中创建用户dockerexec-ita80f1f8b692cbashroota80f1f8b692c:/# useradd qdroota80f1f8b692c:/# exit# 在宿主机中查看该用户su- gaoqd# 输出su: user gaoqd does not exist容器常用操作命令小结命令说明create创建容器run运行容器pause暂停容器unpause取消暂停继续运行容器stop发送 SIGTERM 停止容器kill发送 SIGKILL 快速停止容器start启动容器restart重启容器attachattach 到容器启动进程的终端exec在容器中启动新进程通常使用-it参数logs显示容器启动进程的控制台输出用-f持续打印rm从磁盘中删除容器网络01 none和host网络的适用场景Docker 安装时会自动在 host 上创建三个网络bridge、host、none。可用docker network ls查看。none 网络driver 类型是 nullIPAM 字段为空。挂在这个网络下的容器除了 lo没有其他任何网卡。容器创建时可以通过--networknone指定使用 none 网络。适用场景一些对安全性要求高并且不需要联网的应用比如生成随机密码的容器。host 网络挂在 host 网络上的容器共享宿主机的 network namespace即容器的网络配置与 host 网络配置完全一样。可以通过--networkhost指定。使用场景对网络传输效率有较高要求的容器或者让容器直接配置 host 网络比如某些跨 host 的网络解决方案。不便之处是牺牲灵活性要考虑端口冲突问题。# 查看网络列表dockernetworkls# 查看 none 网络详情dockernetwork inspect none# 使用 none 网络启动容器dockerrun-it--networknone busybox /# ifconfig# 只看到 lo没有 eth0# 使用 host 网络启动容器dockerrun-it--networkhost busybox /# ip a# 可以看到 host 的所有网卡包括 ens160、docker0 等/# hostnamedocker# hostname 与宿主机相同02 学容器必须懂bridge网络Docker 安装时会创建一个命名为docker0的 Linux bridge可以理解为一个软件交换机。如果不指定--network创建的容器默认都会挂到 docker0 上。创建容器时会创建一对 veth pair 接口一端在容器内即eth0另一端在本地并被挂载到 docker0 网桥名称以veth开头。通过这种方式主机可以跟容器通信容器之间也可以相互通信。# 安装 bridge-utilsyuminstall-ybridge-utils# 查看网桥brctl show# 输出 docker0此时没有接口# 创建容器dockerrun-itd--namebusybox1 busybox# 再次查看网桥brctl show# 可以看到 docker0 上挂了一个 veth 接口如 vethddb2744# 查看容器网络配置dockerexec-itbusybox1sh/# ip a# 看到 eth0if25IP 为 172.17.0.2/16# 在宿主机查看ipa# 看到 25: vethddb2744if24证明 veth pair 关系# 查看 bridge 网络详情dockernetwork inspect bridge# 看到 Subnet: 172.17.0.0/16Gateway: 172.17.0.103 自定义网络除了 none、host、bridge 这三个自动创建的网络用户也可以根据业务需要创建 user-defined 网络。Docker 提供三种 user-defined 网络驱动bridge、overlay 和 macvlan。overlay 和 macvlan 用于创建跨主机的网络。可通过 bridge 驱动创建类似前面默认的 bridge 网络dockernetwork create--driverbridge my_netDocker 会自动分配一个 IP 网段如 172.18.0.0/16。也可以自己指定 IP 网段dockernetwork create--driverbridge--subnet172.22.16.0/24--gateway172.22.16.1 my_net2只有使用--subnet创建的网络才能指定静态 IP--ip。# 创建自定义网络 my_netdockernetwork create--driverbridge my_net# 查看网桥新增 br-89f7bc11b602brctl show# 创建指定子网和网关的网络 my_net2dockernetwork create--driverbridge--subnet172.22.16.0/24--gateway172.22.16.1 my_net2 brctl show# 新增 br-ec761bc51778# 查看 my_net2 详情dockernetwork inspect my_net2# Subnet 为 172.22.16.0/24Gateway 为 172.22.16.1# 使用新网络启动容器dockerrun-it--networkmy_net2--namebusybox2 busybox /# ip a# eth0 的 IP 为 172.22.16.2/24# 指定静态 IPdockerrun-it--networkmy_net2--ip172.22.16.8--namebusybox3 busybox /# ip a# eth0 的 IP 为 172.22.16.8/2404 理解容器之间的连通性同一网络中的容器、网关之间都是可以通信的。不同网络默认不能通信因为 iptables 的 DOCKER-ISOLATION 规则 DROP 掉了网桥之间的双向流量。如果希望不同网络的容器通信可以通过docker network connect为容器添加一块其他网络的网卡。# busybox2 和 busybox3 都在 my_net2可以互通dockerexec-itbusybox2sh/# ping -c 3 172.22.16.8# 通# busybox2 ping busybox1默认 bridge 网络/# ping -c 3 172.17.0.2# 不通100% packet loss# 将 busybox1 连接到 my_net2dockernetwork connect my_net2 busybox1# 再次查看 busybox1 网络dockerexec-itbusybox1sh/# ip a# 新增 eth1IP 为 172.22.16.3# busybox2 再次 ping busybox1 的 my_net2 IPdockerexec-itbusybox2sh/# ping -c 3 172.22.16.3# 通了05 容器间通信的三种方式容器之间可通过 IP、Docker DNS Server 或 joined 容器三种方式通信。IP 通信两个容器要能通信必须要有属于同一个网络的网卡。通过--network指定网络或docker network connect加入网络。Docker DNS Server从 Docker 1.10 版本开始docker daemon 实现了一个内嵌的 DNS server使容器可以直接通过“容器名”通信。方法是在启动时用--name为容器命名。限制只能在 user-defined 网络中使用默认的 bridge 网络无法使用 DNS。joined 容器使用--networkcontainer:web1使两个或多个容器共享一个网络栈共享网卡和配置信息joined 容器之间可以通过 127.0.0.1 直接通信。适合不同容器中的程序希望通过 loopback 高效快速通信或者希望监控其他容器的网络流量。# 创建自定义网络dockernetwork create my_net2# 启动 bbox1 和 bbox2dockerrun-it--networkmy_net2--namebbox1 busyboxdockerrun-it--networkmy_net2--namebbox2 busybox# 在 bbox2 中 ping bbox1dockerexec-itbbox2sh/# ping -c 3 bbox1# 通DNS 解析成功# 在默认 bridge 网络中测试 DNSdockerrun-it--namebbox3 busyboxdockerrun-it--namebbox4 busyboxdockerexec-itbbox4sh/# ping -c 3 bbox3# 失败ping: bad address bbox3# joined 容器示例dockerrun-d-it--nameweb1 httpddockerrun-it--networkcontainer:web1 busybox /# wget 127.0.0.1# 成功获取 httpd 首页/# cat index.html# htmlbodyh1It works!/h1/body/html注意默认 bridge 网络不支持 DNS 解析容器名只有自定义网络才支持。06 容器如何访问外部世界容器默认就能访问外网关键机制是 NAT。在 docker host 上查看 iptables 规则iptables-tnat-S其中有一条-APOSTROUTING-s172.17.0.0/16!-odocker0-jMASQUERADE含义是如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包把它交给 MASQUERADE 处理。MASQUERADE 将包的源地址替换成 host 的地址发送出去即做了一次网络地址转换NAT。# 容器内 ping 外网dockerrun-itbusybox /# ping -c 3 www.baidu.com# 通# 在宿主机查看 NAT 规则iptables-tnat-S# 使用 tcpdump 观察地址转换yuminstall-ytcpdump# 窗口1监控 docker0tcpdump-idocker0-nicmp# 窗口2监控 ens160tcpdump-iens160-nicmp# 窗口3容器内 ping www.baidu.comdockerrun-itbusyboxping-c3www.baidu.com# 观察结果# docker0 上172.17.0.2 www.baidu.com# ens160 上192.168.108.30 www.baidu.com# 说明源地址被替换成了 host 的 IP07 外部世界如何访问容器答案是端口映射。Docker 可将容器对外提供服务的端口映射到 host 的某个端口外网通过该端口访问容器。容器启动时通过-p参数映射端口-p 80将容器 80 端口映射到 host 的随机端口。-p 8080:80将容器 80 端口映射到 host 的 8080 端口。每一个映射的端口host 都会启动一个docker-proxy进程来处理访问容器的流量。# 随机映射dockerrun-d-p80httpddockerps# 看到 PORTS: 0.0.0.0:32768-80/tcpdockerport容器ID# 输出80/tcp - 0.0.0.0:32768# 访问curl192.168.108.30:32768# 输出htmlbodyh1It works!/h1/body/html# 指定端口映射dockerrun-d-p8080:80 httpdcurl192.168.108.30:8080# 输出相同# 查看 docker-proxy 进程ps-elf|grepdocker-proxy# 可以看到多个 docker-proxy 进程# 实战安装 tomcatdockerpull tomcatdockerrun-itd-p8080:8080 tomcat# 访问 http://192.168.108.30:8080 显示 404# 进入容器dockerexec-it容器IDbash# 查看 webapps 为空lswebapps# 文件在 webapps.distlswebapps.dist# 替换mvwebapps.dist/* webapps# 再次访问成功显示 Tomcat 首页踩坑Tomcat 官方镜像默认webapps目录为空需要把webapps.dist里的内容移过去才能看到默认首页。总结“容器”介绍了容器的运行、进入、常用操作、状态机、内存和 CPU 限制、export/import、以及底层 cgroup 和 namespace 技术。通过实验掌握了docker run、docker create、docker start、docker exec、docker attach、docker stop、docker restart、docker pause等命令并学会了如何限制容器资源、查看容器状态、导出导入容器。“网络”介绍了 Docker 的 none、host、bridge 三种原生网络以及自定义网络、容器间通信、容器访问外网、外部访问容器。通过实验掌握了docker network create、docker network connect、docker network inspect、brctl show、iptables、tcpdump等工具理解了 veth pair、docker0 网桥、NAT、端口映射和 docker-proxy 的原理。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门