视频链接
Docker概述
Docker为什么会出现
一款产品:开发-上线 两套环境!应用环境,应用配置!
开发----运维。问题:我在我的电脑上,我可以运行!版本更新,导致服务不可用。
开发即运维!!!!环境配置(集群Redis、Es、Hadoop....)!费时费力
发布一个项目(jar+(Redis MySQL jdk ES)),项目带上环境安装打包!
之前在服务器配置一个应用的环境 Redis MySQL jdk Es Hadoop,配置都很麻烦,不能跨平台。
Windows,最后发布到Linux!
传统:开发jar,运维来做!
现在:开发打包部署上线,一套流程做完!
java -- apk -- 发布 (应用商店) ---- 张三使用apk ---- 安装即可用!
java --- jar(环境)---打包项目带上环境(镜像)---(Docker仓库:商店) ---下载我们发布的镜像---直接运行既可!
Docker给以上的问题提出了解决方案!
Docker的思想就来自于集装箱!
JRE ---多个应用(端口冲突) ---原来都是交叉的!
隔离:Docker核心思想!打包装箱!每个箱子都是互相隔离的。
Docker的历史
2010年,几个搞IT的年轻人,就在美国成立了一家公司dotCloud
做一些pass的云计算服务!LXC有关的容器技术!
他们将自己的技术(容器化技术) 命名 就是Docker!
Docker刚刚诞生的时候,没有引起行业的注意!dotCloud,活不下去!
开源
开发源代码!
2013年,Docker开源!
Docker越来越多的人发现了docker的有点!火了,Docker每个月都会更新一个版本!
2024年4月9日,Docker1.0发布!
Docker为什么这么火?十分的轻巧!
在容器技术出来之前,我们都是使用的虚拟机技术!
虚拟机:在window中装一个Vmware,通过这个软件,我们可以虚拟出来一台或者多台电脑!
虚拟机也是虚拟化技术,Docker容器技术,也是一种虚拟化技术!
vm,Linux centos原生镜像(一台电脑)隔离,需要开启多个虚拟机!几个G
docker,隔离,镜像(最核心的环境4m+jdk+mysql)十分轻巧,运行镜像就可以了!小巧!几个M KB 秒级启动!
每一个开发人员都必须会的一门技术。
Docker是基于Go语言开发的
Docker能做什么
虚拟机技术缺点:
1.资源占用十分多
2.重复步骤多
3.启动很慢
容器化技术
容器化技术不是模拟一个完整的操作系统
比较Docker和虚拟机技术的不同:
传统虚拟机:虚拟出一条硬件,运行一个完整的操作系统,然后在这个系统上安装运行软件
容器内的应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
每个容器之间是相互隔离的,每个容器内都有一个属于自己的文件系统,互不影响。
DevOps(开发、运维)
应用更快速的交付和部署
传统:一堆帮助文档,安装程序
Docker:打包镜像发布测试,一键运行
更便捷的升级和扩缩容
使用Docker之后,我们部署应用就和搭积木一样
项目打包成一个镜像,扩展 服务器A !服务器B
更简单的系统运维
在容器化之后,我们开发,测试环境都是高度一致的。
更高效的计算资源利用
Docker是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例!服务器的性能可以被压榨到极致
Docker安装
Docker的基本组成
镜像(image):
就好比是一个模板,通过这个模板来创建容器服务,tomcat镜像===》run===》tomcat01容器(提供服务器),通过这跟镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
容器(container):
Docker利用容器技术,独立运行一个或者一个组应用,通过镜像来创建的。
启动,停止,删除,基本命令!
目前就可以把这个容器理解为就是一个简易的linux系统
仓库(repository):
仓库就是存放镜像的地方!
仓库分为公有仓库和私有仓库!
Docker Hub(默认是国外的)
阿里云....都有容器服务器(配置镜像加速!)
安装Docker
环境准备
需要会一点点linux的基础
CentOS7
使用xshell连接远程的服务器进行操作!
环境查看
cat /etc/os-release
查看使用文档
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
yum install -y yum-utils
yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker- ce/linux/centos/docker-ce.repo --配置阿里云仓库
yum makecache fast
yum install docker-ce docker-ce-cli containerd.io
systemctl start docker
docker version
docker run hello-world
docker images
#卸载依赖
yum remove docker-ce docker-ce-cli containerd.io
#删除资源
rm -rf /var/lib/docker
# /var/lib/docker docker的默认工作路径!
阿里云镜像加速
1.登录阿里云
2.进入控制台
3.左边搜索容器镜像服务
4.左边有个镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://f929gmzr.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
desktop for docker的话就是
回顾Hello World流程
底层原理
docker是怎么工作的?
docker是一个Client-Server结构的系统,Docker的守护进程运行在主机上,通过Socket从客户端访问!
Docker-Server接收到Docker-Client的指令,就会执行这个命令
Docker为什么比VM快
1.Docker有着比虚拟机更少的抽象层。
2.docker利用的是宿主机的内核,vm需要的是Guest OS。
所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统的内核,避免引导。虚拟机时加载Gsest OS,分钟级别的,而docker时利用宿主机的操作系统,省略了这个复杂的过程,秒级!
Docker的常用命令
帮助命令
docker version #显示docker的版本信息
docker info #显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help #万能命令
帮助文档的地址
镜像命令
docker images
查看所有本地的主机上的镜像
-a,--all #列出所有镜像
-q,--quiet #只显示镜像的id
-aq
docker search
搜索镜像
docker search mysql
--filter=STARS=3000 搜索出来的镜像就是STARS大于3000的
docker pull
下载镜像,当搜索到了镜像之后,就可以开始下载镜像了
docker pull mysql:latest
如果不写tag(用来指定版本下载的),那么就默认时latest(最新版)
分层下载的,docker image的核心 联合文件系统
下载后还有一个签名
还有一个真实的地址
docker rmi
删除镜像
docker rmi -f e73346bdf465
docker rmi -f e73346bdf465 e73346bkf231
docker rmi -f $(docker images -aq) #这个是删除前部的容器
容器命令
说明:只有当我们有了镜像才可以创建容器,linux ,下一个centos镜像来测试学习
docker pull centos
docker run
新建容器并启动
docker run [可选参数] image
#参数说明
--name="Name" 容器名字 tomcat01 tomcat02(主要用来区分容器)
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
-P(大写) 随机指定端口
#测试(启动、并且进入容器)
[root@iZ2 ~]#docker run -it centos /bin/bash
[root@cd5b /]# ls #查看容器内的centos,基础版本,很多命令都是不完善的
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
#从容器中退出
[root@cd5b7850a131 /]# exit
docker ps
列出所有正在运行的容器
-a 列出当前正在运行的容器+带出历史运行过的容器
-n=3 显示最近创建的三个容器
-q 只显示容器的编号
退出容器(在容器内部)
exit #直接容器停止并退出
Ctrl + P + Q #容器不停止退出
删除容器
docker rm 容器id 删除指定的容器(不能删除正在运行的容器,如果想,必须使用-f强制删除)
docker rm -f $(docker ps -aq) 删除指定的容器
docker ps -a -q|xargs docker rm 删除所有的容器
启动和停止容器的操作
docker start 容器id #启动容器
docker restart 容器id #重启容器
docker stop 容器id #停止当前正在运行的容器
docker kill 容器id 强制停止当前的容器
常用其他命令
后台启动容器
docker run -d centos
注意:
发现docker ps没有找到centos,原因是容器里面没有一个前台进程,也没有提供任何的服务。就会被停止了。
查看日志
docker logs -f -t --tail [10] dce7b86171bf
#自己编写一段shell脚本(目的是保持这个容器一直运行着不停掉)
docker run -d centos /bin/sh -c "while true;do echo kuangshen;slepp 1;done"
#使用docker ps找到需要知道日志的容器ID
-tf #要显示日志条数
-tail number #要显示的日志条数
docker logs -f -t --tail [10] dce7b86171bf
查看容器中进程信息
docker top dce7b86171bf
查看镜像中的元数据
docker inspect dce7b86171bf
#输出
[
{
"Id": "65763ad37c38bc842477179717f83aed5771129eb7697a6ff2f69dd7ea40708d",
"Created": "2025-03-15T07:46:20.138091413Z",
"Path": "/usr/sbin/sshd",
"Args": [
"-D"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 6605,
"ExitCode": 0,
"Error": "",
"StartedAt": "2025-03-15T07:46:20.44389666Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:b8fe5db7af94088273c7e3109035fa69a1a16512a988531abecbd9323ad0fa69",
"ResolvConfPath": "/var/lib/docker/containers/65763ad37c38bc842477179717f83aed5771129eb7697a6ff2f69dd7ea40708d/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/65763ad37c38bc842477179717f83aed5771129eb7697a6ff2f69dd7ea40708d/hostname",
"HostsPath": "/var/lib/docker/containers/65763ad37c38bc842477179717f83aed5771129eb7697a6ff2f69dd7ea40708d/hosts",
"LogPath": "/var/lib/docker/containers/65763ad37c38bc842477179717f83aed5771129eb7697a6ff2f69dd7ea40708d/65763ad37c38bc842477179717f83aed5771129eb7697a6ff2f69dd7ea40708d-json.log",
"Name": "/python",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": [
"125a660ab3d80c7f5e435405220aaddd5a4ccca6c3a0b45f47ec545a1e9ac975"
],
"HostConfig": {
"Binds": [
"/app:/app"
],
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "bridge",
"PortBindings": {
"22/tcp": [
{
"HostIp": "",
"HostPort": "2222"
}
],
"8885/tcp": [
{
"HostIp": "",
"HostPort": "8885"
}
]
},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
25,
159
],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": [],
"BlkioDeviceWriteBps": [],
"BlkioDeviceReadIOps": [],
"BlkioDeviceWriteIOps": [],
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": [],
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
"/sys/devices/virtual/powercap"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/365f99061b8b71aaa6f2b61c1884d0542347695bc311a992035320c3a8652c43-init/diff:/var/lib/docker/overlay2/ewi4ry2pzk0ascl11v5r4tgxp/diff:/var/lib/docker/overlay2/78b880d5bfb71d560c08f2fa6fe611a8f7ed66339bd4b965f6b54203bb2394c9/diff:/var/lib/docker/overlay2/1e9c62b1f386ab29719286c404c644154e3a4ec5317662ccfe33938fbcc3f2d2/diff:/var/lib/docker/overlay2/66f27f903ef1ab63b1da911fb759803061c9b844955a97a02672cbe92dcf9998/diff:/var/lib/docker/overlay2/8fcc3307afdb0d689158dcb134e3667a4bc5a30c6df20b1f6036cecfcb48a6b5/diff:/var/lib/docker/overlay2/18e8d06926087fa48c411580b5100c5cddca83879e12947626d6b1bfda9bc641/diff:/var/lib/docker/overlay2/c241dbdec836c9bc29ee432fd56bd256a5cf082340e3247386c841561669c91f/diff:/var/lib/docker/overlay2/80c50c70f6f1b633e828077540c8ee8b521d4ca1a550b1c4a3a7c4a7cee8e252/diff:/var/lib/docker/overlay2/c903f64a7a8030f650c56c0b3ae469ac6b564e34c4e3893d0e4058a47f862248/diff:/var/lib/docker/overlay2/f6672a7b890478723112afb77f820d2701e911c68f406ec888a2ea3d501b6976/diff",
"MergedDir": "/var/lib/docker/overlay2/365f99061b8b71aaa6f2b61c1884d0542347695bc311a992035320c3a8652c43/merged",
"UpperDir": "/var/lib/docker/overlay2/365f99061b8b71aaa6f2b61c1884d0542347695bc311a992035320c3a8652c43/diff",
"WorkDir": "/var/lib/docker/overlay2/365f99061b8b71aaa6f2b61c1884d0542347695bc311a992035320c3a8652c43/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "bind",
"Source": "/app",
"Destination": "/app",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
"Config": {
"Hostname": "65763ad37c38",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"22/tcp": {},
"8885/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568",
"PYTHON_VERSION=3.9.9",
"PYTHON_PIP_VERSION=21.2.4",
"PYTHON_SETUPTOOLS_VERSION=57.5.0",
"PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.py",
"PYTHON_GET_PIP_SHA256=c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309"
],
"Cmd": [
"/usr/sbin/sshd",
"-D"
],
"Image": "python-ssh",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "db19f3f2fa705c5f4fae4ac57b9e9c3db8dd6f3ab5c7e655e3adeb17f9441686",
"SandboxKey": "/var/run/docker/netns/db19f3f2fa70",
"Ports": {
"22/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "2222"
},
{
"HostIp": "::",
"HostPort": "2222"
}
],
"8885/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "8885"
},
{
"HostIp": "::",
"HostPort": "8885"
}
]
},
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "97e6d3dc4fb7ec564b4e9cc0e13737754f7c825253ddc8fd188b15389875a01b",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.5",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:05",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"MacAddress": "02:42:ac:11:00:05",
"NetworkID": "d1873b5c82ffeba7bd1acbf1088b364a535b67874073faafde3f5d3fa97d9745",
"EndpointID": "97e6d3dc4fb7ec564b4e9cc0e13737754f7c825253ddc8fd188b15389875a01b",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.5",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"DriverOpts": null,
"DNSNames": null
}
}
}
}
]
进入当前正在运行的容器
方式一:
#我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
#测试
[root@iZ2ze2kw6hfrrx7qs65neyZ ~]# docker exec -it f5bff212d913 /bin/bash
root@f5bff212d913:/#
方式二:
[root@iZ2ze2kw6hfrrx7qs65neyZ ~]# docker attach c808decaed6
docker exec #进入容器后开启一个新的终端,可以在里面操作
docker attach #进入容器正在执行的终端,不会启动新的进程!
从容器内拷贝文件到主机上
#首先进入容器
docker attach 容器id
#在容器里面进行文件的创建
touch test.java
#然后ctrl+Q+P
退出容器
#然后指定容器进行拷贝(无需容器运行,只要容器存在)
docker cp aa66af05c309:/test.java /home
#拷贝是一个手动过程,未来我们使用-v卷的技术,可以实现,自动同步
学习方式
全部命令敲一遍
小结
作业
作业一:部署Nginx
步骤一:
建议去docker hub上面搜索,可以看到帮助文档等信息镜像仓库
C:\Users\Administrator>docker search Nginx
NAME DESCRIPTION STARS OFFICIAL
nginx Official build of Nginx. 20880 [OK]
nginx/nginx-ingress NGINX and NGINX Plus Ingress Controllers fo… 108
nginx/nginx-prometheus-exporter NGINX Prometheus Exporter for NGINX and NGIN… 51
nginx/unit This repository is retired, use the Docker o… 65
nginx/nginx-ingress-operator NGINX Ingress Operator for NGINX and NGINX P… 2
nginx/nginx-quic-qns NGINX QUIC interop 1
nginx/nginxaas-loadbalancer-kubernetes 1
nginx/unit-preview Unit preview features 0
bitnami/nginx Bitnami container image for NGINX 200
ubuntu/nginx Nginx, a high-performance reverse proxy & we… 131
bitnamicharts/nginx Bitnami Helm chart for NGINX Open Source 1
kasmweb/nginx An Nginx image based off nginx:alpine and in… 8
rancher/nginx 2 0
步骤二:
C:\Users\Administrator>docker pull nginx
docker images #查看容器是否拉取下载成功
步骤三:
docker run -d --name nginx01 -p 3344:80 nginx
docker ps #查看是否运行成功
步骤四:
C:\Users\Administrator>curl localhost:3344 #使用curl的方法
<!DOCTYPE html>
<h1>Welcome to nginx!</h1>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
步骤四:
docker stop nginx01
思考:我们每次改动nginx配置文件,都需要进入容器内部?十分麻烦,我要是可以在容器内部提供一个映射路径,达到在容器外部修改文件内容,容器内部就可以自动修改?-v 数据卷!
作业二:装一个tomcat
#官方的使用
$ docker run -it --rm tomcat:9.0
#我们之前的启动都是后台的,停止了容器之后,容器还是可以查到 docker run -it --rm,一般用来测试,用完删除
#下载之后再启动
docker pull tomcat:9.0
#启动运行
docker run -d -p 3344:8080 --name tomcat01 tomcat:9.0
#进入容器
C:\Users\Administrator>docker exec -it tomcat01 /bin/bash
root@4cb02335354c:/usr/local/tomcat# ls
bin CONTRIBUTING.md LICENSE NOTICE RUNNING.txt webapps
BUILDING.txt filtered-KEYS logs README.md temp webapps.dist
conf lib native-jni-lib RELEASE-NOTES upstream-KEYS work
root@4cb02335354c:/usr/local/tomcat# cd webapps
root@4cb02335354c:/usr/local/tomcat/webapps# ls
root@4cb02335354c:/usr/local/tomcat/webapps#
发现这里的webapps下面是空的,所以我们可以发现:
1.linux命令少了
2.没有webapps。
因为阿里云镜像的原因。默认是最小的镜像,所有不必要的都剔除掉,保证最小的运行环境
#给他添加页面,然后localhost:3344访问
root@4cb02335354c:/usr/local/tomcat# cp -r webapps.dist/* webapps
思考:我们以后要部署项目,如果每次都要进入容器很麻烦,我们可以再容器外部提供一个映射路径,webapps,我们在外部放置项目,就自动同步到内部就好了!
作业三:部署es+kibana
#es 暴露的端口很多!
#es 十分的耗内存
#es 的数据一般需要放置到安全目录!挂载
#启动elasticsearch
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
#启动了 linux 就卡住了 docker stats 查看 cpu的状态
#es 是十分耗内存的, 1.xG , 1核2G!
#查看 docker stats
#测试一下es是否成功了
#如果成功了,赶紧关闭tomcat,增加内存的限制
#赶紧关闭,增加内存的限制,修改配置文件 -e 环境配置修改
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e ES_JAVA_OPTS="-Xms64m -Xmx512m" -e "discovery.type=single-node" elasticsearch:7.6.2
#查看cpu状态
docker stats
作业:使用kibana连接es
可视化
- portainer(先用这个)
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
- Rancher(CI/CD再用)
什么是portainer?
Docker图形化界面管理工具!提供一个后台面板供我们操作!
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
访问测试:外网:8088端口
刚进去需要设置初始密码:我设置为888888888888
测试的,平时不会用到
Docker镜像
镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基本运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
所有的应用,直接打包docker镜像,就可以直接跑起来!
如何得到镜像:
- 从远程仓库下载
- 朋友拷贝给你
- 自己制作一个镜像DockerFile
Docker镜像加载原理
UnionFS(联合文件系统)
我们下载的时候看到的一层一层的就是这个原因
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem).Union文件系统是Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux、Unix系统是一样的,包含boot加载和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载boofs。
黑屏------进入系统(就是一个加载的过程,bootfs去引导加载kernel的过程)
rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。
分层理解
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!
思考:为什么Docker镜像要采用这种分层的结构呢?
最大的好处,资源共享,比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过docker image inspect mysql:latest
命令!
C:\Users\Administrator>docker image inspect elasticsearch:7.6.2
[
{
"Id": "sha256:f29a1ee41030e3963026369105f3bee76d75fdecbeca07932ac054126be7bff9",
"RepoTags": [
"elasticsearch:7.6.2"
],
"RepoDigests": [
"elasticsearch@sha256:1b09dbd93085a1e7bca34830e77d2981521a7210e11f11eda997add1c12711fa"
],
"Parent": "",
"Comment": "",
"Created": "2020-03-26T06:39:43.989341611Z",
"DockerVersion": "19.03.6",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"9200/tcp": {},
"9300/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/share/elasticsearch/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"ELASTIC_CONTAINER=true"
],
"Cmd": [
"eswrapper"
],
"Image": "sha256:9e2869a99c9fad439619d3106e68fc6c7a2b5aff34a980e09d7076322df3aaba",
"Volumes": null,
"WorkingDir": "/usr/share/elasticsearch",
"Entrypoint": [
"/usr/local/bin/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "2020-03-26T06:34:37.794943Z",
"org.label-schema.license": "Elastic-License",
"org.label-schema.name": "Elasticsearch",
"org.label-schema.schema-version": "1.0",
"org.label-schema.url": "https://www.elastic.co/products/elasticsearch",
"org.label-schema.usage": "https://www.elastic.co/guide/en/elasticsearch/reference/index.html",
"org.label-schema.vcs-ref": "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"org.label-schema.vcs-url": "https://github.com/elastic/elasticsearch",
"org.label-schema.vendor": "Elastic",
"org.label-schema.version": "7.6.2",
"org.opencontainers.image.created": "2020-03-26T06:34:37.794943Z",
"org.opencontainers.image.documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/index.html",
"org.opencontainers.image.licenses": "Elastic-License",
"org.opencontainers.image.revision": "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"org.opencontainers.image.source": "https://github.com/elastic/elasticsearch",
"org.opencontainers.image.title": "Elasticsearch",
"org.opencontainers.image.url": "https://www.elastic.co/products/elasticsearch",
"org.opencontainers.image.vendor": "Elastic",
"org.opencontainers.image.version": "7.6.2"
}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 790680163,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/93ff2e206626a1e4d935d6224844a8e358df49aff73d0a93550e9231a4a21510/diff:/var/lib/docker/overlay2/07840f497f738654e76207081d86440663e8163748fbd4ae0f9cf8fafff54241/diff:/var/lib/docker/overlay2/767d7e35675dcc98c44e5dbaddbb11d0a1724b6395c4ad9fac82b78b94f78df6/diff:/var/lib/docker/overlay2/3f37ee9b92e93f75ccafc1ce5a0c774618245bcec6e51edf0fa0e0a6f52c7d4c/diff:/var/lib/docker/overlay2/50d6afd08b59ad656d010fbb72c40d8799f80033e76a70985307783ef00cea7c/diff:/var/lib/docker/overlay2/c9a20589c316540f0a09416032a1b19d47d575a8c1b9a3954b01147273eb139e/diff",
"MergedDir": "/var/lib/docker/overlay2/0f054a783ac64440fd96e189f712106ac77a828e3c8b6b5a21d4b235d9b43b74/merged",
"UpperDir": "/var/lib/docker/overlay2/0f054a783ac64440fd96e189f712106ac77a828e3c8b6b5a21d4b235d9b43b74/diff",
"WorkDir": "/var/lib/docker/overlay2/0f054a783ac64440fd96e189f712106ac77a828e3c8b6b5a21d4b235d9b43b74/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:77b174a6a187b610e4699546bd973a8d1e77663796e3724318a2a4b24cb07ea0",
"sha256:7712f32688d1bef330aa3b4fac2683cec1d7339335ce403483b329423debffb6",
"sha256:1a090720e70c88e61053c89d3ff01932943b198644f4a7e86426e576b721d2e3",
"sha256:0535424758bd9ab49a3d03af52a9fa5447b807198fadc35e9f80123a0929402e",
"sha256:4d2f8f4a58623431cca0ee321bbee273b87070f9e381b3147e4293e83dc146d7",
"sha256:77c5267605c2c7cf2426242d5df50af78931e66403e9d0d06f2b9fd7adc3adc9",
"sha256:537370aeea86be07f79bbcd25464a1df23f347bb5313e0ff7ca15d6aad70e074"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
理解:所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,加入基于Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层,如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含三个镜像层,如图所示。
在添加二外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点是非常重要的。如下图中举了一个重要的例子,每个镜像层包含三个文件,而镜像包含了来自两个镜像层的六个文件
特点
docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
commit镜像
就是把官方镜像运行起来成容器,然后进入容器里面修改,接着退出容器,然后把容器提交成为自己的镜像。
docker commit 提交容器成为一个新的副本
# 命令和git原理类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
#这个是直接加载官方的镜像来运行成容器
docker run -it -p 3344:8080 tomcat
#然后就是在里面进修改(递归拷贝webapps.dist下的所有文件到webapps文件夹下
cp -r webapps.dist/* webapps
#然后就是提交成为新的镜像
docker commit -a="huangmingfeng" -m="add webapps app" 容器id tomcat02:1.0
#确认是否提交成功
docker images
容器数据卷
什么是容器数据卷
docker的理念回顾
将应用和环境打包成一个镜像!
数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!需求:数据可以持久化
MySQL,容器删了,删库跑路!需求:MySQL数据可以存储在本地!
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是咱们的卷技术!目录的挂载,将我们容器内的目录,挂载到Linux上面!
总结一句话:容器的持久化和同步操作!容器间也是可以数据共享的!
使用数据卷
方式一:直接使用命令来挂载 -v
docker run -it -v C:\Users\Administrator\Desktop\RAG:/home centos:7 /bin/bash
#列出容器信息,查看挂载
C:\Users\Administrator\Desktop\RAG>docker inspect 6a9b750499e3
#查找到Mounts这个key
"Mounts": [
{
"Type": "bind",
"Source": "C:\\Users\\Administrator\\Desktop\\RAG",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],、
就可以实现数据同步,例如我在外面进行创建文件夹kk,里面也有啦
好处:以后修改只需要在本地修改即可,容器内会自动同步
实战:安装MySQL
思考:MySQL的数据持久化的问题!
#获取镜像
docker pull mysql:5.7
#运行容器,需要做数据挂载!#安装启动mysql,需要配置密码的,这是要注意点!
#官方测试:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
#启动我们的
-d 后台运行
-p 端口映射
-v 挂载卷
-e 环境配置
--name 启动后容器名
docker run -d -p 3310:3306 -v C:\Users\Administrator\Desktop\RAG\mysql\conf:/etc/mysql/conf.d -v C:\Users\Administrator\Desktop\RAG\mysql\data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
C:\Users\Administrator\Desktop\RAG>docker rm -f c1d18ac0b982
c1d18ac0b982
这样就实现了我们容器数据的持久化功能
具名挂载和匿名挂载
-v 容器内路径!
docker run -d -p --name nginx01 -v /etc/nginx nginx
#查看所有的卷的情况
C:\Users\Administrator\Desktop\RAG>docker volume ls
DRIVER VOLUME NAME
local 8f9a99bbe430a2a4f7c8eeb9fe4e5b791283bb5fea6f1513414c19509845909b
local 210904267e23b0e42317c3cd65d25ad86c13cf596a90b4fc7bd99d4f39fdfd6f
local ff58145cd3b55442a315f464333d557b7fa4d14c6ed11a519e56aa0ff1a9a143
#这里发现,这种就是匿名挂载,我们在-v的时候,只写了容器内的路径,没有写容器外的路径
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
C:\Users\Administrator\Desktop\RAG>docker volume ls
DRIVER VOLUME NAME
local juming-nginx
#通过 -v 卷名:容器内路径
#查看一下这个卷
C:\Users\Administrator\Desktop\RAG>docker volume inspect juming-nginx
[
{
"CreatedAt": "2025-07-07T09:25:36Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]
这里可以在docker 的工作目录下找到所有的容器信息,以及挂载信息
我们通过具名挂载,可以方便的找到我们的卷,大多数情况在使用的是“具名挂载”
如何确定是具名还是匿名,还是指定路径挂载
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载!
拓展:
#ro readonly 只读
#rw readwrite 可读可写
#一旦设置了容器权限,容器对我们挂载出来的内容就有限定了!
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
#ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作的!
初识DockerFile
DockerFile就是用来构建docker镜像的构建文件!命令脚本!
通过这个脚本可以生成镜像,镜像是一层一层的,脚本就是一个一个的命令,每个命令都是一层!
#创建一个dockerfile文件,名字可以随机,建议Dockerfile
#文件中的内容 指令(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "------end------"
CMD /bin/bash
#这里的每一个命令,就是镜像的一层
[root@iZ2ze2kw6hfrrx7qs65neyZ docker-test-volume]# vim dockerfile1
[root@iZ2ze2kw6hfrrx7qs65neyZ docker-test-volume]# cat dockerfile1
#这里要注意,镜像名字前面不可以有/
[ docker-test-volume]# docker build -f dockerfile1 -t kuangshen/centos .
[root@iZ2ze2kw6hfrrx7qs65neyZ docker-test-volume]# docker run -it kuangshen/centos
[root@4c4c6a7195d7 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
[root@4c4c6a7195d7 /]# ls -l
total 56
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxrwxrwt 7 root root 4096 Sep 15 2021 tmp
drwxr-xr-x 12 root root 4096 Sep 15 2021 usr
drwxr-xr-x 20 root root 4096 Sep 15 2021 var
drwxr-xr-x 2 root root 4096 Jul 7 11:22 volume01
drwxr-xr-x 2 root root 4096 Jul 7 11:22 volume02
这里可以发现这两个目录都挂载出来了。(这个是匿名挂载)
这里可以通过 docker inspect 4c4c6a7195d7
后找到里面的mounts可以看看挂载到哪里了。
这种方式我们使用的非常多,因为我们通常会构建自己的镜像!
假设构建镜像时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径!
数据卷容器
多个mysql同步数据!
#启动3个容器,通过自己写的镜像启动
docker run -it --name docker02 --volumes-from docker01 kuangshen/centos
docker run --name docker03 --volume-from docker01 kuangshen/centos
# 测试,可以删除docker01,查看一下docker02和docker03是否还可以访问这个文件
# 测试依旧可以访问
只要或者就能够拷贝
多个mysql实现数据共享
C:\Users\Administrator\Desktop\RAG>docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
73fdec30005815185ca78a39233319adb207a762dc4b89b30340f19700d40a0a
C:\Users\Administrator\Desktop\RAG>docker run -d -p 3311:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
f8660b34eb1ba0fc6c9bff7e5d0b81d34b4b2c739bf841afe30a8e8af7d0c77b
结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦持久到本地,这个时候,本地的数据是不会删除的!
DockerFile
dockerfile是用来构建docker镜像的文件!它就是一个命令参数脚本!
构建步骤:
1.编写一个dockerfile文件
2.docker build构建成为一个镜像
3.docker run 运行镜像
4.docker push 发布镜像(DockerHub、阿里云镜像仓库)
DockerFile构建过程
基础知识:
1.每个保留关键字(指令)都是必须是大写字母
2.执行从上到下顺序执行
3.#表示注释
4.每一个指令都会创建提交一个新的镜像层,并提交!
dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单!
Docker镜像逐渐成为了企业交付的标准,必须要掌握!
步骤:开发,部署,运维。。。缺一不可
DockerFile:构建文件,定义了一切的步骤,源代码
DockerImages:通过DockerFile构建生成的一个镜像,最终发布和运行的产品
Docker容器:容器就是镜像运行起来提供服务的
DockerFile的指令
以前的话我们就是使用别人的,现在我们知道了这些指令后,我们来练习自己写一个镜像!
From #基础镜像,一切从这里开始 centos
MAINTAINER #镜像是谁写的,姓名+邮箱
RUN #镜像构建的时候需要运行的命令
ADD #步骤:tomcat镜像,这个tomcat压缩包!添加的内容
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPOST #暴露的端口
CMD #指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT #指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD #当构建一个被继承 DockerFile 这个时候就会运行ONBUILD的指令,触发指令
COPY #类似ADD,将我们文件拷贝到镜像中
ENV #构建的时候设置环境变量!
实战测试
Docker Hub中99%都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行构建。
创建一个自己的centos
# 1. 编写Dockerfile的文件
FROM centos
MAINTAINER MINGFENG<2856055319@qq.com>
ENV MYPATH /user/local
WORKDIR $MYPATH
RUN yun -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "-------end-------"
CMD /bin/bash
#2.通过这个文件构建自己的镜像
# 命令 docker build -f mydockerfile(文件路径) -t 镜像名:[TAG] .
#测试
docker build -f mydockerfile -t mycentos:0.1 .
Successfully build e2bd75cfe070
Successfully tagged mycentos:0.1
#测试运行一下
对比:之前的原生的centos
我们增加之后的镜像,会发现工作目录变成/usr/local ,以及多了很多命令例如vim
可以使用docker history 容器id
这个命令可以查看任何一个容器的dockerfile,
我们平时拿到一个镜像,可以研究一下它是怎么做的了
CMD ENTRYPOINT区别
#1编写dockerfile文件
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# vim dockerfile-cmd-test
FROM centos
CMD ["ls","-a"]
#2构建镜像
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest .
#3run运行,发现我们的ls -a命令生效
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker run cmdtest
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
#想追加一个命令 -l ls -al
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker run cmdtest -l
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "-l": executable file not found in $PATH: unknown.
#cmd的清理下 -l 替换了CMD ["ls","-a"]命令,-l不是命令所以报错!
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# vim dockerfile-entrypoint-test
FROM centos
ENTRYPOINT ["ls","-a"]
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker build -f dockerfile-entrypoint-test -t entrypoint-test .
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker run entrypoint-test
.
..
.dockerenv
bin
dev
etc
#这里我们命令行追加的命令是直接拼接在我们的ENTRYPOINT命令的后面的
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker run entrypoint-test -l
total 56
drwxr-xr-x 1 root root 4096 Jul 8 02:02 .
drwxr-xr-x 1 root root 4096 Jul 8 02:02 ..
-rwxr-xr-x 1 root root 0 Jul 8 02:02 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 8 02:02 dev
drwxr-xr-x 1 root root 4096 Jul 8 02:02 etc
实战:tomcat镜像
1.准备镜像文件 tomcat压缩包,jdk的压缩包!
2.编写dockerfile文件,官方命名Dockerfile
,build会自动寻找这个文件,就不需要-f指定了!
FROM centos
MAINTAINER kuangshen<2856055319@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8ull-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.22.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.22
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /url/local/apache-tomcat-9.0.22/bin/logs/catalina.out
3.构建镜像
#docker build -t diytomcat .
4.启动镜像成容器
docker run -d -p 9090:8080 --name kuangshentomcat -v /home/kuangshen/build/tomcat/test: /url/local/apache-tomcat-9.0.22/webapps/test -v/home/kuangshen/build/tomcat/tomcatlogs/:/url/local/apache-tomcat-9.0.22/logs diytomcat
5.访问测试
6.发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了!)
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
</web-app>
还需要在webapp里面做一个index.jsp文件
以后开发的步骤,就是需要掌握Dockerfile的编写!我们以后的一切都是使用docker镜像来发布运行的!
发布自己的镜像
DockerHub
1.地址 https://hub.docker.com/注册自己的账号
2.确定这个账号可以登录
3.在我们服务器上提交自己的镜像
docker login -u xxx -p xxx
4.登录完毕之后,就可以开始提交镜像了
docker push 镜像Name:[tag]
#push镜像的问题?
The push refers to repository [docker.io/kuangshen/diytomcat2]
An image does not exist locally with the tag:kuangshen/diytomcat2
#解决这个问题,添加一个tag
docker tag 镜像id 新镜像name:tag
提交的时候也是按照镜像的层级来进行提交的。
阿里云镜像服务上
1.登录阿里云
2.找到容器镜像服务
3.创建命名空间
4.创建镜像仓库
5.浏览仓库信息,跟着步骤就可以上传镜像了
Docker所有流程小结
Docker网络原理
理解Docker网络
测试
三个网络
#问题:docker是如何处理容器网络访问的?
#下面这个是没有任何的容器在运行的时候,就是只有docker01一个网卡
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0a:7f:3a brd ff:ff:ff:ff:ff:ff
inet 10.70.141.25/16 brd 10.70.255.255 scope global dynamic eth0
valid_lft 291179128sec preferred_lft 291179128sec
inet6 fe80::216:3eff:fe0a:7f3a/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:a1:30:38:4f 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:a1ff:fe30:384f/64 scope link
#这里是启动一个tomcat01容器,然后查看网卡信息
docker run -d -P --name tomcat01 tomcat ip addr
这里可以发现就多了一个
126: veth1a417e9@if125: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 9e:a2:f0:c7:5c:2f brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::9ca2:f0ff:fec7:5c2f/64 scope link
valid_lft forever preferred_lft forever
#如果再启动一个tomcat容器,就会发现又多一个网卡信息
docker run -d -P --name tomcat02 tomcat ip addr
128: veth95a7a7f@if127: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether c6:ec:72:c2:ab:20 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::c4ec:72ff:fec2:ab20/64 scope link
valid_lft forever preferred_lft forever
#进入容器,并且使用apt-get先安装一下ip命令
docker exec -it tomcat01 /bin/bash
apt-get update && apt-get install -y iproute2
#查看容器的内部网络地址 ip addr,发现容器启动的时候会得到一个eth0@if262 ip地址,docker分配的!
root@f82b93f6a457:/usr/local/tomcat# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
125: eth0@if126: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
如果再启动一个容器就是
172.17.0.3
#思考:linux能不能ping通容器内部!
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.057 ms
#linux 可以ping通docker容器内部!
原理
1.我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡docker0
桥接模式,使用的技术是evth-pair技术!
我们发现这个容器带来的网卡,都是一对对的(linux一个,容器一个)
evth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
正因为有这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备的
openStac,Docker容器之间的连接,OVS的连接,都是使用evth-pair技术
2.我们来测试一下tomcat01 和tomcat02 是否能够ping通
docker exec -it tomcat02 ping 172.17.02
#结论:容器和容器之间可以互相ping通的!
绘制一个网络模型图
结论:tomcat01和tomcat02是公用的一个路由器,docker02
所有的容器不指定网络下,都是docker0路由的,docker会给我们容器分配一个默认的可用IP
小结
Docker使用的是Linux的桥接,宿主机中是一个Docker容器的网桥docker0.
Docker中的所有网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件!)只要容器删除,对应的网桥(一对网卡)就没有了。
高可用!
思考一个场景,我们编写了一个微服务,database url=ip;项目不重启,数据库ip换掉了 ,我们希望可以处理这个问题,可以通过我们的名字来进行访问容器!
docker network ls
docker network inspect bridge网卡的id
--link
给tomcat02安装ping命令
root@aad4bfe13583:/usr/local/tomcat# apt-get install -y iputils-ping
然后在linux上面使用精简命令使得tomcat02 ping tomcat01 这里我们发现了根本就ping不通
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known
#通过--link就可以解决网络连通问题
docker run -d -P --name tomcat03 --link tomcat02 tomcat
docker exec -it tomcat 03 ping tomcat02
会发现这里可以ping通
#反向可以ping通吗?(就是tomcat02 ping tomcat03)会发现行不通。
探究:inspect!(这里是使用的docker network inspect 网卡bridge的id)
其实这个tomcat03就是在本地配置了tomcat02的配置?
docker exec -it tomcat02 cat /etc/hosts
--link 就是我们在hosts配置中增加了一个172.17.0.3 tomcat02 aad4bfe13583
我们现在玩Docker已经不建议使用--link了!
自定义网络,不适用docker0!
docker0问题:他不支持容器名连接访问!
自定义网络
查看所有的docker网络
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker network ls
NETWORK ID NAME DRIVER SCOPE
cdfb53d37ca4 bridge bridge local
0ab7551a3614 host host local
8494dc1c6d7b my_net bridge local
6969a1201ceb none null local
网络模式
bridge:桥接 docker大桥(默认,自己创建也是用bridge模式)
none :不配置网络
host :和宿主机共享网络
container:容器内网络连通(用的少)
测试
我们直接启动的命令 --net bridge,而这个就是我们的docker01
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat
#docker0特点,默认,域名不能访问,--link可以打通连接!
#我们可以自定义一个网络!
# --driver bridge 网络模式
# --subnet 192.168.0.0/16 子网
# gateway 192.168.0.1 网关
命令如下
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
da9fe65aa180c62f53c7892c51c3f2b43c2b1138b9aa75451474078acf9e837e
docker network ls
NETWORK ID NAME DRIVER SCOPE
cdfb53d37ca4 bridge bridge local
0ab7551a3614 host host local
da9fe65aa180 mynet bridge local
6969a1201ceb none null local
#自定义成功之后,就可以查看细节了
docker network inspect mynet
[
{
"Name": "mynet",
"Id": "da9fe65aa180c62f53c7892c51c3f2b43c2b1138b9aa75451474078acf9e837e",
"Created": "2025-07-08T15:48:51.520985696+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
这里我们的服务就以及创建好了,那我们的服务就都可以放在我们的网络服务里面了,下面我们就创建服务放在这个网络里面,然后进行通过这个容器名字互ping,发现可以正常互ping
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker run -d -P --name tomcat-net-01 --net mynet mytomcat
8053a8cd5a036166c0c235e8d70979e395cf2aff04db9c428056426622110cab
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker run -d -P --name tomcat-net-02 --net mynet mytomcat
51692baefb409c1c7b3a3ec7f596b1946d803dcc221b8aa28514e1d8754869dc
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.131 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.079 ms
我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络!
好处:
redis - 不同的集群是使用不同的网络,保证集群是安全和健康的
mysql - 不同的集群使用不同的网络,保证集群是安全和健康的
网络连通
测试打通tomcat01 - mynet
#连通之后就是将tomcat01 直接加入到mynet网络下
#一个容器两个ip地址 阿里云服务:公网ip 私网ip
docker network connect mynet tomcat01
docker network inspect mynet
[
"e3c51f093fc81baf919d2bc760c2d82eeec2117ae894e23d7a91a2b0d8fc6d6c": {
"Name": "tomcat01",
"EndpointID": "8379f3ec6b7572c414918ac4cd47819f06e4c99b83ec5e2b0a3c306b84790c52",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
"Options": {},
"Labels": {}
}
]
#01 连接ok
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker exec -it tomcat01 ping tomcat-net-01
PING tomcat-net-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.109 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.071 ms
#02 依旧是不能够连通的
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker exec -it tomcat02 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known
结论:假设要跨网络去操作别人,就需要使用docker network connect...
连通!。。。。
实战:部署Redis集群
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker network create redis --subnet 172.38.0.0/16
ba28c459831db7850380e8f6e6911eb5348cebed8744d667b177d22032fa466b
[root@iZ2ze2kw6hfrrx7qs65neyZ dockerfile]# docker network ls
NETWORK ID NAME DRIVER SCOPE
cdfb53d37ca4 bridge bridge local
0ab7551a3614 host host local
da9fe65aa180 mynet bridge local
6969a1201ceb none null local
ba28c459831d redis bridge local