Docker

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/

文档:https://docs.docker.com/

仓库:https://hub.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
  1. REPOSITORY: mysql
    • 镜像名称是mysql(官方 MySQL 镜像)
  2. TAG: 8.0
    • 你下载的是 MySQL 8.0 版本
    • 没有指定版本时会默认下载latest标签
    • 镜像的唯一标识(这里是前 12 位,完整 ID 更长)
  3. CREATED: 17小时前
    • 这个镜像是在 17 小时前构建的
    • 表示这是相对较新的镜像构建
  4. 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

浏览器访问http://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中所有网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件)

只要容器一删除,对应的一对网桥就没有!

思考一个场景:我们编写了一个微服务,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.就说明集群搭建成功

相关推荐
开开心心_Every2 小时前
Win10/Win11版本一键切换工具
linux·运维·服务器·edge·pdf·web3·共识算法
啟明起鸣2 小时前
【Nginx 网关开发】从源码分析 Nginx 的多进程启动原理
运维·nginx
一体化运维管理平台2 小时前
容器监控难题破解:美信监控易全面支持K8s、Docker
云原生·容器·kubernetes
怣502 小时前
Linux创意命令组合:让终端变得有趣又高效
linux·运维·服务器
啟明起鸣3 小时前
【Nginx 网关开发】上手 Nginx,简简单单启动一个静态 html 页面
运维·c语言·前端·nginx·html
Tinyundg3 小时前
Linux系统分区
linux·运维·服务器
要做一个小太阳3 小时前
华为Atlas 900 A3 SuperPoD 超节点网络架构
运维·服务器·网络·华为·架构
江畔何人初3 小时前
service发现
linux·运维·云原生
life码农3 小时前
Linux系统清空文件内容的几种方法
linux·运维·chrome