1.Docker概述
Docker为什么会出现?
一款产品: 开发--上线 两套环境!应用环境,应用配置!
开发 --- 运维。 问题:我在我的电脑上可以允许!版本更新,导致服务不可用!对于运维来说考验十分大?
环境配置是十分的麻烦,每一个及其都要部署环境(集群Redis、ES、Hadoop...) !费事费力
发布一个项目( jar + (Redis MySQL JDK ES) ),项目能不能带上环境安装打包!
之前在服务器配置一个应用的环境 Redis MySQL JDK ES Hadoop 配置超麻烦了,不能够跨平台。
开发环境Windows,最后发布到Linux!
传统: 开发jar,运维来做!
现在: 开发打包部署上线,一套流程做完!
安卓流程: java --- apk ---发布(应用商店)一 张三使用apk一安装即可用!
docker流程: java-jar(环境)--- 打包项目帯上环境(镜像)--- ( Docker仓库:商店)---下载我们发布的镜像 ---直接运行即可!
Docker给以上的问题,提出了解决方案!
Docker的思想来源于集装箱!
JRE ---多个应用(端口冲突)---原来都是交叉的!
隔离:Docker核心思想!打包装箱!每个箱子都是相互隔离的。
Docker通过隔离机制可以将服务器利用到极致!
本质:所有的技术都是因为出现了一些问题,我们需要去解决,才去学习!
Docker的历史
2010年,几个的年轻人,就在美国成立了一家公司 dotcloud
做一些pass的云计算服务!LXC(Linux Container容器)有关的容器技术!
Linux Container容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。
他们将自己的技术(容器化技术)命名就是 Docker
Docker刚刚延生的时候,没有引起行业的注意!dotCloud,就活不下去!
开源
2013年,Docker开源!
越来越多的人发现docker的优点!火了。Docker每个月都会更新一个版本!
2014年4月9日,Docker1.0发布!
docker为什么这么火?十分的轻巧!
在容器技术出来之前,我们都是使用虚拟机技术!
虚拟机:在window中装一个VMware,通过这个软件我们可以虚拟出来一台或者多台电脑!笨重!
虚拟机也属于虚拟化技术,Docker容器技术,也是一种虚拟化技术!
shell
VMware : linux centos 原生镜像(一个电脑!) 隔离、需要开启多个虚拟机! 几个G 几分钟
docker: 隔离,镜像(最核心的环境 4m + jdk + mysql)十分的小巧,运行镜像就可以了!小巧! 几个M 秒级启动!
聊聊Docker
Docker基于Go语言开发的!开源项目!
docker官网:https://www.docker.com/
Docker能干嘛
虚拟机技术缺点
1、 资源占用十分多
2、 冗余步骤多
3、 启动很慢!
容器技术
容器化技术不是模拟一个完整的操作系统
比较Docker和虚拟机技术的不同:
- 传统虚拟机,虚拟出一套容器内的应用直接运行在宿主机硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件
- 容器内的应用直接运行在宿主机内,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
- 每个容器间是相互隔离的,每个容器内都有一个属于自己的文件系统,互不影响
DevOps (开发、运维)
应用更快速的交付和部署
传统:一堆帮助文档,安装程序
Docker:打包镜像发布测试,一键运行
更便捷的升级和扩缩容
使用了Docker之后,我们部署应用就和搭积木一样!
项目打包为一个镜像,扩展服务器A! 服务器B
更简单的系统运维
在容器化之后,我们的开发,测试环境都是高度一致的。
更高效的计算资源利用
Docker是内核级别的虚拟化,可以在一个物理机上运行很多个容器实例!服务器的性能可以被压榨到极致。
2.Docker安装
Docker的基本组成
镜像(image):
docker镜像就好比是一个目标,可以通过这个目标来创建容器服务,tomcat镜像>run>容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
容器(container):
Docker利用容器技术,独立运行一个或者一组应用,通过镜像来创建的.
启动,停止,删除,基本命令
目前就可以把这个容器理解为就是一个简易的 Linux系统。
仓库(repository):
仓库就是存放镜像的地方!
仓库分为公有仓库和私有仓库。(很类似git)
Docker Hub是国外的。
阿里云...都有容器服务器 (配置镜像加速!)
安装Docker
环境准备
1.Linux要求内核3.0以上
2.ubuntu22.4
环境查看
shell
# 系统内核
docker@docker:~$ uname -r
6.8.0-40-generic
# 系统版本
docker@docker:~$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
docker@docker:~$
安装
shell
# 1. 清理可能的冲突
sudo apt remove -y podman-docker docker docker-engine docker.io containerd runc 2>/dev/null
sudo apt remove -y docker-ce docker-ce-cli containerd.io 2>/dev/null
# 2. 安装docker.io
sudo apt update
sudo apt install -y docker.io
# 3. 启动服务
sudo systemctl start docker
# 设置开机自启
sudo systemctl enable docker
# 4. 验证安装
docker version


安装成功,我的版本为28.2.2
bash
# 5. 配置用户权限(额外重要步骤)
sudo usermod -aG docker $USER
newgrp docker # 使权限立即生效
# 6. 配置镜像加速器(使用国内源)
# 停止服务
sudo systemctl stop docker
sudo mkdir -p /etc/docker
sudo apt install -y vim # 如果没安装vim
sudo vim /etc/docker/daemon.json
# 复制黏贴以下内容
{
"registry-mirrors": [
"https://cdoid6va.mirror.aliyuncs.com", # 阿里云镜像加速器
"https://docker.mirrors.ustc.edu.cn", # 中科大镜像源(稳定)
"https://docker.nju.edu.cn", # 南京大学镜像源(稳定)
"https://do.nark.eu.org", # 源1
"https://dc.j8.work", # 源2
"https://docker.m.daocloud.io", # DaoCloud镜像源
"https://dockerproxy.com" # DockerProxy镜像源
]
}
# 重新加载配置并重启Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
# 7. 测试安装
docker run hello-world


shell
#8.查看一下下载的hello-world镜像
docker images
# 检查Docker服务状态
systemctl status docker
# 简写
sudo systemctl status docker


了解:卸载docker
shell
# 1.卸载依赖(Ubuntu版)
sudo apt remove -y docker-ce docker-ce-cli containerd.io
# 2.删除资源
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
# 删除配置文件
sudo rm -rf /etc/docker
底层原理
Docker是怎么工作的?
Docker是一个Client-Server结构的系统,Docker的守护进程运行在宿主机上,通过Socket从客户端访问!
DockerServer接受到Docker-Client的指令,就会执行这个命令!
Docker为什么比VM快?
1、Docker有着比虚拟机更少的抽象层
2、Docker利用的是宿主机的内核,vm需要Guest Os。
所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载Guest Os,分钟级别的,而docker是利用当前宿主机的操作系统,省略了复杂的过程,秒级的!
3.Docker的常用命令
帮助命令
plain
docker version # 显示docker的版本信息
docker info # 显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help # 帮助命令
帮助文档的地址:https://docs.docker.com/engine/reference/commandline/build/
镜像命令
docker images
shell
docker images

shell
#解释
REPOSITORY 镜像的仓库源
TAG 镜像标签
IMAGE ID 镜像id
CREATED 镜像的创建时间
SIZE 镜像的大小
#可选项
Options:
-a, --all # 列出所有镜像
-q, --quiet # 只显示镜像id
docker pull 下载镜像
shell
# 下载镜像 docker pull 镜像名[:tag]
#docker pull mysql #我的电脑是拉取了9.6的
# 指定版本下载
docker pull mysql:8.0
# 查看已下载的MySQL镜像
docker images mysql


- REPOSITORY: mysql
- 镜像名称是mysql(官方 MySQL 镜像)
- TAG: 8.0
- 你下载的是 MySQL 8.0 版本
- 没有指定版本时会默认下载latest标签
- 镜像的唯一标识(这里是前 12 位,完整 ID 更长)
- CREATED: 17小时前
- 这个镜像是在 17 小时前构建的
- 表示这是相对较新的镜像构建
- SIZE: 786MB
- 镜像大小是 786MB
- 包含了 MySQL 8.0 的运行环境和基础系统
docker rmi 删除镜像
plain
docker rmi -f 镜像id #删除指定镜像
docker rmi -f 镜像id 镜像id 镜像id #删除多个镜像
docker rmi -f $(docker images -aq) #删除全部镜像
容器命令
说明:有了镜像才可以创建容器,linux,下载一个ubuntu镜像来学习
bash
# 拉取Ubuntu镜像
docker pull ubuntu:22.04

新建容器并启动
plain
docker run [可选参数] image
# 参数说明
--name="Name" 容器名字 tomcat01 tomcat02 ,用来区分容器
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:80
-p ip:主机(即宿主机)端口:容器端口
-p 主机端口:容器端口 #这种方式常用
-p 容器端口
容器端口P
-P 随机指定端口(大写P)
bash
# 测试,启动并进入容器
docker run -it ubuntu:22.04 /bin/bash
# 在容器内执行:
ls # 查看容器内的ubuntu系统
# 注意:Ubuntu基础镜像更精简,你会发现比你的宿主机少很多内容
# 从容器中退回主机
exit

列出所有运行的容器
plain
# docker ps 命令
(不加) # 列出当前正在运行的容器
-a # 列出当前正在运行的容器 + 带出历史运行过的容器
-n=? # 显示最近创建的容器
-q # 只显示当前容器的编号
bash
# 查看当前正在运行的容器
docker ps
# 查看所有容器(包括已停止的),-a = --all 的简写
docker ps -a
# 查看最近创建的 1 个容器(包括已停止的)
docker ps -a --last=1
# 只显示所有容器的 ID
docker ps -aq

退出容器
shell
exit # 直接退出容器
Ctrl + p + q # 容器不停止退出
删除容器
plain
docker rm 容器id # 删除指定容器,不能删除正在运行的容器,如果要强制删除 rm -f
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -a -q|xargs docker rm # 删除所有容器
# 先查看容器ID
docker ps -a
# 删除指定容器(用实际的容器ID替换)
docker rm <容器id>
# 删除所有容器
docker rm -f $(docker ps -aq)
# 或者
docker ps -a -q | xargs docker rm
bash
# 删除所有hello-world容器
docker rm $(docker ps -aq -f ancestor=hello-world)

启动和停止容器的操作
plain
docker start 容器id # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止当前正在运行的容器
docker kill 容器id # 强制停止当前正在运行的容器
bash
# 先创建一个测试容器
docker run -d --name test_container ubuntu:22.04 sleep 3600
docker stop test_container
docker start test_container
docker restart test_container
docker kill test_container

常用其他命令
后台启动容器
shell
# 命令 docker run -d 镜像名
# 1. 后台启动Ubuntu容器
docker run -d ubuntu:22.04
# 2. 立即查看运行状态
docker ps

你会发现容器停止了
常见的坑,docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止。比如nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
查看日志
plain
docker logs -f -t --tail 数字 容器id
# 显示日志
# -tf # 显示日志(-t时间戳,-f跟随)
# --tail # 要显示的日志条数
bash
# 1. 创建一个会持续运行的容器(用于查看日志)
docker run -d --name log_test ubuntu:22.04 /bin/sh -c "while true; do echo 'Test log message'; sleep 2; done"
# 2. 查看容器ID,发现是28526bc6169f
docker ps
# 3. 执行指导书命令
docker logs -f -t --tail 10 28526bc6169f
# 或
docker logs -tf --tail 10 log_test

查看容器中进程信息
plain
# 命令 docker top 容器id
docker top <容器id>
bash
docker top 28526bc6169f

查看镜像的元数据
plain
# 命令
#docker inspect 容器id
bash
# 1. 先创建一个运行中的容器
docker run -d --name test_inspect ubuntu:22.04 sleep 600
# 2. 查看容器ID
docker ps
# 容器ID是 2cfe2b0fdb62
# 3. 执行指导书命令(用你的实际容器ID)
docker inspect test_inspect
# 或
docker inspect 2cfe2b0fdb62
输出一个JSON,包含容器的详细信息



进入当前正在运行的容器
shell
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 1. 确保有运行中的容器(上面创建的)
docker ps
# 2. 方式一:docker exec
docker exec -it test_inspect /bin/bash
# 在容器内执行
ls
ps -ef
exit # 退出容器
# 3. 方式二:docker attach
# 重新启动容器(如果是停止状态)
docker start test_inspect
docker attach test_inspect
# 按 Ctrl+P+Q 退出而不停止容器(不是exit)
# docker exec # 进入容器后开启一个新的终端,可以在里面操作(常用)
# docker attach # 进入容器正在执行的终端,不会启动新的进程

从容器内拷贝文件到主机上
shell
# 命令格式
docker cp 容器id:容器内目标文件路径 目的主机路径
bash
# 1. 查看当前主机目录
ls
# 2. 进入docker容器内部
docker exec -it test_inspect /bin/bash
# 3. 在容器内
# 在exec的bash会话中执行:
cd /home/
ls # 查看目录
touch test.java
echo "Created with docker exec" > test.java
ls -la # 确认文件创建
# 重要:这里用exit退出bash,但容器继续运行
exit
# 4. 查看容器状态
docker ps -a
# 如果看到 "Exited",说明容器停了,需要:docker start test_inspect
# 5. 将docker内文件拷贝到主机上
docker cp test_inspect:/home/test.java /home/docker/
# 6. 验证
ls /home/docker/
# 应该能看到 test.java 文件
# 拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现自动同步


小结
shell
attach Attach to a running container # 当前shell下attach连接指定运行的镜像
build Build an image from a Dockerfile # 通过Dockerfile定制镜像
commit Create a new image from a container 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 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 # 杀死指定的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 # 给docker容器重新命名
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 # 截取容器停止时的退出状态值
作业练习
Docker 安装Nginx
bash
# 1.搜索镜像 search 建议去docker搜索,可以看到帮助文档
# 2.下载镜像 pull
docker pull nginx
# 3.运行测试
docker images
# -d 后台运行
# --name 给容器命名
# -p 宿主机端口:容器内部端口 【端口映射操作】
docker run -d --name nginx01 -p 3344:80 nginx
# 查看运行状态
docker ps
# 本地测试访问nginx
curl localhost:3344
# 进入容器
docker exec -it nginx01 /bin/bash
# 在容器内执行
whereis nginx
cd /etc/nginx/
ls
exit



端口暴露的概念
**思考问题:**我们每次改动nginx配置文件,都需要进入容器内部?十分麻烦,我要是可以在容器外部提供一个映射路径,达到在容器外部修改文件名,容器内部就可以自动修改?-v 数据卷 技术
Docker装tomcat
bash
# 官方文档命令(测试用完即删)
# docker run -it --rm tomcat:9.0
# 按Ctrl+C停止并自动删除容器
# 指导书解释:我们之前的启动都是后台,停止了容器之后,容器还是可以查到
# 下载
docker pull tomcat:9.0
# 启动运行
docker run -d -p 3355:8080 --name tomcat01 tomcat:9.0
#测试访问没有问题(在浏览器http://localhost:3355或者命令行)
curl localhost:3355

bash
# 进入容器发现问题
docker exec -it tomcat01 /bin/bash
# 1、linux命令少了(确实是最小镜像)
# 2、webapps内没有内容(这是阿里云镜像的原因:默认是最小镜像,所有不必要的都删除)
# 保证最小可运行环境
# 在容器内执行
cd webapps
ls
# 发现webapps目录是空的
cd ..
ls
# 看到:BUILDING.txt CONTRIBUTING.md LICENSE NOTICE README.md RELEASE-NOTES RUNNING.txt bin conf lib logs native-jni-lib temp webapps webapps.dist work
cd webapps.dist/
ls
# 看到:ROOT docs examples host-manager manager
cd ..
cp -r webapps.dist/* webapps
cd webapps
ls
# 现在看到:ROOT docs examples host-manager manager
# 退出容器
exit

拷贝完成就可以访问了:
bash
# 重启容器使更改生效
docker restart tomcat01
# 再次测试
curl localhost:3355



思考问题: 我们以后要部署项目,如果每次都要进入容器是不是十分麻烦?我要是可以在容器外部提供映射路径,webapps,我们在外部放置项目,就自动同步到内部就好了
部署elasticsearch+kibana
shell
# es 暴露的端口很多!
# es 十分耗内存
# es 的数据一般需要放置到安全目录!挂载
# --net somenetwork?网络配置
# 启动 elasticsearch
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
# 测试es是否成功
curl localhost:9200
# 查看docker容器占用资源情况
docker stats
# 你会发现ES非常耗内存


shell
# 关闭第一个容器
docker stop elasticsearch
docker rm elasticsearch
# 增加内存限制启动
docker run -d --name elasticsearch02 \
-p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms256m -Xmx512m" \
elasticsearch:7.6.2
# 要等待稍微久一点
curl localhost:9200
# 再次查看资源占用
docker stats
# 现在内存占用会小很多


shell
# 测试
curl localhost:9200

可视化
- 部署portainer(用这个)
shell
# 单行命令(按指导书格式)
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/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图形化界面管理工具!提供一个后台面板供我们操作!
shell
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock \
--privileged=true portainer/portainer
外部访问测试:http://localhost:8088/
可视化面板我们平时不会使用,自己测试玩玩即可
4.Docker镜像讲解
镜像是什么
镜像是一种轻量级、可执行的独立软件保,用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件。
所有应用,直接打包docker镜像,就可以直接跑起来!
如何得到镜像
- 从远程仓库下载
- 别人拷贝给你
- 自己制作一个镜像 DockerFile
Docker镜像加载原理
UnionFs (联合文件系统)
UnionFs(联合文件系统):Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下( unite several directories into a single virtual filesystem)。Union文件系统是 Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
boots(boot file system)主要包含 bootloader和 Kernel, bootloader主要是引导加载 kernel, Linux刚启动时会加载bootfs文件系统,在 Docker镜像的最底层是 boots。这一层与我们典型的Linux/Unix系统是一样的,包括bootloader和 Kernel。当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发行版, boots基本是一致的, rootfs会有差別,因此不同的发行版可以公用bootfs.
虚拟机是分钟级别,容器是秒级!
分层理解
分层的镜像
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层层的在下载
思考:为什么Docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过docker image inspect 命令
bash
# 拉取Redis镜像
docker pull redis

bash
# 运行Redis容器
# 简单运行
docker run -d --name redis01 redis
# 带端口映射
docker run -d --name redis02 -p 6379:6379 redis
bash
# 查看Redis镜像
docker images redis
# 查看镜像元数据
docker image inspect redis
# 或
docker inspect redis


bash
# 进入容器
docker exec -it redis01 /bin/bash
bash
# 使用redis-cli
docker exec -it redis01 redis-cli
# 测试Redis
set test "hello"
get test
exit

理解:
所有的 Docker镜像都起始于一个基础镜像层,当进行修改或添加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而整体的大镜像包含了来自两个镜像层的6个文件。
上图中的镜像层跟之前图中的略有区別,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版。
这种情況下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux上可用的存储引撃有AUFS、 Overlay2、 Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于 Linux中对应的文件系统或者块设备技术,井且每种存储引擎都有其独有的性能特点。
Docker在 Windows上仅支持 windowsfilter 一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW [1]。
特点
Docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
如何提交一个自己的镜像?
commit镜像
plain
docker commit 提交容器成为一个新的副本
# 命令和git原理类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[版本TAG]
实战测试
bash
#1、启动一个默认的tomcat
# 如果还没有tomcat镜像,先拉取
docker pull tomcat
# 启动一个默认的tomcat
docker run -d --name tomcat-commit -p 8080:8080 tomcat
# 查看是否运行
docker ps
bash
#2、发现这个默认的tomcat是没有webapps应用的,镜像的原因。官方的镜像默认webapps下面是没有文件的!
# 进入容器查看
docker exec -it tomcat-commit /bin/bash
# 查看webapps目录(按指导书:官方的镜像默认webapps下面是没有文件的!)
cd webapps
ls
# 发现是空的
# 查看webapps.dist目录
cd ..
ls
cd webapps.dist/
ls
# 看到有:ROOT docs examples host-manager manager
bash
#3、我自己将webapp.dist下文件拷贝至webapps下
cd ..
cp -r webapps.dist/* webapps/
# 验证
cd webapps
ls
# 现在应该有:ROOT docs examples host-manager manager
# 退出容器
exit
bash
#4、将我们操作过的容器通过commit提交为一个镜像!我们以后就可以使用我们修改过的镜像了,这就是我们自己的一个修改的镜像
# 查看容器ID
docker ps
# 假设容器ID是 abc123def456
# 使用commit命令(按指导书格式)
docker commit -m="add webapps files" -a="yourname" tomcat-commit tomcat-webapps:1.0
# 或使用容器ID
docker commit -m="add webapps files" -a="yourname" abc123def456 tomcat-webapps:1.0
# 命令解释(按指导书):
# -m="描述信息"
# -a="作者"
# 容器id
# 目标镜像名:[版本TAG]
bash
# 查看新镜像
docker images
# 应该能看到 tomcat-webapps:1.0
# 使用新镜像运行容器
docker run -d --name tomcat-new -p 8081:8080 tomcat-webapps:1.0
# 测试访问
curl localhost:8081
# 应该能看到Tomcat默认页面,不需要再拷贝文件了
如果你想要保存当前容器的状态,就可以通过commit来提交,获得一个镜像,就好比我们我们使用虚拟机的快照
5.容器数据卷
什么是容器数据卷
docker的理念回顾
将应用和环境打包成一个镜像!
数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!需求:数据可以持久化
MySQL,容器删除了,删库跑路!需求:MySQL数据可以存储在本地!
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Linux上面!
总结一句话:容器的持久化和同步操作!容器间也是可以数据共享的!
使用数据卷
方式一:直接使用命令来挂载
plain
docker run -it -v 主机目录:容器目录
bash
# 创建测试目录
mkdir -p /home/docker/ceshi
# 运行容器并挂载目录(按指导书命令格式)
docker run -it -v /home/docker/ceshi:/home ubuntu:22.04 /bin/bash
# 命令格式:docker run -it -v 主机目录:容器目录
# 在容器内操作
cd /home
touch container-file.txt
echo "hello from container" > container-file.txt
exit

bash
# 查看宿主机目录(应该已经有容器创建的文件)
ls -la /home/docker/ceshi/
cat /home/docker/ceshi/container-file.txt

测试文件的同步
在容器内指定目录下添加或修改一个文件,会同步到主机指定目录下!反之,在主机目录下做相关操作,也会同步到容器对应的目录下!
再来测试!
1、停止容器
bash
docker stop $(docker ps -q) 2>/dev/null
2、宿主机修改文件
bash
# 在宿主机修改容器创建的文件
echo "宿主机追加内容" >> /home/docker/ceshi/container-file.txt
# 在宿主机创建新文件
echo "宿主机创建的文件" > /home/docker/ceshi/host-created.txt
3、重新进入容器验证
bash
# 重新运行容器(相同挂载)
docker run -it -v /home/docker/ceshi:/home ubuntu:22.04 /bin/bash
# 查看文件是否同步
cd /home
ls -la
cat container-file.txt
cat host-created.txt
4、在容器内修改
bash
# 在容器内修改文件
echo "容器内追加" >> container-file.txt
# 创建容器新文件
echo "容器新文件" > container-new.txt
exit
5、验证宿主机同步
bash
# 查看宿主机
ls -la /home/docker/ceshi/
cat /home/docker/ceshi/container-file.txt
cat /home/docker/ceshi/container-new.txt

以后修改只需要在本地修改即可,容器内会自动同步
实战:安装MySQL
思考:MySQL的数据持久化的问题!
shell
# 获取镜像
docker pull mysql:8.0
# 运行容器,需要做数据挂载! # 安装mysql,需要配置密码,这是要注意的点!
# 官方测试:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动我们的MySQL容器
-d 后台运行
-p 端口映射
-v 卷挂载
-e 环境配置
--name 容器名字
# 启动成功之后,我们在本地使用sqlyog 连接测试一下
# sqlyog ------ 连接到服务器的3310 ------ 3310和容器内的3306映射,这个时候我们就可以连接上了!
# 本地测试创建一个数据库,查看一下我们的映射的路径是否ok!
bash
# 创建目录并设置权限
sudo mkdir -p /home/mysql/conf
sudo mkdir -p /home/mysql/data
sudo chmod 755 /home/mysql
# 运行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:8.0
# 查看是否运行成功
docker ps | grep mysql
# 查看启动日志(如果需要)
docker logs mysql01

bash
# 本地连接测试
# 1. 进入容器bash
docker exec -it mysql01 /bin/bash
# 2. 在容器内连接MySQL
mysql -uroot -p123456
# 3. 执行SQL
SHOW DATABASES;
EXIT;
# 4. 退出容器
exit

sql
#本地测试创建一个数据库,查看一下我们的映射的路径是否ok
# 在MySQL中执行:
CREATE DATABASE testdb;
SHOW DATABASES;
EXIT;

假设我们将容器删除会发现,我们挂载到本地的数据卷依旧没有丢失,这就实现了容器数据持久化功能
具名和匿名挂载
bash
# 匿名挂载
# -v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看所有卷的情况
docker volume ls

bash
# 这里发现,这种就是匿名挂载,我们在 -v 后只写了容器内的路径,没有写容器外的路径!
# 具名挂载
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
# 验证
docker volume ls
docker volume inspect juming-nginx

所有的docker容器内的卷,没有指定目录的情况下都是在**/var/lib/docker/volumes/xxxx/_data**下
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况使用 具名挂载
如何确定是具名挂载还是匿名挂载,还是指定路径挂载!
-v 容器内路径 匿名挂载
-v 卷名:容器内路径 具名挂载
-v /宿主机路径:容器内路径 指定路径挂载!
拓展:
通过 -v 容器内路径:ro 或 rw 改变读写权限
ro #readonly 只读
rw #readwrite 可读可写
默认是 rw
ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!
bash
# 一旦创建容器时设置了容器权限,容器对我们挂载出来的内容就有限定了!
# 只读
docker run -d -P --name nginx05 -v juming:/etc/nginx:ro nginx
# 可读写
docker run -d -P --name nginx06 -v juming:/etc/nginx:rw nginx

初始Dockerfile
Dockerfile 就是用来构建 docker镜像的构建文件!命令脚本! 先体验一下!
通过这个脚本可以生成镜像,镜像是一层一层的,脚本是一个个的命令,每个命令都是最终镜像的一层!
shell
# 创建一个dockerfile文件,名字可以随机,建议 dockerfile
vim dockerfile
bash
# 文件中的内容:指令(大写) 参数
FROM ubuntu:22.04
VOLUME ["volume01","volume02"]
CMD echo "----end----"
CMD /bin/bash
# 这里的每个命令,就是镜像的一层!

这两个卷和外部一定有两个同步的目录
查看一下卷挂载在主机上的路径
docker inspect 容器id
bash
docker inspect nginx02

测试一下刚才的文件是否同步出去了!
这种方式我们未来使用十分的多,因为我们通常会构建自己的镜像!
假设构建镜像的时候没有挂在卷,要手动镜像挂载即可: (参考上文具名和匿名挂载)
shell
-v 卷名:容器内路径
数据卷容器
多个mysql同步数据!
在docker03下创建docker03文件后,进入docker01发现也依旧会同步过来:
测试1:删除 docker01 后,docker02 和 docker03 是否还能访问原来 docker01 创建的文件?
是的,依旧可以访问。
因为数据实际存放在 Docker Volume(宿主机卷) 里,不在 docker01 容器本身。
测试2:删除 docker01 后,docker02 和 docker03 是否还能互相同步?
是的,依旧可以同步。
只要 docker02 和 docker03 还在使用同一个数据卷,卷就会一直存在并保持共享。
多个mysql实现数据共享
创建 mysql01:
bash
docker run -d -p 3306:3306 \
-v /home/mysql/conf:/etc/mysql/conf.d \
-v /home/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql01 mysql:8.0
先进入 mysql01:
bash
docker exec -it mysql01 mysql -uroot -p123456
在 MySQL 里执行:
sql
CREATE DATABASE dockertest;
USE dockertest;
CREATE TABLE t1(id INT PRIMARY KEY, info VARCHAR(20));
INSERT INTO t1 VALUES(1,'from_mysql01');
SELECT * FROM t1;
退出:
plain
exit;

再到 mysql02 查看是否能读到同样的数据
停止 mysql01(释放 ibdata1 文件锁):
bash
docker stop mysql01
第一次创建 mysql02
plain
docker run -d -p 3307:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql02 --volumes-from mysql01 mysql:8.0

运行mysql02
bash
docker start mysql02

进入 mysql02:
bash
docker exec -it mysql02 mysql -uroot -p123456
执行:
plsql
USE dockertest;
SELECT * FROM t1;

看到了 from_mysql01 这一行数据,说明两个容器确实共享同一份数据目录 .这个时候,可以实现两个容器数据同步
结论:
容器之间通过数据卷实现数据共享,数据卷的生命周期独立于容器,只要仍有容器使用该数据卷,数据卷就不会消失。
当数据卷映射到宿主机目录后,即使删除容器,宿主机上的数据仍会保留,不会被删除。
6.DockerFile
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的指令
plain
FROM # 基础镜像,一切从这里开始构建
MAINTAINER # 镜像是谁写的,姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤:tomcat镜像,这个tomcat压缩包! 添加内容
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露端口配置,跟 -p 是一个道理
CMD # 指定这个容器启动时要执行的命令,只有最后一个命令会生效,可悲替代
ENTRYPOINT # 指定这个容器启动的时候要执行的命令,可以追加命令
ONBUILD # 当构建一个被继承DockerFile 这个时候就会运行ONBUILD的指令。触发指令
COPY # 类似ADD,将我们文件拷贝到镜像中
ENV # 构建的时候设置环境变量,跟 -e 是一个意思
# CMD 和 ENTRYPOINT 的区别说明:(后面也会介绍)
# 若CMD 和 ENTRYPOINT 后跟的都是 ls -a 这个命令,当docker run 一个容器时,添加了 -l 选项,则CMD里的ls -a 命令就会被替换成-l;而ENTRYPOINT中的 ls -a会追加-l变成 ls -a -l
实战测试
Docker Hub中99%镜像都是从这个基础镜像过来的( FROM scratch ),然后配置需要的软件和配置来构建。
创建一个自己的ubuntu
shell
# 1、编写DockerFile文件,内容如下:
mkdir -p ~/dockerfile && cd ~/dockerfile
cat > mydockerfile-ubuntu <<'EOF'
FROM ubuntu:22.04
MAINTAINER ztx<123456@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN apt-get update && apt-get install -y vim net-tools && rm -rf /var/lib/apt/lists/*
EXPOSE 80
CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash
EOF

bash
# 2、通过这个文件构建镜像
# 命令docker build -f dockerfile文件路径 -t 镜像名:[tag] .
docker build -f mydockerfile-ubuntu -t myubuntu:0.1 .
# 3、测试运行
docker history myubuntu:0.1



注:net-tools 包含一系列程序,构成了 Linux 网络的基础。
CMD 和 ENTRYPOINT 的区别
测试CMD
shell
# 1.编写dockerfile文件
vim dockerfile-test-cmd
# 以下为文件内容
FROM ubuntu:22.04
CMD ["ls","-a"]
# 2.构建镜像
docker build -f dockerfile-test-cmd -t cmd-test:0.1 .
# 3.运行镜像
docker run --rm cmd-test:0.1
# 想追加一个命令 -l 成为ls -al
docker run --rm cmd-test:0.1 -l



它显示的是 Error ,但这是这个实验故意要得到的"正确现象"
这说明:
- 对系统来说:这是一次"运行失败"的错误(所以叫 Error)
- **对实验来说:这正好证明了 CMD 的规则:**docker run 镜像 参数会覆盖 CMD
测试ENTRYPOINT
shell
# 1.编写dockerfile文件
vim dockerfile-test-entrypoint
FROM ubuntu:22.04
ENTRYPOINT ["ls","-a"]
# 2.构建镜像
docker build -f dockerfile-test-entrypoint -t entrypoint-test:0.1 .
docker run --rm entrypoint-test:0.1
# 我们的命令,是直接拼接在我们的ENTRYPOINT命令后面的
docker run --rm entrypoint-test:0.1 -l




输出 total 56 并显示详细列表,说明最终执行效果为 ls -a -l,追加参数被拼接到 ENTRYPOINT 后面。
结论: ENTRYPOINT 模式下,docker run 镜像 参数 不会覆盖 ENTRYPOINT,而是将参数追加到 ENTRYPOINT 命令后形成完整命令执行。
实战:Tomcat镜像
1、 准备本地目录
bash
mkdir -p ~/build/tomcat/test ~/build/tomcat/tomcatlogs
2、 启动官方 tomcat 镜像(关键:把本地目录挂到 webapps/test 和 logs)
shell
docker run -d -p 9090:8080 --name zxytomcat02 \
-v /home/docker/build/tomcat/test:/usr/local/tomcat/webapps/test \
-v /home/docker/build/tomcat/tomcatlogs:/usr/local/tomcat/logs \
tomcat:9.0
3、 访问测试(虚拟机里)
shell
curl http://localhost:9090/

4、发布项目(由于做了卷挂载,我们就可以直接在本地发布项目了)
在/home/kuangshen/build/tomcat/test目录下创建WEB-INF目录,在里面创建web.xml文件:
xml
mkdir -p ~/build/tomcat/test/WEB-INF
cat > ~/build/tomcat/test/WEB-INF/web.xml <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
</web-app>
EOF
在回到test目录,添加一个index.jsp页面:
html
cat > ~/build/tomcat/test/index.jsp <<'EOF'
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello zhangxuanyu</title>
</head>
<body>
Hello World!<br/>
<%
System.out.println("---my test web logs---");
%>
</body>
</html>
EOF

发现:test项目部署成功,可以直接访问!
访问地址:
bash
curl http://localhost:9090/test/


注意:这时进入/home/kuangshen/build/tomcat/tomcatlogs/目录下就可以看到日志信息了:
shell
docker logs zxytomcat02 --tail 50

之前一直访问失败是web.xml配置有问题,最后也是查看该日志提示,才得以解决
我们以后开发的步骤:需要掌握Dockerfile的编写!我们之后的一切都是使用docker镜像来发布运行
7.Docker网络
理解Docker0
清空本次实验相关环境,避免冲突
bash
docker rm -f tomcat01 tomcat02 net02 2>/dev/null
shell
# docker run -d -P --name tomcat01 tomcat
# 建议用 alpine 做网络测试(自带 ping 和 ip,更稳定)
docker run -d --name tomcat01 alpine sleep 3600
# 查看容器的内部网络地址ip addr, 发现容器启动的时候会得到一个 eth0@if43 ip地址,docker分配的!
docker exec -it tomcat01 ip addr
# linux能ping通docker容器内部
# 先查IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tomcat01
# 比如插到的是172.17.0.8
ping -c 3 172.17.0.8


原理
1、我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要装了docker,就会有一个docker01网卡。
桥接模式,使用的技术是veth-pair技术!
再次测试 ip addr,发现多了一对网卡
2、再启动一个容器测试,发现又多了一对网卡
bash
# 再启动一个容器(用于容器互通测试)
docker run -d --name net02 alpine sleep 3600

我们发现这个容器带来网卡,都是一对对的
veth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
正因为有这个特性,veth-pair 充当一个桥梁,连接各种虚拟网络设备
OpenStack,Docker容器之间的连接,OVS的连接都是使用veth-pair技术
3、容器与容器之间是可以相互ping通的
shell
# 先获取 tomcat01 IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tomcat01
# 假设 tomcat01 的 IP 是 172.17.0.8
docker exec -it net02 ping -c 3 172.17.0.8

结论:tomcat01 和 tomcat02 是公用一个路由器,即 docker0 !
所有的容器不指定网络的情况下,都是经 docker0 路由的,docker 会给我们的容器分配一个默认的可用ip
小结
Docker使用的是Linux的桥接技术,宿主机是一个Docker容器的网桥 docker0
**注意:**Docker中所有网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件)
只要容器一删除,对应的一对网桥就没有!
--link
思考一个场景:我们编写了一个微服务,database url = ip ,项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以通过名字来访问容器?
清理旧容器,避免名字冲突
bash
docker rm -f tomcat01 tomcat02 tomcat03 2>/dev/null
bash
# 用 alpine 做实验(自带 ping,避免 tomcat 镜像没有 ping)
docker run -d --name tomcat01 alpine sleep 3600
docker run -d --name tomcat02 alpine sleep 3600
shell
# tomcat02 想通过直接ping 容器名(即"tomcat01")来ping通,而不是ip,发现失败了!
docker exec -it tomcat02 ping -c 2 tomcat01
# 如何解决这个问题呢?
# 通过--link 就可以解决这个网络联通问题了!!! 发现新建的tomcat03可以ping通tomcat02
docker run -d --name tomcat03 --link tomcat02 alpine sleep 3600
docker exec -it tomcat03 ping tomcat02
# 反向能ping通吗? 发现tomcat02不能ping通tomcat03
docker exec -it tomcat02 ping tomcat03

其实这个tomcat03就是在本地配置了到tomcat02的映射:
shell
# 查看hosts 配置,在这里发现原理!
docker exec -it tomcat03 cat /etc/hosts

看到类似(内容可能不完全一样,但关键是会有 tomcat02 的映射):
plain
...
172.17.x.x tomcat02 <container_id>
...
本质探究:--link 就是我们在 hosts 配置中增加了一个 tomcat02 -> IP 的绑定(像 windows 的 hosts 文件一样)。
我们现在玩Docker已经不建议使用 --link 了!!!
自定义网络,不使用docker0!
docker0问题:默认 bridge 网络下不支持容器名解析访问(需要 --link 或者更推荐的自定义网络 DNS)
自定义网络
查看所有的docker网络
网络模式
bridge :桥接 (docker默认,自己创建也使用bridge模式!)
none :不配置网络
host :和宿主机共享网络
container :容器网络连通,容器直接互联!(用的少!局限很大!)
测试
latex
# 我们之前直接启动的命令 (默认是使用--net bridge,可省),这个bridge就是我们的docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat
# 上面两句等价
# docker0(即bridge)默认不支持域名访问 ! --link可以打通连接,即支持域名访问!
shell
# 我们可以自定义一个网络!
# --driver bridge 网络模式定义为 :桥接
# --subnet 192.168.0.0/16 定义子网 ,范围为:192.168.0.2 ~ 192.168.255.255
# --gateway 192.168.0.1 子网网关设为: 192.168.0.1
docker network ls

自己的网络就创建好了:
清理旧容器(避免名字冲突)
bash
docker rm -f tomcat01 tomcat02 tomcat-net-01 tomcat-net-02 2>/dev/null
shell
# 默认 bridge 启动两个容器(等价于 docker0)
docker run -d --name tomcat01 alpine sleep 3600
docker run -d --name tomcat02 alpine sleep 3600
# 查看容器 IP(默认一般是 172.17.x.x)
docker inspect -f '{{.Name}} -> {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tomcat01 tomcat02
# 创建自定义网络 mynet
docker network create --driver bridge --subnet 192.168.100.0/24 --gateway 192.168.100.1 mynet
# 查看网络:
docker network ls
# 在自定义网络 mynet 启动两个容器
docker run -d --name tomcat-net-01 --net mynet alpine sleep 3600
docker run -d --name tomcat-net-02 --net mynet alpine sleep 3600
# 再次测试 ping 连接
# 先查 tomcat-net-02 的 IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tomcat-net-02
docker exec -it tomcat-net-01 ping -c 3 192.168.100.3
# 现在不使用 --link,也可以ping 名字了
docker exec -it tomcat-net-01 ping -c 3 tomcat-net-02

我们在使用自定义的网络时,docker都已经帮我们维护好了对应关系,推荐我们平时这样使用网络!!!
好处:
redis------不同的集群使用不同的网络,保证了集群的安全和健康
mysql------不同的集群使用不同的网络,保证了集群的安全和健康
网络连通
shell
# 测试打通 tomcat01 --- mynet
docker network connect mynet tomcat01
# 连通之后就是将 tomcat01 放到了 mynet 网络下! (见下图)
# 这就产生了 一个容器有两个ip地址 ! 参考阿里云的公有ip和私有ip
docker network inspect mynet


shell
# tomcat01 连通ok
docker exec -it tomcat01 ping -c 3 tomcat-net-01
# tomcat02 是依旧打不通的
docker exec -it tomcat02 ping -c 3 tomcat-net-01

bash
docker inspect -f '{{.Name}} -> {{range $k,$v := .NetworkSettings.Networks}}{{$k}}:{{$v.IPAddress}} {{end}}' tomcat01

会看到 tomcat01 同时在 bridge 和 mynet
**结论:**假设要跨网络操作别人,就需要使用docker network connect 连通
实战:部署Redis集群
删除可能冲突的旧环境
bash
docker rm -f redis-1 redis-2 redis-3 redis-4 redis-5 redis-6 2>/dev/null
docker network rm redis 2>/dev/null
启动6个redis容器,上面三个是主,下面三个是备!
使用shell脚本启动!
shell
# 创建redis集群网络
docker network create --driver bridge --subnet 172.38.0.0/16 redis
bash
# 通过脚本创建六个redis配置
for port in $(seq 1 6); do
mkdir -p ~/mydata/redis/node-${port}/conf ~/mydata/redis/node-${port}/data
cat > ~/mydata/redis/node-${port}/conf/redis.conf <<EOF
port 6379
bind 0.0.0.0
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done
bash
# 查看创建的六个redis
cd ~/mydata/
ls
cd redis/
ls
# 查看redis-1的配置信息
cd node-1
cd conf/
cat redis.conf

启动 6 个 redis 容器(固定 IP)
shell
for port in $(seq 1 6); do
docker run -d --name redis-${port} \
-p 637${port}:6379 -p 1637${port}:16379 \
-v /home/docker/mydata/redis/node-${port}/data:/data \
-v /home/docker/mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
--net redis --ip 172.38.0.1${port} \
redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
done
检查
bash
docker ps | grep redis-

shell
# 进入容器:
docker exec -it redis-1 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
它会问 Can I set the above configuration?
输入:
plain
yes


验证集群状态(仍在 redis-1 容器里)
plain
redis-cli --cluster check 172.38.0.11:6379
看到:[OK] All 16384 slots covered.就说明集群搭建成功
