文章目录
基本结构
镜像(image):
docker镜像可以当作一个模板,通过这个模板可以创建多个容器。
例如一个tomcat镜像=>运行=>容器(提供服务)
容器(container):
docker利用容器技术,可以独立运行一个或一组应用(容器间相互隔离)
docker容器通过镜像来创建,即容器中的进程依赖于镜像中的文件。
docker容器类似于虚拟机,可以启动,停止,删除等。可以把docker当作一个简易的Linux系统。
仓库(repository):
存放镜像的地方。分为公有仓库和私有仓库。
默认是使用国外的仓库docker hub。
流程及原理
流程:
以第一层docker run hello-world为例
在本机寻找镜像-->判断是否有该镜像
如果有该镜像-->直接运行
如果没有这个镜像-->去所设置的源中寻找这个镜像-->下载-->运行
原理:
docker是一个CS结构系统,docker的守护进程在主机上。通过socket从客户端访问,docker-server接收到docker-client指令,就好执行该命令。
常用命令
帮助命令
shell
docker version #查看docker版本信息
docker info #显示docker系统信息,比version更相信
docker 命令 --help #docker --help直接查看帮助信息
docker官方文档:https://docs.docker.com/engine/reference/commandline
镜像命令
docker images 查看所有本地主机上的镜像
shell
root@VM-4-17-ubuntu:/home/ubuntu# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tophant/arl v2.6.1 658157563173 10 months ago 1.19GB
hello-world latest d2c94e258dcb 18 months ago 13.3kB
#解释
REPOSITORY 镜像的仓库源
TAG 镜像标签
IMAGE ID 镜像id
CREATED 镜像创建时间
SIZE 镜像大小
#可选项
docker images --help #查看可选项
-a, -all #列出所有镜像
-q, -quiet #只显示镜像id
-f, --filter filter(过滤器) filter可以是dangling=true(悬空镜像)
--format string(格式化字符串) string可以是json table等
也可以直接docker images 后面跟上镜像的名称 搜索指定镜像
docker search 搜索镜像(如果配置的是国内的镜像源会超时)
shell
docker search images(写镜像名称即可)
docker pull 下载镜像
shell
docker pull 镜像名称(默认是最新版本)
docker pull 镜像名称[:tag] #指定版本
root@VM-4-17-ubuntu:/home/ubuntu# docker pull mysql
Using default tag: latest #如果不写,默认就是latest
latest: Pulling from library/mysql
f1a9f94fc2db: Pull complete #分层下载
f98254a2b688: Pull complete
6ad83e89f981: Pull complete
a42d733ea779: Pull complete
6fd1af2601dd: Pull complete
0233a63dc5cd: Pull complete
5f31e56c9bea: Pull complete
c0fb96d14e5b: Pull complete
d57074c62694: Pull complete
7030c241d9b8: Pull complete
Digest: sha256:2be51594eba5983f47e67ff5cb87d666a223e309c6c64450f30b5c59a788ea40 #签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest #真实地址
docker pull mysql 等价于 docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 #下载指定5.7版本的mysql
docker rmi -f 删除镜像
shell
docker rmi -f 镜像名称或ID
docker rmi -f $(docker images) #批量删除所有镜像
docker commit 提交镜像
shell
docker commit 提交容器成为一个新副本
#与git类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名[:tag]
容器命令
有镜像才能创建容器,下载一个centos镜像来测试
shell
docker pull centos #下载centos镜像
新建容器,并启动
shell
docker run [可选项] image 使用的shell
#参数
--name='Name' #容器名字
-d #后台方式运行
-it #使用交互方式运行,进入容器查看内容
-p #指定容器端口
-p 主机端口:容器端口 (常用)
-p ip:主机端口:容器端口
-p 容器端口
-P #随机指定端口
-e #环境配置
#启动并且进入容器,以/bin/bash启动交互
root@VM-4-17-ubuntu:/home/ubuntu# docker run -it centos /bin/bash
#此时已经进入centos
[root@edcaca222a0c /]#
[root@edcaca222a0c /]# exit
退出容器
docker ps 查看正则运行的容器
shell
docker ps
#可选项
-a #查看历史运行过的容器
-n=? #显示最近创建的容器
-q #只显示编号
exit 退出容器
shell
exit #直接停止容器并退出
Ctrl+P+Q #容器不停止,但退出
docker rm 删除容器
shell
docker rm 容器id #删除所有容器(正在运行的容器不能删除)
docker rm -d 容器id #强制删除
docker rm -f $(docker ps -sq) #删除全部容器
启动和停止容器
shell
docker start 容器id #启动容器
docker restart 容器id #重启容器
docker stop 容器id #停止容器
docker kill 容器id #强制停止容器
其他命令
后台启动容器
shell
docker run -d centos #后台运行centos
但是会停止,因为容器使用后台时,需要有一个前台进程,docker发现没有应用,就会自动停止
查看日志
shell
docker logs
#可选项
--details #详细显示
-f
--follow
--since string
-n
--tail number #要显示的日志的条数
-t #显示时间戳
--timestamps
--until
例:
docker logs -f -t --tail 10 bdc75926ba54 #查看容器id为bdc75926ba54的近十条日志
#自己写一段脚本
docker run -d centos /bin/bash -c "while true;do echo success;sleep 1;done"
docker ps
查看容器中的进程信息
shell
docker top 容器ID
查看进程源数据
shell
docker inspect 容器ID #很详细的信息
root@VM-4-17-ubuntu:/home/ubuntu# docker inspect bdc75926ba54
[
{
"Id": "bdc75926ba5407e16b35c9bc7f3ae0458f42760de6cd4438b5241107ca45cd07",
"Created": "2024-11-18T12:42:53.59866176Z",
"Path": "/bin/bash",
"Args": [
"-c",
"while true;do echo success;sleep 1;done"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 3288327,
"ExitCode": 0,
"Error": "",
"StartedAt": "2024-11-18T12:42:53.709853768Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6",
"ResolvConfPath": "/var/lib/docker/containers/bdc75926ba5407e16b35c9bc7f3ae0458f42760de6cd4438b5241107ca45cd07/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/bdc75926ba5407e16b35c9bc7f3ae0458f42760de6cd4438b5241107ca45cd07/hostname",
"HostsPath": "/var/lib/docker/containers/bdc75926ba5407e16b35c9bc7f3ae0458f42760de6cd4438b5241107ca45cd07/hosts",
"LogPath": "/var/lib/docker/containers/bdc75926ba5407e16b35c9bc7f3ae0458f42760de6cd4438b5241107ca45cd07/bdc75926ba5407e16b35c9bc7f3ae0458f42760de6cd4438b5241107ca45cd07-json.log",
"Name": "/nervous_hamilton",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "docker-default",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "bridge",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
23,
168
],
"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/e1f14591fd92ea633340505f56178f100b15e4b3c3d5c99d4cd6fdce7b45f0fb-init/diff:/var/lib/docker/overlay2/d3b747475083e2e4f9f55fdbd0bfcb7014302c609b6e78e71285b3cbc167ada9/diff",
"MergedDir": "/var/lib/docker/overlay2/e1f14591fd92ea633340505f56178f100b15e4b3c3d5c99d4cd6fdce7b45f0fb/merged",
"UpperDir": "/var/lib/docker/overlay2/e1f14591fd92ea633340505f56178f100b15e4b3c3d5c99d4cd6fdce7b45f0fb/diff",
"WorkDir": "/var/lib/docker/overlay2/e1f14591fd92ea633340505f56178f100b15e4b3c3d5c99d4cd6fdce7b45f0fb/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "bdc75926ba54",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash",
"-c",
"while true;do echo success;sleep 1;done"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20210915",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "f917fe5b9d9b12a387c0839145d48519157f1cc1de48ff5946de78dadb34e7f2",
"SandboxKey": "/var/run/docker/netns/f917fe5b9d9b",
"Ports": {},
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "4c25273ed065481b657a6b4878f81d5edcd6d82054d34a5fb197908f75765a3c",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:03",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"MacAddress": "02:42:ac:11:00:03",
"DriverOpts": null,
"NetworkID": "1d4a3714574bf3de87a9553dfc005fad8166af95134e91e3b2bb80c9ac3a65b5",
"EndpointID": "4c25273ed065481b657a6b4878f81d5edcd6d82054d34a5fb197908f75765a3c",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"DNSNames": null
}
}
}
}
]
进入正在运行的容器
外面平时使用的容器通常都是后台方式运行的
shell
方式一(进入容器后开启一个新的终端)
docker exec -it 容器ID 要使用的shell
例:
docker exec -it bdc75926ba54 /bin/bash
方式二(进入容器正在执行的终端,不会启动新的进程)
docker attach 容器ID
例:
docker attach bdc75926ba54
从容器拷贝文件到主机上
shell
docker cp 容器ID:容器内文件路径 目的主机路径
例: #将容器内的test.txt文件拷贝到主机的/root目录下
docker cp 6b627de01b09:/home/test.txt /root
查看docker的cpu状态
shell
docker stats
练习
部署nginx
shell
1、搜索镜像 search (最好去docker hub网站上搜索)
2、下载镜像 pull
3、运行测试 run
shell
docker pull nginx #拉取镜像
docker run -d --name nginx01 -p 3456:80 nginx
#以后台方式运行该镜像,起名为nginx01,3333为主机端口,80为容器地址
curl localhost:3456
#本机自测一下,可以通过
root@VM-4-17-ubuntu:/home/ubuntu# docker exec -it nginx01 /bin/bash
root@b4b82f2c26a3:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@b4b82f2c26a3:/# cd /etc/nginx
root@b4b82f2c26a3:/etc/nginx# ls
conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
部署tomcat
shell
#官方使用
docker run -it --rm tomcat:9.0 #用完即删,一般用于测试
#正常下载启动
docker pull tomcat #拉取镜像
docker run -d -p 3457:8080 --name tomcat01 tomcat
#和上面部署nginx一样,此时可以访问,但不是完整的(是一个阉割版)
root@VM-4-17-ubuntu:/home/ubuntu# docker exec -it tomcat01 /bin/bash
root@cd322dbdf282:/usr/local/tomcat# ls
bin BUILDING.txt conf CONTRIBUTING.md lib LICENSE logs native-jni-lib NOTICE README.md RELEASE-NOTES RUNNING.txt temp webapps webapps.dist work
root@cd322dbdf282:/usr/local/tomcat# cd webapps #应用一般放在这个webap ps文件夹中
root@cd322dbdf282:/usr/local/tomcat/webapps# ls #但是webapps里面什么都没有
#里面没有完整的linux命令、没有webapps,因为他只提供最小可运行的环境
root@cd322dbdf282:/usr/local/tomcat# cd webapps.dist
root@cd322dbdf282:/usr/local/tomcat/webapps.dist# ls
docs examples host-manager manager ROOT
#可以看到webapps.dist文件夹下有内容,将这个里面内容拷贝到webapps目录下
root@cd322dbdf282:/usr/local/tomcat# cp -r webapps.dist/* webapps
root@cd322dbdf282:/usr/local/tomcat# ls webapps
docs examples host-manager manager ROOT
#此时再次刷新网页,即可正常显示
部署ES+Kibana
es和kibana之间需要进行网络通信,首先要创建一个虚拟网络
shell
docker network create es-net
部署es
shell
#ES暴露的端口很多,ES很耗内存!!!,且ES的数据一般需要放置到安全目录挂载
#下载启动
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
#由于十分耗内存,内存小可能无法启动,这里使用-e参数修改环境配置
docker run -d --name elasticsearch02 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2
#docker stats 查看docker的cpu状态,可以发现内存很小了,
#curl localhost:9200 发现web页面也可以正常访问了
部署kibana(要于es的版本一致)
shell
docker pull kibana:7.6.2 #用的国内的源,无法下载最新的
#启动(如果遇到Kibana server is not ready yet等一会就可以了)
docker run -d --name kibana02 -e ELASTICSEARCH_HOSTS=http://4554b397a6f3:9200 --net=es-net -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" -p 5601:5601 kibana:7.6.2
#第一个-e是设置es的节点地址,如果用容器ID不好使就换成服务器ip
#将es的容器ID替换成服务器的ip也可以
成功进入
镜像原理
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、库、环境变量、配置文件。
加载原理
联合文件系统(UnionFS):一种分成、轻量级且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下
docker的镜像是机械上由一层一层的文件系统组成,这种层级文件系统被称为联合文件系统。
bootfs(boot file system):是docker镜像的最底层,主要包含boot加载器和kernel内核,而boot加载器用来引导加载kernel。
linux启动时会加载bootfs系统,加载完之后内存的使用权就由bootfs交给内核了,系统此时也会卸载bootfs
rootfs(root file system) :是在bootfs之上一层的文件系统,包含linux系统中的dev/ /proc /bin /etr
标准目录和文件。是一个小型的虚拟环境
rootfs就是各种操作系统的发行版,如centos、bunut等
分层原理
所有docker镜像都起始于一个基础镜像层,当修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
例如下载镜像时候,第一层相同,直接复用,其他几层分层下载
afb6ec6fdc1c: Already exists
608641ee4c3f: Pull complete
668ab9e1f4bc: Pull complete
78a12698914e: Pull complete
d056855f4300: Pull complete
618fdf7d0dec: Pull complete
举一个例子:例如创建一个ubuntu镜像,这是该镜像的第一层。如果添加了一个python环境包,就会在基础镜像层上创建第二个镜像层。如果继续添加一个补丁,就好创建第三个镜像层。
在额外添加镜像时,镜像始终保持当前镜像的组合。
例如:镜像包含了来自两个镜像层的6个文件,每个镜像层有三个文件。
如果变成三层镜像,其中文件7是文件5的更新版本,上层镜像层中的文件会覆盖底层镜像层中的文件。
所有镜像层合并,对外统一视图仍为6个文件
docker镜像都是只读的,当容器启动时,一个新的可写层被加=加载到镜像的顶部。(这一层被称为容器层,容器层之下都被称为镜像层)!
commit镜像
shell
docker commit 提交容器成为一个新副本
#与git类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名[:tag]
例:
shell
# 1、启动一个默认的tomcat
docker run -d -p 8080:8080 tomcat
# 2、这个默认的tomcat是没有webapps应用,官方的镜像默认webapps下面是没有文件的
docker exec -it 容器id
# 3、拷贝文件进去
cp -r webapps.dist/* webapps
# 4、将操作过的容器通过commit提交为一个镜像。
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[TAG]
docker commit -a="jess" -m="add webapps app" 容器id tomcat02:1.0
容器数据卷
场景:如果数据存储在容器中,删除容器数据就会被删除。
需求:数据可以持久化。例如:mysql的数据保存在本地
容器之间都数据共享技术。docker产生的数据同步到本地,这就是卷技术。本质上就是目录的挂载,将容器中的目录挂载到我们的主机系统上。
总结:容器的同步操作和持久化,容器间也可以数据共享。
使用数据卷
主要使用-v参数
shell
docker run -it --name centos01 -v 主机目录:容器目录 容器ID 使用的shell
这里以centos镜像为例
shell
docker run -it --name centos01 -v /home/test:/home centos /bin/bash
此时主机的/home/test目录与容器中的/home目录中的所有内容都互通。
无论在哪一端修改,里面的内容都会同步
使用inspect命令查看是否挂载成功
shell
#docker inspect 14309fc52d5d
回显内容包括以下内容即挂载成功
"Mounts": [
{
"Type": "bind",
"Source": "/root/test",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
前面说的修改nginx配置文件很麻烦,每次都要进入容器内部才能修改。
但是通过-v容器卷挂载,在容器外部提供一个映射路径,就可以解决这个问题了。
实战:mysql同步数据
shell
#启动mysql时需要设置密码
docker run -d --name mysql01 -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql
使用Navicat连接,这里的主机名改为自己的服务器ip即可。
连接成功,并新建一个数据库,名为test
可以看到在主机的/home/docke/data文件夹下也生成了test文件
将mysql容器删除,再次查看该文件夹,发现文件仍存在。这就达到了使用容器数据卷挂载的目的。
具名挂载、匿名挂载
shell
匿名挂载:当使用-v参数不指定主机目录时
docker run -d --name nginx03 -P -v /etc/nginx nginx
#-P随机映射端口
具名挂载:卷名/容器内目录 #这里的卷名不带路径'/'
docker run -d -P --name nginx04 -v juming-nginx:/etc/nginx nginx
docker volume ls #查看所有的卷
可以看到这两个卷,那么这两个卷在哪里呢
shell
docker volume inspect juming-nginx #查找卷的路径
#可以看到,不指定目录的情况下,都会保存在/var/lib/docker/volumes目录下
[
{
"CreatedAt": "2024-11-20T19:11:24+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]
shell
总结:
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载(比指定路径少一个/)
-v /主机路径:容器内路径 #指定路径挂载
shell
拓展:还可以通过-v参数设置读写权限,但是一旦设置之后就不能修改
ro #readonly只读
rw #readwrite可读可写
docker run -d -P --name nginx04 -v juming-nginx:/etc/nginx:ro nginx #只读
docker run -d -P --name nginx04 -v juming-nginx:/etc/nginx:rw nginx #可读可写
如果看到ro,说明这个路径只能通过主机操作,容器内无法操作
容器间数据同步
使用--volumes-from
参数实现容器间数据共享(可以当作备份、拷贝)
shell
docker run -it --name docker01 centos_plus /bin/bash
docker run -it --name docker02 --volumes-from docker01 centos_plus /bin/bash
docker run -it --name docker03 --volumes-from docker02 centos_plus /bin/bash
操作其中一个容器,其他容器也会随之改变。
如果删除其中一个容器,其他容器中的文件还会在,数据卷容器的生命周期一直会持续到没有容器使用为止。
如果持久化到本地,本地的数据是不会删除的。
Dockerfile
简介及构建DockerFile方式
DockerFile就是用来构建docker镜像的构建文件(可以看作命令脚本,通过这个脚本可以生成镜像)
基础知识:
1、每个保留关键字(指令)都要大写
2、执行命令从上到下执行
3、#表注释
4、每个指令都会创建提交一个新的镜像层并提交
DockerFile:构建文件,定义了一切的步骤、源代码
DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品
Docker容器:容器就是镜像运行起来提供服务器
构建步骤 :
1、编写DockerFile文件
2、docker build构建镜像
3、docker run运行镜像
4、docker push发布镜像
dockerfile
#一个小例子,命名为file1
FROM centos
VOLUME ["volume01","volume02"] #匿名挂载
CMD echo "----end----"
CMD /bin/bash
shell
docker build -f file1 -t centos_test .
-f参数 指定文件
-t参数 指定生成的镜像名
DockerFile指令
dockerfile
FROM #基础镜像,一切从这里开始构建
MAINTAINER #镜像是谁写的
RUN #镜像构建时需要运行的命令
ADD #添加内容。以构建tomcat镜像为例,这是tomcat压缩包
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPOSE #暴露端口配置 相当于-p
CMD #指定这个容器启动时要运行的命令,但只有最后一个会生效,可以被替代
ENTRYPOINT #与CMD类似,但可以追加命令
ONBUILD #当构建一个被继承的DockerFile时,就会运行ONBUILD指令
COPY #类似ADD,将文件拷贝到镜像中
ENV #构建时,设置环境变量 -e
CMD与ENTRYPOINT的区别
CMD
dockerfile
#dockerfile文件
FROM centos:7
CMD ["ls","-a"]
build成功之后,run该容器时会执行该命令。
shell
docker run 3608c6aac6fd -l #会报错
docker run 3608c6aac6fd ls -al #会执行最后一个命令ls -al
docker run 3608c6aac6fd pwd #只会执行pwd命令
说明CMD命令不能拼接命令,且只能执行最后一个命令(最后的命令会覆盖前面的命令)
ENTRYPOINT
dockerfile
FROM cnetos:7
ENTRYPOINT ["ls","-a"]
build成功之后
shell
docker run 6beb6f712117 -l #会执行这两个参数,即ls -al
说明ENTRYPOINT可以进行拼接命令
实战:构建自己的centos
Docker Hub中绝大多数镜像都是在FROM scratch
这个基础镜像之上构建的,然后配置需要的软件和配置来构建。
注意dockerifle文件中,最好不要有注释,构建之后会很难看
dockerfile
#编写dockerfile文件内容
FROM centos:7
MAINTAINER clockwise #声明作者
ENV MYPATH /usr/local
WORKDIR $MYPATH #定义工作目录,进入直接会进入该目录
#这里换成阿里云的centos7镜像
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
#基础镜像命令也是不全的,这里进行安装vim和ifconfig命令
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80 #暴露80端口
CMD echo $MYPATH
CMD echo "end"
CMD /bin/bash
shell
#通过dockerfile构建镜像
docker build -f file1 -t mycentos:1.0 .
shell
#运行镜像
docker run -it mycentos:1.0
进入之后会发现当前的目录就是上面定义的/usr/local
vim命令和ifconfig命令也可以使用。
shell
#使用history命令查看变更历史
docker history 6bc730024d72
可以看到镜像的制作过程命令(说明命令是一条一条进行编译的)
实战:构建自己的tomcat镜像
1、首先准备镜像文件:tomacat压缩包和jdk压缩包
2、构建DockerFile,官方命名为Dockerfile
,使用该命名就不需要指定路径了
dockerfile
FROM centos:7
MAINTAINER clockwise
#通过ADD命令添加会自动解析
ADD jdk-8u201-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.12-src.tar.gz /usr/local/
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_201
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.12-src
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.12-src
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.12-src/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.12/bin/logs/catalina.out
shell
docker run -d -p 9090:8080 --name mytomcat -v /root/tomcat_test/test:/usr/local/apache-tomcat-9.0.12-src/webapps/test -v /root/tomcat_test/logs:/usr/local/apache-tomcat-9.0.12-src/logs smalltomcat
docker网络
理解docker 0
这里服务器内网ip为192.168.12.87。
shell
root@hcss-ecs-2214:/# 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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether fa:16:3e:a5:09:54 brd ff:ff:ff:ff:ff:ff
inet 192.168.12.87/20 brd 192.168.15.255 scope global dynamic noprefixroute eth0
valid_lft 315275508sec preferred_lft 315275508sec
inet6 fe80::f816:3eff:fea5:954/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:e2:20:15:88 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:e2ff:fe20:1588/64 scope link
valid_lft forever preferred_lft forever
#1、lo为本机回环地址
#2、为服务器内网地址
#3、docker0地址
这里使用tomcat进行测试,由于tomcat镜像过于精简,不带iproute2功能,这里需要自己新建一个镜像
dockerfile
#Dockerfile文件
FROM tomcat:latest
RUN apt update && apt install -y iproute2 #添加ip addr命令
RUN apt install -y iputils-ping #添加ping命令
shell
docker build -t newtomcat #自己新建一个镜像起名为newtomcat
测试主机和容器之间的通信:
shell
docker run -d -P --name tomcat01 newtomcat
docker exec -it tomcat01 ip addr
可以看到一个是回环地址127.0.0.1,一个是eth0@if123(docker自动分配的地址)的地址172.17.0.2
主机ping这个ip,发现可以Ping通。进入容器ping主机ip,发现也可以ping通。
说明主机和docker之间可以进行通信。
原理:
当启动docker容器时,docker会自动给docker容器分配一个ip,只要安装了docker就会有一个网卡docker0桥接模式,使用的技术是evth-pair技术
当只有一个容器的时候,在主机使用ip add命令查看网卡
可以看到主机中有一个·vethbdd6727@if122
此时,再建立一个容器命名为tomcat02,再去查看主机网卡
发现多了一个vethf99a830@if124
这说明docker容器生成的网卡都是一对一对的。
evth-pair就是一对的虚拟设备接口,他们是成对出现的,一段连接着协议,一段彼此相连。
由于这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备
OpenStac,Docker容器之间的连接,OVS的连接,都是使用evth-pair技术
测试容器之间的通信:
shell
使用tomcat02去ping tomcat01的ip
docker exec -it tomcat02 ping 172.17.0.2
#可以ping通的
tomcat01和tomcat02都是共用一个路由器--docker0
所有容器不指定网络的情况下,都是由docker0给容器分配一个默认可用的ip。
虚拟网络接口的转发效率高
--link
通过--link
,不通过网络、地址,容器之间通过容器名ping通。(--link只有构建容器时才可以使用)
shell
docker exec -it tomcat02 ping tomcat01
#ping: tomcat01: Name or service not known
docker run -d -P --name tomcat03 --link tomcat02 newtomcat
docker exec -it tomcat03 ping tomcat02
#tomcat03可以ping通tomcat02
#但是tomcat02不能ping通tomcat03
那么如何才能让tomcat02ping通tomcat03?
可以通过docker network inspect查看一下bridge中的网卡信息
shell
root@hcss-ecs-2214:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
0d149ac9fdda bridge bridge local
d5280ca25d4b host host local
6855cd3d7f31 none null local
root@hcss-ecs-2214:~# docker network inspect 0d149ac9fdda
[
{
"Name": "bridge",
"Id": "0d149ac9fddafe29418226287d8450e5016bb5a120a57f0a3726f5dbca5079e6",
"Created": "2024-11-20T18:01:30.899264891+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"37bcfb2c3a5ee8a6990d962654fd928803d7dcca75ce95019101875242116e44": {
"Name": "tomcat03",
"EndpointID": "9c295d4859973080e89c835997fa3f5afa592c22028bc71509cb3a044a8bf202",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
},
"4e3107cadc5e7984d7cb500b9a3d6dd121d597a35ba3c03ed48386379072db59": {
"Name": "tomcat01",
"EndpointID": "b10e9a3b00226b66013239f18121f0f1c71f317d84eee0f923ffbe5a5c0c9eaf",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"65aca855c46bc57e730842cff5a1cabb2ca14633b8ddc20536e6ad9ee69b13b2": {
"Name": "tomcat02",
"EndpointID": "0e81426b6c74065dd90036b6d9911f8b6cf136d653625144ca2e320543527ce8",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
然后查看tomcat03的hosts文件
可以看到在tomcat03本地已经配置了tomcat02的地址
shell
docker exec -it tomcat03 cat /etc/hosts
root@hcss-ecs-2214:~# docker exec -it tomcat03 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 tomcat02 65aca855c46b
172.17.0.4 37bcfb2c3a5e
由于docker0不支持容器名访问等问题,因此出现了自定义网络
自定义网络
shell
docker network --help查看docker网络相关命令
查看所有docker网络
shell
docker network ls
root@hcss-ecs-2214:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
0d149ac9fdda bridge bridge local
d5280ca25d4b host host local
6855cd3d7f31 none null local
桥接模式:
bridge:桥接docker
none:不配置网络
host:和宿主机共享网络
container:容器内网络连通(用的少)
shell
#平时启动容器时,默认使用--net bridge
docker run -d -P --name tomcat01 newtomcat
docker run -d -P --name tomcat01 --net bridge newtomcat
#自定义网络
docker network create --help #查看相关命令
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
#--driver bridge 桥接模式
#--gateway参数设置网关,家庭路由器默认就是192.168.0.1
#使用inspect命令查看
docker network inspect mynet
可以看到已经创建成功
shell
#启动两个容器测试一下
docker run -d -P --name tomcat01 --net mynet newtomcat
docker run -d -P --name tomcat02 --net mynet newtomcat
inspect查看一下mynet,可以看到成功指定该网络
shell
#现在使用容器名和ID都可以互相ping通过了
docker exec -it tomcat01 ping tomcat02
网络连通
shell
docker run -d -P --name tomcat-net-01 --net mynet tomcat-net
docker run -d -P --name tomcat-net-02 --net mynet tomcat-net
docker run -d -P --name tomcat-01 tomcat-net
docker run -d -P --name tomcat-02 tomcat-net
测试tomcat-01和mynet打通(tomcat-02同理)
shell
docker network connect mynet tomcat-01
然后使用inspect查看
docker network inspect mynet
发现成功将tomcat-01放到mynet网络下。(即一个容器两个地址 )
之后在该mynet网络下的容器就可以互相通信了。
实战:Redis集群部署
shell
#创建redis网卡
docker network create redis --subnet 172.38.0.0/16
#通过脚本创建(之间复制到命令行即可)
for port in $(seq 1 6):
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat <<EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
#执行完可以看到在/mydata/redis目录下生成了6个node-目录
docker run -p 6371:7379 -p 16371:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
#这是第一个,以此类推,写六个
docker run -p 6371:7379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
#这是第二个
docker run -p 6372:7379 -p 16372:16379 --name redis-2 \
-v /mydata/redis/node-2/data:/data \
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
进入第一个容器(这里没有/bin/bash,要用/bin/sh)
shell
docker exec -it redis-1 /bin/sh
#创建集群
redis-cli --cluster create 172.38.0.11:6371 172.38.0.12:6372 172.38.0.13:6373 172.38.0.14:6374 172.38.0.15:6375 172.38.0.16:6376 --cluster-replicas 1
shell
for port in $(seq 1 6):
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat <<EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
docker run -p 6371:7379 -p 16371:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
done
Docker Compose
Docker-Compose是Docker官方的开源项目,用来实现对Docker容器集群的快速编排。
通过yml文件(yaml也是一样的,没有任何区别)配置应用程序需要的所有服务。
使用步骤:
1、使用Dockerfile定义的应用程序环境
2、使用docker-compose.yml定义构成应用程序的服务(使他们在隔离环境中一起运行)
3、执行docker-compose up命令来启动运行整个应用程序