Docker高级

2023-06-26,

一、Docker安装企业级开发应用

1、Docker搭建MySQL主从

(1) 创建master主机MySQL
docker run -p 3307:3306 --name mysql-master --privileged=true \
-v /ly/data/mysql-master/log:/var/log/mysql \
-v /ly/data/mysql-master/data:/var/lib/mysql \
-v /ly/mydata/mysql-master/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
(2) 进入/ly/data/mysql-master/conf创建my.cnf
cd /ly/data/mysql-master/conf

vim my.cnf

[mysqld]
## 设置server_id,同一局域网中需要唯
server_id=101
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
##开启二进制日志功能
log-bin=ly-mysql-bin
##设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式 (mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
(3) 进入mysql-master主机容器
docker exec -it 1f654abf60ac bash
(4) docker进入容器出现bash-4.2#解决办法

docker exec -it 1f654abf60ac bash
bash-4.2# cd /etc/skel/
bash-4.2# cp .bash_profile /root/
bash-4.2# cp .bashrc /root/
bash-4.2# exit
docker exec -it 1f654abf60ac bash
(5) 在容器中创建数据同步用户
mysql -uroot -p

CREATE USER'slave'@'%' IDENTIFIED BY '123456';

GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';

show master status;

(6) 创建sleve从机MySQL
docker run -p 3308:3306 --name mysql-slave --privileged=true \
-v /ly/data/mysql-slave/log:/var/log/mysql \
-v /ly/data/mysql-slave/data:/var/lib/mysql \
-v /ly/data/mysql-slave/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
(7) 进入/ly/data/mysql-slave/conf创建my.cnf
cd /ly/data/mysql-slave/conf

vim my.cnf

[mysqld]
## 设置server_id,同一局域网中需要唯
server_id=102
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
##开启二进制日志功能
log-bin=ly-mysql-slave-bin
##设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式 (mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=ly-mysql-relay-bin
##log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
##slave设置为只读(具有super权限的用户除外)
read_only=1

修改了配置文件记得记得重启容器(docker restart 容器ID|容器名)

(8) 在主机mysql-master容器内部查看主从状态
docker exec -it mysql-master bash

mysql -uroot -p

show master status;
(9) 进入mysql-slave容器配置主从
docker exec -it mysql-slave bash

mysql -uroot -p

change master to master_host='192.168.126.159',master_user='slave', master_password='123456',master_port=3307,master_log_file='ly-mysql-bin.000001',master_log_pos=617,master_connect_retry=30;

show slave status \G;

(10) 从数据库(slave-mysql)中开启主从同步
start slave;

(11) 主从测试

主机创建库、创建表、添加数据、查看数据

show databases;

create database db_ly;

use db_ly;

create table t1(id int , name varchar(32));

show tables;

insert into t1(id,name) values(1,"ly");

select id,name from t1;

从机查看表、查看数据

use db_ly;

select id, name from t1;

2、Redis集群版(三主三从)

(1) 快速安装6台Redis容器
# node 1
docker run -d --name redis-node-1 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-1:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6381 # node 2
docker run -d --name redis-node-2 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-2:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6382 # node 3
docker run -d --name redis-node-3 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-3:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6383 # node 4
docker run -d --name redis-node-4 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-4:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6384 # node 5
docker run -d --name redis-node-5 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-5:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6385 # node 6
docker run -d --name redis-node-6 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-6:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6386
(2) 详细的参数解析

(3) 构建6台容器之间的主从关系
# 进入任意容器
docker exec -it redis-node-1 /bin/bash # 执行构建集群命令
redis-cli --cluster create 192.168.126.159:6381 192.168.126.159:6382 192.168.126.159:6383 192.168.126.159:6384 192.168.126.159:6385 192.168.126.159:6386 --cluster-replicas 1

(4) 查看一下集群状态
# 连接一个节点
redis-cli -p 6381 # 查看集群信息
cluster info

# 查看所有节点
cluster nodes

(5) 主从容错切换迁移
# 连接Redis集群
redis-cli -p 6381 -c # 测试一下
set a b

查看集群状态

redis-cli --cluster check 192.168.126.159:6381

stop掉6381的redis-node-1

docker stop redis-node-1

6384从节点成了master

恢复6381,它依旧是从节点

(6) 主从扩容

加入两台6387、6388新的容器

# node 7
docker run -d --name redis-node-7 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-7:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6387 # node 8
docker run -d --name redis-node-8 --net host --privileged=true \
-v /ly/data/redis/share/redis-node-8:/data \
redis:latest --cluster-enabled yes --appendonly yes \
--port 6388

将新增的6387容器作为master加入原来的集群

# redis-cli --cluster add-node IP地址:6387 IP地址:6381
redis-cli --cluster add-node 192.168.126.159:6387 192.168.126.159:6381

可以发现6387没有插槽

分配插槽

redis-cli --cluster reshard 192.168.126.159:6381

可以看出,前面三个节点会每个几点匀出一点槽位,分配给6387

给6387挂载从节点6388

redis-cli --cluster add-node 192.168.126.159:6388 192.168.126.159:6387 --cluster-slave --cluster-master-id 43c48408d8d8e37ce0eed21a5e5f52400d4bcf55

(7) 主从缩容

从集群中删除6388节点

# redis-cli --cluster del-node IP地址:6387 节点ID
redis-cli --cluster del-node 192.168.126.159:6388 1621a95b96c75e6dfb5bee5bc4ea1e397b638bb4

清空6387的槽位全部分配给6381

redis-cli --cluster reshard 192.168.126.159:6381

分配完后,删除6387

redis-cli --cluster del-node 192.168.126.159:6387 43c48408d8d8e37ce0eed21a5e5f52400d4bcf55

二、Dockerfile

(1) 含义

Dockerfile是一种文本文件,用于定义Docker镜像的构建过程。通过Dockerfile文件,开发者可以按照自己的需要指定Docker镜像中的操作系统、应用软件、配置文件和环境变量等。使用Dockerfile可以方便地构建出完全符合自己需求的Docker镜像。

通常来说,一个Dockerfile文件由一系列的指令组成,每个指令都代表着一条构建镜像的操作。例如,指定基础镜像、安装软件包、复制文件、设置环境变量等等。

通过编写Dockerfile文件,我们可以重复使用相同的镜像构建过程,也可以在不同的环境中使用同一份Dockerfile文件,从而实现易于维护和部署的Docker镜像构建流程。

常见的Dockerfile指令包括FROM、LABEL、ADD、RUN、CMD、ENTRYPOINT、ENV、EXPOSE、HEALTHCHECK、USER、WORKDIR等。使用这些指令,开发者可以自定义Docker镜像的构建过程,并生成可重用的Docker镜像。

(2) Docker执行Dockerfile的大致流程

1、docker从基础镜像运行一个容器
2、执行一条指令并对容器做出修改
3、执行类似docker commit的操做提交一个新的镜像层
4、docker再基于刚提交的镜像运行一个新容器
5、执行dockerfile中的下一条指令知道所有指令都执行完成

(3) 站在开发者的角度看待Dockerfile、Docker镜像、Docker容器

1、Dockerfile,需要定义一个Dockerfile, Dockerfle定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等。
2、Dockerj镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker饶像时会真正开始提供服务。
3、Docker容器,容器是直接提供服务的。

(4) Docker常用的保留字指令
1. FROM

基础镜像,当前新镜像是基于那个镜像的,指定一个已经存在的镜像作为模板,第一条必须是FROM

2. MAINTAINER

镜像维护者的姓名和邮箱地址

3. RUN

容器build时需要执行的命令,分为两种形式

shell格式

RUN 命令行命令

exec格式

RUN ["可执行文件","参数1","参数2"]
4.EXPOSE

当前容器对外暴露的端口

5.WORKDIR

指定容器创建后,终端默认登录进来的工作目录

6.USER

指定该镜像以什么样的用户去执行,不指定默认是root

7.ENV

在构建镜像的过程中设置环境变量

8.ADD *

将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包

9.COPY

类似ADD,拷贝文件和目录到镜像中。只不过他不能解压tar压缩包

10.VOLUME

容器数据卷,用于数据保存和持久化

11.CMD *

指定容器启动后要干的事儿

Dockerfile中可以有多个CMD指令,但是只有最后一个生效,CMD会被docker run之后的参数替换

CMD和RUN命令的区别:

CMD是在docker run时运行
RUN是在docker build时运行

12.ENTRYPOINT *

用来指定一个容器启动时要运行的命令

(5) Dockerfile实战案例 :使用Dockerfile自定义一个镜像
1.准备工作

pull一个centos镜像

docker pull centos

进入容器我们去执行vim命令,可以发现没有vim这个命令

docker run -it 5d0da3dc9764 /bin/bash

2.我们的需求基于这个centos让它具备vim+ifconfig+jdk8等这些功能

JKD下载地址:https://mirrors.yangxingzhen.com/jdk/

3.Dockerfile三部曲

编写

创建一个mydockerfile文件夹和Dockerfile文件;D :大写

mkdir /ly/mydockerfile

touch Dockerfile

把前面准备工作中的jdk上传到mydockerfile文件夹中

Dockerfile文件内容如下:

# 指定基础镜像
FROM centos:7
# 指定镜像制作的作者和邮箱
MAINTAINER ly<startqbb@163.com>
# 定义MYPATH变量
ENV MYPATH /usr/local
# 指定容器创建后,终端默认登录进来的工作目录;引用MYPATH
WORKDIR $MYPATH
# 安装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-8u152-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u152-linux-x64.tar.gz /usr/local/java/
# 配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_152
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
# 暴露80端口
EXPOSE 80
# 控制台输出 $MYPATH "---success---OK---"
CMD echo $MYPATH
CMD echo"---success---OK---"
CMD /bin/bash

构建

# 格式:docker build -t 新镜像的名字:TAG .
# 别忘了最后的一个 点
docker build -t centos-java8:1.0 .

运行

docker run -it 84645066ae02

检验一下ifconfig,vim,java时候都构建成功

(6) 构建一个虚悬镜像
1.什么是虚悬镜像?

Docker中的虚悬镜像(dangling image)是指没有被任何Docker容器或镜像所依赖的镜像。当我们构建新的镜像或者删除现有的容器或镜像时,可能会产生一些虚悬镜像。
虚悬镜像不会占据太多磁盘空间,但是会干扰镜像管理和容器部署。通过docker images命令可以查看所有的本地镜像,其中包括虚悬镜像。
如果想要清理虚悬镜像,可以使用docker image prune命令,该命令可以删除所有虚悬镜像、无用的中间镜像和未被使用的缓存数据。
例如,运行下面的命令可以清理掉所有的虚悬镜像:

docker image prune

总而言之,定期清理虚悬镜像可以有效地释放磁盘空间和优化Docker镜像管理。

2.制作流程
# 创建一个testdockerfile目录
mkdir testdockerfile # 创建一个Dockerfile文件
vim Dockerfile # 加入以下内容
FROM ubuntu
CMD echo "---is---success---" # 保存退出
wq # 构建
docker build .

3.我们查看一下虚悬镜像的特征
docker images

4.查看全部的虚悬镜像
docker image ls -f dangling=true

5.删除所有虚悬镜像
docker image prune

(7) Docker微服务实战
1.构建一个微服务

这个简单,就不演示了

package com.ly.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import java.util.UUID; /**
* @author ly (个人博客:https://www.cnblogs.com/qbbit)
* @date 2023-04-22 20:10
* @tags 喜欢就去努力的争取
*/
@RestController
public class HelloController { @Value("${server.port}")
private String port; @GetMapping("/hello")
public String hello() {
return "Hello Dockerfile ---> " + UUID.randomUUID();
} @GetMapping("/port")
public String port() {
return "服务端口为:" + port;
} }

把我们这个jar包上传到linux服务器中

2.通过Dockerfile发布微服务部署到Docker容器
FROM openjdk:8

MAINTAINER ly<startqbb@163.com>
# 指定临时文件目录为/temp,在主机/ver/lib/docker目录下创建一个临时文件并链接到容器的/temp
VOLUME /tmp
# 将jar包添加到容器中并更名为ly_docker.jar
ADD springboot-docker-1.0-SNAPSHOT.jar ly_docker.jar
# 运行jar包
ENTRYPOINT ["java","-jar","ly_docker.jar"]
# 暴露端口
EXPOSE 9001

3.运行容器

docker run -d -p 9001:9001 56e35e11eaf7
4.测试一下:

虚拟机IP:9001/hello

虚拟机IP:9001/port

友情提示:这里会有几个坑

1.我开发使用的是JDK17,而我在Dockerfile中使用的是FROM openjdk:8,这在运行容器的时候会出问题的,最好把maven编译改为JDK8;或者把Dockerfile的FROM openjdk:8 改为 FROM openjdk:17-jdk-alpine
2.在执行mvn package打包时,一定要加上spring-boot-maven-plugin打包插件,具体的区别我就不赘述了,大家可以查一查

三、Docker网络

(1) docker常用网络命令

查看docker网络模式命令

docker network ls

查看网络源数据

docker network inspect 网络名称

创建网络

docker network create 网络名称

删除网络

docker network rm 网络名称

(2) docker网络到底能干嘛?

实现容器件的互联和通信以及端口映射
我们在重启容器的时候容器的IP可能会发生变化,这个时候就可能造成我们容器间的调用出现错误;所以在容器IP发生变换的时候可以通过服务名直接实现网络通信而不受到影响

(3) 网络模式

在说网络模式之前,先看一下容器实例内默认网络IP生成规则

先启动两个redis

docker run -d -p 8888:6379 --name r1 redis:latest

docker run -d -p 9999:6379 --name r2 redis:latest

查看两个容器的网络信息

# docker inspect 容器ID or 容器名字
docker inspect r1 | tail -n 20

# docker inspect 容器ID or 容器名字
docker inspect r2 | tail -n 20

关闭r1新建一个r3,查看IP是不是如我们之前所说会发生改变

可以看到r3的IP变成了之前的r1的

所以docker中的容器IP是可能发生变化的

1.bridge模式:使用--network bridge指定,默认使用docker0

① Docker 服务默认会创建一个 docker0 网桥(其上有-个 docker 内部接口),该桥接网络的名称为ocker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker 接口 的P 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

我们先查看一下bridge网络的详细信息

docker network inspect bridge | grep name

再来看看本机的docker网络

ifconfig

docker启动默认就是初始化一个docker0网桥

② docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址

③ 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。

整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);

每个容器实例内部也有一块网卡,每个接口叫eth0;

docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。

通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的

④ 我们来做个案例演示一下

删除前面创建的两个redis

docker rm -f r2 r3

重新创建两个redis容器,不指定网络模式默认就是bridge

docker run -d -p 9989:6379 --name r1 redis:latest

docker run -d -p 9989:6379 --name r1 redis:latest

先看一下宿主机的网络

ip addr | tail -n 8

进入r1容器

docker exec -it r1 bash

进入r2容器

docker exec -it r2 bash

可以看到网络是一一对应的

etho --- veth

2.host模式:使用--network host指定

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换

演示略......

3.none模式:使用--network none指定

在none模式下,并不为Docker容器进行任何网络配置。

也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo

需要我们自己为Docker容器添加网卡、配置IP等。

演示略......

4.container模式:使用--network container:name|容器ID指定

新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

演示略......

以上四种网络模式的图文解释:

5.自定义网络模式

先看一下不自定义网络会遇到的问题

启动两个tomcat容器

docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8

docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8

查看各自的ip,并且ping另一个容器ip

docker exec -it tomcat81 bash

docker exec -it tomcat82 bash

都是可以ping通的

我们换成服务名试试

都是不可以ping通的

前面我们知道,容器的IP是会变得;所以我们希望通过服务名就能找到对应的容器,而不是写死IP;

这里像极了我们的微服务开发,通过服务名去注册中心找到对应的服务ip:port

那么问题来了,我们需要怎么做呢?来吧展示

首先我们需要自定义一个网络,使用默认的bridge模式

docker network create ly

新建容器并是使用刚才创建的自定义网络ly

记得删除前面测试的两个tomcat

docker run -d -p 8081:8080 --network ly --name tomcat81 billygoo/tomcat8-jdk8

docker run -d -p 8082:8080 --network ly --name tomcat82 billygoo/tomcat8-jdk8

这时候我们再来试一下ping服务名

可以发现是没问题的

结论:自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都可以ping通)

四、Docker-compose容器编排

1、Docker-compose是什么?

Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器

2、Docker-compose能干啥?

docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?

如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具

例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。。。。。。

Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。

可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。

3、安装

官网

安装参考

(1) 下载
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
(2) 设置权限
chmod +x /usr/local/bin/docker-compose
(3) 检查
docker-compose --version
(4) 卸载
sudo rm /usr/local/bin/docker-compose

4、Compose核心概念

一文件:

docker-compose.yml

两要素:

服务:一个个应用容器实例,比如订单微服务、库存为服务、mysql微服务
工程:由一组关联的应用容器组成的一个完整的业务单元,在docker-compose.yml文件中定义

5、使用Docker-compose三个步骤

(1) 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
(2) 使用docker-compose.yml定义一个完整的业务单元,安排好整体应用中的各个容器服务
(3) 最后执行docker-compose up命令,来启动并运行整个应用程序,完成一键部署上线

6、Compose常用命令

7、Docker-compose实战

我们来部署一个基于前面的springboot-docker基础版的升级版,我们来实现一键部署一个微服务(springboot项目+mysql+redis);如果我们不使用docker-compose的话其实也是可以的,就是先部署mysql,再部署redis,再去部署我们写的springboot项目;但是这样就得分三次部署,有点麻烦;所以我们使用docker-compose实现容器编排

(1) 完善我们的springboot-docker项目,加入mysql和redis

具体的功能我就省略了

package com.ly.controller;

import com.ly.domain.Book;
import com.ly.service.BookService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*; import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit; /**
* @author ly (个人博客:https://www.cnblogs.com/qbbit)
* @date 2023-04-27 1:07
* @tags 喜欢就去努力的争取
*/ @RequestMapping("/book")
@RestController
public class BookController { private final BookService bookService;
private final RedisTemplate redisTemplate; public BookController(BookService bookService, RedisTemplate redisTemplate) {
this.bookService = bookService;
this.redisTemplate = redisTemplate;
} private final String oneCacheKey = "book:list:one:";
private final String allCacheKey = "book:list:all"; @GetMapping("/list")
public List<Book> list() {
List<Book> list = (List<Book>) redisTemplate.opsForValue().get(allCacheKey);
if (CollectionUtils.isEmpty(list)) {
list = bookService.list();
redisTemplate.opsForValue().set(allCacheKey, list, 1, TimeUnit.HOURS);
}
return list;
} @GetMapping("/getById/{id}")
public Book getById(@PathVariable("id") Long id) {
Book book = (Book) redisTemplate.opsForValue().get(oneCacheKey + id);
if (Objects.isNull(book)) {
book = bookService.getById(id);
redisTemplate.opsForValue().set(oneCacheKey + id, book, 1, TimeUnit.HOURS);
}
return book;
} @DeleteMapping("/remove/{id}")
public String remove(@PathVariable("id") Long id) {
boolean isSuccess = bookService.removeById(id);
if (isSuccess) {
redisTemplate.delete(oneCacheKey + id);
}
return isSuccess ? "success" : "error";
}
}
(2) 项目打包

(3) 编写docker-compose.yml文件
# 使用docker-compose 3版本
version: "3" services:
# 微服务配置
microService:
image: myapp:1.1
container_name: ly-service
ports:
- "9001:9001"
volumes:
- /ly/mydockercompose/data:/data
networks:
- ly
depends_on:
- redis
- mysql
# redis容器配置
redis:
image: redis:latest
container_name: ly-redis
ports:
- "6379:6379"
volumes:
- /ly/mydockercompose/redis/redis.conf:/etc/redis/redis.conf
- /ly/mydockercompose/redis/data:/data
networks:
- ly
command: redis-server /etc/redis/redis.conf
# mysql配置
mysql:
image: mysql:5.7
container_name: ly-mysql
environment:
MYSQL_ROOT_PASSWORD: 'root'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'qbb01'
MYSQL_USER: 'ly'
MYSQL_PASSWORD: 'root'
TZ: 'Asia/Shanghai'
ports:
- "3306:3306"
volumes:
- /ly/mydockercompose/mysql/db:/var/lib/mysql
- /ly/mydockercompose/mysql/conf/my.cnf:/etc/mysql/my.cnf
# 数据库还原目录 可将需要还原的sql文件放在这里
- /ly/mydockercompose/mysql/source:/docker-entrypoint-initdb.d
networks:
- ly
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
# 配置指定的网络
networks:
ly:
(4) 修改我们项目的application.yml文件
server:
port: 9001
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://ly-mysql:3306/qbb01?serverTimezone=UTC
username: root
password: root
redis:
host: ly-redis
port: 6379
database: 1
password: root mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

(5) 编写Dockerfile文件
# 基础镜像使用java
FROM openjdk:17-jdk-alpine # 作者
MAINTAINER startqbb@163.com # VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp # 将jar包添加到容器中并更名为app.jar
ADD springboot-docker-1.0-SNAPSHOT.jar app.jar # 运行jar包
ENTRYPOINT ["java","-jar","app.jar"] #暴露6001端口作为微服务
EXPOSE 9001
(6) 构建镜像文件
docker build -t myapp:1.1 .

(7) 执行docker-compose
docker-compose up
或者
docker-compose up -d

(8) 测试一下

(9) 关停docker-compose
docker-compose stop

五、Docker轻量级可视化工具Portainer

1. Portainer是什么?

Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。

2. Portainer能干啥?

1.集群管理:支持kubernetes、docker swarm等各类集群,能够非常清楚展示各集群的节点、服务等情况,支持多类集群,集群信息可视化。

2.容器管理:通过Portainer可以轻松地创建、删除、启动、停止和重启容器,还可以查看容器的日志和详细信息。

3.镜像管理:Portainer提供了镜像管理功能,可以管理本地和远程仓库中的镜像,还可以拉取、推送和删除镜像。

4.仓库管理:Portainer支持Docker Hub、Docker Registry和Harbor等多个仓库,并提供了仓库管理功能。5.用户和团队管理:Portainer支持创建用户和团队,并分配不同的权限和角色。

6.仪表盘和监控:Portainer提供了仪表盘和监控功能,可以轻松地查看容器运行状态、资源使用情况、日志等信息。

7.插件和扩展:Portainer支持插件和扩展,可以通过它们扩展和定制Portainer的功能,例如,Portainer提供了Kubernetes插件,可以将Portainer与Kubernetes集成,从而管理Kubernetes集群。

8.存储管理:支持存储卷的查询、增加和删除,非常方便。

9.安全支持:Portainer提供了许多安全性特性,例如基于角色的访问控制、多租户支持、LDAP集成等,使得用户可以安全地管理和监控容器和集群。

3、安装

docker一键安装

docker run -d -p 8000:8000 -p 9000:9000 --name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer

4、登录

直接访问ip:9000,首次登录用户名admin,密码随意12位数即可;登录成功会让你设置密码

5、Portainer常规操作

我就不一一演示了,一句话点点点...

查看docker服务相关信息:docker system df

六、Docker容器监控之CAdvisor+InfluxDB+Granfana

1、原生命令查看docker服务

实时查看当前宿主机上的所有容器的cpu、内存、网络等数据

docker stats -a

# 去掉-a 就是查看正在运行的

2、容器监控三剑客

(1) CAdvisor监控收集

CAdvisor是一个容器资源监控工具,包括容器的内存,CPU,网络IO,磁盘IO等监控,同时提供了一个WEB页面用于查看容器的实时运行状态。CAdvisor默认存储2分钟的数据,而且只是针对单物理机。不过,CAdvisor提供了很多数据集成接门支持InfluxDB,Redis,Kafka,Elasticsearch等集成,可以加上对应配置将监控数据发往这些数据库存储起来。

CAdvisor功能主要有两点

展示Host和容器两个层次的监控数据
展示历史变化数据

(2) InfluxDB存储数据

InfluxDB是用Go语言编写的一个开源分布式时序、事件和指标数据库,无需外部依赖.

CAdvisor默认只在本机保存最近2分钟的数据,为了持久化存储数据和统一收集展示监控数据,需要将数据存储到InfluxDB中。InfluxDB是一个时序数据库,专门用于存时序相关数据,很适合存储CAdvisor的数据。而且,CAdvisor本身已经提供了InfluxDB的集成方法,丰启动容器时指定配置即可。

nfluxDB主要功能:

基于时间序列,支持与时间有关的相关函数(如最大、最小、求和等)
可度量性:你可以实时对大量数据进行计算:
基于事件:它支持任意的事件数据:

(3) Granfana图表展示

Grafana是一个开源的数据监控分析可视化平台,支持多种数据源配置(支持的数据源包括nfluxDB,MySQL,Elasticsearch,OpenTSDB,Graphite等)和丰富的插件及模板功能支持图表权限控制和报警。

Grafan主要特性:

灵活丰富的图形化选项
可以混合多种风格
支持白天和夜间模式
多个数据源

3、使用compose编排一键搭建重量级容器监控

先创建一个cig的目录

mkdir cig

编写docker-compose.yml文件

version: '3'

volumes:
grafana_data: {} services:
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
- PRE_CREATE_DB=cadvisor
ports:
- "8083:8083"
- "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
# 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

执行docker-compose.yml

docker compose up -d

在不指定名字的时候,默认会加上前后缀指定容器名称:cig-cadvisor-1

4、查看各个容器的相关信息

cAdvisor : http://192.168.126.159:8080/

influxdb : http://192.168.126.159:8083/

grafana : http://192.168.126.159:3000/

至此Docker高级的内容整理完毕

Docker高级的相关教程结束。

《Docker高级.doc》

下载本文的Word格式文档,以方便收藏与打印。