超长文警告!!!建议先看目录
如有错误,欢迎指正!
编辑不易,赞同的话请点个赞哦~
最后更新时间 ===2020 06 17===
本文档基于B站教学视频,强烈推荐去看一看这个UP主的教学视频,视频地址
如果可以的话请点击视频链接,在视频下方点赞投币分享转发哦,你的支持对我们真的很重要!
MarkDown 文件下载
仓库里还有许多的文件,喜欢的话点个 Star 哦
--->
【带上环境打包项目(镜像)】--->
【发布到Docker仓库】--->
【下载镜像,直接运行】官网首页: https://www.docker.com/
官方文档: https://docs.docker.com/
Docker镜像仓库地址: https://hub.docker.com/
【了解技术的历史才能更好的学习技术】
一台电脑中需要有内核、依赖库,在这个基础上再运行应用
虚拟机技术就是在电脑上再模拟出一台计算机
虚拟机技术的缺点
比起 Docker 和虚拟机技术的不同
使用了Docker之后,我们部署应用就和搭积木一样
uname -r // 查看系统内核
cat /etc/os-release // 查看系统基本信息
接下来的操作跟随着帮助文档即可
# 1. 卸载旧的版本
$ sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 2. 需要的安装包
$ sudo yum install -y yum-utils
# 3. 设置镜像仓库 这里默认是国外的,我们最好用国内的 推荐使用阿里云
$ sudo yum-config-manager \
--add-repo \
# https://download.docker.com/linux/centos/docker-ce.repo
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 4. 文档上接下来的是一些配置【Optional: Enable the nightly or test repositories.】
# 没有什么用,这里就暂时不配置了
# 5. 安装docker相关的内容 docker-ce是社区版 ee是企业版 推荐使用ce
# 在安装之前最好先更新一下yum软件包索引 => yum makecache fast
$ sudo yum install docker-ce docker-ce-cli containerd.io
# 这里也可以指定版本安装,帮助文档的第二点就是安装的方法,先查看版本列表,再使用命令指定版本安装
# 6. 启动Docker
$ sudo systemctl start docker
# 7. 查看是否启动成功
docker version # 查看版本
# 8. 启动hello-world
$ sudo docker run hello-world
# 9. 卸载docker 了解即可 两个步骤【卸载依赖 删除目录】
$ sudo yum remove docker-ce docker-ce-cli containerd.io
$ sudo rm -rf /var/lib/docker
# 10.升级docker引擎 可以下载更新的软件包文件然后再重复一遍安装步骤
# 然后使用 yum -y upgrade 代替 yum -y install 并指向新文件
链接地址:https://cr.console.aliyun.com/cn-shanghai/instances/repositories
Docker 是怎么工作的?
Docker 是一个Client · Server 结构的系统,Docker的守护进程(Docker daemon)运行在主机上,通过Socket从客户端访问!
Docker Server 接收到 Docker Client 的指令,就会执行这个命令
Docker 为什么比虚拟机快?
1、Docker 有着比虚拟机更少的抽象层
2、Docker 利用的是宿主机的内核,VM 需要的是GuestOS【就是需要再搭建一个系统环境】
所以说,新建一个容器的时候,Docker 不需要像虚拟机那样重新加载一个操作系统的内核,避免一些引导性的操作;而虚拟机是加载 Guest OS,是分钟级别的,Docker 是利用宿主机的操作系统,省略了这个复杂的过程,所以是秒级的启动
如果看着还不是很懂,没关系,接着往下看,当你学习完所有的命令之后再回来看这段理论,就会清晰许多。
docker version # 显示docker版本信息
docker info # docker的系统信息,包括镜像和容器的数量
docker 命令 --help # 帮助命令
如果碰到不知道的可查看帮助文档:【https://docs.docker.com/engine/reference/commandline/build/】
【官方文档】
# 查看帮助 其他命令的用法类似
[root@iZwz98zprwjrt7d2pp9g0zZ ~]# docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Options:
-a, --all 列出所有镜像
--digests 显示摘要
-f, --filter filter 根据提供的条件过滤输出
--format string 指定返回值的模板文件
--no-trunc 不截断输出,即显示完整的镜像信息
-q, --quiet 只显示镜像ID
# 比较常用的是 -a -q
# 查看本地主机上的镜像
[root@admin ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 5 months ago 13.3kB
# 解释
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的id
CREATED 镜像的创建时间
SIZE 镜像的大小
网页版可以通过 Docker Hub 搜索镜像,在Linux命令行里用 docker search
搜索
[root@admin~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMA
TED
mysql MySQL is a widely used, open-source relation… 9621 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3495 [OK]
# 可选项,通过收藏或其他来过滤检索结果
--filter=stars=3000 # 搜索出来的镜像就是STARS大于3000的
[root@admin~]# docker search mysql --filter=stars=8000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 9621 [OK]
# 下载镜像 docker pull 镜像名[:tag] tag可以指定版本,没有的话默认使用最新版
[root@admin ~]# docker pull mysql
Using default tag: latest # 如果不写tag 默认就是最新版
latest: Pulling from library/mysql
8559a31e96f4: Pull complete # 分层下载,docker image 的核心,联合文件系统
d51ce1c2e575: Pull complete
c2344adc4858: Pull complete
fcf3ceff18fc: Pull complete
16da0c38dc5b: Pull complete
b905d1797e97: Pull complete
4b50d1c6b05c: Pull complete
c75914a65ca2: Pull complete
1ae8042bdd09: Pull complete
453ac13c00a3: Pull complete
9e680cd72f08: Pull complete
a6b5dc864b6c: Pull complete
Digest: sha256:8b7b328a7ff6de46ef96bcf83af048cb00a1c86282bfca0cb119c84568b4caf6 # 签名信息
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
# 即:docker pull mysql 和 docker pull docker.io/library/mysql:latest 是等价的
# 指定版本下载 版本不可乱写 可在 docker hub 上查看
[root@admin ~]# docker pull mysql:5.7 # 加上了tag标签后可指定版本下载
5.7: Pulling from library/mysql
8559a31e96f4: Already exists # 可以看到这里会显示一些文件已存在
d51ce1c2e575: Already exists # 这就是分层下载的好处 可以共用一些文件
c2344adc4858: Already exists
fcf3ceff18fc: Already exists
16da0c38dc5b: Already exists
b905d1797e97: Already exists
4b50d1c6b05c: Already exists
d85174a87144: Pull complete
a4ad33703fa8: Pull complete
f7a5433ce20d: Pull complete
3dcd2a278b4a: Pull complete
Digest: sha256:32f9d9a069f7a735e28fd44ea944d53c61f990ba71460c5c183e610854ca4854
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
# 下载完成后查看镜像
[root@admin ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 9cfcce23593a 4 days ago 448MB
mysql latest be0dbf01a0f3 4 days ago 541MB
hello-world latest bf756fb1ae65 5 months ago 13.3kB
可以通过IMAGE ID删除,也可以根据镜像名称来删除
# 删除指定id的镜像
[root@admin ~]# docker rmi -f 容器id
# 删除多个镜像
[root@admin ~]# docker rmi -f 容器id 容器id 容器id
# 删除全部镜像
[root@admin ~]# docker rmi -f $(docker images -aq)
【说明】有了镜像才可以创建容器,要先下载一个centos镜像来测试学习 docker pull centos
docker run [可选参数] image
# 常用参数说明
--name="NAME" 容器名字 用于区分容器
-d 后台方式运行
-it 使用交互方式运行,例如要进入容器查看内容
-p 指定容器端口 -p 8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口 (常用)
-p 容器端口
容器端口 (直接写容器端口也行)
-P 大写的P 随机指定端口
# 测试一波 启动并进入容器
# 以交互模式启动 centos 并进入容器 同时指定使用 centos 里面的 bash 作为控制台进行交互
[root@admin ~]# docker run -it centos /bin/bash
# 这里可以看到命令行的前缀发生了变化 表示已经进入容器了
[root@67d60e7e973b /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
# 退出容器
[root@67d60e7e973b /]# exit
exit
[root@admin ~]# ls
mysql80-community-release-el7-3.noarch.rpm shell
-a # 列出当前正在运行的容器 + 历史运行过的容器 就是全部容器
-n=? # 显示最近创建的容器 ?表示个数
-q # 只显示容器的编号
[root@admin ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@admin ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
67d60e7e973b centos "/bin/bash" 15 minutes ago Exited (0) 15 minutes ago keen_yalow
95b5db0ebd50 hello-world "/hello" 4 hours ago Exited (0) 4 hours ago wizardly_leavitt
exit # 直接停止容器并退出
ctrl + p + q # 容器不停止退出
docker rm 容器id # 删除指定id的容器,不能删除正在运行的容器 rm -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 id 镜像名 后台启动
docker run -d centos # 后台启动
# 启动docker后ps 会发现centos停止了
# 常见的坑:docker 容器使用后台运行,就必须要有一个前台进程(就是交互)
# 如果 docker 发现没有应用,自己没有提供服务,就会立刻停止
docker logs -f -t --tail 容器id # 查看容器运行日志
# 举例 在容器内运行脚本 while循环输出字符串
# 编写脚本
[root@admin ~]# docker run -d centos /bin/sh -c "while true;do echo hello;sleep 1;done"
c482e0df32230d949c36dd9047f93257c23702960298317830f1b8b2ad42e28c
# 查看容器
[root@admin ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c482e0df3223 centos "/bin/sh -c 'while t…" 11 seconds ago Up 10 seconds ecstatic_dhawan
# 显示日志
[root@admin ~]# docker logs -tf --tail 10 c482e0df3223
2020-06-13T08:04:41.312351474Z hello
2020-06-13T08:04:42.314853838Z hello
2020-06-13T08:04:43.317313743Z hello
2020-06-13T08:04:44.319772893Z hello
2020-06-13T08:04:45.322357813Z hello
2020-06-13T08:04:46.324866999Z hello
2020-06-13T08:04:47.327293107Z hello
2020-06-13T08:04:47.327293107Z hello
2020-06-13T08:04:47.327293107Z hello
2020-06-13T08:04:47.327293107Z hello
...(直接输出10条日志后 后面还会继续增加 因为while循环一直在输出)
# 不加 --tail 10 就是显示全部日志
# 查看容器内的进程信息
docker top 容器id
# 查看容器内所有信息
docker inspect 容器id
# 测试
[root@admin ~]# docker inspect 09b2534befb1
[
{
# 容器id 能发现命令中的id只是这里面的一小部分
"Id": "09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26",、
# 创建时间
"Created": "2020-06-13T08:12:31.964868162Z",
# 交互前台路径
"Path": "/bin/bash",
# 携带参数
"Args": [],
# 容器状态
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 2076,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-06-13T08:12:32.29364097Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
# 镜像来源
"Image": "sha256:470671670cac686c7cf0081e0b37da2e9f4f768ddc5f6a26102ccd1c6954c1ee",
"ResolvConfPath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/hostname",
"HostsPath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/hosts",
"LogPath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26-json.log",
"Name": "/compassionate_kepler",
"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/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae-init/diff:/var/lib/docker/overlay2/5108dfc3feeecdef61ca695a9c4b8c459eb743214117505341369d6c7f62fe53/diff",
"MergedDir": "/var/lib/docker/overlay2/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae/merged",
"UpperDir": "/var/lib/docker/overlay2/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae/diff",
"WorkDir": "/var/lib/docker/overlay2/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae/work"
},
"Name": "overlay2"
},
# 挂载信息
"Mounts": [],
# 基本配置
"Config": {
"Hostname": "09b2534befb1",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
# 环境变量 没有java的所以用不了java
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
# 命令行
"Cmd": [
"/bin/bash"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20200114",
"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-01-14 00:00:00-08:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
# 网络设置
"NetworkSettings": {
"Bridge": "",
"SandboxID": "26ea3297b66bbae1ac7798d19783e51fcc1f88037409888db0137bf3549b18d3",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/26ea3297b66b",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "25b52bae99d2c591b03ba97e644dc3b3a26917209523d76bb78f3190b364a3a3",
"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": "695424c9542cb18c12700a9df49559009382acf4ace4668dafc94367620a0522",
"EndpointID": "25b52bae99d2c591b03ba97e644dc3b3a26917209523d76bb78f3190b364a3a3",
"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
}
}
}
}
]
# 我们通常都是将容器放在后台运行,有时候就需要进入容器,修改一些配置
# 命令 1
docker exec -it 容器id bashShell
# 测试
[root@admin ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09b2534befb1 centos "/bin/bash" 11 minutes ago Up 11 minutes compassionate_kepler
[root@admin ~]# docker exec -it 09b2534befb1 /bin/bash
[root@09b2534befb1 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:12 pts/0 00:00:00 /bin/bash
root 15 0 0 08:24 pts/1 00:00:00 /bin/bash
root 28 15 0 08:24 pts/1 00:00:00 ps -ef
# 命令 2
docker attach 容器id
# 测试
[root@admin ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09b2534befb1 centos "/bin/bash" 13 minutes ago Up 13 minutes compassionate_kepler
[root@admin ~]# docker attach 09b2534befb1
[root@09b2534befb1 /]#
两种命令的区别:
1、docker exec 是进入容器后【开启了一个新的终端】,可以在里面操作
2、docker attach 是进入容器【正在执行的终端】,不会启动新的进程
# 命令
docker cp 容器id:容器内路径 目的主机路径
# 测试
# 在容器内新建文件
[root@09b2534befb1 /]# cd /home
[root@09b2534befb1 home]# ls
[root@09b2534befb1 home]# touch hello.txt
# 拷贝文件可退出容器拷贝,即使停止了数据依然在
[root@09b2534befb1 home]# exit
exit
[root@admin ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 拷贝数据到主机的home目录下
[root@admin ~]# docker cp 09b2534befb1:/home/hello.txt /home
[root@admin ~]# cd /home
# 查看文件
[root@admin home]# ls
hello.txt study
# 拷贝是一个手动过程,之后我们可以使用 -v 卷的技术,自动同步
要将所有命令全部敲一遍哦,这样才能加深印象
Docker 的命令是十分多的,上面我们学习的那些都是最常用的容器和镜像的命令
attach Attach local standard input, output, and error streams to a running container # 当前 shell 下 attach 连接指定运行镜像
build Build an image from a Dockerfile # 通过 Dokcerfile 定制镜像
commit Create a new image from a container's changes # 提交当前容器为新的镜像
cp Copy files/folders between a container and the local filesystem # 从容器中拷贝指定文件或目录到宿主机中
create Create a new container # 创建一个新的容器,同 run 但是不启动容器
diff Inspect changes to files or directories on a container's filesystem # 查看 Docker 容器变化
events Get real time events from the server # 从 Docker 服务获取容器实时时间
exec Run a command in a running container # 在已存在的容器上运行命令
export Export a container's filesystem as a tar archive # 到处容器的内容流作为一个 tar 归档文件(对应 import)
history Show the history of an image # 显示镜像形成历史
images List images # 列出系统当前镜像
import Import the contents from a tarball to create a filesystem image # 从 tar 包中的内容创建一个新的文件系统映像(对应 export)
info Display system-wide information # 显示系统相关信息
inspect Return low-level information on Docker objects # 查看容器详细信息
kill Kill one or more running containers # kill 指定 docker 容器
load Load an image from a tar archive or STDIN # 从一个 tar 包中加载镜像(对应 save)
login Log in to a Docker registry # 注册或登录一个 docker 源服务器
logout Log out from a Docker registry # 从当前 Docker registry 退出
logs Fetch the logs of a container # 输出当前容器日志信息
pause Pause all processes within one or more containers # 暂停容器
port List port mappings or a specific mapping for the container # 查看映射端口对应的容器内部源端口
ps List containers # 列出容器列表
pull Pull an image or a repository from a registry # 从 docker 镜像源服务器拉去指定镜像或者库镜像
push Push an image or a repository to a registry # 推送指定镜像或者库镜像至 docker 源服务器
rename Rename a container # 重命名一个容器
restart Restart one or more containers # 重启一个或多个容器
rm Remove one or more containers # 移除一个或多个容器
rmi Remove one or more images # 移除一个或多个镜像(无容器使用该镜像才可移除,否则需删除相关容器才可继续或 -f 强制执行)
run Run a command in a new container # 创建一个新的容器并运行一个命令
save Save one or more images to a tar archive (streamed to STDOUT by default) # 保存一个镜像为一个 tar 包(对应load)
search Search the Docker Hub for images # 在 docker hub 中搜索镜像
start Start one or more stopped containers # 启动容器
stats Display a live stream of container(s) resource usage statistics # 显示实时的容器资源使用情况统计流
stop Stop one or more running containers # 停止容器
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE # 给源中的镜像打标签
top Display the running processes of a container # 查看容器中运行的进程信息
unpause Unpause all processes within one or more containers # 取消暂停容器
update Update configuration of one or more containers # 为容器更新配置
version Show the Docker version information # 查看 docker 版本号
wait Block until one or more containers stop, then print their exit codes # 阻塞直到一个或多个容器停止,然后打印其退出代码,即截取容器停止时的退出状态值
作业1、使用 Docker 安装 Nginx (学习暴露端口)
1、 搜索镜像 search
[root@admin ~]# docker search --filter=stars=8000 nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 13326 [OK]
2、 下载镜像 pull
[root@admin ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
8559a31e96f4: Pull complete
8d69e59170f7: Pull complete
3f9f1ec1d262: Pull complete
d1f5ff4f210d: Pull complete
1e22bfa8652e: Pull complete
Digest: sha256:21f32f6c08406306d822a0e6e8b7dc81f53f336570e852e25fbe1e3e3d0d0133
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
3、 查看镜像 images
[root@admin ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2622e6cca7eb 3 days ago 132MB
centos latest 470671670cac 4 months ago 237MB
hello-world latest bf756fb1ae65 5 months ago 13.3kB
4、 启动 run -d 后台运行 -p 指定端口 主机的3344映射到容器的80端口
[root@admin ~]# docker run -d --name nginx01 -p 3344:80 nginx
129642f969d4cff63d173ba822c096192a435830f64a6ffe77e8d6743f342767
5、 查看启动 ps
[root@admin ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
129642f969d4 nginx "/docker-entrypoint.…" 4 seconds ago Up 4 seconds 0.0.0.0:3344->80/tcp nginx01
6、 测试
[root@admin ~]# curl localhost:3344
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
至此,如果使用阿里云服务器,且在安全组配置中打开了3344端口的访问,就可以直接打开nginx的页面
那么问题来了:难道我们每次改动 nginx 配置文件,都需要进入容器内部改动吗?那岂不是非常的麻烦,如果我能够在容器外部提供一个映射路径,让我做到在容器外部修改文件,容器内部就能够自动修改,这样就非常方便了。这就是之后要学的卷技术。
作业2、使用 docker 安装 tomcat (学习官方测试和进入容器查看内容)
# 官方的使用
# 我们之前的启动都是后台,停止了容器之后,容器还是可以查到
# --rm 的意思是 用完即删 停止之后就将容器删除 一般用来测试
docker run -it --rm tomcat:9.0
# 但是我们要使用的还是要按照正常流程来使用
# 1、拉取 下载
docker pull tomcat
# 2、查看镜像
docker images
# 3、创建容器运行镜像
docker run -d -p 3355:8080 --name tomcat01 tomcat:9.0
# 4、查看
docker ps
# 5、测试访问 但是发现是404页面找不到
# 6、进去容器内一探究竟
docker exec -it tomcat01 /bin/bash
# 7、查看webapps文件夹
cd webapps
ls
# 8、发现目录是空的
# 9、原来这个官方镜像是阉割版的,很多的文件都是没有配置的
# 阿里云镜像的原因,默认是最小的镜像,所有不必要的都剔除了,保证做小可运行的环境
# a. 少了一些Linux命令,例如 ll 命令
# b. webapps 目录为空
# 解决办法: 容器内的根目录里面有一个 webapps.dist 文件夹,里面就包含了默认的例子
# 这样可以将文件夹里面的内容拷贝到webapps下 cd -r webapps.dist/* webapps
那么问题又来了:如果每次部署项目都要进入容器,就会非常的麻烦,那这个答案就很显而易见了,我们可以利用卷技术与外部自动同步,就不用每次都进入了。
作业3、部署 ES + kibana (学习查看容器状态和修改)
难点:
1、es 需要暴露的端口非常多
2、es 十分耗内存
3、es 的数据一般需要放置到安全目录 ==> 可用挂在解决
# 1、老规矩搜索安装启动
# --net somenetwork ? 网络配置 先去掉
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.7.1
# 启动之后 linux会非常卡 可以使用 docker stats 查看cpu状态
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
7d399bee6df4 elasticsearch 0.45% 1.229GiB / 3.7GiB 33.20% 0B / 0B 0B / 0B 50
c92ee0f0ac1c tomcat01 0.15% 76.88MiB / 3.7GiB 2.03% 0B / 0B 0B / 0B 36
# 可以看到占用内存非常大
# 2、测试一下是否启动成功了
[root@admin ~]# curl localhost:9200
{
"name" : "7d399bee6df4",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "BRfKhOPTS52FScc55t3vew",
"version" : {
"number" : "7.7.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "ad56dce891c901a492bb1ee393f12dfff473a423",
"build_date" : "2020-05-28T16:30:01.040088Z",
"build_snapshot" : false,
"lucene_version" : "8.5.1",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
# 3、赶紧关闭,增加内存使用的限制 修改配置文件 -e 环境配置修改
# 就是再启动命令上增加参数,限制内存的使用
# -e ES_JAVA_OPTS="-Xms64m Xmx512m"
docker run -d --name elasticsearch02 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.7.1
有两种工具:
什么是 portainer ?
# --restart=always 启动方式
# -v /var/run/docker.sock:/var/run/docker.sock 挂载
# --privileged=true 权限
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
curl localhost:8088
进入之后首先会让你创建用户,然后选择Local,就能进入一个控制面板
单词都比较简单,还是比较容易看得懂的。
镜像是一种【轻量级、可执行的独立软件包】,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的【所有内容】,包括【代码、运行时、库、环境变量和配置文件】
所有的应用,直接打包docker镜像,就可以直接跑起来!
那么如何得到镜像呢?
UnionFS(联合文件系统)
我们下载的时候看到的一层层的就是这个!
UnionFS(联合文件系统):是一种【分层、轻量级并且高性能】的文件系统,它支持对文件系统的修改,作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite directories into a single virtual filesystem)。UnionFS是Docker镜像的基础,镜像可以通过分层来继承,基于基础镜像(没有父镜像的镜像),可以制作各种具体的应用镜像
【特性】一次同时加载多个文件系统,但从外面看起来只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
【例子】:比方说 mysql 和 tomcat 都需要 centos 环境,那我先安装了 mysql ,就有了 centos 的环境,那我要再安装 tomcat ,就可以共用这一层 centos ,不需要再下载 centos 。
Docker 镜像加载原理
Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
BootFS(Boot file system)主要包含 bootloader 和 kernel,bootloader 主要是引导加载 kernel,Linux 刚启动时会加载 BootFS文件系统,在 Docker 镜像的最底层是 BootFS。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器 和 内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 BootFS 转交给内核,此时系统也会卸载 BootFS。
RootFS(Root File System),在 BootFS 之上,包含的就是典型 Linux 系统中的 /dev,/proc,/bin,/etc 等标准目录和文件。RootFS就是各种不同的操作系统发行版,比如 Ubuntu CentOS 等等。
那么问题来了,平时我们安装进虚拟机的 CentOS 都是好几个G,为什么Docker这里才200M?
那是因为对于一个精简的OS,RootFS可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用 Host 的 kernel,自己只需要提供 RootFS 就可以了,由此可见对于不同的 Linux 发行版, BootFS 基本是一致的,RootFS 会有差别,因此不同的发行版可以公用 BootFS。
这也就是虚拟机启动是分钟级别而容器是秒级启动的原因所在!
分层的镜像
可以观察一下下载一个镜像的时候的日志输出,会发现是一层一层的在下载的
【问】为什么Docker镜像要采用这种分层的结构呢?
【答】最大的好处莫过于是资源共享了。比方说有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需要在磁盘上保留一份 base 镜像,同时内存中也只需要加载一份 base 镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
我们可以通过 docker inspect
命令从 查看镜像分层
【加深理解】
所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上创建新的镜像层。
【例子】第一层镜像:基于 Ubuntu Linux 16.04 创建一个新的镜像;如果在这个镜像中添加 Python 包,就会在基础镜像层上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层,如下图。
在添加额外的镜像层的同时,镜像始终保持时当前所有镜像的组合,理解这一点非常重要,下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件
上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有六个文件,这是因为最上层中的文件 7 是 文件 5 的一个更新版本
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux 上可用的存储引擎有 AUFS、OverLay2、Device Mapper、Btrfs、以及 ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
Docker 在 Windows 上仅支持 windows filter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和 CoW
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
特点
Docker 镜像都是【只读】的,当容器启动时,一个新的可写层被加载到镜像的顶部!
docker commit 提交容器成为一个新的副本
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名[tag]
测试:
前面说到 tomcat 容器的 webapps 下是没有文件的
那每次启动的时候都要将 webapps.dist 目录下的内容拷贝到 webapps 目录下
我现在自己拷贝进去了一些基本的应用 => cp -r webapps.dist/* webapps/
那我们自己打包一个镜像,方便日后使用
docker commit -a="hello223123" -m="add webapps app" 6f36f6f81aa3 tomcatnew:1.0
查看镜像
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcatnew 1.0 78e7ed5717e7 3 seconds ago 652MB
redis latest 235592615444 3 days ago 104MB
tomcat 9.0 2eb5a120304e 3 days ago 647MB
tomcat latest 2eb5a120304e 3 days ago 647MB
可以看到我们自己生成的要大一些
要先理解概念,即使模棱两可,然后一定要去实践,最后实践和理论相结合,总结搞定这个知识点
【需求】
使用卷技术就是为了容器的持久化和同步操作!容器间的数据也可以共享!
方式一:直接使用命令来挂载 (方式二在 《初始 DockerFile》)
docker run -it -v 主机目录:容器内目录
测试:将容器的home目录挂载到主机的home目录下的ceshi目录
docker run -it -v /home/ceshi:/home centos /bin/bash
进入容器后,进入 home 目录,新建一个文件
touch hello.txt
ls 查看目录下的内容,能看到这个文件
这个时候切换到主机,或者再开一个终端查看主机的 home 目录下的 ceshi 目录
cd /home/ceshi
ls 查看目录下的内容,发现 hello.txt 文件已经同步过来了
那我们再来测试,当我们关掉容器之后数据还能同步吗?
1、先 exit 退出容器
2、docker ps 查看容器是否真的停止了
3、主机上 vim /home/ceshi/hello.txt 修改文件
4、docker start 启动容器
5、容器内 cat /home/hello.txt 查看文件内容
能够发现文件内容跟之前修改的是一样的,表示文件已经同步过来了
1、先获取 mysql 镜像 这里使用 5.7 版本
docker pull mysql:5.7
2、运行容器 挂载数据和配置文件 【注意】mysql启动需要配置密码
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、启动成功之后,可以使用navicat来连接一下mysql 或者 docker exec -it 进入容器 mysql -p 进入 mysql 控制台
4、创建一个数据库,再查看主机上映射的路径,就能查看到刚刚新创建的数据库文件
5、来波刺激的,删除调这个容器 docker rm -f mysql01
6、再去查看刚刚的路径 cd /home/mysql/data 然后 ls 查看文件
7、能发现文件依然存在,没有丢失,这就实现了【容器数据持久化】的功能
-v 容器内路径 【不去写主机上的路径,会自动创建一个路径】
-P 【大写的P,随机指定端口】
docker run -d -P --name nginx01 -v /etc/nginx nginx
安装之后可以通过 docker volume 查看卷
可以通过 docker volume --help 查看可选项
docker volume ls 显示卷列表
能够发现卷的名称是一串字符,这种就是匿名挂载
我们在 -v 的时候只写了容器内的路径,没有写容器外的路径
如果路径前面有 / 则代表是绝对路径,如果没有,就代表只是一个名字,而不是一个目录
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
再查看卷列表 docker volume ls
就能看到我们命名为 juming-nginx 的卷
通过 -v 卷名:容器内路径 来为卷命名
那我们也可以通过这个卷名来查看路径
docker volume inspect juming-nginx
/var/lib/docker/volumes/xxxx/_data
】那么问题来了,我们该如何确定是具名挂载还是匿名挂载,亦或是指定路径挂载呢?
-v 容器内路径 => 匿名挂载
-v 卷名:容器内路径 => 具名挂载
-v /宿主机路径:容器内路径 => 指定路径挂载 会有一个 / 表示绝对路径
【拓展】:
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
Docker File 就是用来【构建 docker 镜像】的构建文件。是一段【命令脚本】,可以通过这个脚本生成镜像。
镜像是一层一层的,那么对应的,这个脚本就是一个个的命令,每个命令就是镜像的一层。
方式二:在自己搭建的镜像中设置 启动镜像的时候就自动挂载
既然是数据卷,那么在外部就一定有一个同步的目录,另外,因为我们【只写了容器内的目录,所以这是个匿名挂载】,那我们找的话,卷名应该是一个不规则的字符串。
还记得我们前面的 inspect 命令吗,它能查看镜像的详细信息,其中有一个 Mounts 节点,保存了挂载点的信息,我们就可以在这里去查看挂载点的信息【先docker ps
查看镜像ID,再docker inspect 镜像ID
查看】
这种方式以后使用会十分多,因为我们通常会构建自己的镜像,加入构建镜像的时候没有挂在卷,要手动挂载【-v 卷名:容器内路径】
【上手操作】
通过我们刚才生成的镜像来启动两个容器 docker01 和 docker02
docker run -it --name docker01 centosdemo
docker run -it --name docker02 --volumes-from docker01 centosdemo
然后进入 docker01 中,进入 volume01 目录下,新建一个 docker01 文件
再进入 docker02 中,进入 volume01 目录下,查看目录内容
能发现两个容器之间的数据已经同步了
所以我们只要通过 --volumes-from
创建容器,就能实现容器之间的数据共享。【注意】这个数据共享是即使创建这个文件的容器被删了,文件依旧存在。
更像是一种引用,数据卷容器挂载到宿主机的目录中,然后其他的容器挂载到数据卷容器时,使用的是对该文件的引用(数据地址的拷贝),如果是将文件进行拷贝,不仅效率低,而且浪费空间。
多个MySQL实现数据共享
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
DockerFile 的核心是用来构建 docker 镜像的文件,是一个命令参数脚本
构建步骤:
1、编写一个 dockerfile 文件
2、docker build 构建成为一个镜像
3、docker run 运行镜像
4、docker push 发布镜像(DockerHub、阿里云镜像仓库)
基础知识
每个保留关键字(指令)必须是大写字母
指令是从上到下 顺序执行
#
号表示注释
dockerfile 是面向开发的,我们以后发布项目,做镜像,就需要编写 dockerfile 文件,这个文件十分简单
相关名词解释
Dockerfile:构建文件,定义了一切的步骤,相当于源代码
DockerImages:通过 Dockerfile 构建生成的镜像,最终发布和运行的产品,相当于原来的 war包、jar包
Docker容器:容器就是镜像运行起来提供服务的
FROM 基础镜像 例如 centos/ubuntu 一切从这里开始构建
MAINTAINER 镜像是谁写的,一般是姓名+邮箱
RUN 镜像构建的时候需要运行的命令
ADD 步骤 比如说我们要添加tomcat,那就需要一个tomcat的压缩包,这个压缩包就是要添加的内容
WORKDIR 镜像的工作目录
VOLUME 挂载的目录
EXPOSE 指定暴露端口
CMD 指定这个容器启动的时候要运行的命令,只有最后一个会生效,会被替代
ENTRYPOINT 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD 当构建一个被继承的 dockerfile 这个时候就会运行 ONBUILD 指令
COPY 类似 ADD 命令,将文件拷贝到镜像中
ENV 构建的时候设置环境变量
DockerHub 中绝大部分的镜像都是从这个基础镜像过来的 FROM scratch
,然后配置需要的软件和配置来进行构建
那我们就基于官方的这个镜像,再加一些我们需要的环境
1、先进入一个目录,创建一个 dockerfile 目录迎来存放一些文件
cd /home
mkdir dockerfile
2、创建一个dockerfile文件,命名就随便,这里命名为 mydockerfile
vim mydockerfile
3、开始写指令
FROM centos # 基础镜像
MAINTAINER hey<[email protected]> # 作者信息
ENV MYPATH /usr/local # 创建一个变量 存放一个值
WORKDIR $MYPATH # 启动后的工作目录,就是进入容器后的默认目录
RUN yum -y install vim # 执行指令安装 vim
RUN yum -y install net-tools # 执行指令安装 net-tools
EXPOSE 80 # 暴露端口
CMD echo $MYPATH # 输出 MYPATH 变量
CMD echo "------end---------" # 输出信息
CMD /bin/bash # 启动后用bash命令行
4、构建镜像
docker build -f mydockerfile -t mycentos:0.1 .
5、构建成功
Successfully built fa1d3cda51dc
Successfully tagged mycentos:0.1
6、测试运行
docker run -it mycentos:0.1
7、我们可以通过 history 命令查看一下运行过的指令
docker history 镜像ID
以下步骤建议上手操作一下
1、创建一个 dockerfile
FROM centos
CMD ["ls","-a"]
2、构建镜像
docker build -f dockercmd -t cmdtest .
3、直接运行镜像
docker run a9d76c1b34d2
4、能发现 ls -a 命令生效了
5、我们现在想追加一个 -l 命令,就是执行 ls -al 命令
根据之前的内容,我们可以在 docker run 命令后面加上 -l 命令作为参数
即 docker run a9d76c1b34d2 -l
但是当我们确认执行过后,却发现报错了
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349:
starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
ERRO[0000] error waiting for container: context canceled
为什么呢?因为 CMD 的情况下,-l 替换了 CMD ["ls","-a"] 命令,但是 -l 又不是命令,所以就报错了。
那我们可以直接加上完整的命令进行追加【docker run a9d76c1b34d2 ls -al】这样就能执行成功了,不过非常麻烦
6、引入 ENTRYPOINT 进行操作,重新创建一个 dockerfile
FROM centos
ENTRYPOINT ["ls","-a"]
7、重新构建运行
docker build -f dockerentrypoint -t entrypointtest .
docker run 7855e875f6e1
截至到这一步为止,结果都跟使用CMD没有什么差别
8、docker run 命令后追加 -l
docker run 7855e875f6e1 -l
会发现结果不仅没报错,而且还跟执行 ls -al 一样,这样就能看出这两个命令之间的差距
表示这里 -l 命令是直接拼接在 ENTRYPOINT 命令的后面的
Dockerfile 中很多命令都是十分相似的,我们需要了解他们的区别,最好的学习方法就是对比学习然后测试效果。
FROM centos # 基本镜像
MAINTAINER hey<[email protected]> # 作者信息
COPY readme.txt /usr/local/readme.txt # 复制readme到容器内部的这个路径
ADD /home/dockerfile/jdk-8u11-linux-x64.tar.gz /usr/local/ # ADD 命令会自动解压 将tar包解压到这个路径
ADD /home/dockerfile/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 # java 环境变量
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/toos.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22 # tomcat 环境变量
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.22
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
# 暴露 tomcat 端口
EXPOSE 8080
# 启动 tomcat 可以通过 && 拼接一些参数 tail -F 显示文件新追加的内容
CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.22/bin/logs/catalina.out
doocker build -t diytomcat .
】文件命名为Dockerfile
后可以不用 -f 指定文件,能自动匹配docker run -d -p 9090:8080 --name testtomcat -v /home/tomcat/test:/usr/local/apache-tomcat-9.0.22/webapps diytomcat
】docker exec -it 容器id /bin/bash
】【ls -l
】地址:https://hub.docker.com/
1、先在命令行上登录 输入命令后会让你输入密码,出现 Login Succeeded 表示登录成功
docker login -u flow11
2、提交镜像 docker push 不带版本号就是提交最新版 latest
这里就要求我们在构建镜像的时候就带上账号名,以防混乱
docker push flow11/mycentos
如果没带上 tag,默认是 latest,推荐加上tag
重新打 tag 的命令: docker tag flow11/mycentos flow11/mycentos:1.0
发布到阿里云镜像仓库上
登录阿里云
跟着操作指南来操作,提交镜像,【参考官方文档即可】
docker push flow11/mycentos:latest
Docker 网络的核心就是 【Docker0】
【问题】 docker 是如何处理容器网络访问的?
比如说现在有两个容器(A 容器和B 容器),那么现在 A 容器的 Tomcat 里面的应用要访问 B 容器的 MySql,这里是怎么进行连接的呢?是用上面三个地址的哪一个地址进行访问的呢?
前面我们配置 ES 的时候也提出过这样的问题,怎么样让 kibana 连接上 ES 呢?
1、我们先运行一个 tomcat 镜像,再查看一下网卡
docker run -d -P --name tomcat01 tomcat
docker exec -it tomcat01 ip addr
我们能发现容器启动的时候会得到一个 eth0@if81 这样的标识,这是 docker 分配的
----- 运行结果 -----
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
80: eth0@if81: <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
-------------------
2、我们来试一试能不能 ping 容器内部
ping 172.17.0.2
发现能够 ping 通
【分析】
有没有发现出什么!!??
我们创建完容器之后查看到的网卡中有一个是【80: eth0@if81
】,刚好就和上图的网卡是相反的
发现又多了一对网卡,注意,是【一对】!这种一对一对出现的网卡就是因为使用了 veth-pair 技术
veth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一端连着协议,一端彼此相连
正因为有这个特性,通常用这个技术来充当桥梁,连接各种虚拟网络设备
例如 OpenStack Docker容器之间的连接 ovs的连接 都是使用了 veth-pair 技术
当我们在 2 去 ping 3 的时候,或先通过 veth-pair技术 从 261 转发到 262 上,1 再通过保存的端口注册信息,再从 264 发到 263 ,这样就完成了一次数据通信,而不是直接从 2 发送到 3 上面,这就是桥接的概念。
这里有两种机制,一个类似路由转发,直接转发到特定的端口上面;一个是广播,散发出去,查看那个端口接收处理了。
如果学过《计算机网络》的会比较好理解,不懂的话建议自己去找一些资料看一下。
所有容器在不指定网络的情况下,都是 docker0 路由的,docker 会给我们的容器分配一个默认的可用 ip
可以使用 -net 指定网络
小结
思考一个场景,我们编写了一个微服务,需要通过一个URL连接数据库,那每次启动容器,就会重新分配 IP,IP就会变动,URL地址也就会失效了。
1、我们先来尝试一下能不能通过容器名 ping 通
[root@admin ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known
发现不能 ping 通
2、新创建一个 tomcat03 容器 使用 --link
[root@admin ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
d6f626ab5aa0f56f48e56ae2c2603444d19c2c96e7e56a5fa15bcbbf692d0869
3、在 tomcat03 使用容器名 ping tomcat02
[root@admin ~]# 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.121 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.078 ms
发现 ping 通了!!!
4、但是,当我们在 tomcat02 使用容器名 ping tomcat03 时,结果却不如我们所愿
[root@admin ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
结果却 ping 不通了
【探究】
1、查看 docker 网络列表
docker network ls
NETWORK ID NAME DRIVER SCOPE
7bc4edf2a0e8 bridge bridge local
de44533d6de3 host host local
32983fccf962 none null local
2、我们查看一下第一个
docker network inspect 7bc4edf2a0e8
这里我们能看到三个容器的网络信息
接下来我们查看一下我们刚刚创建的 tomcat03 容器的配置信息,其中在 HostConfig 里面有一个 Links
根据 DNS解析 的流程,当我们访问一个域名/地址,会首先去查看 host 文件,那我们也能查看一下 tomcat03 的 host 文件
这就代表着我们只要请求 tomcat02,就会直接转发到 172.17.0.3,所以就可以 ping 通
而 --link
就是我们在 host 配置中增加了 tomcat02 的映射
当 IP 变了,就表示容器重启了,那么--link
重新链接,配置文件也就自动改了
可以发现,tomcat02 里面就没有这些配置
其实这个的本质就是 host 映射,这种方式太笨了,【不建议使用】,我们需要更高级的
我们需要的是【自定义网络】,不适用【docker0】,docker0 是官方的网桥,是局限的
比方说 docker0
【不支持容器名连接访问】
所以进阶的操作是使用【自定义网络】
查看所有的 docker 网络
网络模式
测试
之前我们直接启动的命令 默认是加上 --net bridge 的 这个就是我们的 docker0
docker run -d -P --name tomcat01 --net bridge tomcat
docker0 是默认的,域名不能访问的 我们可以使用 --link 可以打通连接
我们可以自定义一个网络
--driver bridge 表示使用桥接模式
--subnet 192.168.0.0/16 表示子网ip 可以分配 192.168.0.2 到 192.168.255.255
--gateway 192.168.0.1 表示网关
mynet 表示网络名
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
创建好过后我们可以查看一下
我们可以使用我们创建的自定义网络来启动一个容器
docker run -d -P --name tomcat-mynet --net mynet tomcat
docker run -d -P --name tomcat-mynet-02 --net mynet tomcat
再查看 mynet 的元数据就能看见两个容器
docker network inspect mynet
--link
也可以通过容器名来 ping 了像前面说的那种情况,假设我的Redis集群中的一个Redis想要访问Tomcat,那该怎么办呢?
两个不同的网段之间直接 ping 是肯定不能通的
那我们来使用命令测试一下 打通 tomcat-mynet,这里我们先要用正常的方法先创建两个容器 tomcat01 和 tomcat02
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat02 tomcat
就是连通之后,将 tomcat01 放到了 mynet 网络下
就是【一个容器,两个地址】,就好像我们的阿里云服务器那样,一个公网ip,一个私网ip
想让 tomcat02 能 ping 通 tomcat-mynet,只需将 tomcat02 connect 到 mynet 上即可
1、创建 redis 的网络
docker network create redis --subnet 172.38.0.0/16
2、使用 shell 脚本创建 6 个 redis 容器,并配置
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
3、使用配置文件创建 6 个 redis 容器
docker run -p 6371:6379 -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:6379 -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
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \
-v /mydata/redis/node-3/data:/data \
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6374:6379 -p 16374:16379 --name redis-4 \
-v /mydata/redis/node-4/data:/data \
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6375:6379 -p 16375:16379 --name redis-5 \
-v /mydata/redis/node-5/data:/data \
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
4、创建集群
先进入 redis-01,redis 官方镜像没有 bash ,要用 sh
docker exec -it redis-01 /bin/sh
输入命令 配置集群
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172
.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
显示OK即可
进入 集群
redis-cli -c
查看结点
cluster nodes
可以看到三个 master 主机;三个 slave 从机
5、测试 我们来设个值,然后存值的那个主机停掉,再看看能不能获取到那个值
插值
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.38.0.13:6379
OK
在宿主机停掉 172.38.0.13 对应的 redis-3
[root@admin ~]# docker stop redis-3
redis-3
再去获取值
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b"
获取成功了!能看到拿到值的地方是 172.38.0.14 ,这个就是 redis-3 的从机,他顶替了 redis-3
6、我们再使用 cluster nodes 查看结点信息
可以看到 172.38.0.13 显示 master,fail
再看到 172.38.0.14 之前是显示 slave 的,但是现在却显示 master,就说明 redis-4 顶替了 redis-3 成为了主机
总共来说就是五个步骤: