从零开始学Docker
说明
Docker的安装
查看centos版本(Docker 要求CentOS系统的内核版本高于3.10)
[root@dawn ~]# uname -r
3.10.0-1062.12.1.el7.x86_64
升级软件包及内核
[root@dawn ~]# yum update
安装Docker
[root@dawn ~]# yum install docker
启动Docker
systemctl start docker
开机自动启动Docker
systemctl enable docker
关闭Docker
systemctl stop docker
Docker的常用命令
帮助命令
docker version #显示docker的版本信息
docker info #显示docker的系统信息,包括镜像和容器信息
docker 命令 --help #帮助命令
帮助文档的地址:https://docs.docker.com/engine/reference/commandline/docker/
镜像命令
docker images 查看主机所有镜像
## 可选项
- -a,--all #列出所有镜像
- -q,--quiet #只显示出镜像的id
## 解释
- RePOSITORY #镜像的仓库源
- TAG #镜像的标签
- IMAGE ID #镜像的id
- CREATED #镜像的创建时间
- SIZE #镜像的大小
docker search 搜索镜像
docker search mysql
## 可选项,通过搜索来过滤
- --filter=STARS=3000
## 搜索出来的镜像就是STARS大于3000的
docker search mysql --filter=STARS=3000
docker pull 下载镜像
## 下载镜像 docker pull 镜像名[:tag]
- docker pull mysql #如果不写tag,默认下载latest
- docker pull mysql:5.7 #指定版本下载
docker rmi 删除镜像
docker rmi -f 容器id #删除指定的容器
docker rmi -f 容器id 容器id #删除多个容器,空格分隔
docker rmi -f $(docker images -aq) # 删除全部的容器
容器命令
说明:我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习
docker pull centos
新建容器并启动
docker run [可选参数] image
# 参数说明
- --name="Name" 容器名字 tomcat01 tomcat01, 用来区分容器
- -d 后台方式运行
- -it 使用交互方式运行,进入容器查看内容
- -p 指定容器的端口 -p 8080:8080
-p ip: 主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
- -P 随机指定端口
# 测试,启动并进入容器
[root@dawn /]# docker run -it centos:7 /bin/bash
[root@123066df2102 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
退出容器
exit #直接停止容器并退出
Ctrl + p + q #容器不停止退出
列出所有运行的容器
docker ps 命令
- #列出当前正在运行的容器
- -a #列出当前正在运行的容器+运行过的容器
- -n=? #显示最近创建的容器
- -q #只显示容器的编号
删除容器
docker rm 容器id #删除指定的容器,不能删除正在运行的容器,如果是强制删除 rm -f
docker rm $(docker ps -aq) #删除所有的容器
docker ps -a -q|xargs docker rm #删除所有的容器
启动和停止容器的操作
docker start 容器id #启动容器
docker restart 容器id #重启容器
docker stop 容器id #停止当前正在运行的容器
docker kill 容器id #强制停止当前容器
Docker的其他命令
后台启动容器
# 命令 docker run -d 镜像名!
[root@dawn /]# docker run -d centos:7
ff50f950600a2498860fad73a375090a7c3ac165bcff3a0ead5313ba1073939d
[root@dawn /]# docker ps
# 问题docker ps,发现 centos 停止了
# 城建的坑, docker 容器使用后台进行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
# nginx,容器启动后,发现自己没有提供服务就会以为没有程序了,就会立即停止
查看日志
docker logs -tf --tall 10 容器id
# 显示日志
- -tf #显示日志
- --tail number #显示日志条数
查看容器中进程信息
# docker top 容器id
[root@dawn /]# docker top 123066df2102
UID PID PPID C STIME TTY TIME CMD
root 10218 10202 0 11:02 pts/0 00:00:00 /bin/bash
查看镜像元数据
#命令
docker inspect 容器id
[root@dawn /]# docker inspect 123066df2102
[
{
"Id": "123066df21023544dc65329734fb184c04c2d0dc66f21992f9788a5a05d5dd47",
"Created": "2020-11-26T02:25:01.792197429Z",
"Path": "/bin/bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 10218,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-11-26T03:02:54.008546447Z",
"FinishedAt": "2020-11-26T02:38:40.78598665Z"
},
"Image": "sha256:8652b9f0cb4c0599575e5a003f5906876e10c1ceb2ab9fe1786712dac14a50cf",
"ResolvConfPath": "/var/lib/docker/containers/123066df21023544dc65329734fb184c04c2d0dc66f21992f9788a5a05d5dd47/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/123066df21023544dc65329734fb184c04c2d0dc66f21992f9788a5a05d5dd47/hostname",
"HostsPath": "/var/lib/docker/containers/123066df21023544dc65329734fb184c04c2d0dc66f21992f9788a5a05d5dd47/hosts",
"LogPath": "/var/lib/docker/containers/123066df21023544dc65329734fb184c04c2d0dc66f21992f9788a5a05d5dd47/123066df21023544dc65329734fb184c04c2d0dc66f21992f9788a5a05d5dd47-json.log",
"Name": "/stoic_knuth",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Capabilities": null,
"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",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"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"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/3bffcf81d93ab1e3c1815077352287e44434ab01ba34991ec3394b557266cbfe-init/diff:/var/lib/docker/overlay2/ddd4290a76fdaa5ba65393d2b59b979a0d6daf3a1b5c3b3967901bc29c70bf47/diff",
"MergedDir": "/var/lib/docker/overlay2/3bffcf81d93ab1e3c1815077352287e44434ab01ba34991ec3394b557266cbfe/merged",
"UpperDir": "/var/lib/docker/overlay2/3bffcf81d93ab1e3c1815077352287e44434ab01ba34991ec3394b557266cbfe/diff",
"WorkDir": "/var/lib/docker/overlay2/3bffcf81d93ab1e3c1815077352287e44434ab01ba34991ec3394b557266cbfe/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "123066df2102",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"Image": "centos:7",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20201113",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-11-13 00:00:00+00:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "4dc97ef13891a406ba86760b0b736bf8488c971080fb98d15ed1a37d2deb263c",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/4dc97ef13891",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "f729f565726f7a3d5ea8e36a2947bf38e8a8ed6c8184c4b16f3971823d3ebde5",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "58388b98caaa30951f981df32ddccffd9cb445ee2b26bf5b09e4e55b893f59e9",
"EndpointID": "f729f565726f7a3d5ea8e36a2947bf38e8a8ed6c8184c4b16f3971823d3ebde5",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
进入当前正在运行的容器
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 命令
- 方式一: docker exec -it 容器id /bin/bash
- 方式二: docker attach 容器id
# docker exec # 进入容器后开启一个新的终端,可以在里面操作(常用)
# docker attach # 进入容器正在执行的终端,不会启动新的进程
从容器内拷贝文件到主机上
docker cp 容器id:容器内路径 目的主机路径
# 拷贝是一个手动过程,未来可以使用 -v 卷的技术,可以实现
Docker练习
部署Nginx
- 搜索镜像,search 建议去docker搜索,可以看到文档帮助
- 下载镜像 pull
[root@dawn /]# docker pull nginx
- 运行测试
[root@dawn /]# docker run -d --name nginx01 -p 3344:80 nginx
# -d 后台运行
# --name 给容器命名
# -p 宿主机端口,容器内部端口
- 进入容器
[root@dawn /]# docker exec -it nginx01 /bin/bash
root@271a455fce1b:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@271a455fce1b:/# ls
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
boot docker-entrypoint.d etc lib media opt root sbin sys usr
root@271a455fce1b:/#
- 外网访问
部署tomcat
- 下载tomcat
[root@dawn /]# docker pull tomcat
- 启动运行
[root@dawn /]# docker run -d --name nginx01 -p 3344:80 nginx
- 外网访问
- 进入容器
[root@dawn /]# docker exec -it nginx01 /bin/bash
发现问题,1.linux命令少了。2.没有webapps.阿里云镜像的原因。默认是最小的镜像,所以不必要的都剔除掉。保证最小的运行环境
部署 es + kibana
es 暴露的端口很多 -9200、9300
es 十分的耗内存 - “ES_JAVA_OPTS=-Xms512m -Xmx512m”
es 的数据一般需要放置到安全目录!挂载data、plugins、conf
- 扩展命令
docker stats 容器id # 查看容器的cpu内存和网络状态
- 直接运行es测试
(–net somenetwork ? 网络配置)
9200作为Http协议,主要用于外部通讯( ES节点 和 外部 通讯使用 )
9300作为Tcp协议,jar之间就是通过tcp协议通讯( ES节点之间通讯使用 )
[root@dawn /]# docker run --name es01 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
- 启动后发下很卡
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
ba236371acd0 elasticsearch 0.32% 1.226GiB / 3.7GiB 33.12% 0B / 0B 0B / 0B 43
- 增加上内存限制启动
[root@dawn /]# docker run -d --name es01 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2
- 启动之后,使用 docker stats 查看下cpu状态
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
179b21241d90 es01 0.72% 342MiB / 1.7GiB 9.02% 0B / 0B 0B / 0B 44
- 测试es是否成功
[root@dawn /]# curl localhost:9200
{
"name" : "57bb765ae7f9",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "pPMOBwVzTgyIMBJdz6ujNQ",
"version" : {
"number" : "7.6.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"build_date" : "2020-03-26T06:34:37.794943Z",
"build_snapshot" : false,
"lucene_version" : "8.4.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
- 使用kibana连接es
正在思考
容器数据卷
docker的理念回顾
将应用和环境打包成一个镜像
容器之间可以有一个数据共享的技术,Docker容器中产生的数据同步到本地。这就是卷的技术,目录的挂在,将我们容器内的目录,挂载到Linux上上面。
总结一句话:容器的持久化和同步操作!容器间也是可以数据共享的!
使用数据卷
方式一:直接使用命令来挂在 -v
# 测试
[root@dawn /]# docker run -it -v /home/ceshi:/home centos /bin/bash
# 启动起来时候我们可以通过 docker inspect 容器id
测试文件同步(停止容器,在宿主机修改文件,容器内的文件依旧同步)
实战:安装MySql
思考:MySql 的数据持久化问题
- 获取镜像
[root@dawn ~]# 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 容器名字
[root@dawn /]# docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf -v /home/mysql/conf -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysqltest mysql:5.7
启动成功之后,我们在本地使用数据库客户端 来连接测试一下,注意打开安全组
连接到服务器的3306端口 和容器的3306端口映射,我们连接上了
在本地创建一个数据库,查看一下我们的映射路径是否ok!
具名挂载和匿名挂载
- 匿名挂载
-v 容器内路径
[root@dawn /]# docker run -d -P --name nginx01 -v /ect/nginx nginx
20732ef060165cfd2f6dee1890b5be775c06db5bee00c871112a079a7e9e3185
# 查看所有的volume 的情况
[root@dawn /]# docker volume ls
DRIVER VOLUME NAME
# 这里发现,这种就是匿名挂载,我们在 -v 只写了容器内路径
local ee2f8fc49acdde5dacb4d63a7625ce3bf29dd6d6fa592bb5b4d3da2f7e855060
local fc6c71802abd65ab382879869a0df942fdbba2a5105b5d5e6631062a08ef31c2
- 具名挂载(推荐)
# 通过 -v 卷名:容器内路径
[root@dawn /]# docker run -d -P --name nginx02 -v juming-nginx:/ect/nginx nginx
d34b1f3bb27edf0c9ad0e2399250b08152ed4f99fc78b5796ae86ef7878a2b33
[root@dawn /]# docker volume ls
DRIVER VOLUME NAME
local ee2f8fc49acdde5dacb4d63a7625ce3bf29dd6d6fa592bb5b4d3da2f7e855060
local fc6c71802abd65ab382879869a0df942fdbba2a5105b5d5e6631062a08ef31c2
local juming-nginx
# 查看一下这个卷
[root@dawn /]# docker volume inspect juming-nginx
[
{
"CreatedAt": "2020-11-27T10:53:12+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]
# 所有的docker 容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumes/xxxx/_data
[root@dawn /]# cd /var/lib/docker/volumes/
[root@dawn volumes]# ls
ee2f8fc49acdde5dacb4d63a7625ce3bf29dd6d6fa592bb5b4d3da2f7e855060 juming-nginx
fc6c71802abd65ab382879869a0df942fdbba2a5105b5d5e6631062a08ef31c2 metadata.db
我们通过具名挂载可以方便的找到我们的一个卷。
如何确定是剧名挂载还是匿名挂在,还是指定路径挂载
-v 容器内路径 # 匿名挂在
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径:容器内路径 # 指定路径挂载
拓展
通过 -v 容器内路径,ro rw 改变读写权限
ro readonly # 只读
rw readwrite # 可读可写
一旦这个设置了容器权限,容器对我们挂在出来的内容就有了限定
ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作的
docker run -d -P --name nginx02 -v juming-nginx:/ect/nginx:rw nginx
docker run -d -P --name nginx02 -v juming-nginx:/ect/nginx:ro nginx
初识Dockerfile
创建自己的容器
Dockerfile 就是用来构建docker 镜像的构建文件,命令脚本。
通过这个脚本可以生成镜像,镜像是一层一层的,脚本一个个的命令,每个命令都是一层!
# 创建一个dockerfile文件,建议 Dockerfile
# 文件中的的内容 指令(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "----end----"
CMD /bin/bach
启动自己的容器
这个卷和外部一定有一个同步目录
匿名挂载,查看一下卷挂载的路径
测试一下刚才的文件是否同步出去了。
这种方式我们使用的很多,假设构建镜像的时候没有挂载卷,哟啊手动镜像挂载 -v 卷名:容器内路径
数据卷容器
–volumes-form
多个 mysql 同步数据
# 启动容器
[root@dawn /]# docker run -it --name docker01 down/centos /bin/bash
[root@00865e79e4dd /]# [root@dawn /]#
# 启动第二个容器
[root@dawn /]# docker run -it --name docker02 --volumes-from docker01 down/centos /bin/bash
[root@d86228473db2 /]# ls
# 启动第三个容器
[root@dawn ~]# docker run -it --name docker03 --volumes-from docker02 down/centos /bin/bash
[root@3350eb0b2956 /]# ls
删除一个容器,其他容器数据并不影响
多个mysql实现数据共享
docker run -d -p 3306:3306 -v /etc/mysql/conf -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysqltest mysql:5.7
docker run -d -p 3307:3306 -v /etc/mysql/conf -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysqltest --volumes-form mysql:5.7
结论
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用位置。
但是一旦你持久化到本地,这个时候,本地的数据是不会删除的!
DockerFile
dockerfile 是用来构建docker镜像的文件!命令参数脚本!
构建步骤:
- 编写一个 dokerfile 文件
- docker build 构建成为一个镜像
- docker run 运行镜像
- docker push 发布镜像(DockerHub、阿里云镜像仓库)
很多官方镜像都是基础包,很多功能没有,我们通常会自己搭建自己的镜像
DicjerFile 构建过程
基础知识
- 每个保留关键字(指令)都必须是大写字母
- 按照从上到下顺序执行
- 表示注释
- 每一个指令都会创建提交一个新的镜像层,并提交
dockerfile 是面向开发的,我们以后发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单
Docker镜像逐渐成为企业交付的标准,必须要掌握
步骤:开发,部署,运维。。。缺一不可
DockerFile:构建文件,定义了一切的步骤,源代码
DockerImages:通过DockerFile 构建生成的镜像,最终发布和运行的产品
Docker容器:容器就是镜像运行起来提供的服务器
DockerFile 的指令
> FROM # 基础镜像,一切从这里开始构建
> MAINTAINER # 镜像是谁写的, 姓名+邮箱
> RUN # 镜像构建的时候需要运行的命令
> ADD # 步骤,tomcat镜像,这个tomcat压缩包!添加内容
> WORKDIR # 镜像的工作目录
> VOLUME # 挂载的目录
> EXPOSE # 保留端口配置
> CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
> ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
> ONBUILD # 当都贱一个被继承 DockerFile 这个时候就会运行 ONBUILD 的指令,触发指令
> COPY # 类似ADD ,将我们文件拷贝到镜像中
> ENV # 构建的时候设置环境变量
实战测试
Docker Hub 中 99% 镜像都是从这个基础镜像过来的 FROM scratch ,然后配置需要的软件和配置进行构建的
创建一个自己的centos
# 1. 编写Dockerfile的文件
FROM centos
MAINTAINER dawn<1114613633@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vm
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 2. 通过这个文件构建镜像
# 命令 docker build -f dockerfile文件名 -t 镜像名:[tag]
docker build -f my_centos -t mycentos:1.0 .
# 3. 测试运行
对比:之前的原生的centos
我们增加之后的镜像
我们还可以列出本地镜像的变更历史
我们平时拿到一个镜像时,可以研究一下它是怎么做的
实战:Dockerfile制作tomcat+jdk 镜像
- 准备镜像文件 tomcat 压缩包,jdk压缩包
- 编写dockerfile文件,官方命名Dockerfile,build 会自动秀找这个文件,就不需要-f 指定了
FROM centos
MAINTAINER dawn<1114613633@qq.com>
ADD jdk-8u212-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.39.tar.gz /usr/local
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
# jdk 环境配置
ENV JAVA_HOME /usr/local/jdk1.8.0_212
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
# tomcat 环境配置
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.39
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.39
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.39/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.39/bin/logs/catalina.out
- 构建镜像
[root@dawn tomcatDemo]# docker build -t my_tomcat .
- 启动镜像
[root@dawn /]# docker run -d -p 9090:8080 --name my_tomcat_01 -v /data/docker/mount/my_tomcat/text:/usr/local/apache-tomcat-9.0.39/webapps/test -v /data/docker/mount/my_tomcat/logs/:/usr/local/apache-tomcat-9.0.39/logs my_tomcat
- 访问测试
curl localhost:9090
http://toneystk.cn:9090/
- 发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了)
# WEB-INF/web.xml
<web-app 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"
version="2.4">
<description>
JSP 2.0 Examples.
</description>
<display-name>JSP 2.0 Examples</display-name>
</web-app>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello dawn</title>
</head>
<body>
Hello World!<br/>
<%
System.out.println("hello dawnnnnn");
%>
</body>
</html>
发布自己的镜像
DockerHub
- 地址:https://hub.docker.com/ 注册自己的账号
- 确定这个账号可以登录
- 在我们的服务器上提交自己的镜像
[root@dawn ~]# docker login --help
Usage: docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
- 登录完毕后就可以发布镜像了
# push 自己的镜像到服务器上,增加一个tar
# docker tag 镜像id 名称:[tar]
# 自己发布的镜像,尽量带上版本号
docker tag 2ljweq21 dawn/my_tomcat:1.0
# 提交的时候也是按照镜像的层级来进行提交的
发布到阿里云镜像
Docker 网络
理解Docker0
清空所有环境
测试
三个网络
问题,docker是如何处理容器网络访问的?
[root@dawn ~]# docker run -d -P --name tomcat01 tomcat
# 查看容器的内部网络地址 ip addr , 发现容器启动的时候会得到一个 eht0@if127 ip地址,docker分配的
[root@dawn ~]# docker exec -it tomcat01 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
126: eth0@if127: <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
# 思考,linux 能不能ping 通容器内部!
[root@dawn ~]# 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.051 ms
# linux 可以ping通 docker 容器内部
原理
- 我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个docker0桥接模式,使用的技术是evth-pair技术
再次测试 ip addr
- 在启动一个容器测试,发现又多了一对网卡
我们发现,这个容器带来的网卡,都是一对一对的
evth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
正式因为有这个特性,evth-pair 充当一个桥梁,连接各种虚拟网络设备
OpenStac,Docker 容器之前的连接,ovs的连接,都是使用evth-pair技术 - 我们来测试下 tomcat01和tomcat02 是否可以ping通
[root@dawn ~]# docker exec -it tomcat02 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.076 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.045 ms
结论:tomcat01 和 tomcat02 是公用的一个路由器,docker0.
所有的容器不指定网络的情况下,都是docker0 路由的,docker会给我们的容器分配一个默认的可用IP
小结
Docker 使用的是Linux的桥接,宿主机中是一个Docker容器的网桥 docker0.
Docker 中的所有网络接口都是虚拟的。虚拟的转发效率高
只要容器删除,对应的网桥就消失了
–link
思考一个场景,我们编写了一个微服务,database url=ip,项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以用名字来进行访问容器?
[root@dawn ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known
# 如何可以解决
[root@dawn ~]# docker run -d --name tomcat03 --link tomcat02 -P tomcat
[root@dawn ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.068 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.044 ms
# 其实这个tomcat03 就是在本地配置了tomcat02的配置
# 查看 hosts 配置,在这里原理发现
[root@dawn ~]# 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 f8b747fff7e6
172.17.0.4 2799b3911dcb
本质探究:–link 就是我们在hosts 配置中增加了一个 172.18.0.3 tomcat02 f8b747fff7e6
我们现在玩Docker 已经不建议使用 --link了
自定义网络,不适用docker0
docker0 问题:它不支持容器名连接访问
自定义网络
网络模式
birdge: 桥接 docker (默认,自己创建也会使用 bridge 模式)
none: 不配置网络
hose: 和宿主机共享网络
container:容器网络连通
# 我们直接启动的命令 --net bridge ,这个就是我们的docker0
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
[root@dawn ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
[root@dawn ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
4c49603e3416 bridge bridge local
0997bd5e2dbe host host local
8ca9cd7fa8c5 mynet bridge local
94adb6185e33 none null local
# 在自己的网络部署tomat
[root@dawn ~]# docker run -d -P tomcat-net-01 --net mynet tomcat
Unable to find image 'tomcat-net-01:latest' locally
^[[D^[[Adocker: Error response from daemon: pull access denied for tomcat-net-01, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
See 'docker run --help'.
[root@dawn ~]# docker run -d -P --name tomcat-net-01 --net mynet tomcat
0eff92ad3e846930e28d702acc89bdf5081b5c12293f7fb4c0c97c766274a2f4
[root@dawn ~]# docker run -d -P --name tomcat-net-02 --net mynet tomcat
b678ee8dfe743d364d3e86dfb6d1be1413b4ae7da0ab968128e7d920fbab9e52
[root@dawn ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "8ca9cd7fa8c5c5954880e601b28022a9ebd94528c6db909a4058ecf7720f94e6",
"Created": "2020-12-02T09:41:20.743746524+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": {
"0eff92ad3e846930e28d702acc89bdf5081b5c12293f7fb4c0c97c766274a2f4": {
"Name": "tomcat-net-01",
"EndpointID": "ae73925d78b84db252573ef4b9fb377bb05d3cde97b23a2f6f7868535c815506",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"b678ee8dfe743d364d3e86dfb6d1be1413b4ae7da0ab968128e7d920fbab9e52": {
"Name": "tomcat-net-02",
"EndpointID": "4312cfd8fb0b7a3908bba83218dd4217cef82bccd12d48ce0b5de3a51ffab847",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
# 再次测试ping连接
[root@dawn ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.044 ms
# 现在不使用 --link,也可以ping 名字
[root@dawn ~]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data.
我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时使用自定义网络
好处:保证不同集群使用不同的网络,保证集群安全和健康
网路连通
# 测试打通
[root@dawn ~]# docker network connect mynet tomcat01
# 连通之后就是将 tomcat01 放到了 mynet网络下
# 一个容器两个ip地址
例:阿里云,公网ip 私网ip
# 01 连通
[root@dawn ~]# 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.054 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.045 ms
# 02 是依旧打不通的
[root@dawn ~]# docker exec -it tomcat02 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known
结论:假设要跨无网络操作别人,就需要使用docker network connect连通
SpringBoot微服务打包Docker镜像
- 构建springboot项目
- 打包应用
- 编写dockerfile
FROM java:8
COPY *.jar /app.jar
CMD ["--server.post=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
- 构建镜像
- 发布运行