Docker原理

发布时间:2026/8/8 10:34:55
Docker原理 一、基础概念1、核心概念docker简单来说就是用容器化技术给应用程序封装独立的运行环境每个运行环境就是一个容器运行容器的计算机被称为宿主机。2、容器和虚拟机区别docker容器与虚拟机最大区别是docker容器之间共用同一个系统内核而每个虚拟机都包含一个操作系统的完整内核所以docker容器比虚拟机更轻、更小、启动速度更快。3、镜像镜像是容器的模板我们可以把镜像类比成软件安装包而容器是安装出来的软件。举个例子镜像与容器的关系就像是用模具做糕点docker镜像就像是模具而容器就像是糕点我们可以使用一个模具做出很多个糕点当然我们也可以把模具分享给其他人使用。4、镜像仓库Docker Registry镜像仓库就是用来存放、分享镜像的地方每个人都可以把自己的镜像上传到仓库里面然后其他人就可以下载镜像并且使用。docker的官方镜像仓库是Docker Hubhub.docker.com上面存储了很多人分享的docker镜像。二、一键安装docker系统版本Ubuntu 26.04 64位curl -fsSL https://gitee.com/tech-shrimp/docker_installer/releases/download/latest/linux.sh| bash -s docker --mirror Aliyunservice docker start参考资料https://github.com/tech-shrimp/docker_installer三、常用docker命令1、docker pull用来从镜像仓库下载镜像docker pull docker.io/library/nginx:latest 或者 docker pull nginxdocker.ioregistry仓库地址docker.io是官方仓库可省略librarynamespace命名空间作者名library是官方作者可省略nginx镜像名称latesttag标签版本号latest表示最新版本可省略镜像库存放一个镜像的不同版本示例#从私有仓库docker.n8n.io中下载由n8nio上传的最新版本n8n镜像docker pull docker.n8n.io/n8nio/n8n由于国内访问不到官方仓库需要先配置镜像站vi /etc/docker/daemon.json{registry-mirrors: [https://docker.1ms.run]}service docker restart现在再执行docker pull nginx就不会报错了其他相关参数docker pull --platformxxx nginx--platformxxx表示拉取特定CPU架构的镜像大部分情况pull会自动选择合适宿主机的镜像无需关注此参数2、docker images列出所有下载过的docker镜像docker imagesi Info → U In UseIMAGE ID DISK USAGE CONTENT SIZE EXTRAnginx:latest 5a88c9c45479 241MB 66MB3、docker rmi删除镜像docker rmi 镜像名IMAGE|镜像IDrootiZ2vc2x5d289zm85e8b60zZ:~# docker rmi 5a88c9c45479Untagged: nginx:latestDeleted: sha256:5a88c9c45479443d7be2eadc894b4ed0a9801bae03d97a5760ae13b5c2005942rootiZ2vc2x5d289zm85e8b60zZ:~# docker imagesi Info → U In UseIMAGE ID DISK USAGE CONTENT SIZE EXTRA4、docker run使用镜像创建并运行容器#使用docker run 的时候如果本地不存在此镜像会自动pull镜像然后创建并运行容器因此也可以不下载镜像直接创建并运行容器docker run -d -p 80:80 -v 宿主机目录:容器内目录 镜像名|镜像ID-d在后台运行-p 80:80端口映射冒号前的80是宿主机的冒号后的80是容器的表示将宿主机的80端口的访问转发到容器内的80端口进行处理。每个docker容器都运行在一个独立的虚拟环境里面容器的网络与宿主机是隔离的默认情况下并不能直接从宿主机访问到docker的内部网络-v 宿主机目录:容器内目录挂载卷把宿主机与容器的文件目录进行绑定容器内对这个文件夹的修改会影响宿主机的文件夹而在宿主机对文件夹的修改同样会影响容器内的文件夹。宿主机与容器通过这个目录紧密的联系到了一起这种目录也被称为是挂载卷挂载卷的最大作用是数据的持久化保存因为当我们删除容器的时候容器内的所有数据会被同时删除掉如果我们使用了挂载卷容器内对应目录的数据就会保存在宿主机的对应目录里面这样删除容器的时候保证了数据不会被删除相当于把数据做了一个持久化的保存示例docker run -d -p 80:80 -v /website/html:/usr/share/nginx/html nginx#关闭防火墙ufw disable systemctl disable --now ufw#如果是云服务器还需要放开入方向TCP 80端口宿主机/website/html目录为空挂载卷后会覆盖容器内的html目录可以在宿主机/website/html创建一个网页文件index.html!DOCTYPE html html head meta charsetutf-8 title测试页面/title /head body h1Hello Nginx/h1 p网页访问成功/p /body /html访问宿主机的80端口绑定挂载和命名卷挂载区别1、绑定挂载-v 宿主机目录:容器内目录2、命名卷挂载创建一个存储空间命名卷docker volume create nginx_html-v 卷的名字:容器内目录挂载卷nginx_html在宿主机的真实目录在哪呢可以使用docker volume inspect查询docker volume inspect nginx_html[{CreatedAt: 2026-08-04T18:36:3608:00,Driver: local,Labels: null,Mountpoint: /var/lib/docker/volumes/nginx_html/_data,Name: nginx_html,Options: null,Scope: local}]示例docker run -d -p 80:80 -v nginx_html:/usr/share/nginx/html nginx命名卷第一次使用的时候docker会把容器内的文件夹同步到命名卷里面进行一个初始化而绑定挂载就没这个功能会把容器内的文件夹覆盖掉访问宿主机的80端口可以看到nginx默认网页#列出所有创建过的命名卷docker volume list#删除一个命名卷docker volume rm nginx_html#删除所有没有任何容器在使用的命名卷docker volume prune -a-e往容器里面传递环境变量示例创建并运行mongo容器设置数据库用户名为vorn密码为123456docker run -d -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAMEvorn -e MONGO_INITDB_ROOT_PASSWORD123456 mongo#现在就可以使用上述密码登录mongo数据库了mongosh mongodb://vorn:1234568.137.210.99:27017如果不清楚容器的环境变量有哪些可以去Docker Hub上搜索mongo这个镜像其文档中列出了其可以传递的环境变量。如果是开源项目的话可以去GitHub仓库找一下--name 自定义名字可以给容器起一个自定义名字注意这个名字在宿主机上必须唯一示例docker run -d --name my_nginx nginx-it让控制台进入容器进行交互--rm当容器停止的时候就把容器删除掉示例docker run -it --rm alpine#alpine是一个轻量级的linux系统创建容器后会直接进去容器内可以执行linux命令exit退出容器后容器就会被删除docker ps查询发现已经没有这个容器了。-it --rm常组合到一起使用用于临时调试一个容器rootiZ2vc2x5d289zm85e8b60zZ:~# docker run -it --rm alpine/ #/ # lsbin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var/ # exit--restart用来配置容器在停止时的重启策略常用的有always只要容器停止了就会立即重启包含容器因为内部错误崩溃或者宿主机断电等等场景unless-stopped和always非常类似关键的区别是手动停止的容器就不会尝试重启了示例docker run -d --restart unless-stopped nginx5、docker create和docker run非常类似唯一的区别是create只创建容器但不立即启动6、docker logs查看容器日志docker logs 容器ID|容器名称-f 滚动查看容器日志示例rootiZ2vc2x5d289zm85e8b60zZ:~# docker logs b4c96c444ae9 -f/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d//docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh/docker-entrypoint.sh: Configuration complete; ready for start up2026/08/05 04:29:56 [notice] 1#1: using the epoll event method2026/08/05 04:29:56 [notice] 1#1: nginx/1.31.32026/08/05 04:29:56 [notice] 1#1: built by gcc 14.2.0 (Debian 14.2.0-19)2026/08/05 04:29:56 [notice] 1#1: OS: Linux 7.0.0-28-generic2026/08/05 04:29:56 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1024:5242882026/08/05 04:29:56 [notice] 1#1: start worker processes2026/08/05 04:29:56 [notice] 1#1: start worker process 292026/08/05 04:29:56 [notice] 1#1: start worker process 307、docker ps查看正在运行的容器docker ps -a查看所有的容器包括正在运行的和已经停止的CONTAINER ID容器IDIMAGE镜像名NAMES容器名称docker run的时候如果没有设置名字系统会随机分配一个名字8、docker rm删除容器docker rm -f 容器ID|容器名称-f如果容器是运行态的可以加-f强制删除9、docker start启动已有容器docker start 容器ID|容器名称10、docker stop停止运行中的容器docker stop 容器ID|容器名称11、docker inspect打印容器信息如创建容器时的参数等等docker inspect 容器ID|容器名称四、调试容器Docker利用了Linux内核的两大原生功能实现容器化Cgroups用来限制和隔离进程的资源使用可以为每个容器设定CPU、内存、网络带宽等资源的使用上限确保了一个容器的资源消耗不会影响到宿主机或者其他正在运行的容器。Namespaces用来隔离进程的资源视图Namespaces使得容器只能看到自己内部的进程ID、网络资源和文件目录而看不到宿主机的。所以容器本质上还是一个特殊的进程不过当我们进入到容器内部后容器内部看起来就像一个独立的操作系统每个Docker容器都是一个独立的运行环境每个容器内部表现的都像一个独立的Linux系统。#可以在容器的内部执行Linux命令docker exec 容器ID Linux命令示例docker exec a2dd21dd30dc ps -efUID PID PPID C STIME TTY TIME CMDmongodb 1 0 0 Aug05 ? 00:10:06 mongod --auth --bind_ip_allroot 239 0 42 01:50 ? 00:00:00 ps -ef#进入一个正在运行的docker容器内部获得一个交互式的命令行环境容器内部表现出来的就像一个独立的操作系统我们可以进入容器内部执行Linux命令、查看文件系统、管理容器内的进程或者更加深入的进行调试docker exec -it 容器ID bash示例rootiZ2vc2x5d289zm85e8b60zZ:~# docker exec -it c5a0221cc4f5 bashrootc5a0221cc4f5:/# cat /etc/os-release #查容器内的Linux是什么发行版的发现是DebianDebian安装软件的命令是aptPRETTY_NAMEDebian GNU/Linux 13 (trixie)NAMEDebian GNU/LinuxVERSION_ID13VERSION13 (trixie)VERSION_CODENAMEtrixieDEBIAN_VERSION_FULL13.6IDdebianHOME_URLhttps://www.debian.org/SUPPORT_URLhttps://www.debian.org/supportBUG_REPORT_URLhttps://bugs.debian.org/rootc5a0221cc4f5:/# apt update #更新apt索引rootc5a0221cc4f5:/# apt install vim -y #安装vim五、构建镜像Dockerfile是什么容器是用镜像这个模具生产出来的糕点镜像是制作糕点的模具而Dockerfile就是制作模具的图纸Dockerfile是一个文件里面详细的列出了镜像是如何制作的六、Docker网络1、桥接docker网络默认是bridge桥接模式所有的容器默认都连接到这个网络每个容器都分配了一个内部IP地址一般是172.17开头的在这个内部子网里面容器可以通过内部IP地址互相访问但容器网络与宿主机的网络是隔离的。我们可以使用docker network create创建出子网默认情况下创建出来的子网也是属于桥接模式的一种然后可以指定容器加入不同的子网同一个子网的容器可以互相通信而跨子网则不可以通信创建子网还有一个好处是同一个子网的容器可以使用容器的名字互相访问而不必使用内部IP地址示例#创建一个叫做network1的子网docker network create network1#创建并运行一个mongodb容器这里并没有做端口映射因此使用宿主机是无法访问这个容器的docker run -d \ --name my_mongodb \ -e MONGO_INITDB_ROOT_USERNAMEvorn \ -e MONGO_INITDB_ROOT_PASSWORD123456 \ --network network1 \ mongo#创建并运行一个mongo_express容器mongo_express是mongodb的网页客户端同样把它加入到network1子网中因为跟mongodb属于同一个子网所以他们之间是可以互相访问的。mongo_express上面做了端口映射因此宿主机可以访问mongo_express容器而mongo_express和mongodb是同一个子网就可以访问mongodb容器相当于宿主机可以间接使用mongo_express的数据库网页客户端对mongodb进行增删改查#注意这里传了个环境变量这里没有传mongodb容器在子网的IP地址而是直接把名字告诉了它由于同一个子网的容器可以使用容器的名字互相访问的因此这里写容器名字即可docker run -d \ --name my_mongodb_express \ -p 8081:8081 \ -e ME_CONFIG_MONGODB_SERVERmy_mongodb \ -e ME_CONFIG_MONGODB_ADMINUSERNAMEvorn \ -e ME_CONFIG_MONGODB_ADMINPASSWORD123456 \ --network network1 \ mongo-express在浏览器输入服务器的IP地址端口8081第一次访问需要登陆一下(admin/pass)成功登陆进来可以创建数据库、数据库中可以查询、新增等等创建子网的桥接模式原理图2、Host模式Docker容器直接共享宿主机的网络容器直接使用宿主机的IP地址而且无需-p参数进行端口映射容器内的服务直接运行在宿主机的端口上通过宿主机的IP和端口就能访问到容器。host模式可以解决一些非常棘手的网络问题。示例docker run -d --network host nginx#直接访问服务器80端口就可以看到nginx页面#进入容器查看容器IPdocker exec -it 84d62246239e bash#nginx容器不带网络工具手动安装一下apt update apt install iproute2 -y ip a1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host noprefixroutevalid_lft forever preferred_lft forever2: eth0: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:16:3e:0c:8e:3c brd ff:ff:ff:ff:ff:ffaltname enp0s5altname ens5altname enx00163e0c8e3cinet172.16.40.224/20 metric 100 brd 172.16.47.255 scope global dynamic eth0valid_lft 1891843122sec preferred_lft 1891843122secinet6 fe80::216:3eff:fe0c:8e3c/64 scope link proto kernel_llvalid_lft forever preferred_lft forever...可以看到容器内的172.16.40.224这个IP和宿主机的内网IP一样。宿主机云服务器内网IP如下从这个例子可以看出docker容器使用host的模式它直接共享了宿主机的网络空间3、none模式不联网#展示出所有docker网络发现只有bridge、host、none三种模式docker network listNETWORK ID NAME DRIVER SCOPE55a1ebc784c5 bridge bridge locala651717cd5fb host host local6f96de378180 network1 bridge localdd7a2e70198b none null local#network1是刚才创建的子网可以删掉docker network rm 6f96de378180七、容器编排技术docker compose有些时候一个完整的应用可能是很多部分组成的比如前端、后端、数据库等等这时候怎么用Docker把它们容器化呢我们可以很自然的想到把这些模块都打包在一起做成一个巨大的容器但这样只要有一个模块发生了故障比如后端出现了内存泄露整个容器都有可能崩溃并且可伸缩性差如果想给系统扩容只能把整个大容器再复制一份而做不到针对某个模块的精准性扩容。多应用的最佳实践是把每一个模块都打包成一个独立的容器不过这样使用多容器增加了很多的使用成本因为我们想创建多个容器就得多次执行docker run还得把它们之间的网络配置好尝试管理这些容器的时候很多地方都会出错。这时候一个容器编排技术就很有用也就是docker composedocker compose使用yaml文件管理多个容器里面列出了容器之间是如何创建以及如何协同工作的我们可以简单的把docker compose文件理解成一个或者多个的docker run命令按照特定的格式列到了一个文件里面。docker compose是一个轻量级的容器编排技术它适合个人使用单机运行对于企业级服务器集群大规模的容器编排需求那就需要另外一个软件Kubernetes登场。示例#使用docker命令创建mongodb和mongo_express先创建子网然后创建mongodb和mongo_express两个容器并且把它们加入子网docker network create network1docker run -d \ --name my_mongodb \ -e MONGO_INITDB_ROOT_USERNAMEvorn \ -e MONGO_INITDB_ROOT_PASSWORD123456 \ --network network1 \ mongodocker run -d \ --name my_mongodb_express \ -p 8081:8081 \ -e ME_CONFIG_MONGODB_SERVERmy_mongodb \ -e ME_CONFIG_MONGODB_ADMINUSERNAMEvorn \ -e ME_CONFIG_MONGODB_ADMINPASSWORD123456 \ --network network1 \ mongo-express#使用docker-compose实现上述操作。因为docker会为每一个compose文件都自动创建一个子网同一个compose文件里面定义的所有容器都会自动加入同一个子网因此不需要手动创建子网了。docker compose还有一个额外功能可以自定义容器的启动顺序比如我们可以在mongo_express下面加上depends_on它的意思是mongo_express依赖my_mongodb所以程序会先把my_mongodb容器启动再启动mongo_express容器。services: my_mongodb: image: mongo environment: MONGO_INITDB_ROOT_USERNAME: vorn MONGO_INITDB_ROOT_PASSWORD: 123456 volumes: - /my/datadir:/data/db my_mongodb_express: image: mongo-express ports: - 8081:8081 environment: ME_CONFIG_MONGODB_SERVER: my_mongodb ME_CONFIG_MONGODB_ADMINUSERNAME: vorn ME_CONFIG_MONGODB_ADMINPASSWORD: 123456 depends_on: - my_mongodbservice每个service对应一个容器容器名称my_mongodb、my_mongodb_express就写在service之后image镜像名environment对应docker命令中的-evolumes对应-vports对应-p使用方法vim docker-compose.yamlservices:my_mongodb:image: mongoenvironment:MONGO_INITDB_ROOT_USERNAME: vornMONGO_INITDB_ROOT_PASSWORD: 123456volumes:- /my/datadir:/data/dbmy_mongodb_express:image: mongo-expressports:- 8081:8081environment:ME_CONFIG_MONGODB_SERVER: my_mongodbME_CONFIG_MONGODB_ADMINUSERNAME: vornME_CONFIG_MONGODB_ADMINPASSWORD: 123456depends_on:- my_mongodb#根据yaml文件创建并启动容器。如果容器已经在运行了重复执行compose up命令并不会启动新的容器不会有任何效果。docker compose up -d#docker-compose创建的容器名称自动加了个前缀和编号用来更加方便的管理[] up 3/3✔ Network root_default Created 0.1s✔ Containerroot-my_mongodb-1Started 0.5s✔ Containerroot-my_mongodb_express-1Started注我们执行docker compose命令的时候它会自动识别当前目录下面严格叫做docker-compose.yaml的这个文件。如果这个文件不叫这个名字需要特别指定一下例如test.yaml如果test.yaml不在当前目录就得写绝对路径docker compose -f test.yaml up -ddocker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa5bb61727922 mongo-express /sbin/tini -- /dock… 7 seconds ago Up 6 seconds 0.0.0.0:8081-8081/tcp, [::]:8081-8081/tcp root-my_mongodb_express-1e8ce8e2b0e4b mongo docker-entrypoint.s… 7 seconds ago Up 6 seconds 27017/tcp root-my_mongodb-1#访问服务#停止并删除容器docker compose down[] down 3/3✔ Container root-my_mongodb_express-1 Removed 0.4s✔ Container root-my_mongodb-1 Removed 0.3s✔ Network root_default Removeddocker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES#只停止不删除docker compose stop#把停止的容器启动起来docker compose start