一,Window中安装Docker
1)简介:
因为Docker是运行在linux中的容器技术,它是依赖于Linux内核环境的,
但是Docker也可以在windows平台中部署,原理就是利用win10自带的虚拟化技术Hyper -V,
2) 安装方法:
2.1)首先开启主板的虚拟化:
一般默认是开启的,打开任务管理器,在性能---CPU中就可以查看虚拟化是否已启用,如果没有启用,你就需要重启电脑进入bios中开启虚拟化,不过一般都是默认自动开启状态
-
-
-
2.2)然后点击控制面板---程序----程序和功能-----启动或关闭windows功能,在这里面勾选Hyper-V以及内部的所有选项
并重启电脑, 只有打开了Hyper -V后,才能正常安装WSL2
(不知道为什么我的台式电脑中没有Hyper -V, 但还是能正常运行Docker, 如果没有Hyper -V的话,这一步暂且忽略吧,反正如果电脑有Hyper -V你就勾选吧)-
-
-
2.3) 然后直接安装docker软件,基本上就是傻瓜式安装,非常简单
首先访问docker网址:www.docker.com
然后点击右上角的get Started
然后Download for windows进行下载安装包并点击安装包进行安装软件
然后点击运行软件,它会弹出一个网址,需要你下载一个linux内核,你点击网址后,点击下载Linux内核更新包,并加载运行这个安装包
此时docker软件就安装完毕了-
-
-
-
2.4) 然后安装完docker后检测是否存在wsl, 不存在的话docker是无法运行的:
a)首先 docker version //查看版本b)然后查看wsl版本:
输入:wsl -l -v //所显示的数字就是你当前所使用的版本, 一般使用的是版本2
//如果不存在则显示"适用于Linux的windows子系统没有安装的分发版"
//就是让你手动安装wsl//如果你的电脑不存在wsl的话,你双击打开Docker软件的时候,会一直无法打开
如果wsl已经存在并正常运行,会显示内容: docker-desktop-data Running 2
如果wsl被关闭会显示:docker-desktop-data Stopped 2, //出现这种情况不好慌,发现你只要将docker软件打开,wsl才会被自动开启,并没有什么影响
c) 然后安装WSL2方法:
首先在管理员模式下打开PowerShell然后输入:wsl --install //通过该命令安装的wsl会默认设置成WSL 2
//在安装wsl报错0x80370102,
//解决方法:以管理员身份打开powerShell, 运行命令:// bcdedit /set hypervisorlaunchtype auto, 然后重启就解决了该问题
//我在笔记本电脑中安装的wsl设置的用户名是zfy, 密码是:123456
最后只需要将WSL 2设置成默认版本就行:
打开PowerShell,然后输入命令: wsl --set-default-version 2
2.5) 最后直接设置镜像加速(对于win10系统)
打开docker软件-----点击设置按钮-----点击Docker Engine
在右侧配置如下代码:
{
"registry-mirrors":["https://dcci5rhb.mirror.aliyuncs.com"],
//这个就是配置的镜像加速地址, docker pull下载镜像失败,docker build构建镜像失败,其实都和这个镜像源有关系,这里配置的这个阿里云地址速度快,所以一般是不会失败的
//在登录阿里云后,点击产品----容器----容器镜像服务ACR, 购买该服务比较贵,所以你可以直接点击免费试用,该试用一般不限时长,进入使用页面后,点击镜像工具----镜像加速器
//我试用的服务中的镜像加速器的地址是:https://dcci5rhb.mirror.aliyuncs.com
//注意:虽然你可以配置别人的镜像加速器地址,也可以正常使用,但是此时你将无法将你的镜像push到阿里云的镜像仓库中去
你只有配置你自己的镜像加速器地址,这样才能将你的镜像上传到你的镜像仓库中去"builder": { //这个builder是自带的
"features": {
"buildkit": true
},
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false
}
并点击Apply使配置生效
查看配置的镜像加速地址是否生效:
输入命令:docker info
当显示的内容是 Registry Mirrors:
"https://yxzrazem.mirror.aliyuncs.com"这样则表示配置生效了
总结:下面来说一说我在两个电脑中安装docker所遇到的事情:
首先我的台式电脑:
我是直接安装docker就可以正常使用的,非常顺利,直接就能使用,我也不知道为什么
当我打开控制面板中的程序的时候,发现该电脑是没有Hyper -V的,反正安装完软件就能使用
然后是我的笔记本电脑:
我在安装之前已经勾选的Hyper -V,
然后正常的安装了Docker, 但是始终无法正常使用,Docker软件也一直打不开
然后我输入命令wsl -l -v显示的内容是"适用于Linux的windows子系统没有安装的分发版"
这个就说明我的笔记本电脑不存在wsl
然后我就开始安装wsl,在安装wsl总是报错0x80370102,查了一个多小时才解决,
解决方法:以管理员身份打开powerShell, 运行命令:bcdedit /set hypervisorlaunchtype auto, 然后重启电脑就解决了该问题,
然后我安装完wsl后,再次查看wsl -l -v
却显示Ubuntu Stopped 2, 我最开始还以为wsl被启动失败,又傻逼的去查原因
最后发现你只需要将Docker软件打开, wsl才会被自动启动,此时就显示的是Ubuntu Running 2
至此终于解决了在笔记本上安装docker的全部问题了
dockerfile和docker-compose文件:
Dockerfile文件:
FROM openjdk:8-jre COPY *.jar /app.jar CMD ["--server.port=8080"] EXPOSE 8080 ENTRYPOINT ["java","-jar","/app.jar"]docker-compose.yml文件:
version: "3.2" services: mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: 123456 TZ: Asia/Shanghai #mysql注意设置时区 volumes: - "D:/新建文件夹/BB/mysql/data:/var/lib/mysql" - "D:/新建文件夹/BB/mysql/conf:/etc/mysql/conf.d/" hmdp1: image: hmdp #定义服务名称,你可以当成是容器的一个别名 build: ./hmdp #调用dockerfile构建镜像 environment: TZ: Asia/Shanghai #容器设置时区 ports: - "8082:8080" hmdp2: image: hmdp environment: TZ: Asia/Shanghai #容器设置时区 ports: - "8083:8080" redis: image: redis ports: - "6379:6379" zookeeper: image: wurstmeister/zookeeper container_name: zookeeper #这个一定要定义容器名称,不然kafka将无法连接到 restart: always kafka: image: wurstmeister/kafka links: - zookeeper:zookeeper environment: - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092 #需要手动指定kafka的访问地址 - KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR:1 restart: always nacos: image: nacos/nacos-server
二,ping的作用
只用能够ping通,就说明可以使用该ip下所提供的端口服务
本地电脑ip: 192.168.0.179
redis1容器ip: 172.17.0.2
redis2容器ip: 172.17.0.3
进入redis1容器内部: docker exec -it 容器Id bash
1) ping 192.168.0.179 //可以ping通本地电脑
2) ping 172.17.0.3 //可以ping通容器Ip
3) ping redis2 //通过容器名称也可以ping通,只不过该容器的创建需要使用自定义网络,在自定义网络时,会给该网络分配DNS服务器,使其可以通过名称访问4) ping aa //这个aa就相当于是redis3这个容器的一个服务名称,你可以当成是redis3容器的一个别名,通常在docker-compose中会定义容器的服务名称,docker-compose会自动创建一个网络,自然也就可以使用DNS服务解析,从而通过服务命aa解析出对应的ip了
总结:这就说明在容器内部可以直接通过,容器ip,容器名称,容器别名,本地电脑Ip来找到对应的容器服务
三,Dockerfile的使用
将Springboot项目打包成容器
首先我的springboot项目只连接了一个redis容器,连接redis的时候,直接使用localhost:6379
此时直接启动项目后,可以正常连接并访问redis
但是当我将springboot打包成容器之后,却发现始终连接redis失败,显示无法连接
经过两个多小时的查找发现,容器和容器直接只能通过容器ip进行连接然后通过docker inspect redis容器id, 查看到了redis的容器ip为172.19.0.2
然后在springboot项目中的yml中修改:
spring:
redis:
host: 172.17.0.6 //这里的地址是redis容器的ip
port: 6379 //你可别直接启动项目,需要将该项目变成容器之后,才能连接到redis容器
//说到底,核心就是: 既然想要将springboot项目创建成容器, 你就要将当前yml文件想象并代入到容器内部场景中
//这样可以方便你理解如何正确的连接到其他容器
//你就想象成你是在容器内部写代码//host值可以是:
// host: redis2 //这是容器名称,可以连通 (推荐使用这种)
// host: 192.168.0.179 //这是本地电脑的ip, 通过映射出来的端口也可以连通-
-
-
然后再将springboot项目打包成jar包
-
-
-
然后编写Dockerfile:
FROM openjdk:8-jreCOPY *.jar /app.jar //复制当前目录中的jar包到容器的根目录中,并重命名app.jar
CMD ["--server.port=8080"] //定义jar包启动时的端口EXPOSE 8080 //这个是镜像所保留的对外接口, 在生成容器的时候可以和外界进行映射:-p 8081:8080, 这样映射的话,访问地址就是localhost:8081
//同时该容器内部所运行的jar包,它的服务端口必须定义成8080,和所暴露的端口相对应,这样外界才能访问到容器内部服务
ENTRYPOINT ["java","-jar","/app.jar"] //容器创建成功后自动启动该命令:java -jar /app.jar --server.port=8080-
-
-
-
然后将jar包和Dockerfile放在新建的AA目录中去
-
-
-
然后在AA目录中进入cmd, 并输入"docker build -t bb ." //该命令默认会找到名称为Dockerfile, 如果你起的是其他名字,比如将dockerfile的名字起名为aa, 则要输入命令"docker build -f aa -t bb .", -f指向dockerfile的文件名
此时就生成了名字为bb的镜像,-
-
-
-
-
-
最后运行镜像并生成容器:
docker run --name springboot -p 8080:8080 -e TZ=Asia/Shanghai -d bb//在这里使用环境变量-e, 是为了保证容器的时间是北京时间, 你的java代码中的LocalDateTime.now()的时间是和容器时间保持一致的
此时发现springboot容器可以正常启动
然后访问的时候,也发现springboot容器可以正常连接到redis容器
至此终于解决了
总结: 全部步骤做下来,你会发现一个很大的缺点:
当springboot项目如果连接了十几个容器的话
你就需要手动将这十几个容器创建出来,非常麻烦
此时docker-compose就产生了
四,Docker-compose的使用
将springboot及内部所使用的容器统一管理
简介:Docker Compose 是一个用于定义和管理多个 Docker 容器的工具
首先创建号各自目录: (要想使用docker-compose, 目录结构要定好)
在AA目录中有两个目录mysql,heima和一个文件docker-compose.yml,其目录结构为:
mysql:
conf: //conf文件夹
data: //该data中已经存放了多个数据库表文件, 我们在创建mysql容器的时候,只需要和该目录进行挂载,就可以使用内部的所有mysql表数据了
//直接将本地的C:\ProgramData\MySQL\MySQL Server 5.7\Data目录中的所有文件全部复制,并粘贴到当前的data目录中去,一般本地的所有mysql数据库文件都会保存到这个文件夹中heima:
Dockerfile //order-service目录下的文件, 文件内容为: FROM openjdk:8-jre-alpine COPY *.jar /app.jar ENTRYPOINT ["java","-jar","/app.jar"]
heima.jar //将各自项目的jar包放在各自文件夹中
docker-compose.yml //这个是编写的docker-compose.yml文件
-
-
-
--
然后在docker-compose.yml文件中:总共创建了四个容器
version: "3.2"services:
mysqlAAA: //定义服务名称,你可以当成是容器的一个别名
image: mysql:5.7 //这个同样也是调用镜像,没有的话就自动pull拉取镜像
environment:
MYSQL_ROOT_PASSWORD: 123456 //定义数据库密码TZ: Asia/Shanghai //注意设置时区,东八区
volumes:
- "$PWD/mysql/data:/var/lib/mysql" //$PWD表示当前docker-compose文件运行的时候所在的目录,这个就是指目录AA, 但是只能在linux环境下才有效
//如果在window环境下,需要你使用绝对地址: "D:\新建文件夹\BB\mysql\data:/var/lib/mysql", 这样才能进行同步,你这里还是错误的,要使用"/",不能使用"\"
//所以才说linux环境下才能更好的编成, 在window环境下,你需要将复制过来的地址修改成:"D:/新建文件夹/BB/mysql/data:/var/lib/mysql"
- "$PWD/mysql/conf:/etc/mysql/conf.d/"heima1: //定义服务名称,你可以当成是容器的一个别名
image:heima //定义镜像名称,使用该镜像,如果没有则调用build命令构建镜像
build: ./heima //调用dockerfile构建镜像
container_name: heima1 // 定义容器名称environment:
TZ: Asia/Shanghai //设置容器内的时间为北京时间,由于java代码中的localDateTime.now()所获取的时间就是容器内部的时间
ports:
- "8080:8080"
heima2:
image:heima //由于上面构建之后已经存在了镜像"heima", 所以这里就不需要再次使用build命令了,直接使用该镜像就可以了
container_name: heima2 //容器名称environment:
TZ: Asia/Shanghai //设置容器内的时间为北京时间,由于java代码中的localDateTime.now()所获取的时间就是容器内部的时间
ports:
- "8081:8080"redis: //定义服务名称,你可以当成是容器的一个别名
image: redis
ports:
- "6379:6379"
注意:一般如果没有定义容器名称和镜像名称的话,docker-compose会自动进行命名
命名方式为: 镜像名称: "当前文件夹名称-当前服务名称", 比如:当前文件夹是AA, 名称就是AA-heima1
容器名称: "当前文件夹名称-当前服务名称-1", 比如:AA-heima1-1-
-
-
-
-
-
然后在sprignboot项目中,直接将数据库,redis地址都修改成对应的服务名称 (直观)
并不需要再像之前一样查询容器ip并将地址修改成容器ip了 (不直观)
比如:在application.yml中
spring:
redis:
host: redis //这个是docker-compose中定义的服务名称
port: 6379 //想象是在容器内部写代码,host的值可以是: 当前电脑ip, redis容器的别名, redis容器的名称, redis容器的ip
//都可以正常连接到redis容器//自己想象一下,在容器内部写代码后,该如何连接其他的容器服务
-
-
-
-
-
-
然后将springboot打包成jar包和编写好的Dockerfile文件都放在AA目录中的heima这个目录中-
-
-
-
-
-
最后在AA目录中运行docker-compose.yml文件, 该文件放在了AA目录中的
在AA目录中进入cmd并输入命令:
docker-compose up -d //这个就是运行docker-componse.yml文件,并自动生成容器
//此时生成了四个容器,名称分别为:AA-mysqlAA-1, AA-redis-1,heima1, heima2
//通过dockerfile构建的镜像名称为:heima
//这可能是docker-compose内部默认的一种命名方式吧:
// 所生成的镜像的命名为: "docker-compose文件所在的目录名称-服务名称", 如果没事指定镜像名,默认的名称为:AA-heima1
// 所生成的容器的命名为: "docker-compose文件所在的目录名称-服务名称-1", 默认的容器名称为:AA-heima1-1
其他常用命令:
docker-compose logs -f //查看日志, 它会自动查看所创建的所有容器的日志, -f表示持续查看所有容器的日志
docker-compose logs -f userservice //查看指定服务的日志
docker-compose restart gateway userservice orderservice //根据容器名称进行重启容器docker compose down //停止所有正在运行的容器
docker compose rm -f //删除所有已停止的容器
注意事项:
a)在实际项目中,使用docker run xxXx 和docker-compose up -d 不在同一个网段,一个是默认是172.17.x.x, 另一个是172.19.x.x。为解决这个问题需要自定义一个网络
b)redis容器报错:
运行的redis容器突然报错:MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled,
表示无法在磁盘持久化,所以才报错
解决方法:
直接使用本地的客户端连接这个redis容器,然后输入命令: config set stop-writes-on-bgsave-error no, 这样就解决了
我使用的客户端是: Redis Insight
五,springboot容器时间和系统时间不一致,解决方法:设置环境变量
首先通过:docker exec -it 容器Id bash //进入容器后台
然后输入:date //查看当前容器时间是否和本地时间一致
//由于在容器中运行了java程序,java代码LocalDateTime.now()获取的是容器内部的时间,所以必须保证容器的时间和本地时间一致
-
-
解决方法:
a) 在 docker run 添加时间参数:
docker run -p 8080:8080 --name springboot -e TZ=Asia/Shanghai -d bb //这个bb是所创建的项目容器-
-
-
-
-
-
b) docker-compose解决方案:
environment:
TZ: Asia/Shanghai //在环境变量中设置时区,这个就是设置容器的环境变量,使容器时间和本地电脑时间保持一致一般只会用这两种方式创建容器:要么直接docker run, 要么使用docker-compose创建容器
六,Docker网络
a) 同一网段之间的容器通信
docker run -P --name redis1 -d redis
docker run -P --name redis2 -d reds
这里创建了两个容器,它们默认使用bridge这个网段,所以可以直接相互通信
并且在容器内部可以使用通过本地电脑ip访问到本地电脑
-
-
-
-
-
-
b) 不同网段之间的容器通信
首先创建一个网段: docker network create --subnet 192.168.0.0/24 --geteway 192.168.0.1 mynet //这里就是创建了一个网卡为mynet
-
-
然后创建容器:
docker run -P --name redis1 -d redis //这里就是在网卡mynet下创建的容器,得到的ip是192.168.0.2docker run -P --name redis2 -d redis //这个就是使用默认网卡bridge创建的容器,得到的ip是172.17.0.2
//此时这两个容器属于不同的网段,它们直接是无法直接通信的
-
-
-
然后输入:
docker network connect mynet redis2 //这个就是给redis2分配了mynet网段中的一个ip
//此时redis2就相当于有两个ip了,分别是172.17.0.2 和 192.168.0.3
-
-
-
-
此时进入redis1容器内部输入:
ping 192.168.0.3 //可以正常ping通
ping redis2 //也可以正常ping通, 它会先找到172.17.0.2无法连通,就会换一个192.168.0.3发现可以连通
-
-
-
-
-
删除容器中的某个网段分配的ip
输入:docker network disconnect mynet redis2 //这个就是将mynet网段所分配的ip从redis2中删除, 也就是将192.168.0.3给删除了
结语: 使用docker部署springboot项目,关键在于springboot项目变成容器之后,该如何能够连接到其他容器,只有正常连接到其他容器,才能使用其他容器所提供的服务,
最简单的方法就是想象成是在容器内部写代码,这样方便你理解该如何连接其他容器