基础篇
@TOC
编码开发微服务
上线部署容器化
时时刻刻要监控
devops
一、Docker简介
1.1.官网
docker官网:https://www.docker.com/
Docker Hub仓库官网:https://hub.docker.com/
Docker hub 安装docker镜像的仓库
Docker中有几个重要的概念:
1.2.镜像和容器
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔离,对外不可见。
一切应用最终都是代码组成,都是硬盘中的一个个的字节形成的文件。只有运行时,才会加载到内存,形成进程。
而镜像,就是把一个应用在硬盘上的文件、及其运行环境、部分系统函数库文件一起打包形成的文件包。这个文件包是只读的。
容器呢,就是将这些文件中编写的程序、函数加载到内存中允许,形成进程,只不过要隔离起来。因此一个镜像可以启动多次,形成多个容器进程。
1.3.Docker架构
我们要使用Docker来操作镜像、容器,就必须要安装Docker。
Docker是一个CS架构的程序,由两部分组成:
- 服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
- 客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。
如图:
二、Docker安装步骤
Docker必须部署在Linux内核的系统上,如果其他系统想部署Docker就必须安装一个Linux环境
2.1.Centos7及以上的版本
uname命令用于打印当前系统相关信息(内核版本号、硬件架构、主机名称和操作系统类型等)
[root@192 ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
[root@192 ~]#
[root@192 ~]# uname -r
3.10.0-1160.el7.x86_64
Docker的基本组成:镜像(image)、容器(container)、仓库(repository)
2.2.卸载旧版本
https://docs.docker.com/engine/install/centos/
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
docker-ce
2.3.yum安装gcc相关
Centos7能上网
yum -y install gcc
yum -y install gcc-c++
2.4.安装需要的软件包
# 国外网址,容易报错,建议不要照着国外安装
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
报错:
1[Errno 14] curl#35 -TCP connection reset by peer
2 [Errno 12] curl#35 -Timeout
虚拟机联网,安装yum工具
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken
2.5.设置stable镜像仓库【国内aliyun】
然后更新本地镜像源:
# 设置docker镜像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
2.6.更新yum软件包索引--以后安装更快捷
# 更新yum软件包索引
yum makecache fast
2.7.安装Docker-Ce
然后输入命令:
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
或者
yum install -y docker-ce
docker-ce为社区免费版本。稍等片刻,docker即可安装成功。
2.8.启动Docker
Docker应用需要用到各种端口,逐一去修改防火墙设置。非常麻烦,因此建议大家直接关闭防火墙!
# 关闭
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
systemctl start docker # 启动docker服务
systemctl stop docker # 停止docker服务
systemctl restart docker # 重启docker服务
2.9.测试
[root@192 docker]# ps -ef | grep docker
root 2001 1 7 13:40 ? 00:00:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 2291 1475 0 13:41 pts/0 00:00:00 grep --color=auto docker
[root@192 docker]#
[root@192 docker]# docker -v
Docker version 24.0.7, build afdd53b
[root@192 docker]#
[root@192 docker]# docker version
2.10.卸载
# 关闭docker
sudo systemctl stop docker
sudo yum remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
# 卸载第三方库包
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
2.11.配置阿里云镜像加速
docker官方镜像仓库网速较差,我们需要设置国内镜像服务:
参考阿里云的镜像加速文档:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
https://promotion.aliyun.com/ntms/act/kubernetes.html
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://hjisj8si.mirror.aliyuncs.com"]
}
EOF
# 查看文件内容
cat /etc/docker/daemon.json
sudo systemctl daemon-reload
# 重启docker
sudo systemctl restart docker
三、Docker常用命令
3.1.docke帮助启动类命令
启动docker: systemctl start docker
停止docker: systemctl stop docker
重启docker: systemctl restart docker
查看docker状态: systemctl status docker
开机启动: systemctl enable docker
查看docker概要信息: docker info
查看docker总体帮助文档: docker --help
查看docker命令帮助文档: docker 具体命令 --help
3.2.【镜像命令】
3.2.1.列出本地主机上的镜像
# 列出本地主机上的镜像
docker images
#列出本地所有的镜像(含历史映像层)
docker images -a
# 只显示镜像ID
docker images -q
#只列出N个镜像,默认是25个
docker search --limit
#只列出5个redis镜像
docker search --limit 5 redis
3.2.2.查找镜像
# 查找镜像
docker search [POTIONS]镜像名字
3.2.3.下载镜像
# 下载镜像
docker pull 镜像名字[:TAG] {TAG:镜像的标签版本号}
#没有TAG就是最新版本
docker pull 镜像名字
docker pull 镜像名字:latest
docker pull ubuntu
docker pull redis:6.0.8
3.2.4.查看镜像/容器/数据卷所占的空间
# 查看镜像/容器/数据卷所占的空间
docker system df
3.2.5.保存镜像
# 保存镜像
docker save -o [保存的目标文件名称] [镜像名称]
docker save -o nginx.tar nginx:latest
3.2.5.加载本地镜像
# 加载本地镜像
docker load -i nginx.tar
3.2.6.删除某个镜像
#删除某个镜像
docker rmi 某个镜像名字ID
#强制删除某个镜像
docker rmi -f 某个镜像名字ID
#强制删除多个镜像
docker rmi -f 镜像名1:TAG 镜像名2:TAG
#强制删除多个镜像
docker rmi -f $(docker images -qa)
3.2.7.面试题:谈谈虚悬镜像是什么,怎么删除?
仓库名、标签都是<none>的镜像,俗称虚悬镜像dangling image
对于这样的虚悬镜像一定要删除。
查看虚悬镜像命令
docker images ls -f dangling=true
命令结果:
这种虚悬镜像已经失去了存在价值,可以删除。
删除虚悬镜像命令:
docker image prune
3.3.容器命令【演示用ubuntu】
下载一个ubuntu或者Centos镜像演示
3.3.1.拉取镜像
#拉取镜像
docker pull centos
docker pull ubuntu
3.3.2.新建+启动容器
#新建+启动容器
docker run [OPTIONS] IMAGE [COMMAND][ARG...]
OPTIONS说明(常用):有些是一个减号,有些是两个减号
--name="容器新名字" 为容器指定一个名称;
-d : 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
-i : 以交互模式运行容器,通常与-t同时使用;
-t : 为容器重新分配一个伪输入终端,通常与-i同时使用;也即启动交互式容器(前台有伪终端,等待交互);
3.3.3.启动交互式容器(前台命令行)
#启动交互式容器(前台命令行)
docker run -it ubuntu bash
docker run -it ubuntu /bin/bash
docker run -it --name=myu1 ubuntu bash
3.3.4.列出当前所有正在运行的容器
#列出当前所有正在运行的容器
docker ps
docker ps [OPTIONS]
OPTIONS说明(常用):
-a :列出当前所有正在运行的容器+历史上运行过的
-l :显示最近创建的容器。
-n :显示最近n个创建的容器。
-q :静默模式,只显示容器编号。
3.3.5.退出容器--两种退出方式
#退出容器--两种退出方式
exit run进去容器,exit退出,容器停止
ctrl+p+q run进去容器,ctrl+p+q退出,容器不停止
3.3.6.启动已停止运行的容器
#启动已停止运行的容器
docker satrt 容器ID或者名字
3.3.7.重启容器
#重启容器
docker resatrt 容器ID或者名字
3.3.8.停止容器
#停止容器
docker stop 容器ID或者名字
3.3.9.强制停止容器
#强制停止容器
docker kill 容器ID或者名字
3.3.10.删除已停止的容器
#删除已停止的容器
docker rm 容器ID
docker rm -f 容器ID
#一次性删除多个容器实例
docker rm -f $(docker ps -a -q)
docker ps -a -q | xargs docker rm
3.4.下载一个Redis6.0.8镜像演示
3.4.1.启动守护式容器(后台服务器)
docker pull redis:6.0.8
#启动守护式容器(后台服务器)
-d : 表示服务后台运行模式
docker run -d 容器名
3.4.2.前台交互式启动【不建议】
#前台交互式启动【不建议】
docker run -it redis:6.0.8
#启动后台守护式启动(后台运行)
docker run -d redis:6.0.8
3.4.3.查看容器日志
#查看容器日志
docker logs 容器ID
3.4.4.查看容器内运行的进程
#查看容器内运行的进程
docker top 容器ID
3.4.5.查看容器内部细节
#查看容器内部细节
docker inspect 容器ID
3.4.6.进入正在运行的容器并以命令交互
#进入正在运行的容器并以命令交互
docker exec -it 容器ID bashShell
或者重新进入【不建议】:
docker attach 容器ID
attach : 直接进入容器启动命令的终端,不会启动新的进程,用exit退出,会导致容器的停止
exec : 是在容器中打开新的终端,并且可以启动新的进程,用exit退出,不会导致容器的停止
推荐大家使用docker exec命令,因为退出容器终端,不会导致容器的停止
[root@192 soft]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e58709da98ef redis "docker-entrypoint.s…" 14 minutes ago Up 14 minutes 6379/tcp vigilant_williams
[root@192 soft]# docker exec -it e58709da98ef /bin/bash
root@e58709da98ef:/data#
root@e58709da98ef:/data# redis-cli -p 6379
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> exit
root@e58709da98ef:/data# exit
exit
[root@192 soft]#
3.4.7.从容器内拷贝文件到主机上
#从容器内拷贝文件到主机上
容器->主机
docker cp 容器ID:容器内路径 目的主机路径
[root@192 soft]# docker run -it ubuntu /bin/bash
root@38933ab36249:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@38933ab36249:/# pwd
/
root@38933ab36249:/# cd /tmp
root@38933ab36249:/tmp# touch a2024.txt
root@38933ab36249:/tmp# ll
total 0
drwxrwxrwt. 1 root root 23 Jan 7 10:11 ./
drwxr-xr-x. 1 root root 17 Jan 7 10:08 ../
-rw-r--r--. 1 root root 0 Jan 7 10:11 a2024.txt
root@38933ab36249:/tmp#
[root@192 soft]#
[root@192 soft]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38933ab36249 ubuntu "/bin/bash" 3 minutes ago Up 3 minutes boring_brahmagupta
e58709da98ef redis "docker-entrypoint.s…" 27 minutes ago Up 27 minutes 6379/tcp vigilant_williams
[root@192 soft]# docker cp 38933ab36249:/tmp/a2014.txt /app/soft
Error response from daemon: Could not find the file /tmp/a2014.txt in container 38933ab36249
[root@192 soft]# docker cp 38933ab36249:/tmp/a2024.txt /app/soft
Successfully copied 1.54kB to /app/soft
[root@192 soft]#
[root@192 soft]# ll
总用量 2761172
-rw-r--r--. 1 root root 0 1月 7 18:11 a2024.txt
[root@192 soft]# docker exec -it 38933ab36249 /bin/bash
root@38933ab36249:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@38933ab36249:/# exit
exit
[root@192 soft]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38933ab36249 ubuntu "/bin/bash" 6 minutes ago Up 6 minutes boring_brahmagupta
e58709da98ef redis "docker-entrypoint.s…" 31 minutes ago Up 31 minutes 6379/tcp vigilant_williams
[root@192 soft]#
3.4.8.导入和导出容器
#导入和导出容器
export 导出容器的内容留作为一个tar归档文件[对应import命令]
docker export 容器ID > 文件名.tar
import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]
cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
cat ubuntu-001.tar | docker import - user/ubuntu:001
[root@192 soft]# docker rm -f 38933ab36249
38933ab36249
[root@192 soft]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e58709da98ef redis "docker-entrypoint.s…" 39 minutes ago Up 39 minutes 6379/tcp vigilant_williams
[root@192 soft]#
[root@192 soft]# cat ubuntu-001.tar | docker import - user/ubuntu:001
sha256:d7ff238c9005c659f4c5819554181bf5c5e56e15c2ad36db66af1acb347423fe
[root@192 soft]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
user/ubuntu 001 d7ff238c9005 15 seconds ago 72.8MB
redis latest 7614ae9453d1 2 years ago 113MB
ubuntu latest ba6acccedd29 2 years ago 72.8MB
redis 6.0.8 16ecd2772934 3 years ago 104MB
[root@192 soft]# docker run -it d7ff238c9005 /bin/bash
root@493bd15c2a4c:/# cd /tmp
root@493bd15c2a4c:/tmp# ll
total 0
drwxrwxrwt. 2 root root 23 Jan 7 10:11 ./
drwxr-xr-x. 1 root root 18 Jan 7 10:26 ../
-rw-r--r--. 1 root root 0 Jan 7 10:11 a2024.txt
root@493bd15c2a4c:/tmp#
[root@192 soft]#
3.5.常用命令小结
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 #提交当前容器为新的镜像
Copy files/folders from the containers filesystem to the host path #从容器中拷贝指定文件或者目录到宿主机中
create Create a new container #创建一个新的容器,同run,但不启动容器
diff Inspect changes on a container's filesystem #查看docker容器变化
events Get real time events from the server #从docker服务获取容器实时事件
exec Run a command in an existing container #在已存在的容器上运行命令
export Stream the contents of a container as a tar archive #导出容器的内容流作为一个tar归档文件[对应import]
history Show the history of an image #展示一个镜像形成历史
images List images #列出系统当前镜像
import Create a new filesystem image from the contents of a tarball #从tar包中的内容创建一个新的文件系统映像[对应expor]
info Display system-wide information #显示系统相关信息
inspect Return low-level information on a container #查看容器详细信息
kill Kill a running container # kill指定docker容器
load Load an image from a tar archive #从一个tar包中加载一个镜像[对应save]
login Register or Login to the docker registry server #注册或者登陆一个docker源服务器logout Log out from a Docker registry server #从当前Docker registry退出
logs Fetch the logs of a container #输出当前容器日志信息
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT #查看映射端口对应的容器内部源端口
pause Pause all processes within a container #暂停容器
ps List containers#列出容器列表
pull Pull an image or a repository from the docker registry server #从docker镜像源服务器拉取指定镜像或者库镜像
push Push an image or a repository to the docker registry server #推送指定镜像或者库镜像至docker源服务器
restart Restart a running container #重启运行的容器
rm Remove one or more containers #移除一个或者多个容器
rmi Remove one or more images #移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或-f强制删除]
run Run a command in a new container #创建一个新的容器并运行一个命令
save Save an image to a tar archive #保存一个镜像为一个tar包[对应load]
search Search for an image on the Docker Hub #在docker hub中搜索镜像
start Start a stopped containers #启动容器
stop Stop a running containers #停止容器
tag Tag an image into a repository #给源中镜像打标签
top Lookup the running processes of a container #查看容器中运行的进程信息
unpause Unpause a paused container #取消暂停容器
version Show the docker version information #查看docker版本号
wait Block until a container stops, then print its exit code #截取容器停止时的退出状态值
四、Docker镜像
镜像
是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。
只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。
4.1.分层的镜像
以我们的pu为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载>
4.2.UnionFS联合文件系统
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
4.3.Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot fle system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统bootfs。这一层与我们典型的LinuxUnix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。rootfs (root fle system),在bootfs之上。包含的就是典型Linux系统中的/dev, /proc, bin, letc等标准目录和文件。roots就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M?
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就行了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别,因此不同的发行版可以公用bootfs。
4.4.为什么Docker镜像采用这种分层结构呢?
镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。
比如说有多个镜像都从相同的 base镜像构建而来,那么Docker Host只需在磁盘上保存一份base镜像;同时内存中也只需加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
4.5.重点理解
Docker镜像层都是只读的,容器层是可写的
当容器启动时一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层"之下的都叫“镜像层”。当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
4.6.docker镜像commit操作案例--演示ubuntu安装vim
4.6.1.提交镜像
docker commit 提交容器副本使之成为一个新的镜像
docker commit -m="提交的描述信息" -a="作者" 容器ID要创建的目标镜像名:[标签名]
4.6.2.从Hub上下载ubuntu镜像到本地并成功运行
docker pull ubuntu
4.6.3.原始的默认ubuntu镜像是不带着vim命令的
[root@192 soft]# docker run -it ubuntu /bin/bash
root@7878b5ae864f:/#
root@7878b5ae864f:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@7878b5ae864f:/# vim a.txt
bash: vim: command not found
4.6.4.外网连通的情况下,安装vim
#先更新我们的包管理工具
apt-get update
# 然后安装我们需要的vim
apt-get install vim
root@7878b5ae864f:/# cd /tmp
root@7878b5ae864f:/tmp# vim a.txt
root@7878b5ae864f:/tmp# cat a.txt
this is a docker
4.6.5.安装完成后,commit我们自己的新镜像
docker commit -m="vim cmd add ok" -a="lb" 139727b83d29 itcast/myubuntu:1.1
-a: 指定镜像作者
-m: 自定义的提交描述信息
-139727b83d29: 容器名字,这里也可以用容器ID代替
-itcast/myubuntu:1.1: 指定要创建目标镜像的自定义名字(这里为itcast/myubuntu)和自定义版本(1.1)
[root@192 soft]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
139727b83d29 ubuntu "/bin/bash" 3 minutes ago Up 3 minutes flamboyant_hellman
[root@192 soft]# docker commit -m="vim cmd add ok" -a="ldy" 139727b83d29 itcast/myubuntu:1.1
sha256:1fd9ce6dfd6992d8c7033dccb9d2898e4dc2b99f1babf631db620474e11e2336
[root@192 soft]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
itcast/myubuntu 1.1 1fd9ce6dfd69 58 seconds ago 189MB
ubuntu latest ba6acccedd29 2 years ago 72.8MB
4.6.6.启动我们的新镜像并和原来的对比
原来72.8MB 现在189MB
4.7.总结
Docker中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似Java继承于一个Base基础类,自己再按需扩展。新镜像是从base镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。
五、本地镜像发布到阿里云
5.1.本地镜像发布到阿里云流程
5.2.镜像的生成方法
基于当前容器创建一个新的镜像,新功能增强
docker commit [OPTIONS] 容器ID [REPOSITORY[:TAG]]
commit我们自己的新镜像docker commit -m="vim cmd add ok" -a="lb" 139727b83d29
-a: 提交的镜像作者
-m: 提交的说明文字
5.3.将本地镜像推送到阿里云
阿里云开发者平台:https://promotion.aliyun.com/ntms/act/kubernetes.html
创建仓库镜像:选择控制台,进入容器镜像服务;选择个人实例;命名空间;仓库名称;进入管理界面获得脚本
5.4.将阿里云的镜像下载本地
六、CentOS7安装DockerCompose
6.1.下载
Linux下需要通过命令下载:
# 安装
curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
如果下载速度较慢,或者下载失败,可以使用课前资料提供的docker-compose文件:
上传到/usr/local/bin/
目录也可以。
6.2.修改文件权限
修改文件权限:
# 修改权限
chmod +x /usr/local/bin/docker-compose
6.3.Base自动补全命令:
# 补全命令
curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
如果这里出现错误,需要修改自己的hosts文件:
echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts
七、本地镜像发布到私有库--Docker Registry
搭建镜像仓库可以基于Docker官方提供的DockerRegistry来实现。
官网地址:https://hub.docker.com/_/registry
7.1.下载镜像--创建registry容器并开放端口
docker pull registry
- 运行私有库Registry,相当于本地有个私有Docker hub
Docker官方的Docker Registry是一个基础版本的Docker镜像仓库,具备仓库管理的完整功能,但是没有图形化界面。
搭建方式比较简单,命令如下:
docker run -d \
--restart=always \
--name registry \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
--privileged=true \
registry
# -p指定端口,一内一外
# -v表示挂载,前者是宿主机,后者是容器
# Docker挂载主机目录访问如果出现cannot open directory .: Permission denied解决办法:在挂载目录后多加一个--privileged=true参数即可
如果是CentOS7安全模块会比之前系统版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为,
在SELinux里面挂载目录被禁止掉了额,如果要开启,我们一般使用--prvileged=true命令,扩大容器的权限解决挂载目录没有权限的
问题,也即使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。
命令中挂载了一个数据卷registry-data到容器内的/var/lib/registry 目录,这是私有镜像库存放数据的目录。
访问http://YourIp:5000/v2/_catalog
可以查看当前私有镜像服务中包含的镜像
7.2.案例演示创建一个新镜像,ubuntu安装ifconfig命令
[root@192 soft]# docker run -it ubuntu /bin/bash
root@e65d61ff09d8:/# ifconfig
root@e65d61ff09d8:/# apt-get -y install net-tools
root@e65d61ff09d8:/# ifconfig
[root@192 soft]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e65d61ff09d8 ubuntu "/bin/bash" 3 minutes ago Up 3 minutes objective_leakey
daafa6a212e1 registry "/entrypoint.sh /etc…" 7 minutes ago Up 7 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp registry
98d2c389f54a 1fd9ce6dfd69 "/bin/bash" 26 minutes ago Up 26 minutes objective_meninsky
[root@192 soft]#
[root@192 soft]# docker commit -m="ifconfig cmd add" -a="ldy" e65d61ff09d8 ubuntu:1.2
sha256:811d655579989129269df684b6f4f1fcda542f69988de2005e19a9b610bfd050
[root@192 soft]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 1.2 811d65557998 5 seconds ago 122MB
registry.cn-hangzhou.aliyuncs.com/dockerk8speed/myubuntu1.1 1.1 1fd9ce6dfd69 2 hours ago 189MB
ubuntu latest ba6acccedd29 2 years ago 72.8MB
7.3.curl验证私服库上有什么镜像
指令格式如下所示:
curl -XGET http://主机映射的网络地址:主机映射网络端口/v2/_catalog
防踩坑:
如果记得自己映射的主机Registy网络地址就可以直接输入;如果不记得,可以使用docker ps -a查看私服库Registry的网络映射信息(映射网络地址和映射端口)。
[root@192 soft]# curl -XGET http://192.168.229.129:5000/v2/_catalog
{"repositories":[]}
[root@192 registry-ui]# curl -XGET http://192.168.229.129:8080/v2/_catalog
{"repositories":["nginx"]}
可以看到,目前私服库http://192.168.229.129:5000/v2/_catalog没有任何镜像上传过。。。。。。。【空的】
如下图所示,我的本地私有库Registry的网络地址为0.0.0.0,映射端口为5000。
7.4.将新镜像ubuntu;修改符合私服规范的Tag
推送镜像到私有镜像服务必须先tag,步骤如下:
重新tag本地镜像,名称前缀为私有仓库的地址:192.168.229.129:5000
docker tag nginx:latest 192.168.229.129:5000/nginx:1.0
使用命令docker tag 将 tag nginx:latest 这个镜像修改为 192.168.229.129:5000/nginx:1.0
#自己host主机ip地址
按照公式:docker tag 镜像ID或镜像名:Tag Host:Port/Repository:Tag
上面代码的参数含义如下所示:
(1)镜像ID或镜像名
:要上传到私有库Registry的镜像ID或名字;
(2)Tag
:要上传的镜像版本号;
(3)Host
:本地私有库的映射网址(本文为0.0.0.0);
(4)Post
:本地私有库的映射端口(本文为5000);
(5)Repository:Tag
:上传到私有库Registry后自定义的镜像名字、版本号。
[root@192 soft]# docker tag ubuntu:1.2 192.168.229.129:5000/ubuntu:1.2
[root@192 soft]#
[root@192 soft]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.229.129:5000/ubuntu 1.2 811d65557998 7 minutes ago 122MB
ubuntu 1.2 811d65557998 7 minutes ago 122MB
registry.cn-hangzhou.aliyuncs.com/dockerk8speed/myubuntu1.1 1.1 1fd9ce6dfd69 2 hours ago 189MB
ubuntu latest ba6acccedd29 2 years ago 72.8MB
[root@192 soft]#
7.5.修改配置文件使之支持http-配置Docker信任地址
安装的docker只支持阿里云镜像加速网址的http连接,为了实现镜像上传到私有库Registry,我们还要配置支持本地安装私有库Registry的http连接
我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:
# 打开要修改的文件
vi /etc/docker/daemon.json
# registry-mirrors配置的是国内阿里提供的镜像加速地址,不用加速的话访问官网的会很慢。
# vim命令添加内容:http://192.168.229.129:8080为自己设置的私服库地址
"insecure-registries":["http://192.168.229.129:8080"]
#上述理由: docker默认不允许http方式推送镜像,通过配置选项来取消这个限制。====>修改完后如果不生效.建议重启docker
# 重新加载
systemctl daemon-reload
# 重启docker
systemctl restart docker
7.6.带有图形化界面版本--更改私有库
使用DockerCompose部署带有图象界面的DockerRegistry,命令如下:
version: '3.0'
services:
registry:
image: registry
volumes:
- ./registry-data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- 8080:80
environment:
- REGISTRY_TITLE=个人私有仓库
- REGISTRY_URL=http://registry:5000
depends_on:
- registry
[root@192 soft]# mkdir registry-ui
[root@192 soft]# cd registry-ui/
[root@192 registry-ui]# ll
总用量 0
[root@192 registry-ui]# touch docker-compose.yml
[root@192 registry-ui]# vim docker-compose.yml
[root@192 registry-ui]#
[root@192 registry-ui]# cat docker-compose.yml
version: '3.0'
services:
registry:
image: registry
volumes:
- ./registry-data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- 8080:80
environment:
- REGISTRY_TITLE=个人私有仓库
- REGISTRY_URL=http://registry:5000
depends_on:
- registry
[root@192 registry-ui]# ll
总用量 4
-rw-r--r--. 1 root root 322 1月 8 07:27 docker-compose.yml
[root@192 registry-ui]# docker-compose up -d
Creating network "registry-ui_default" with the default driver
Pulling ui (joxit/docker-registry-ui:static)...
static: Pulling from joxit/docker-registry-ui
540db60ca938: Pull complete
197dc8475a23: Pull complete
39ea657007e5: Pull complete
37afbf7d4c3d: Pull complete
0c01f42c3df7: Pull complete
d590d87c9181: Pull complete
3333c94ae44f: Pull complete
33d7cca6fc9f: Pull complete
076b2dd9bdd1: Pull complete
b70198f04ee7: Pull complete
1fb6c5acc953: Pull complete
Digest: sha256:b0657b6be748173583516e411bd71552e54cb7d5dda94964726297ce8774415c
Status: Downloaded newer image for joxit/docker-registry-ui:static
Creating registry-ui_registry_1 ... done
Creating registry-ui_ui_1 ... done
[root@192 registry-ui]#
[root@192 registry-ui]#
[root@192 registry-ui]# docker-compose logs -f
Attaching to registry-ui_ui_1, registry-ui_registry_1
ui_1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
ui_1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
ui_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
ui_1 | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
ui_1 | 10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf differs from the packaged version
ui_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
ui_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
ui_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/90-docker-registry-ui.sh
ui_1 | /docker-entrypoint.sh: Configuration complete; ready for start up
registry_1 | time="2024-01-07T23:31:22.196329571Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.11.2 instance.id=513135ab-7e70-40dc-8e08-70a6164afe97 service=registry version=v2.7.1
registry_1 | time="2024-01-07T23:31:22.196463852Z" level=info msg="redis not configured" go.version=go1.11.2 instance.id=513135ab-7e70-40dc-8e08-70a6164afe97 service=registry version=v2.7.1
registry_1 | time="2024-01-07T23:31:22.200039301Z" level=info msg="Starting upload purge in 12m0s" go.version=go1.11.2 instance.id=513135ab-7e70-40dc-8e08-70a6164afe97 service=registry version=v2.7.1
registry_1 | time="2024-01-07T23:31:22.203727763Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.11.2 instance.id=513135ab-7e70-40dc-8e08-70a6164afe97 service=registry version=v2.7.1
registry_1 | time="2024-01-07T23:31:22.20447663Z" level=info msg="listening on [::]:5000" go.version=go1.11.2 instance.id=513135ab-7e70-40dc-8e08-70a6164afe97 service=registry version=v2.7.1
7.7.push推送到私服库
命令格式为:
docker push 符合私服规范的Tag的镜像名称或ID:版本号
docker tag nginx:1.0 192.168.229.129:8080/nginx:1.0
docker push 192.168.229.129:8080/nginx:1.0
docker tag ubuntu:1.2 192.168.229.129:8080/ubuntu:1.2
docker push 192.168.229.129:8080/ubuntu:1.2
7.8.curl验证私服库上有什么镜像
上传私服库完成,在终端输入命令curl -XGET http://192.168.229.129:8080/v2/_catalog查看私服库上是否有推送的镜像
[root@192 soft]# curl -XGET http://192.168.229.129:8080/v2/_catalog
{"repositories":["nginx","ubuntu"]}
7.9.pull到本地并运行
使用docker pull
命令从私有库拉取镜像到docker中:
docker pull Registry的网络映射:Registry的端口映射/私有库中的镜像名字:版本号
docker pull 192.168.229.129:8080/nginx:1.0
防踩坑
:上面的curl指令只显示了私服库上的镜像名字,如为了获得版本号,需要如下指令,需要注意的ubuntu-advanced是要拉取的镜像名字:
curl -XGET http://192.168.229.129:8080/v2/nginx/tags/list
八、Docker容器数据卷
将docker容器内的数据保存进宿主机的磁盘中
8.1.运行一个带有容器卷存储功能的实例
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
将运用与运行的环境打包镜像,run后形成容器实例运行,但是我们对数据的要求希望是持久化的
Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了。
为了能保存数据在docker中我们使用卷。
特点:
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可以直接实时生效,爽
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
8.2.宿主vs容器之间映射添加容器卷
8.2.1.绑定映射目录
#读写默认
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
docker run \
-it --name myubt \
--privileged=true \
-v /tmp/myHostData:/tmp/myDockerData \
ubuntu /bin/bash
8.2.2.查看数据卷是否挂载成功
docker inspect 容器ID
8.2.3.容器和宿主机之间数据共享
1 docker修改,主机同步获得
2 主机修改,docker同步获得
3 docker容器stop,主机修改,docker容器重启看数据成功同步。
8.3.容器数据卷读写规则映射添加说明
# 可读可写
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
# 容器实例内部被限制,只能读取不能写
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
ro = read only
此时如果宿主机写入内容,可以同步给容器内,容器可以读取到。
8.4.卷的继承和共享
8.4.1.容器1完成和宿主机的映射
docker run -it --privileged=true -v /tmp/myHostData:/tmp/myDockerData --name u1 ubuntu /bin/bash
8.4.2.容器2继承容器1的卷规则
docker run -it --privileged=true --volumes-from 父类 --name u2 ubuntu
docker run -it --privileged=true --volumes-from u1 --name u2 ubuntu
九、Docker常规安装简介
9.1.总体步骤
搜索镜像
拉取镜像
查看镜像
启动镜像 - 服务端口映射
停止容器
移除容器
9.2.安装Tomcat【新版本webapps已改为webapps.dist】
https://hub.docker.com/
9.2.1.docker hub上面查找tomcat镜像
9.2.2.从docker hub上拉取tomcat镜像到本地
9.2.3.docker images查看是否有拉取到的tomcat
9.2.4.使用tomcat镜像创建容器实例(也叫运行镜像)
docker run -it -p 8080:8080 tomcat
-p :小写,主机端口:docker容器端口
-P :大写,随机分配端口
-i :交互
-t :终端
-d :后台
9.2.5.访问猫首页
可能没有映射端口或者没有关闭防火墙
把webapps.dist目录转成webapps
9.2.6.免修改版说明
docker pull billygoo/tomcat8-jdk8
docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8
9.3.安装mysql:5.7
9.3.1.简单版
9.3.1.1.查询拉取mysql镜像
docker search mysql
# 不加版本号5.7,默认拉取的是最新版
docker pull mysql:5.7
docker images mysql:5.7
9.3.1.2.使用mysql镜像
docker run -p 3306:3306 --name mq -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
docker ps
docker exec -it 容器ID /bin/bash
mysql -u root -p
9.3.1.3.建库建表插入数据
9.3.1.4.外部win10连接运行在docker上的Mysql容器实例服务
9.3.1.5.中文字符集问题
show variables like 'character%';
mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)
9.3.2.实战版本
9.3.2.1.新建mysql容器实例
docker run \
--name mysql \
--privileged=true \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3306:3306 \
-v /app/mysql/log:/var/log/mysql \
-v /app/mysql/data:/var/lib/mysql \
-v /app/mysql/conf:/etc/mysql/conf.d \
-d \
mysql:5.7
--name : 起名字
--privilege=true : 容器数据卷权限开启
-p : 端口映射
-d : 后台运行
或者
docker run \
--name mysql \
--privileged=true \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3306:3306 \
-v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
-v /tmp/mysql/data:/var/lib/mysql \
-d \
mysql:5.7.25
编写hmy.cnf
[mysqld]
skip-name-resolve
character_set_server=utf8
datadir=/var/lib/mysql
server-id=1000
9.3.2.2.新建my.cnf--通过容器卷同步给mysql容器实例
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
9.3.2.3.重新启动mysql容器实例再重新进入并查看字符编码
9.3.2.4.再新建库新建表再插入中文测试
show databases;
create database db01;
use db01;
create table t1(id int,name varchar(20));
insert into t1 values(1,'zs'),(2,'lisi'),(3,'王五');
select * from t1;
show variables like 'character%';
drop database db01;
9.4.安装redis
9.4.1.下载redis镜像到本地标签为6.0.8
docker search redis --limit 5
docker pull redis
docker images redis
9.4.2.入门命令
9.4.3.命令提醒:容器卷记得加入 --privileged=true
9.4.4.在Centos宿主机下新建目录/app/redis
mkdir -p /app/redis
9.4.5.将一个redis.conf文件模板拷贝进/app/redis
- 开启redis验证
requirepass 123456
- 允许redis外地连接,必须注释掉 #bind 127.0.0.1
- daemonize no 后台启动设置为no
将daemonize yes注释起来或者daemonize no设置,因为该配置和docker run中-d参数冲突,会导致容器一直启动失败
- 开启redis数据持久化appendonly yes可选
9.4.6. /app/redis目录下修改redis.conf文件
默认出厂的原始redis.conf
9.4.7.使用redis6.0.8镜像创建容器(也叫运行镜像)
docker run \
--name mr \
--privileged=true \
-p 6379:6379 \
-v /app/redis/redis.conf:/etc/redis/redis.conf \
-v /app/redis/data:/data \
-d redis:6.0.8 \
redis-server /etc/redis/redis.conf
# 命令说明
docker run \
--name mr \ # 给容器起个名称
--privileged=true \ # Docker会赋予容器几乎与主机相同的权限,即root
--cluster-enabled yes \ # 是否开启集群
--appendonly yes \ # 开启AOF持久化功能
-p 6380:6379 \ #宿主机端口号:容器端口 端口映射
-v /app/redis/redis.conf:/etc/redis/redis.conf \ #宿主机目录:容器目录 redis配置文件挂载
-v /app/redis/data:/data \ #redis数据文件挂载
--net host \ # 指定网络类型为host,即与宿主机使用同一网络
-d redis:6.0.8 \ # 后台运行
redis-server /etc/redis/redis.conf \ #运行容器 并使用配置文件启动容器内的 redis-server
# --requirepass 123456 设置密码 (如果你是通过docker 容器内部连接的话,就随意,可设可不设。但是如果想向外开放的话,一定要设置,不然容易中病毒)
9.4.8.测试redis-cli连接上来
9.4.9.请证明docker启动使用了我们自己指定的配置文件
9.4.10.测试redis-cli连接上来第2次
9.5.安装Nginx
9.5.1.基础拉取、下载、运行
docker search nginx --limit 5
docker pull nginx
docker run --name mn -p 80:80 -d nginx
# docker run 启动一个镜像
# -d 表示后台允许
# --name mn 表示为当前容器起一个别名
# -p 80:80 表示将本机的80端口映射到nginx镜像的80端口
9.5.2.进入Nginx容器当中
进入Nginx容器当中
docker exec -it nginx /bin/bash
命令详解:
# docker exec 在运行的容器中执行命令
# -i 以交互模式运行容器,通常与 -t 同时使用;例如-it
# -t 为容器重新分配一个伪输入终端,通常与 -i 同时使用;例如-it
# nginx 容器名
# /bin/bash 其实就是表示载入容器后运行bash(命令语言解释程序), 因为docker中必须要保持一个进程(运行的程序)的运行,要不然整个容器就会退出,所以说,bash就担任起了docker中运行的那个进程的角色!
#而/bin/bash则是bash在linux下的位置
9.5.3.创建并运行nginx容器的命令解读
docker run --name containerName -p 80:80 -d nginx
命令解读:
- docker run :创建并运行一个容器
- --name : 给容器起一个名字,比如叫做mn
- -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
- -d:后台运行容器
- nginx:镜像名称,例如nginx
这里的-p
参数,是将容器端口映射到宿主机端口。
默认情况下,容器是隔离环境,我们直接访问宿主机的80端口,肯定访问不到容器中的nginx。
现在,将容器的80与宿主机的80关联起来,当我们访问宿主机的80端口时,就会被映射到容器的80,这样就能访问到nginx了:
9.5.4.nginx容器挂载配置文件
启动nginx
# 生成容器
docker run --name mn -p 80:80 -d nginx
在/app/nginx目录下建立3个以下文件夹
# 在/app/nginx目录下建立3个以下文件夹
mkdir -p /app/nginx/{log,conf,html}
复制到宿主机
# 将容器nginx.conf文件复制到宿主机
docker cp mn:/etc/nginx/nginx.conf /app/nginx/nginx.conf
# 将容器conf.d文件夹下内容复制到宿主机
docker cp mn:/etc/nginx/conf.d /app/nginx/conf.d
# 将容器中的html文件夹复制到宿主机
docker cp mn:/usr/share/nginx/html /app/nginx
配置文件文件备份
docker exec -it mn /bin/bash
cd /etc/nginx
#cp default.conf default.bak.conf
cp nginx.conf nginx.bak.conf
# 配置文件文件外部备份
#docker cp mn:/etc/nginx/default.conf /app/nginx/default.bak.conf
docker cp mn:/etc/nginx/nginx.conf /app/nginx/nginx.bak.conf
9.5.5.nginx容器挂载配置文件解读
挂载配置文件,就是把装有docker宿主机上面的nginx.conf配置文件映射到启动的nginx容器里面,这需要你首先准备好nginx.conf配置文件,如果你应经准备好了,下一步是启动nginx。
docker run --name nginx -p 8080:80 \
-v /app/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /app/nginx/log:/var/log/nginx \
-v /app/nginx/html:/usr/share/nginx/html \
-v /app/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf \
--restart=always \
-d \
nginx
解释下上面的命令:
- -name 给你启动的容器起个名字,以后可以使用这个名字启动或者停止容器
- -p 映射端口,将docker宿主机的80端口和容器的80端口进行绑定
- -v 挂载文件用的
- 第一个-v表示将你本地的nginx.conf覆盖你要起启动的容器的nginx.conf文件
- 第二个表示将日志文件进行挂载,就是把nginx服务器的日志写到你docker宿主机的/home/docker-nginx/log/下面
- 第三个-v 表示的和第一个-v意思一样的
- -d 表示启动的是哪个镜像
endl
高级篇
@TOC
一、Docker复杂安装详说
1.1 Mysql主从复制--原理-【尚硅谷Mysql高级篇】
1.2 Mysql主从复制--【一主一从】搭建步骤
1.2.1新建--【主服务器】--容器实例--3307
# 安装mysql,启动数据卷,使得将docker内的数据保留进宿主机的磁盘中,设置mysql密码为123456
docker run \
--name mysql-master \
--privileged=true \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3307:3306 \
-v /app/mysql-master/log:/var/log/mysql \
-v /app/mysql-master/data:/var/lib/mysql \
-v /app/mysql-master/conf:/etc/mysql \
-d \
mysql:5.7
1.2.2.进入/app/mysql-master/conf目录下新建my.cnf
cd /app/mysql-master/conf
pwd
vim my.cnf
粘贴以下内容
[client]
default_character_set=utf8
[mysql]
default-character-set=utf8
[mysqld]
## 设置server-id,同一局域网中需要唯一
server-id=101
## binlog-do-db=数据库名 //同步的数据库名称,如果不配置,表示同步所有的库
## 指定不需要同步的数据库名称,忽略mysql库
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(binlog格式:mixed,statement,row)
## ROW(行模式,记录所有变动的行,存入binlog,缺点:当遇到批量修改的sql时,容易导致日志sql过多)
## STATEMENT(记录每条修改的SQL,存入binlog,缺点:当遇到now()这些函数时,会导致主从出现数据误差)
## MIXED(实现ROW和STATMENT切换,缺点:无法识别@@的系统变量,比如@@hostname)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1026错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
collation_server=utf8_general_ci
character_set_server=utf8
1.2.3.修改完配置后重启master实例
docker restart mysql-master
1.2.4.进入mysql-master容器
# 进入master容器 & 进入数据库
docker exec -it mysql-master /bin/bash
mysql -uroot -p123456
1.2.5.master容器实例内创建数据同步用户
# 在主库中添加账号slave,密码为123456并赋予权限
create user 'slave'@'%' identified by '123456';
# 授予权限
grant replication slave,replication client on *.* to 'slave'@'%';
1.2.6.新建---【从服务器】---容器实例--3308
docker run \
--name mysql-slave \
--privileged=true \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3308:3306 \
-v /app/mysql-slave/log:/var/log/mysql \
-v /app/mysql-slave/data:/var/lib/mysql \
-v /app/mysql-slave/conf:/etc/mysql \
-d \
mysql:5.7
1.2.7.进入/app/mysql-slave/conf目录下新建my.cnf
cd /app/mysql-slave/conf
pwd
vim my.cnf
粘贴以下内容
[client]
default_character_set=utf8
[mysql]
default-character-set=utf8
[mysqld]
## 设置server-id,同一局域网中需要唯一
server-id=102
## binlog-do-db=数据库名 //同步的数据库名称,如果不配置,表示同步所有的库
## 指定不需要同步的数据库名称,忽略mysql库
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(binlog格式:mixed,statement,row)
## ROW(行模式,记录所有变动的行,存入binlog,缺点:当遇到批量修改的sql时,容易导致日志sql过多)
## STATEMENT(记录每条修改的SQL,存入binlog,缺点:当遇到now()这些函数时,会导致主从出现数据误差)
## MIXED(实现ROW和STATMENT切换,缺点:无法识别@@的系统变量,比如@@hostname)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1026错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制文件
log_slave_updates=1
## 这个配置项可以设置从库的只读模式,将其值设置为 1 表示只读,设置为 0 表示读写。
## slave设置为只读(具有super权限的用户除外)
read_only=1
collation_server=utf8_general_ci
character_set_server=utf8
1.2.8.修改完配置后重启slave实例
docker restart mysql-slave
1.2.9.在主【数据库中】查看主从同步状态
show master status;
1.2.10.进入mysql-slave容器
docker exec -it mysql-slave /bin/bash
mysql -uroot -p123456
1.2.11.在【从数据库】中配置主从复制
记住File和Position,后面需要用到。
此时一定不要操作Master库,否则将会引起Master状态的变化,File和Position字段也将会进行变化。
查看主机ip(mysql-master)
docker inspect mysql-master
# 设置从库读取主库的服务器配置,分别为:主机IP、账号、密码、MASTER_LOG_FILE是上面查询的File,MASTER_LOG_POS是上面查询的Position
change master to master_host='宿主机ip',
master_user='slave',
master_password='123456',
master_port=3307,
master_log_file='mall-mysql-bin.00001',
master_log_pos=617,
master_connect_retry=30;
主从复制命令参数说明:
master_host : 主数据库的IP地址;
master_port : 主数据库的运行端口;
master_user : 在主数据库创建的用于同步数据的用户账号;
master_password : 在主数据库创建的用于同步数据的用户密码;
master_log_file : 指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
master_log_pos : 指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
master_connect_retry : 连接失败重试的时间间隔,单位为秒。
change master to
master_host='192.168.229.133',
master_user='slave',
master_password='123456',
master_port=3307,
master_log_file='mall-mysql-bin.000001',
master_log_pos=617,
master_connect_retry=30;
使用start slave
开启主从复制过程后,如果 SlavelORunning
一直是Connecting
,则说明主从复制一直处于连接状态,这种情况一般是下面几种原因造成的,我们可以根据Last_IO_Error
提示予以排除。
1、网络不通: 检查ip、端口
2、密码不对 : 检查是否创建用于同步的用户和用户密码是否正确
3、pos不对 : 检查Master的Position正常启动后如下:
1.2.12.在mysql主从复制的时候,使用change to改变主从信息的时候,却发现change不了
mysql创建从节点出现如下报错提示
ERROR 3021 (HY000): This operation cannot be performed with a running slave io thread;
run STOP SLAVE IO_THREAD FOR CHANNEL '' first.
ERROR 3021 (HY000):此操作不能在运行的从io线程中执行;运行停止slave
原因很简单,我们需要,先stop slave;先把slave stop掉 然后再使用change就可以了。
change完之后,记得start slave; 开启slave
因为之前已经创建过主节点,需停掉之前的配置 再重新配置
# 停止主从复制
stop slave;
# 重置主机配置
reset master;
# 再次配置主从即可,查看从节点状态
show slave status\G;
1.2.13.在【从数据库】中查看主从同步状态
#查看主从同步状态[Slave_IO_Running列 和 Slave_SQL_Running列 是否开启主从同步]
show slave status \G;
Slave_lO_Running和Slave_SQL_Running是查看主从是否运行的关键字段,默认为NO,表示没有进行主从复制。
1.2.14.在【从数据库】中开启主从同步
# 开启主从复制
start slave;
1.2.15.【查看从数据库】状态发现已经同步
show slave status \G;
1.2.16.主从复制测试
1.2.16.1.主机新建库--使用库--新建表--插入数据
1.2.16.2.从机使用表--查看记录
1.2.17. 遇见的异常
- 配置好my.conf启动成功后登录时异常:[ERROR] unknown variable 'server-id=1'
将新增的所有主从配置往上移就解决了 执行重启命令systemctl restart mysqld;时异常:Failed to restart mysqld.service: Unit not found
MySQL在安装时没有创建名为mysqld的服务,cd /etc/init.d查看mysql映射的服务名。
我这里是mysql,所以修改重启命令为:systemctl restart mysql
- 在从库设置主库配置时异常:
ERROR 3021 (HY000): This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD FOR CHANNEL '' first.
这是因为从库已经启动了主从复制,先执行STOP SLAVE;停止主从复制,再执行配置即可
- MySQL8.0在从库设置主库配置时异常:
Last_IO_Error: error connecting to master 'slave@111.11.11.111:3306' - retry-time: 60 retries: 18 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.
修改主库slave账号的密码加密方式:alter user 'slave'@'%' identified with mysql_native_password by '123456';
二、安装Redis集群(大厂面试第4季--分布式存储案例真题)
cluster(集群)模式-docker版哈希槽分区进行亿级数据存储
2.1 面试题---【1~2亿条数据需要缓存,请问如何设计这个存储案例】
1~2亿条数据需要缓存,请问如何设计这个存储案例
单机单台100%不可能,肯定是分布式存储,用redis如何落地?
2.2 哈希取余分区
哈希取余分区
: 节点映射、数据变动、容易出错
2亿条记录就是2亿个k,v,我们单机不行必须要分布式多机,
假设有3台机器构成一个集群,用户每次读写操作都是根据公式 : hash(key)%N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点:
简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。
使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。缺点:
原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key)/? 。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。
某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。
2.3 一致性哈希算法分区
一致性哈希算法分区
一致性Hash算法背景
一致性哈希算法在1997年由麻省理工学院中提出的,设计目标是为了解决分布式缓存数据变动和映射问题
,某个机器宕机了,分母数量改变了,自然取余数不OK了。提出一致性Hash解决方案。目的是当服务器个数发生变动时,尽量减少影响客户端到服务器的映射关系。
- 算法构建一致性哈希环
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,
这个集合可以成为一个hash空间[0.,2^32-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(O=2^32),
这样让它逻辑上形成了一个环形空间。
它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对2^32取模,简单来说
,`一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环`,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形),
整个哈希环如下图:整个空间`按顺时针方向组织`,圆环的正上方的点代表0,O点右侧的第一个点代表1,以此类推,2、3、4、……直到2^32-1,
也就是说0点左侧的第一个点代表2^32-1,0和2^32-1在零点中方向重合,我们把这个由2^32个点组成的圆环称为Hash环。
- 服务器IP节点映射
将集群中各个IP节点映射到环上的某一个位置。
将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。
假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:
- key落到服务器的落键规则
当我们需要存储一个kv键值对时,首先计算key的hash值, hash(key)将这个key使用相同的函数Hash计算出哈希值并确定此数据在
环上的位置,`从此位置沿环顺时针“行走”`,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如我们有Qbject A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法
,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
- 一致性哈希算法的
容错性
假设Node C宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。
一般的,在一致性Hash算法中,如果一台服务器不可用,则`受影响的数据仅仅是此服务器到其环空间中前一台服务器
(即沿着逆时针方向行走遇到的第一台服务器)之间数据`,其它不会受到影响。
简单说,就是C挂了,受到影响的只是B、C之间的数据,并且这些数据会转移到D进行存储。
- 一致性哈希算法的
扩展性
数据量增加了,需要增加一台节点Node X,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,
不会导致hash取余全部数据重新洗牌。
- 一致性哈希算法的
数据倾斜
问题
`Hash环的数据倾斜问题`
一致性Hash算法在服务`节点太少`时,容易因为节点分布不均匀而造成`数据倾斜`(被缓存的对象大部分集中缓存在某一台服务器上)问题,
例如系统中只有两台服务器:
为了在节点数目发生改变时尽可能少的迁移数据
将所有的存储节点排列在收尾相接的Hash环上,每个key在计算Hash后会`顺时针`找到临近的存储节点存放。
而当有节点加入或退出时仅影响该节点在Hash环上`顺时针相邻的后续节点。`
`优点`
加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。
`缺点`
数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上的,所以数据在进行存储时达不到均匀分布的效果。
2.4 哈希槽分区
为了解决一致性哈希算法的`数据倾斜问题`,哈希槽实质就是一个数组,数组[0,2^14-1]形成hash slot空间。
`能干什么`
解决均匀分配的问题,`在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,`
现在就相当于节点上放的是槽,槽里放的是数据。
槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。
哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
`多少个hash槽`
一个集群只能有16384个槽,编号0-16383(0-2^14-1)。`这些槽会分配给集群中的所有主节点`,分配策略没有要求。可以指定哪些编号的槽分配给
哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对
应的槽里。slot =CRC16(key)% 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
2.5 哈希槽计算
三、3主3从redis集群扩缩容配置案例架构说明
3.1 架构图
3.2 关闭防火墙+启动docker后台服务
systemctl start docker
systemctl start docker
3.3 redis集群配置【3主3从】
3.3.1.docker运行redis--命令参数--分步解释
命令说明
docker run \ # 创建并运行docker 容器实例
--name mr \ # 给容器起个名称
--privileged=true \ # Docker会赋予容器几乎与主机相同的权限,即获取宿主机root用户权限
--appendonly yes \ # 开启AOF持久化功能
-v /app/redis/redis.conf:/etc/redis/redis.conf \ #容器卷,宿主机地址:docker容器内部地址 redis配置文件挂载
-v /app/redis/data:/data \ #redis数据文件挂载
--net host \ # 使用宿主机的ip和端口,指定网络类型为host,即与宿主机使用同一网络,默认
-d redis:6.0.8 \ # 后台运行 redis镜像和版本号
redis-server /etc/redis/redis.conf \ #运行容器 并使用配置文件启动容器内的 redis-server
--cluster-enabled yes \ # 是否开启redis集群
--port 6386 \ # redis端口号
# -p 6380:6379 \ #宿主机端口号:容器端口 端口映射
# --requirepass 123456 设置密码 (如果你是通过docker 容器内部连接的话,就随意,可设可不设。但是如果想向外开放的话,一定要设置,不然容易中病毒)
3.3.2.新建6个docker容器实例
docker run -d --name redis-node-1 --net host --privileged=true -v /app/share/redis-node-1:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /app/share/redis-node-2:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /app/share/redis-node-3:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /app/share/redis-node-4:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /app/share/redis-node-5:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /app/share/redis-node-6:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6386
3.3.3.进入容器redis-node-1并为6台机器构建集群关系
docker exec -it redis-node-1 /bin/bash
ifconfig
注意,进入docker容器后才能执行一下命令,且注意自己的真实IP地址
redis-cli \
--cluster create \
192.168.229.133:6381 \
192.168.229.133:6382 \
192.168.229.133:6383 \
192.168.229.133:6384 \
192.168.229.133:6385 \
192.168.229.133:6386 \
--cluster-replicas 1
# -a 000415 验证密码
--cluster create
: 构建集群--cluster-replicas 1
:表示为每个master创建一个slave节点-a 000415
:表示设置的密码
3.3.4.链接进入6381作为切入点,查看集群状态
# 查看集群信息
cluster info
# 本集群有哪些节点
cluster nodes
本次实际主从节点对应情况
主服务器Master | 从服务器Slave |
---|---|
6381 | 6384 |
6382 | 6385 |
6383 | 6386 |
3.4 主从容错切换迁移案例
3.4.1.数据读写存储
# 启动1机构成的集群并通过exec进入
docker exec -it redis-node-1 /bin/bash
# 对6381新增两个key
redis-cli -p 6381
# 防止路由失效加参数-c并新增两个key
redis-cli -p 6381 -c
# 查看集群信息
redis-cli --cluster check 192.168.229.133:6381
3.4.1.1.启动1机构成的集群并通过exec进入
3.4.1.2.防止路由失效加参数-c,并新增两个key
3.4.1.3.查看集群状态
3.4.2.容错切换迁移
3.4.2.1. 主6381和从机切换,先停止主机6381
docker stop redis-node-1
3.4.2.2. 再次查看集群信息
3.4.2.3. 先还原之前的3主3从
# 先开启6381
docker restart redis-node-1
# 在停止6384
docker stop redis-node-4
# 再开启6384
docker restart redis-node-4
# 主从机器分配情况以实际情况为准
3.4.2.5. 查看集群状态【redis-cli --cluster check 自己ip
:6381】
redis-cli --cluster check 自己ip
:6381
redis-cli --cluster check 192.168.229.133:6381
3.4.2.6.虚拟机自动关机,重启恢复原样
3.5 主从扩容案例
3.5.1.分析图
3.5.2.实操步骤
新建6387、6388两个节点+新建后启动+查看是否8节点
进入6387容器实例内部--docker exec -it redis-node-7 /bin/bash
将新增的6387节点(空槽号)作为master节点加入原集群
检查集群情况第1次
重新分派槽号
检查集群情况第2次
为主节点6387分配从节点6388
检查集群情况第3次
3.5.3.新建6387、6388
两个节点+新建后启动+查看是否8节点
docker run -d --name redis-node-7 --net host --privileged=true -v /app/share/redis-node-7:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /app/share/redis-node-8:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6388
3.5.4.进入6387
容器实例内部--docker exec -it redis-node-7 /bin/bash
docker exec -it redis-node-7 /bin/bash
3.5.5.将新增的6387
节点【空槽号】作为master节点加入原集群
将新增的6387作为master节点加入集群
redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381
redis-cli --cluster add-node 192.168.229.133:6387 192.168.229.133:6381
6387就是将要作为master新增节点
6381就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群
3.5.6.检查集群情况第1次
3.5.7.重新分派槽号--命令【redis-cli --cluster reshard IP地址:端口号】
# 重新分派槽号
redis-cli --cluster reshard 192.168.229.133:6381
3.5.8.检查集群情况第2次--【槽号分派说明】
redis-cli --cluster check 192.168.229.133:6381
槽号分派说明:为什么6387是3个新的区间,以前的还是连续?
重新分配成本太高,所以前3家各自匀出来一部分,从6381/6382/6383三个旧节点分别匀出1364个坑位给新节点6387
3.5.9.为主节点6387分配从节点6388
命令:
redis-cli --cluster
add-node
ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID
265b4d87bf36015ff8e491a2994753c590ee3d85 -------这个是6387的编号,按照自己实际情况
redis-cli --cluster add-node 192.168.229.133:6388 192.168.229.133:6387 \
--cluster-slave --cluster-master-id 265b4d87bf36015ff8e491a2994753c590ee3d85
3.5.10.检查集群情况第3次
redis-cli --cluster check 192.168.229.133:6387
3.6 主从缩容案例
3.6.1 分析图
3.6.2 实操步骤
# 目的:6387和6388下线
# 检查集群情况1获得6388的节点ID
redis-cli --cluster check 192.168.229.133:6382
ad001acdd52a4484c63c8c6c8d3c9f0de173cd4b
# 将6388删除--从集群中将4号从节点6388删除
redis-cli --cluster del-node 192.168.229.133:6388 ad001acdd52a4484c63c8c6c8d3c9f0de173cd4b
redis-cli --cluster check 192.168.229.133:6382
# 将6387的槽号清空,重新分配,本例将清出来的槽号都给6381
redis-cli --cluster reshard 192.168.229.133:6381
4096
1ce24a697196e2f225204fa664d65d7800b07d06 ---6381槽号ID
265b4d87bf36015ff8e491a2994753c590ee3d85 ---6387槽号ID
done
# 检查集群情况第二次
redis-cli --cluster check 192.168.229.133:6381
4096个槽位都给了6381,它变成了8192个槽位,相当于全部都给6381;不然要输入3次,一锅端
# 将6387删除
redis-cli --cluster del-node ip:从机端口 6387节点ID
redis-cli --cluster del-node 192.168.229.133:6387 265b4d87bf36015ff8e491a2994753c590ee3d85
# 检查集群情况第三次
redis-cli --cluster check 192.168.229.133:6381
3.6.3 检查集群情况1获得6388的节点ID
redis-cli --cluster check 192.168.229.133:6382
ad001acdd52a4484c63c8c6c8d3c9f0de173cd4b
3.6.4 将6388删除--从集群中将4号从节点6388删除
命令:redis-cli --cluster del-node
ip:从机端口 从机6388节点ID
redis-cli --cluster del-node 192.168.229.133:6388 ad001acdd52a4484c63c8c6c8d3c9f0de173cd4b
redis-cli --cluster check 192.168.229.133:6382
3.6.5 将6387的槽号清空,重新分配,本例将清出来的槽号都给6381
redis-cli --cluster reshard 192.168.229.133:6381
4096
1ce24a697196e2f225204fa664d65d7800b07d06 ---6381槽号ID
265b4d87bf36015ff8e491a2994753c590ee3d85 ---6387槽号ID
done
3.6.6 检查集群情况第二次
redis-cli --cluster check 192.168.229.133:6381
4096个槽位都给了6381,它变成了8192个槽位,相当于全部都给6381;不然要输入3次,一锅端
3.6.7 将6387删除
命令:redis-cli --cluster del-node ip:从机端口 6387节点ID
redis-cli --cluster del-node ip:从机端口 6387节点ID
redis-cli --cluster del-node 192.168.229.133:6387 265b4d87bf36015ff8e491a2994753c590ee3d85
3.6.8 检查集群情况第三次
3.7 总结
四、DockerFile解析
4.1 DockerFile介绍
dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
官网:https://docs.docker.com/engine/reference/builder/
构建三步骤:
编写Dockerfile文件
docker build 命令构建镜像
docker run 依镜像运行容器实例
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20201113" \
org.opencontainers.image.title="CentOS Base Image" \
org.opencontainers.image.vendor="CentOS" \
org.opencontainers.image.licenses="GPL-2.0-only" \
org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
CMD ["/bin/bash"]
4.2 DockerFile构建过程解析
4.2.1.镜像结构
镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
我们以MySQL为例,来看看镜像的组成结构:
简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件。
4.2.2.DockerFile基础知识
1:每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2:指令按照从上到下,顺序执行
3: #表示注释
4:每条指令都会创建一个新的镜像层并对镜像进行提交
4.2.3.docker执行DockerFile的大致流程
(1) docker从基础镜像运行一个容器
(2)执行一条指令并对容器作出修改
(3)执行类似docker commit的操作提交一个新的镜像层
(4) docker再基于刚提交的镜像运行一个新容器
(5)执行dockerfile中的下一条指令直到所有指令都执行完成
4.2.4.小总结
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段:
- Dockerfile是软件的原材料
- Docker镜像是软件的交付品
- Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系
的基石。
- 1 Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。
Dockerile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等; - 2 Docker镜像,在用Dockerfle定义一个文件之后,docker buid时会产生一个Docker镜像,当运行Docker镜像时会真正开始提供服务;
- 3 Docker容器,容器是直接提供服务的。
4.3 DockerFile常用保留字指令
参考tomcat8的dockerfile入门:https://github.com/docker-library/tomcat
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。
4.3.1 FROM:定义基于哪个镜像构建新的镜像。
Dockerfile的第一条必须是FROM。指定一个已经存在的基础镜像作为模板,表示新镜像是基于哪个镜像构建的
例如:FROM centos
4.3.2 MAINTAINER:为镜像添加作者信息:姓名和邮箱地址
指定新镜像的创建者名字和邮箱
例如:MAINTAINER bulut<bulut123@163.com>
4.3.3 RUN:在容器构建时需要运行的命令。
新镜像构建时在容器上运行的指令。RUN在docker build
时运行。
shell格式:RUN <命令行命令>
RUN <命令行命令>
等同于,在终端操作的shell命令。
例如:
RUN yum -y install vim
exec格式:RUN [“可执行文件”, “参数1”, “参数2”]
例如:
RUN ["./test.php", "param1", "param2"],等同于RUN ./test.php param1 param2
RUN ["./test.php", "dev", "offline"],等同于RUN ./test.php dev offline
4.3.4 EXPOSE:当前容器对外暴露的端口
EXPOSE 8080
4.3.5 WORKDIR:指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
当容器运行时,通过docker exec -it 容器ID bash
进入容器的工作目录
4.3.6 USER: 指定该镜像以什么样的用户去执行,如果都不指定默认是root
4.3.7 ENV:用来在构建镜像过程中设置环境变量
ENV MY_PATH /app/test
这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样;
也可以在其它指令中直接使用这些环境变量,比如 : WORKDIR $MY_PATH
例如定义了ENV MY_WORKDIR /app
,则可以这样定义WORKDIR:WORKDIR $MY_WORKDIR
例如定义了ENV JAVA_HOME /app/java
,则通过新镜像启动的容器中,可以执行命令echo $JAVA_HOME
ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
RUN mkdir -p "$CATALINA_HOME"
WORKDIR $CATALINA_HOME
4.3.8 ADD:将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
ADD centos-6-docker.tar.xz /
4.3.9 COPY:类似ADD,拷贝文件和目录到镜像中
类似ADD,拷贝文件和目录到镜像中。
将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
COPY src dest
COPY ["src" "dest"]
<src源路径>:源文件或者源目录
<dest目标路径>:容器内的指定路径,该路径不用事先建好,会自动创建
4.3.10 VOLUME:容器数据卷,用于数据保存和持久化工作
定义容器卷,和参数-v
一样
# 将mycat的配置文件的地址暴露出映射地址,启动时直接映射宿主机的文件夹VOLUME /usr/local/mycat
4.3.11 CMD:设置容器默认要运行的命令及参数。
指定通过新镜像启动容器时,需要执行的指令。CMD在docker run
时运行。
CMD
指令的格式和RUN
相似,语法有两种:
shell格式
: CMD <命令>exec格式
: CMD [“可执行文件”, “参数1”, “参数2”, …]- 参数列表格式:CMD [ “参数1”, “参数2”, …]。在指定了
ENTRYPOINT
指令后,用CMD
指定具体的参数。 - Dockerfile可以有多个CMD指令,
但是只有最后一个有效。CMD会被docker run之后的参数替换。
- 如果
docker run
后面指定了容器运行的命令,则CMD指令无效,CMD会被docker run
之后的参数替换。
EXPOSE 8080
CMD ["catalina.sh", "run"]
- CMD 在
docker run
时运行。 - RUN 在
docker build
时运行。
4.3.12 ENTRYPOINT:
指定通过新镜像启动容器时,需要执行的指令。ENTRYPOINT在docker run
时运行
ENTRYPOINT也是用来指定一个容器启动时要运行的命令
类似于CMD指令,但是
ENTRYPOINT不会被docker run后面的命令覆盖
,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序
命令格式:
ENTRYPOINT ["<excuteable>","<param1>","<param2>",...]
ENTRYPOINT可以和CMD一起用,一般是变参
才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参。
当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指
令,他两个组合会变成 <ENTRYPOINT> "<CMD>"
例如:通过如下Dockerfile构建nginx:test
镜像
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
效果如下
是否传参 | 按照dockerfile编写执行 | docker run传参运行 |
---|---|---|
Docker run命令 | docker run -nginx:test | docker run nginx:test - c /etc/nginx/new.conf |
衍生出来的实际命令 | nginx -c /etc/nginx/nginx.conf | nginx -c /etc/nginx/new.conf |
4.3.13 CMD与ENTRYPOINT
- CMD
# dockerfile01
FROM centos
CMD ["ls", "-a"]
docker bulid -f dockerfile01 -t cmd:1.0 .
docker run 镜像id
# 会执行ls -a命令
# 如果执行docker run cmd:1.0 -l, 本来是想执行ls -al,但是 CMD命令会被-l所替代,由于没有-l命令,所以会报错
- ENTRYPOINT
# dockerfile02
FROM centos
ENTRYPOINT ["ls", "-a"]
docker bulid -f dockerfile01 -t cmd:2.0 .
docker run 镜像id
# 会执行ls -a命令
# 如果执行docker run cmd:2.0 -l, 执行正常,命令为ls -al
4.3.14 小总结
BUILD | Both | RUN |
---|---|---|
FROM | WORKDIR | CMD |
MAINTAINER | USER | ENV |
COPY | EXPOSE | |
ADD | VOLUME | |
RUN | ENTRYPOINT | |
ONBUILD | ||
.dockerignore |
FROM: 表示基础镜像,就是我们的镜像从这里开始构建m
MAINTAINER: 表示维护者信息,就是这个镜像是谁创建的,一般需要写姓名和邮箱
RUN:表示镜像构建时需要运行的命令
ADD: 表示为了构建镜像需要添加的压缩等文件,例如要构建tomcat镜像,那么肯定要将tomcat压缩包放进去
WORKDIR: 表示镜像的工作目录
VOLUME: 表示挂载数据卷的目录位置,如果没有设置,就需要在运行run命令时通过-v进行设置
EXPOSE: 表示要暴露的端口,如果没有设置,就需要在运行run命令时通过-p进行设置
CMD:指定容器启动时需要运行的命令,只有最后一个会生效,可以被替代
ENTRYPOINT: 指定容器启动时需要运行的命令,可以追加命令
ONBUILD:当创建一个被继承(--volumes-from)的dockerfile时,就会运行这个指令,属于触发指令
COPY:类似ADD,将文件拷贝到镜像中
ENV:构建的时候设置环境变量
4.4 案例
4.4.1 自定义镜像mycentosjava8
4.4.1.1 要求:Centos7镜像具备vim+ifconfig+jdk8
docker search centos
# 建议下载8版本以下的
# 从官方仓库拉取最新的 CentOS 7 镜像作为基础镜像
docker pull centos:7
docker images centos:7
docker run -it eeb6ee3f44bd /bin/bash
4.4.1.2 下载JDK8
下载地址:https://www.oracle.com/java/technologies/downloads/#java8
4.4.1.3 编写Dockerfile文件
Dockerfile
FROM centos:7
MAINTAINER dy<dy@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum install -y yum-utils
# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络IP
RUN yum -y install net-tools
# 安装java8 及 lib 库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# ADD 是相对路径jar,把jdk-8u391-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u391-linux-x64.tar.gz /usr/local/java
# 配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_391
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.tar:$JAVA_HOME/lib/tools.jar$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin/:$PATH
EXPOSE 80
CMD echo $PATH
CMD echo "success--------------------ok"
CMD /bin/bash
参数说明:
- FROM centos:7:指定基础镜像为 CentOS,版本号为7
- MAINTAINER:指定镜像的维护者和联系方式。
- ENV MYPATH /usr/local:定义一个环境变量 MYPATH,并设置其值为 /usr/local。
- WORKDIR $MYPATH:将工作目录切换到 $MYPATH,即 /usr/local。
- RUN yum -y install vim:在容器内运行命令,安装 Vim 编辑器。
- RUN yum -y install net-tools:在容器内运行命令,安装 net-tools,包括常用的网络工具。
- EXPOSE 80:声明容器将监听的端口号为 80,但这并不会自动映射到主机端口。
- CMD echo $MYPATH:在容器启动时运行的命令,输出环境变量 $MYPATH 的值。
- CMD echo "success--------------------ok":在容器启动时运行的命令,输出固定的字符串 "success--------------------ok"。
- CMD /bin/bash:在容器启动时运行的命令,启动一个交互式的 Bash 终端。
4.4.1.4 构建
# -t:是target的缩写
docker build -t 新镜像名字:TAG .
docker build -t mycentosjava8:1.1 .
# 注意:TAG后面有个空格,有个点
# 最后的小数点:就表明是当前目录。
docker build -f /app/myfile/Dockerfile -t mycentosjava8:1.1 .
-f:后面跟随的是Dockerfile 文件
-t :后面跟随的镜像名和版本号。
4.4.1.5 运行
run -it 39ad01e4fa5e /bin/bash
run -it 39ad01e4fa5e /bin/bash
4.4.1.6. 构建报错【centos8及8版本以上缺乏中文语言包和地址vault.centos.org需更改】
4.4.1.6.1.Error: Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist
在学习使用 docker 技术过程中,基于 centos 镜像自定义新的镜像,其中基础镜像 centos 需要提前安装好 vim
和 net-tools
,然而在刚开始通过 yum -y install vim
安装 vim 时,便出现了错误提示信息:
Error: Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist
[root@192 myfile]# docker build -t centosjava8:1.1 .
[+] Building 60.9s (7/11) docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 836B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:latest 0.0s
=> [1/7] FROM docker.io/library/centos 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 107B 0.0s
=> CACHED [2/7] WORKDIR /usr/local 0.0s
=> ERROR [3/7] RUN yum -y install vim 60.9s
------
> [3/7] RUN yum -y install vim:
60.78 CentOS Linux 8 - AppStream 0.0 B/s | 0 B 01:00
60.78 Errors during downloading metadata for repository 'appstream':
60.78 - Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=container [Could not resolve host: mirrorlist.centos.org]
60.79 Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=container [Could not resolve host: mirrorlist.centos.org]
------
Dockerfile:8
--------------------
6 |
7 | # 安装vim编辑器
8 | >>> RUN yum -y install vim
9 | # 安装ifconfig命令查看网络IP
10 | RUN yum -y install net-tools
--------------------
ERROR: failed to solve: process "/bin/sh -c yum -y install vim" did not complete successfully: exit code: 1
:one:上面的报错信息意思是,从仓库 ‘appstream’ 下载元数据失败:由于镜像列表中没有 URL,不能准备内部镜像列表。
:two: 问题分析
:
:three: 第一种可能的情况便是网络连接问题。检查是否可以连接外部网络,可以使用 ping baidu.com 查看是否有丢包情况。如果丢包,则进一步检查网络连接是否正常;如果没有丢包,继续阅读下文
:four:那么第二种情况,便是 CentOS 已经停止维护的问题。2020 年 12 月 8 号,CentOS 官方宣布了停止维护 CentOS Linux 的计划,并推出了 CentOS Stream 项目,CentOS Linux 8 作为 RHEL 8 的复刻版本,生命周期缩短,于 2021 年 12 月 31 日停止更新并停止维护(EOL),更多的信息可以查看 CentOS 官方公告。如果需要更新 CentOS,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
:five:报错信息
那么针对上面提到的第二种情况,给出的解决方法如下:
首先,进入到 yum 的 repos 目录
cd /etc/yum.repos.d/
其次,修改 centos 镜像内容,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
https://vault.centos.org/centos/8/AppStream/x86_64/os/repodata/
然后,生成缓存更新(第一次更新,速度稍微有点慢,耐心等待 2min 左右)
yum makecache
4.4.1.6.2.CentOS 8
中执行命令,出现报错:Failed to set locale, defaulting to C.UTF-8报错原因:
- 1、没有安装相应的语言包。
2、没有设置正确的语言环境。
RUN yum install glibc-langpack-zh
解决方法1:安装语言包
- 设置语言环境需使用命令 locale
- locale -a 命令,查看目前系统已安装的语言包
- 安装中文语言包,命令
yum install glibc-langpack-zh
- 安装英文语言包,命令
dnf install glibc-langpack-en
或dnf install langpacks-en glibc-all-langpacks -y
解决方法2:设置正确的语言环境
echo "export LC_ALL=en_US.UTF-8" >> /etc/profile
source /etc/profile
或使用命令locale -gen en_US.UTF-8
yum -y install vim
4.4.1.6.3.总结编写Dockerfile,centos8添加以下内容
FROM centos:latest
# 更改镜像,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# 安装中文语言包
RUN yum -y install glibc-langpack-zh
centos8--Dockerfile完整版
FROM centos:latest
MAINTAINER dy<dy@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 更改镜像,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# 安装中文语言包
RUN yum -y install glibc-langpack-zh
RUN yum install -y yum-utils
# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络IP
RUN yum -y install net-tools
# 安装java8 及 lib 库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# ADD 是相对路径jar,把jdk-8u391-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u391-linux-x64.tar.gz /usr/local/java
# 配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_391
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.tar:$JAVA_HOME/lib/tools.jar$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin/:$PATH
EXPOSE 80
CMD echo $PATH
CMD echo "success--------------------ok"
CMD /bin/bash
4.4.1.6.4.再次测试centos8以上版本镜像
Dockerfile
FROM centos:latest
MAINTAINER dy<dy@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 更改镜像,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# 安装中文语言包
RUN yum -y install glibc-langpack-zh
RUN yum install -y yum-utils
# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络IP
RUN yum -y install net-tools
# 安装java8 及 lib 库
RUN yum -y install glibc.i686
EXPOSE 80
CMD echo "success--------------------ok"
CMD /bin/bash
docker pull centos:latest
mkdir -p /app/myfile
cd /app/myfile
vi Dockerfile
docker build -t centos8:1.1 .
4.4.2 虚悬镜像:仓库名、标签都是的镜像,俗称dangling image
Dockerfile
# 测试用的
from ubuntu
CMD echo 'action is success'
docker build
# 查看虚悬镜像
docker image ls -f dangling=true
# 删除虚悬镜像
docker image prune
4.4.3 自定义镜像myubuntu
4.4.3.1 编写Dockerfile
Dockerfile
FROM ubuntu
MAINTAINER dy<dy@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN apt-get update
RUN apt-get install net-tools
RUN apt-get install -y iproute2
RUN apt-get install -y inetutils-ping
EXPOSE 80
CMD echo $MYPATH
CMD echo "install inconfig cmd into ubuntu success--------ok"
CMD /bin/bash
4.4.3.2 构建新镜像
docker build -t 新镜像名字:TAG .
4.4.3.3 运行新镜像
docker run -it 新镜像名字:TAG
4.4.4.案例-自定义镜像-Dockerfile
Dockerfile
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
4.5 小总结
五、Docker微服务实战
5.1.在IDEA新建一个普通微服务模块
5.1.1.建Moudle--spring-boot-docker-demo
5.1.2.改POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/>
</parent>
<groupId>com.itcast</groupId>
<artifactId>spring-boot-docker-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.complier.source>1.8</maven.complier.source>
<maven.complier.target>1.8</maven.complier.target>
<juint.version>4.12</juint.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.18.16</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mapper.version>4.1.5</mapper.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--SpringBoot 通用依赖模版-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
</project>
5.1.3.写yml
server.port=6001
5.1.4.主启动
@SpringBootApplication
public class SpringBootDockerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDockerDemoApplication.class, args);
}
}
5.1.5.业务类
import java.util.UUID;
@RestController
@RequestMapping("/order")
public class OrderController {
@Value("${server.port}")
private String port;
@RequestMapping("/docker")
public String helloDocker() {
return "hell docker" + "\t" + port + "\t" + UUID.randomUUID().toString().replace("-", "");
}
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index() {
return "服务端口号:" + "\t" + port + "\t" + UUID.randomUUID().toString().replace("-", "");
}
}
5.1.6.测试
5.1.7.打包成一个jar包并上传
5.2.通过Dockerfile发布到微服务部署到docker容器
5.2.1.IDEA工具将微服务打包成jar包并上传
5.2.2.编写Dockerfile
5.2.2.1.Dockerfile内容
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER DY
# volume 指定临时文件为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为 dy_docker.jar
ADD spring-boot-docker-demo-0.0.1-SNAPSHOT.jar /dy_docker.jar
# 运行jar包
RUN bash -c 'touch /dy_docker.jar'
ENTRYPOINT ["java","-jar","/dy_docker.jar"]
# 暴露6001端口作为微服务
EXPOSE 6001
5.2.2.2.将微服务jar包和Dockerfile文件上传到同一个目录下/app/mydocker
5.2.3.构建镜像
docker build -t dy_docker.jar:1.1 .
5.2.4.运行容器
docker run -d -p 6001:6001 dy_docker.jar:1.1
5.2.5.访问测试
curl 192.168.229.134:6001/order/docker
curl 192.168.229.134:6001/order/index
curl http://192.168.229.134:6001/order/docker
curl http://192.168.229.134:6001/order/index
六、Docker网络
Docker网络作用:
容器间的互联和通信以及端口映射
容器lP变动时候可以通过服务名直接网络通信而不受到影响
6.1.分析图
6.2.Docker 不启动,默认的网络情况
- virbr0
在CentDS7的安装过程中如果有选择相关虚拟化的的服务安装系统后
,启动网卡时会发现有一个以网桥连接的私网地址的virbr0网卡
(virbr0网卡:它还有一个固定的默认IP地址192.168.122.1),是做虚拟机网桥的使用的,其作用是为连接其上的虚机网卡提供NAT访问外网的功能。
我们之前学习Linux安装,勾选安装系统的时候附带了libvirt服务才会生成的一个东西,如果不需要可以直接将libvirtd服务卸载。
yum remove libvirt-libs.x86_64
6.3.Docker 启动,网络情况
Docker 启动,会产生一个名为docker0的虚拟网桥
6.4.查看Docker 的网络命令模式
6.4.1.docker网络命令大全
# 查看Docker网络
docker network ls
# 查看网络数据源
docker network inspect XXXX网络名字
# 删除网络
docker network rm XXXX网络名字
docker network --help
docker network COMMAND
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
6.4.2.查看Docker网络
# 查看Docker网络
docker network ls
6.4.3.查看网络数据源
# 查看网络数据源
docker network inspect XXXX网络名字
6.4.4.创建网络
# 创建网络
docker network create XXXX网络名字
6.4.5.删除网络
# 删除网络
docker network rm XXXX网络名字
6.5.Docker网络作用
Docker网络作用:
容器间的互联和通信
以及端口映射
容器lP变动时候可以通过服务名直接网络通信而不受到影响
6.6.Docker网络模式
6.6.1.总体介绍
网络模式 | 简介 |
---|---|
bridge | 为每一个容器分配、设置IP等,并将容器连接到一个docker0 虚拟网桥,默认为该模式。 |
host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 |
none | 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP等。 |
container | 新创建的容器不会创建自己的网卡和配置自己的lP而是和—个指定的容器共享IP、端口范围等。 |
命令:
bridge模式:使用--network bridge指定,默认使用dockerO
host模式:使用--network host指定
none模式:使用--network none指定
container模式:使用--network container:NAME或者容器ID指定
6.6.2.网络实例内默认网络IP生产规则
docker inspect 容器ID or 容器名字
# 查看数据源倒数20行
docker inspect u1 | tail -n 20
6.6.2.1.先启动两个ubuntu实例
6.6.2.2.:docker inspect 容器ID or 容器名字
6.6.2.3.关闭u2实例,新建u3,查看ip变化
6.6.2.4.结论:docker容器内部的ip是有可能会发生改变的
6.6.3.案例说明【bridge】
6.6.3.1.bridge是什么
Docker服务默认会创建一个docker0网桥(其上有一个docker0内部接口),该桥接网络的名称为docker0,它在内核层
连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络
。Docker默认指定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
# 查看bridge 网络的详细信息,并通过grep获取名额
docker network inspect bridge | grep name
ifconfig | grep docker
6.6.3.2.说明
- 1 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容
器一个IP地址,称为Container-lP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-lP直接通信。 - 2 docker run的时候,没有指定network的话
默认
使用的网桥模式就是bridge
,使用
的就是docker0
。在宿主机ifconfig,就可以看到doc
ker0和自己create的network(后面讲)eth0,eth1,eth……代表网卡一,网卡二,网卡三……lo代表127.0.0.1,即localhost
,inet addr
用来表示网卡的IP地址 - 3 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
3.1 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
3.2 每个容器实例内部也有一块网卡,每个接口叫eth0;
3.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一 一匹配。
3.4 通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
6.6.3.3.代码
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
6.6.3.4.两两匹配验证
6.6.4.案例说明【host】
6.6.4.1.host是什么
直接使用宿主机的IP地址与外界进行通信,不再需要额外进行NAT转换。
6.6.4.2.说明
容器将不会获得
一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
6.6.4.3.代码
警告:
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
- 问题: docke启动时总是遇见标题中的警告
- 原因: docker启动时指定--network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告,并且通过-p设置的参数将不会起到任何作用,
端口号会以主机端口号为主
,重复时则递增。 - 解决: 解决的办法就是使用docker的其他网络模式,例如--network=bridge,这样就可以解决问题,或者直接无视。
正确代码:
docker run -d --network host --name tomcat84 billygoo/tomcat8-jdk8
6.6.4.4.没有设置-p端口映射,如何启动访问tomcat84
http://宿主机IP:8080/
在CentOS里面用默认的火狐浏览器访问容器内的tomcat84看到访问成功,因为此时容器的IP借用主机的,所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。
6.6.5.案例说明【none】
none是禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
在none模式下,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo。
需要我们自己为Docker容器添加网卡、配置IP等。
docker run -d -p 8085:8080 --network none --name tomcat85 billygoo/tomcat8-jdk8
6.6.6.案例说明【container】
container网络模式
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。
新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
6.6.6.1.Alpine操作系统是一个面向安全的轻型Linux发行版
Apine Linux是一款独立的、非商业的通用Linux发行版,专为追求安全性、简单性和资源效率的用户而设计。
可能很多人没听说过这个Linux发行版本,但是经常用Docker的朋友可能都用过,
因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到6M的大小,所以特别适合容器打包。
6.6.6.2.命令演示
docker pull alpine
docker run -it --name alpine1 alpine /bin/sh
docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
6.6.6.3.运行结果,验证共用搭桥
6.6.6.4.加入关闭alpine1,查看alpine2
6.6.7.案例说明【自定义网络】
6.6.7.1.启动两个tomcat并进入容器内部
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
#上述成功启动并运行docker exec 进入各自容器实例内部
docker exec -it tomcat81 bash
docker exec -it tomcat82 bash
6.6.7.2.按照ip地址ping【ok】
6.6.7.3.按照服务名ping【error】
6.6.7.4.自定义桥接网络,自定义网络默认使用的是桥接网络bridge
6.6.7.5.新建自定义网络
docker network ls
docker network create p_network
6.6.7.6.新建容器加入上一步新建的自定义网络
docker run -d -p 8081:8080 --network p_network --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --network p_network --name tomcat82 billygoo/tomcat8-jdk8
#上述成功启动并运行docker exec 进入各自容器实例内部
docker exec -it tomcat81 bash
docker exec -it tomcat82 bash
6.6.7.7.互相ping测试
6.6.7.8.问题结论:自定义网络本身就维护好了主机名和ip的对应关系( ip和域名都能通)
七、Docker-compose容器编排
7.1.Docker-compose由来
Compose是Docker公司推出的一个工具软件,可以管理多个Docker容器组成一个应用。
你需要定义一个YAML格式的配置文件docker-compose.yml
,写好多个容器之间的调用关系
。
然后,只要一个命令,就能同时启动/关闭这些容器。
Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。
docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?
如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfle然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具
例如:要实现一个Web微服务项目
,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等......
Compose允许用户通过一个单独的docker-compose.yml
模板文件(YAML格式)
来定义一组相关联的应用容器为一个项目
(project可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令
安装这个应用的所有依赖
,完成构建。Docker-Compose解决了容器与容器之间如何管理编排的问题。
7.2.下载、安装及卸载
7.2.1.下载
Docker Compose和Docker版本对应关系官网:https://docs.docker.com/compose/compose-file/compose-file-v3/
官网下载:https://docs.docker.com/compose/install/
Install Compose standalone: https://docs.docker.com/compose/install/standalone/
Docker Compose下载地址:https://github.com/docker/compose/releases
下载地址:https://docs.docker.com/compose/install/linux/#install-the-plugin-manually
7.2.2.安装步骤
命令下载或者手动下载:
curl -SL https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
# 赋予执行权限,读写权限
chmod +x /usr/local/bin/docker-compose
# 版本信息
docker-compose --version
# 建立软连接:
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
7.2.3.卸载步骤
# 如果您使用curl以下方式安装,则卸载 Docker Compose:
sudo rm /usr/local/bin/docker-compose
7.3.Compose核心概念
文件
docker-compose.yml
服务 (service)
一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器
工程 (project)
由一组关联的应用容器组成的一个完整业务单元
,在docker-compose.yml文件
中定义。
7.4.Compose使用的三个步骤
- 编写Dockerfile定义各个
微服务
应用并构建出对应的镜像文件使用docker-compose.yml
- 定义一个完整业务单元,安排好整体应用中的各个容器服务。
- 最后,执行
docker-compose up
命令来启动并运行整个应用程序,完成一键部署上线
7.5.docker-compose.yml文件及常用命令
7.5.1.docker-compose.yml文件解读
内容如下:
version: "3.2"
services:
nacos:
image: nacos/nacos-server
environment:
MODE: standalone
ports:
- "8848:8848"
mysql:
image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123456
volumes:
- "$PWD/mysql/data:/var/lib/mysql"
- "$PWD/mysql/conf:/etc/mysql/conf.d/"
userservice:
build: ./user-service
orderservice:
build: ./order-service
gateway:
build: ./gateway
ports:
- "10010:10010"
可以看到,其中包含5个service服务:
nacos
:作为注册中心和配置中心image: nacos/nacos-server
: 基于nacos/nacos-server镜像构建environment
:环境变量MODE: standalone
:单点模式启动
ports
:端口映射,这里暴露了8848端口
mysql
:数据库image: mysql:5.7.25
:镜像版本是mysql:5.7.25environment
:环境变量MYSQL_ROOT_PASSWORD: 123
:设置数据库root账户的密码为123
volumes
:数据卷挂载,这里挂载了mysql的data、conf目录,其中有我提前准备好的数据
userservice
、orderservice
、gateway
:都是基于Dockerfile临时构建的
7.5.2.docker-compose.yml的编写规则介绍
以mysql服务为例,说明下常用字段的含义:
version: '3'#第一层:compose的版本号
services: #第二层:服务配置信息
mysql1: #服务名
image: mysql #该服务所基于的镜像名
environment: #该服务的环境变量
MYSQL_ROOT_PASSWORD: "ut.123456"
ports: #该服务的暴露端口
- "3306:3306"
container_name: "mysql1" #容器名
networks: #该服务所加入的网络段
- dev
volumes: #挂载数据卷
- /platform/mysql/conf:/etc/my.cnf.d/my.cnf
- /platform/mysql/data:/var/lib/mysql:rw"
networks: #第三层:网络环境
dev:
driver: bridge
7.5.3.Compose命令格式
docker compose 命令的基本格式为:
docker-compose [-f …] [options] [COMMAND] [ARGS…]
7.5.4.Compose常用命令
命令 | 作用 |
---|---|
docker-compose -h | #查看帮助 |
docker-compose up | #启动所有docker-compose服务 |
docker-compose up -d | #启动所有docker-compose服务并后台运行 |
docker-compose down | #停止并删除容器、网络、卷、镜像。 |
docker-compose exec yml里面的服务id | #进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash |
docker-compose ps | #展示当前docker-compose编排过的运行的所有容器 |
docker-compose top | #展示当前docker-compose编排过的容器进程 |
docker-compose logs yml里面的服务id | #查看容器输出日志 |
dokcer-compose config | #检查配置 |
dokcer-compose config -q | #检查配置,有问题才有输出 |
docker-compose restart | #重启服务 |
docker-compose start | #启动服务 |
docker-compose stop | #停止服务 |
7.5.5.以nginx容器操作为例
docker-compose ps 显示所有容器
docker-compose build nginx 构建nginx镜像
docker-compose up -d nginx 构建启动nignx容器
docker-compose exec nginx bash 登录到nginx容器中
docker-compose pause nginx 暂停nignx容器
docker-compose unpause nginx 恢复ningx容器
docker-compose start nginx 启动nignx容器
docker-compose stop nginx 停止nignx容器
docker-compose restart nginx 重新启动nginx容器
docker-compose rm nginx 删除nginx容器
docker-compose down 删除nginx容器和镜像
docker-compose logs -f nginx 查看nginx的实时日志
DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/
7.6.Compose编排微服务
7.6.1.改造升级微服务工程spring-boot-docker-demo
7.6.1.1.SQL建库建表
CREATE TABLE t_user (
id int unsigned NOT NULL AUTO_INCREMENT,
username varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
password varchar(50) NOT NULL DEFAULT '' COMMENT '密码',
sex tinyint NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',
deleted tinyint unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1114 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
insert into t_user (id,username,password)values
(1095,'xyz1',221462),
(1096,'xyz2',221463),
(1097,'xyz3',221464),
(1098,'xyz4',221465),
(1099,'xyz5',221466),
(1100,'xyz6',221467),
(1101,'xyz7',221468),
(1102,'xyz8',221469),
(1103,'xyz9',221470);
7.6.1.2.改pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/>
</parent>
<groupId>com.itcast</groupId>
<artifactId>spring-boot-docker-demo</artifactId>
<version>1.2</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.complier.source>1.8</maven.complier.source>
<maven.complier.target>1.8</maven.complier.target>
<juint.version>4.12</juint.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.18.16</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mapper.version>4.1.5</mapper.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<dependencies>
<!--guava Google 开源的 Guava 中自带的布隆过滤器-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--SpringBoot与Redis整合依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--springCache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--springCache连接池依赖包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>
<!--Mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--SpringBoot集成druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--mybatis和springboot整合-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!-- 添加springboot对amqp的支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<!--通用基础配置junit/devtools/test/log4j/lombok/hutool-->
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<!--persistence-->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<!--通用Mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>${mapper.version}</version>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
</project>
7.6.1.3.写application.yml
server.port=6001
# ========================alibaba.druid相关配置=====================
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.229.134:3306/db2024?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.druid.test-while-idle=false
# ========================redis相关配置=====================
spring.redis.database=0
spring.redis.host=192.168.229.134
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
# ========================mybatis相关配置===================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.itcast.entry
# ========================swagger=====================
spring.swagger2.enabled=true
7.6.1.4.主启动类SpringBootDockerDemoApplication.java
package com.itcast;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.itcast.mapper") //import tk.mybatis.spring.annotation.MapperScan;
public class SpringBootDockerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDockerDemoApplication.class, args);
}
}
7.6.1.5.业务类
7.6.1.5.1.config配置类RedisConfig.java
package com.itcast.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
@Configuration
@Slf4j
public class RedisConfig {
/**
* @param lettuceConnectionFactory
* @return redis序列化的工具配置类,下面这个请一定开启配置
* 127.0.0.1:6379> keys *
* 1) "ord:102" 序列化过
* 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过
*/
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
//设置key序列化方式string
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置value的序列化方式json
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
7.6.1.5.2.config配置类SwaggerConfig.java
package com.itcast.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.text.SimpleDateFormat;
import java.util.Date;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${spring.swagger2.enabled}")
private Boolean enabled;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(enabled)
.select()
.apis(RequestHandlerSelectors.basePackage("com.itcast")) //你自己的package
.paths(PathSelectors.any())
.build();
}
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Api接口测试" + "\t" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()))
.description("docker-compose")
.version("1.0")
.termsOfServiceUrl("https://www.atguigu.com/")
.build();
}
}
7.6.1.5.3.实体类User.java
package com.itcast.entry;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(generator = "JDBC")
private Integer id;
private String username; //用户名
private String password; //密码
private Byte sex; //性别 0=女 1=男
private Byte deleted; //删除标志,默认0不删除,1删除
@Column(name = "update_time")
private Date updateTime; //更新时间
@Column(name = "create_time")
private Date createTime; //创建时间
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Byte getSex() {
return sex;
}
public void setSex(Byte sex) {
this.sex = sex;
}
public Byte getDeleted() {
return deleted;
}
public void setDeleted(Byte deleted) {
this.deleted = deleted;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
7.6.1.5.4.实体类UserDTO.java
package com.itcast.entry;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Data
@ApiModel(value = "用户信息")
public class UserDTO implements Serializable {
@ApiModelProperty(value = "用户ID")
private Integer id;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "性别 0=女 1=男 ")
private Byte sex;
@ApiModelProperty(value = "删除标志,默认0不删除,1删除")
private Byte deleted;
@ApiModelProperty(value = "更新时间")
private Date updateTime;
@ApiModelProperty(value = "创建时间")
private Date createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Byte getSex() {
return sex;
}
public void setSex(Byte sex) {
this.sex = sex;
}
public Byte getDeleted() {
return deleted;
}
public void setDeleted(Byte deleted) {
this.deleted = deleted;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", sex=" + sex +
'}';
}
}
7.6.1.5.5.编写UserMapper.java
package com.itcast.mapper;
import com.itcast.entry.User;
import tk.mybatis.mapper.common.Mapper;
public interface UserMapper extends Mapper<User> {//tk.mybatis.mapper.common.Mapper
}
7.6.1.5.6.在src/main/resources目录下创建mapper文件夹,并编写UserMapper.xml
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itcast.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.itcast.entry.User">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="username" jdbcType="VARCHAR" property="username"/>
<result column="password" jdbcType="VARCHAR" property="password"/>
<result column="sex" jdbcType="TINYINT" property="sex"/>
<result column="deleted" jdbcType="TINYINT" property="deleted"/>
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
</resultMap>
</mapper>
7.6.1.5.7.新建UserService、UserServiceImpl
public interface UserService {
void addUser(User user);
User findUserById(Integer id);
void deleteUser(Integer id);
void updateUser(User user);
}
package com.itcast.service.impl;
import com.itcast.entry.User;
import com.itcast.mapper.UserMapper;
import com.itcast.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
@Slf4j
public class UserServiceImpl implements UserService {
public static final String CACHE_KEY_USER = "user:";
@Resource
private UserMapper userMapper;
@Resource
private RedisTemplate redisTemplate;
@Override
public void addUser(User user) {
//1 先插入mysql并成功
int i = userMapper.insertSelective(user);
if (i > 0) {
//2 需要再次查询一下mysql将数据捞回来并ok
user = userMapper.selectByPrimaryKey(user.getId());
//3 将捞出来的user存进redis,完成新增功能的数据一致性。
String key = CACHE_KEY_USER + user.getId();
redisTemplate.opsForValue().set(key, user);
}
}
@Override
public User findUserById(Integer id) {
User user = null;
String key = CACHE_KEY_USER + id;
//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql
user = (User) redisTemplate.opsForValue().get(key);
if (user == null) {
//2 redis里面无,继续查询mysql
user = userMapper.selectByPrimaryKey(id);
if (user == null) {
//3.1 redis+mysql 都无数据
//你具体细化,防止多次穿透,我们规定,记录下导致穿透的这个key回写redis
return user;
} else {
//3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率
redisTemplate.opsForValue().set(key, user);
}
}
return user;
}
@Override
public void deleteUser(Integer id) {
String key = CACHE_KEY_USER + id;
//1.从mysql里面删除
userMapper.deleteByPrimaryKey(id);
//2.从redis里面删除
redisTemplate.delete(key);
}
@Override
public void updateUser(User user) {
String key = CACHE_KEY_USER + user.getId();
//1.从mysql更新数据
userMapper.updateByPrimaryKey(user);
User user1 = findUserById(user.getId());
//2.从redis更新数据
redisTemplate.opsForValue().set(key, user1);
}
}
7.6.1.5.8.新建UserController
package com.itcast.controller;
import cn.hutool.core.util.IdUtil;
import com.itcast.entry.User;
import com.itcast.entry.UserDTO;
import com.itcast.service.impl.UserServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Random;
@Api(description = "用户User接口")
@RestController
@Slf4j
@RequestMapping("/users")
public class UserController {
@Resource
private UserServiceImpl userService;
@ApiOperation("数据库新增3条记录")
@PostMapping
public void addUser() {
for (int i = 1; i <= 3; i++) {
User user = new User();
user.setUsername("lb" + i);
user.setPassword(IdUtil.simpleUUID().substring(0, 6));
user.setSex((byte) new Random().nextInt(2));
userService.addUser(user);
}
}
@ApiOperation("删除1条记录")
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Integer id) {
userService.deleteUser(id);
}
@ApiOperation("修改1条记录")
@PutMapping
public void updateUser(@RequestBody UserDTO userDTO) {
User user = new User();
BeanUtils.copyProperties(userDTO, user);
userService.updateUser(user);
}
@ApiOperation("查询1条记录")
@GetMapping("/{id}")
public User findUserById(@PathVariable Integer id) {
return userService.findUserById(id);
}
}
7.6.1.7.mvn package命令将微服务形成新的jar包,并上传到Linux服务器 /app/mydocker目录下
7.6.1.8.编写Dockerfile
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER DY
# volume 指定临时文件为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为 docker-compose-test.jar
ADD spring-boot-docker-demo-1.2.jar docker-compose-test.jar
# 运行jar包
RUN bash -c 'touch /docker-compose-test.jar'
ENTRYPOINT ["java","-jar","/docker-compose-test.jar"]
# 暴露6001端口作为微服务
EXPOSE 6001
7.6.1.9.构建镜像
docker build -t docker-compose-test:1.2 .
7.6.2.不使用Compose
7.6.2.1.单独的mysql容器
7.6.2.1.1.新建mysql容器实例
/app/mysql/conf/my.cnf
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
docker run \
-p 3306:3306 \
--name mysql57 \
--privileged=true \
-v /app/mysql/conf:/etc/mysql/conf.d \
-v /app/mysql/logs:/logs \
-v /app/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
7.6.2.1.2.进入mysql容器实例内并新建库db2024 + 新建表t_user
docker exec -it mysql57 bash
create database db2024;
use db2024;
CREATE TABLE t_user (
id int unsigned NOT NULL AUTO_INCREMENT,
username varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
password varchar(50) NOT NULL DEFAULT '' COMMENT '密码',
sex tinyint NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',
deleted tinyint unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1114 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
7.6.2.2.单独的redis容器
docker run \
-p 6379:6379 \
--name redis \
--privileged=true \
-v /app/redis/redis.conf:/etc/redis/redis.conf \
-v /app/redis/data:/data \
-d redis:6.0.8 redis-server /etc/redis/redis.conf
docker exec -it redis608 bash
redis-cli -p 6379
7.6.2.3.微服务工程
docker run -d -p 6001:6001 docker-compose-test:1.2
docker run -d -p 6001:6001 82901d53b6fd
7.6.2.4.上面三个容器实例依次顺序启动成功
7.6.2.5.swagger测试
测试链接:http://192.168.229.134:6001/swagger-ui.html#/
7.6.3.上面成功了,有哪些问题【痛点】
- 先后顺序要求固定,先mysql+redis才能微服务访问成功
- 多个run命令......
- 容器间的
启停或宕机
,有可能导致IP地址
对应的容器实例变化,映射出错
,要么生产IP写死(可以但是不推荐),要么通过服务调用
7.6.4.使用Compose
7.6.4.1.编写docker-compose.yml文件
docker-compose.yml
version: "3"
services:
microService:
image: docker-compose-test:1.3
container_name: ms01
ports:
- "6001:6001"
volumes:
- /app/microService:/data
networks:
- p_net
depends_on:
- redis
- mysql
redis:
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- p_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2024'
MYSQL_USER: 'lb'
MYSQL_PASSWORD: '123456'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- p_net
command: --default-authentication-plugin=mysql_native_password
networks:
p_net:
详解:
version: "3" # 版本号
services:
microService: # 定义服务名字,微服务microService
image: docker-compose-test:1.3 # 镜像名字:版本号
container_name: ms01
ports:
- "6001:6001"
volumes:
- /app/microService:/data
networks:
- p_net
depends_on:
- redis
- mysql
# docker run -d -p 6001:6001 -v /app/microService:/data --network p_net --name ms01 docker-compose-test:1.3
redis:
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- p_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2024'
MYSQL_USER: 'lb'
MYSQL_PASSWORD: '123456'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- p_net
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
# docker network create p_net
networks:
p_net:
7.6.4.2.第二次修改微服务工程
两处修改将192.168.229.134换成对应的服务名称mysql,redis
#spring.datasource.url=jdbc:mysql://192.168.229.134:3306/db2024?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.url=jdbc:mysql://mysql:3306/db2024?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.druid.test-while-idle=false
# ========================redis相关配置=====================
spring.redis.database=0
#spring.redis.host=192.168.229.134
spring.redis.host=redis
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
# ========================mybatis相关配置===================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.itcast.entry
# ========================swagger=====================
spring.swagger2.enabled=true
# 构建新镜像docker-compose-test:1.3
docker build -t docker-compose-test:1.3 .
7.6.4.3.执行 docker-compose up 或者 docker-compose up -d
# 检查配置,有问题才输出
docker-compose config -q
7.6.4.4.进入mysql容器实例并新建库db2024+新建表t_user
docker exec -it d128363b77bc bash
mysql -ulb -p123456
create database db2024;
use db2024;
CREATE TABLE t_user (
id int unsigned NOT NULL AUTO_INCREMENT,
username varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
password varchar(50) NOT NULL DEFAULT '' COMMENT '密码',
sex tinyint NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',
deleted tinyint unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1114 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
7.6.4.5.测试通过
测试链接:http://192.168.229.134:6001/swagger-ui.html#/
7.6.4.6.Compose常用命令
命令 | 作用 |
---|---|
docker-compose -h | #查看帮助 |
docker-compose up | #启动所有docker-compose服务 |
docker-compose up -d | #启动所有docker-compose服务并后台运行 |
docker-compose down | #停止并删除容器、网络、卷、镜像。 |
docker-compose exec yml里面的服务id | #进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash |
docker-compose ps | #展示当前docker-compose编排过的运行的所有容器 |
docker-compose top | #展示当前docker-compose编排过的容器进程 |
docker-compose logs yml里面的服务id | #查看容器输出日志 |
dokcer-compose config | #检查配置 |
dokcer-compose config -q | #检查配置,有问题才有输出 |
docker-compose restart | #重启服务 |
docker-compose start | #启动服务 |
docker-compose stop | #停止服务 |
以nginx容器操作为例,常见操作如下:
docker-compose ps 显示所有容器
docker-compose build nginx 构建nginx镜像
docker-compose up -d nginx 构建启动nignx容器
docker-compose exec nginx bash 登录到nginx容器中
docker-compose pause nginx 暂停nignx容器
docker-compose unpause nginx 恢复ningx容器
docker-compose start nginx 启动nignx容器
docker-compose stop nginx 停止nignx容器
docker-compose restart nginx 重新启动nginx容器
docker-compose rm nginx 删除nginx容器
docker-compose down 删除nginx容器和镜像
docker-compose logs -f nginx 查看nginx的实时日志
DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/
7.6.4.7.关停
7.6.5.Redis总是连接失败总结
7.6.5.1.can't connect to redis-server
Redis-Desktop-Manager连接需要四个参数介绍:
Name:自定义连接名
Host:redis服务器地址,在CentOS终端中使用命令;
ifconfig:该命令显示的,ens33中的inet后的地址即为redis-server host
端口:默认6379
Auth:数据库密码,通常是设置数据库config时自定义的密码
7.6.5.2.将redis.conf文件下列参数更改
#bind 127.0.0.1 # 注释掉,允许远程连接(注释或者改为bind 0.0.0.0)
protected-mode no # 保护模式由yes改为no
appendonly yes # 开启AOP持久化
daemonize no # 后台启动设置为no
#requirepass 123456 # 开启redis验证
# 查看redis服务进程是否正常开启
ps -ef|grep redis|grep -v grep
ps -ef |grep redis
systemctl stop firewalld # 关闭linux防火墙
systemctl status firewalld # 查看firewall的状态
7.6.5.3.测试redis连接
7.6.5.4.docker redis容器连接不上--可以自定义网络模式
# 创建自定义网络
docker network create test_network
# 启动Redis容器,并加入自定义网络
docker run \
-p 6380:6379 \
--network test_network \
--name redis6 \
--privileged=true \
-d redis:latest redis-server
# 启动其他容器,并加入自定义网络
7.6.5.5.Docker运行Redis容器总结
在使用Docker运行Redis容器时,如果遇到连接不上的问题,可以先检查网络配置是否正确
,包括IP地址冲突
和防火墙设置
。
如果仍然无法解决,可以尝试修改Redis配置文件
,并挂载到容器中。
另外,如果需要容器间通信,可以选择合适的网络模式
来解决问题。
7.6.6.docker: Error response from daemon: driver failed programming external connectivity on endpoint redis
docker: Error response from daemon: driver failed programming external connectivity on endpoint redis (88aa3ee8adb07d3da901e2a53d4f086ea9eca3a686c3a0374745d272aff71495): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 6379 -j DNAT --to-destination 172.17.0.3:6379 ! -i docker0: iptables: No chain/target/match by that name.
八、Docker轻量级可视化工具Portainer
8.1.可视化工具Portainer简介
Portainer是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。
8.2.安装Portainer
8.2.1.官网
https://www.portainer.io/
https://docs.portainer.io/v/ce-2.9/start/install/server/docker/linux
8.2.2.docker命令安装
8.2.2.1.搜索portainer镜像
docker search portainer --limit 5
8.2.2.2.拉取portainer镜像
docker pull portainer/portainer
docker images portainer/portainer:latest
8.2.2.3.启动portainer容器
-restart=always
:表示docker容器重启了,portainer容器也跟着重启,如影随形,实时监控
# 启动镜像
docker run -d \
-p 8000:8000 \
-p 9000:9000 \
--name potainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /dockerData/portainer_data:/data \
portainer/portainer:latest
-p : 指定宿主机端口和容器端口 portainer默认端口是9000
-v : 目录映射,将/var/run/docker.sock映射到容器中,用于访问Docker的守护进程,控制Docker。
-v /dockerData/portainer_data保存Portainer的配置信息。
-restart=always: 代表在容器退出时总是重启容器,还有其他几种重启策略:no、on-failure、on-failuer:n、unless-stopped
-p 9000:9000 端口映射默认为9000,映射为9000;9000为对外暴漏外部服务的端口号
--name portainer 镜像名字
8.2.3.第一次登录需创建admin,访问地址:XXX.XXX.XXX.XXX:9000
登录网址 | http://自己IP:9000 |
---|---|
用户名 | 随便,默认admin |
密码 | 随便12345678 |
网址 | http:192.168.229.134:9000 |
8.2.4.设置admin用户和密码后首次登陆
8.2.5.选择local选项卡后本地docker详细信息展示
8.2.6.上一步的图形展示,对应命令--【docker system df】
8.3.登录并演示介绍常用操作case
8.4.Portainer安装niginx
8.4.1.创建并运行nginx容器的命令:
docker run --name containerName -p 80:80 -d nginx
命令解读:
- docker run :创建并运行一个容器
- --name : 给容器起一个名字,比如叫做mn
- -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
- -d:后台运行容器
- nginx:镜像名称,例如nginx
8.4.2.进入mynginx容器
进入我们刚刚创建的mynginx容器的命令为:
docker exec -it mynginx bash
命令解读:
- docker exec :进入容器内部,执行一个命令
- -it : 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互
- mynginx :要进入的容器的名称
- bash:进入容器后执行的命令,bash是一个linux终端交互命令
九、Docker容器监控之 CAdvisor+InfluxDB+Granfana
9.1.docker命令存在的问题
问题:
通过docker stats命令可以很方便的看到当前宿主机上所有容器的CPU,内存以及网络流量等数据,一般小公司够用了。。。
但是,docker stats
统计结果只能是当前宿主机的全部容器
,数据资料是实时的,没有地方存储、没有健康指标过线预警等功能
9.2.CAdvisor监控收集+lnfluxDB存储数据+Granfana展示图表
9.2.1.CAdvisor监控收集
CAdvisor是一个容器资源监控工具,包括容器的内存,CPU,网络IO,磁盘IO等监控,同时提供了一个WEB页面用于查看容器的实时运行状态。
CAdvisor默认存储2分钟的数据,而且只是针对单物理机。不过,CAdvisor提供了很多数据集成接口,支持InfluxDB,Redis,Kafka,Elasticsearch等集成,可以加上对应配置将监控数据发往这些数据库存储起来。
CAdvisor功能主要有两点:
- 展示Host和容器两个层次的监控数据。
- 展示历史变化数据。
9.2.2.lnfluxDB存储数据
InfluxDB是用Go语言编写的一个开源分布式时序、事件和指标数据库,无需外部依赖。
CAdvisor默认只在本机保存最近2分钟的数据,为了持久化存储数据和统一收集展示监控数据,需要将数据存储到InfluxDB中。
InfluxDB是一个时序数据库,专门用于存储时序相关数据,很适合存储CAdvisor的数据。
而且,CAdvisor本身已经提供了InfluxDB的集成方法,丰启动容器时指定配置即可。
lnfluxDB主要功能:
- 基于时间序列,支持与时间有关的相关函数(如最大、最小、求和等);
- 可度量性:你可以实时对大量数据进行计算;
- 基于事件:它支持任意的事件数据;
9.2.3.Granfana展示图表
Grafana是一个开源的数据监控分析可视化平台,支持多种数据源配置(支持的数据源包括
InfluxDB,MySQL,Elasticsearch,OpenTSDB,Graphite等)和丰富的插件及模板功能,支持图表权限控制和报警。
Grafan主要特性:
- 灵活丰富的图形化选项
- 可以混合多种风格。
- 支持白天和夜间模式。
- 多个数据源
9.2.4.总结
9.3.Compose容器编排
9.3.1.新建目录/app/mydocker/cig
mkdir -p /app/mydocker/cig
cd /app/mydocker/cig
pwd
9.3.2.新建3件套docker-compose.yml文件
version: '3.1' # 3.0以上的版本
volumes:
grafana_data: {} # grafana数据的挂载
services:
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
- PRE_CREATE_DB=cadvisorr # 预先在cadvisor创建一个数据库
ports:
- "8083:8083" # web端口
- "8086:8086" # 内部端口
volumes:
- ./data/influxdb:/data
cadvisor:
image: google/cadvisor
links:
- influxdb:influxsrv
command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
restart: always
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
grafana:
user: "104"
image: grafana/grafana
user: "104"
restart: always
links:
- influxdb:influxsrv
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- HTTP_USER=admin
- HTTP_PASS=admin
- INFLUXDB_HOST=influxsrv
- INFLUXDB_PORT=8086
- INFLUXDB_NAME=cadvisor
- INFLUXDB_USER=root
- INFLUXDB_PASS=root
9.3.3.启动docker-compose文件
# 后台启动
docker-compose up -d
# 查看日志
docker-compose logs
9.3.4.查看三个服务器是否启动
9.3.5.测试
9.3.5.1.浏览cAdvisor收集
服务,http://ip:8080/
9.3.5.2.浏览influxdb存储
服务,http://ip:8083/
9.3.5.3.浏览grafana展现
服务,http://ip:3000,默认账号密码admin/admin
9.3.5.3.1. grafana配置步骤
- 配置数据源
- 选择influxdb数据源
- 配置细节
- 配置面板panel
- CAdvisor+InfluxDB+Granfana容器监控系统部署完成
9.3.5.3.2. 配置数据源
9.3.5.3.3. 点击Add data source选择influxdb数据源
9.3.5.3.4. 配置细节
9.3.5.3.5. 配置面板panel
9.3.5.3.6. CAdvisor+InfluxDB+Granfana容器监控系统部署完成
十、终结&总结--知识回顾简单串讲和总结
进阶篇:雷丰阳老师的K8S
评论