Docker 杂记

基础知识

docker的优点

  1. 轻量级和可移植性:Docker 容器非常轻量级,相比于虚拟机,它们只需要少量的系统资源和运行时间,因此可以更快地启动和停止。Docker 容器还具有高度的可移植性,可以在任何运行 Docker 的环境中运行,无需担心依赖项或环境配置的问题。
  2. 简化应用程序部署:Docker 使得应用程序的部署和管理变得更加简单和可靠。通过将应用程序和其依赖项打包成容器,可以将其轻松地部署到任何环境中,从而简化了整个部署过程。
  3. 高度可定制性:Docker 允许用户构建自己的 Docker 镜像,并根据自己的需求进行自定义配置。这使得用户可以根据自己的应用程序需求创建定制化的容器镜像,提高了应用程序的可定制性和可扩展性。
  4. 容器化应用程序隔离:Docker 容器提供了隔离的环境,可以避免应用程序之间的冲突和干扰。每个容器都具有自己的文件系统、网络、内存和 CPU 资源,可以有效地隔离应用程序之间的资源。
  5. 更好的资源利用率:Docker 容器可以共享主机操作系统的内核和其他资源,从而提高了资源利用率。与虚拟机相比,Docker 容器可以更高效地使用系统资源,从而提高了系统的性能和稳定性。

docker 的缺点

容器只是一种特殊的进程,通过Linux Namespace机制进行隔离,但是隔离得不彻底,多个容器使用的还是同一个宿主机的操作系统内核:

  1. 在容器里执行 top 指令,显示的信息居然是宿主机的 CPU 和内存数据,而不是当前容器的数据
  2. 很多资源和对象无法被 Namespace 化,如“时间”

Docker Compose 和 Dockerfile 的区别?

  • Dockerfile:是一种文本文件格式,用于定义 Docker 镜像的构建过程。Dockerfile 中包含了一系列的指令,用于指定基础镜像、安装软件、拷贝文件、配置环境变量等操作,最终生成一个新的 Docker 镜像。Dockerfile 可以通过 docker build 命令执行,将 Dockerfile 转换为 Docker 镜像。
  • FROM,该命令指定基于哪个基础镜像,因为你要指定一个基础镜像才能基于这个镜像之上进行其他操作,DockerFile第一条必须为From指令。如果同一个DockerFile创建多个镜像时,可使用多个From指令(每个镜像一次)
  • MAINTAINER,指定作者信息
  • CMD
  • EXPOSE ,这个是用来暴露端口的
  • ENV ,是用于定义环境变量
  • VOLUME,这个是用来指定挂载点(也可以在idea配置)
  • Docker Compose:用于定义和运行多个容器之间的依赖关系。Docker Compose 中使用 YAML 文件格式,定义了应用程序中的服务、网络、卷等资源,以及它们之间的关系和依赖关系。Docker Compose 可以通过 docker-compose 命令集成管理多个容器。

需要注意的是,Docker Compose 和 Dockerfile 并不是互斥的概念,它们可以一起使用,Dockerfile 定义容器镜像的构建过程,Docker Compose 定义容器之间的关系和依赖关系,可以一起协同工作,实现更加灵活和高效的容器化应用部署和管理。

使用教程

首先给服务器上的防火墙打开端口:

1. portainer(可视化工具)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# docker搜索
docker search portainer
# docker拉取镜像
docker pull portainer/portainer:latest
# 创建数据卷
docker volume create portainer_data
# 运行容器
docker run --name portainer -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer 
#-d #容器在后台运行
#-p 9000:9000 # 宿主机9000端口映射容器中的9000端口
#-v /var/run/docker.sock:/var/run/docker.sock # 把宿主机的Docker守护进程(docker daemon)默认监听的Unix域套接字挂载到容器中
#-v # 把宿主机目录 挂载到 容器目录;
#–-name portainer # 指定运行容器的名称

cd /usr/libexec/docker/
sudo ln -s docker-runc-current docker-runc

参考:https://cloud.tencent.com/developer/article/1867994

2. 生成ca证书

1
2
3
4
5
6
7
8
# 创建ca证书
mkdir -p /usr/local/ca
cd /usr/local/ca/
# 创建密码(输入两次)
openssl genrsa -aes256 -out ca-key.pem 4096
# 依次输入密码、国家、省、市、组织名称等,输入‘.’略过
# common name 填服务器ip
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 生成服务端的 server-key.pem
openssl genrsa -out server-key.pem 4096
# ip地址改成自己的ip或域名
openssl req -subj "/CN=124.221.157.240" -sha256 -new -key server-key.pem -out server.csr
# 配置任何ip+证书可以访问
echo subjectAltName = IP:124.221.157.240,IP:0.0.0.0 >> extfile.cnf
# 密钥仅用于服务器身份验证
echo extendedKeyUsage = serverAuth >> extfile.cnf
# 生成签名证书
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
# 生成客户端的 key.pem
openssl genrsa -out key.pem 4096
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
# 密钥适用于客户端身份验证
echo extendedKeyUsage = clientAuth > extfile-client.cnf
# 生成签名证书 以下表示365天,可以改成更长
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cnf
# 删除过程中产生的配置文件
rm -v client.csr server.csr extfile.cnf extfile-client.cnf
# 密钥读写权限改为读取权限
# from 0600 (rw-------) to 0400 (r--------)
chmod -v 0400 ca-key.pem key.pem server-key.pem
# 证书对外可读,也关闭写权限
# from 0644 (rw-r--r--) to 0444 (r--r--r--)
chmod -v 0444 ca.pem server-cert.pem cert.pem
# 复制证书
cp server-*.pem  /etc/docker/
cp ca.pem /etc/docker/
# docker 关联 ca
vim /usr/lib/systemd/system/docker.service
# 修改ExecStart这一行(添加没有的部分,不删除原有的)
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/etc/docker/ca.pem --tlscert=/etc/docker/server-cert.pem --tlskey=/etc/docker/server-key.pem -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock
# 重新加载 daemon 
systemctl daemon-reload 
# 重启 docker
systemctl restart docker
# 开放2376端口
/sbin/iptables -I INPUT -p tcp --dport 2376 -j ACCEPT
# 回到主机,把ca文件拷贝出来,-r 表示拷贝文件夹
scp -r ubuntu@124.221.157.240:/usr/local/ca ~/Desktop
# 这里遇到了 key.pem 无法拷贝的问题,临时修改了权限
chmod -v 0444 key.pem
# 单独拷贝
scp ubuntu@124.221.157.240:/usr/local/ca/key.pem ~/Desktop/ca
# 再改回来
chmod -v 0400 key.pem
# 测试证书接口
curl https://124.221.157.240:2376/info --cert ./cert.pem --key ./key.pem --cacert ./ca.pem

证书生效了。

参考:https://blog.csdn.net/qq_46126559/article/details/118373855

3. idea部署https

resources/docker/dockerFile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#FROM openjdk:8-jdk-alpine
FROM java:8

RUN mkdir -p /apps
# app改成项目名称
ADD *.jar /apps/app.jar

#对外暴露的端口
EXPOSE 8801

# 环境、jar包路径 
# app改成项目名称
ENTRYPOINT  ["java","-Xmx512m","-Xms512m","-Dspring.profiles.active=test","-jar","/apps/app.jar"]
RUN echo "Asia/shanghai" > /etc/timezone;
ENV LANG C.UTF-8

https://124.221.157.240:2376

选择包含cert.pem、key.pem、ca.pem 的文件夹

/etc/localtime

/usr/share/zoneinfo/Asia/Shanghai

/apps

4. Redis 安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 拉镜像
docker pull redis:latest
# 挂载目录
mkdir /docker-data/redis
cd /docker-data/redis
# 下载redis.conf
wget http://download.redis.io/redis-stable/redis.conf
# 权限
chmod 777 redis.conf
# 修改配置信息
vim /docker-data/redis/redis.conf
1
2
3
4
5
bind 127.0.0.1 # 这行要注释掉,解除本地连接限制
protected-mode no # 默认yes,如果设置为yes,则只允许在本机的回环连接,其他机器无法连接。
daemonize no # 默认no 为不守护进程模式,docker部署不需要改为yes,docker run -d本身就是后台启动,不然会冲突
requirepass 123456 # 设置密码
appendonly yes # 持久化
1
2
3
4
5
6
#启动
docker run --name redis \
-p 6379:6379 \
-v /docker-data/redis/redis.conf:/etc/redis/redis.conf \
-v /docker-data/redis:/data \
-d redis redis-server /etc/redis/redis.conf --appendonly yes

参考:https://cloud.tencent.com/developer/article/1997596

5. minio 配置

1
2
3
4
5
6
7
8
9
docker run -p 9090:9090 -p 9091:9091 \
     --name minio \
     -d --restart=always \
     -e "MINIO_ACCESS_KEY=minioadmin" \
     -e "MINIO_SECRET_KEY=Josh@123" \
     -v /home/minio/data:/data \
     -v /home/minio/config:/root/.minio \
     minio/minio server \
     /data --console-address ":9091" -address ":9090"

9090是服务端口,9091是可视化界面

监控系统

如何配置监控系统?

  1. 安装:编写一个 docker-compose.yml 文件,指定 Prometheus 和 Grafana 的镜像、端口等配置,然后使用 docker-compose up 命令启动 Prometheus 和 Grafana 。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3'

services:
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus:/etc/prometheus/
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
    restart: always

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - ./grafana:/var/lib/grafana
    restart: always
  1. 配置 Prometheus:在 Prometheus 的配置文件中,指定需要监控的目标(例如 Java 应用程序),以及需要采集的指标(例如 JVM 内存使用情况、线程池情况等)。可以使用 Prometheus 的查询语言(PromQL)查询指标,在Grafana面板中,点击“Apply”或“Refresh”按钮,即可执行查询语句并显示结果
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 获取实例的 CPU 使用率:
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# 获取实例的内存使用率:
100 - ((node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) / node_memory_MemTotal_bytes) * 100

# 获取实例的磁盘使用率:
100 - (avg by (instance) (node_filesystem_free_bytes{fstype="ext4"} / node_filesystem_size_bytes{fstype="ext4"}) * 100)

# 获取 HTTP 请求数量:
sum by (job) (http_requests_total)

# 获取 HTTP 请求错误数量:
sum by (job, status) (http_requests_total{status=~"5.."})

# 获取 HTTP 请求响应时间的平均值:
avg by (job) (http_request_duration_seconds)

# 获取 HTTP 请求响应时间的 90% 百分位数:
histogram_quantile(0.9, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

# 获取容器 CPU 使用率:
sum by (container) (rate(container_cpu_usage_seconds_total{container!="POD"}[5m])) * 100

# 获取容器内存使用率:
sum by (container) (container_memory_usage_bytes{container!="POD"}) / sum by (container) (container_spec_memory_limit_bytes{container!="POD"}) * 100
  1. 配置 Grafana:在 Grafana 中,选择“Prometheus”作为数据源类型,创建仪表盘和面板,使用 Grafana 的查询语言(GQL)查询指标,并将查询结果可视化展示。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 获取指定时间范围内的监控数据
SELECT mean("metric_name") FROM "measurement_name" WHERE time >= now() - 1h GROUP BY time(1m) fill(null)

# 数据聚合和重采样
SELECT sum("metric_name") FROM "measurement_name" WHERE time >= now() - 1d GROUP BY time(1h) fill(0)

# 根据标签过滤数据
SELECT mean("metric_name") FROM "measurement_name" WHERE "tag_name" =~ /tag_value/ AND time >= now() - 1h GROUP BY time(1m) fill(null)

# 获取最新的监控数据
SELECT last("metric_name") FROM "measurement_name" WHERE time >= now() - 1h GROUP BY "tag_name" fill(null)

# 数据去重
SELECT distinct("tag_name") FROM "measurement_name"

# 计算两个指标之间的关系
SELECT derivative("metric1_name") / derivative("metric2_name") * 100 FROM "measurement_name" WHERE time >= now() - 1h GROUP BY time(1m) fill(null)

# 计算指标的百分位数
SELECT percentile("metric_name", 95) FROM "measurement_name" WHERE time >= now() - 1h GROUP BY time(1m) fill(null)

Springboot 集成 grafana + Prometheus 获得 99 线和 999 线?

  1. 添加依赖

在 Spring Boot 项目的 pom.xml 文件中添加以下依赖:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
    <version>1.5.9</version>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
    <version>1.5.9</version>
</dependency>

其中,micrometer-core 是 Micrometer 库的核心依赖,micrometer-registry-prometheus 是 Micrometer 的 Prometheus 注册表依赖。

  1. 配置 Prometheus 注册表

在 Spring Boot 项目的配置文件 application.yml 中添加 Prometheus 注册表配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
management:
  endpoints:
    web:
      exposure:
        include: "*"
  metrics:
    tags:
      application: ${spring.application.name}
    distribution:
      percentiles-histogram:
        http.server.requests: true
    export:
      prometheus:
        enabled: true
        step: 10s

其中,management.endpoints.web.exposure.include 配置开启所有的监控端点;management.metrics.tags.application 配置应用程序名称;management.metrics.export.prometheus.enabled 配置开启 Prometheus 注册表;management.metrics.export.prometheus.step 配置采集数据的时间间隔。

  1. 配置 Grafana 数据源

在 Grafana 中添加 Prometheus 数据源,并配置数据源 URL。例如,Prometheus 数据源 URL 可以配置为 http://localhost:9090。

  1. 创建 Dashboard

在 Grafana 中创建 Dashboard,并配置 Panel。例如,可以添加一个新的 Graph Panel,然后在 Metrics 选项卡中选择 http.server.requests 指标并设置 percentiles 为 0.99 和 0.999。

  1. 查看 99 线和 999 线

在创建好的 Dashboard 中,可以查看 http.server.requests 指标的 99 线和 999 线。这些线条将显示在 Graph Panel 中,并显示当前时间范围内的 99% 和 99.9% 的请求响应时间。 以上是 Spring Boot 集成 Grafana 和 Prometheus 并获取 99 线和 999 线的步骤。需要注意的是,具体的配置和实现可能因应用程序的具体情况而异,需要根据具体情况进行调整和修改。

动态配置限流阈值

要根据 Grafana 监控数据动态更改限流阈值,可以使用 Grafana 的 Alerting 功能和 Spring Boot 的 Actuator 端点,具体步骤如下:

  1. 创建 Grafana Alerting 规则

在 Grafana 中创建 Alerting 规则,以监控请求响应时间超过 999 线的情况。具体步骤如下: a. 在 Grafana 中创建一个 Dashboard,并添加一个 Graph Panel。 b. 在 Metrics 选项卡中选择 http.server.requests 指标,并设置 percentiles 为 0.999。 c. 在 Alert 选项卡中创建一个 Alerting 规则,当请求响应时间超过 999 线时触发。 d. 在 Notifications 选项卡中配置 Alert 的通知方式,例如邮件通知。

  1. 创建 Actuator 端点

在 Spring Boot 应用程序中创建 Actuator 端点,用于接收来自 Grafana 的 Alerting 通知,并动态更改限流阈值。具体步骤如下: a. 创建一个实现 Actuator Endpoint 接口的类,用于接收来自 Grafana 的 Alerting 通知。

1
2
3
4
5
6
7
8
@Component
    @Endpoint(id = "my-endpoint")
    public class MyEndpoint {
        @WriteOperation
        public void setRateLimit(@Selector String route, int rateLimit) {
            // 设置限流阈值,例如使用 Redis 缓存保存阈值。
        }
    }

其中,setRateLimit 方法用于设置限流阈值,例如使用 Redis 缓存保存阈值。 b. 在 Spring Boot 应用程序的配置文件 application.yml 中开启 Actuator 端点。

1
2
3
4
5
6
7
8
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    my-endpoint:
      enabled: true
  1. 实现限流器

在 Spring Boot 应用程序中实现限流器,用于根据 Actuator 端点中保存的阈值进行限流。例如,可以使用 Spring Cloud Gateway 的 RequestRateLimiter 过滤器进行限流。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
    public class RateLimiterGatewayFilterFactory extends AbstractGatewayFilterFactory<RateLimiterGatewayFilterFactory.Config> {

        // 从 Redis 缓存中获取限流阈值
        private int getRateLimit(String route) {
            // 例如使用 Redis 缓存保存阈值。
        }

        public RateLimiterGatewayFilterFactory() {
            super(Config.class);
        }

        @Override
        public GatewayFilter apply(Config config) {
            return (exchange, chain) -> {
                String route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
                int rateLimit = getRateLimit(route);
                // 使用 RequestRateLimiter 过滤器进行限流
                return new RequestRateLimiterGatewayFilterFactory().apply(new RequestRateLimiterGatewayFilterFactory.Config().setRateLimiter(new RedisRateLimiter(rateLimit, rateLimit, Duration.ofSeconds(1))));
            };
        }

        public static class Config {
            // 配置
        }
    }

其中,RateLimiterGatewayFilterFactory 是自定义的限流器,使用 Redis 缓存保存限流阈值;getRateLimit 方法用于从 Redis 缓存中获取限流阈值;使用 RequestRateLimiter 过滤器进行限流。

  1. 测试

启动 Spring Boot 应用程序,然后在 Grafana 中创建一个 Alerting 规则,当请求响应时间超过 999 线时触发。当 Alert 触发时,Grafana 会向 Actuator 端点发送通知,Actuator 端点会动态更改限流阈值。限流器会根据新的限流阈值进行限流,从而有效控制请求的并发量。 以上是根据 Grafana 监控数据动态更改限流阈值的步骤。需要注意的是,具体的实现和配置可能因应用程序的具体情况而异,需要根据具体情况进行调整和修改。

docker 容器模式

  1. 默认桥接模式(bridge):Docker会为每个容器分配一个IP地址,并创建一个本地的网络桥接接口,连接所有在该主机上运行的容器。通过桥接接口,容器可以相互通信,也可以与主机上的其他服务通信。桥接模式适用于单主机多容器的情况。
  2. 主机模式(host):在主机模式下,容器与主机共享网络命名空间。容器将直接使用主机的网络接口,而不是创建独立的网络栈。这意味着容器可以使用主机上的所有网络功能和服务,包括使用主机的IP地址和端口。主机模式适用于需要容器与主机之间网络性能最大化的场景。

Grafana、Prometheus 如何获取 JVM 的信息?

Prometheus获取JVM数据的一种常用方式是使用JMX Exporter。以下是使用JMX Exporter收集JVM数据的基本步骤:

  1. 下载和配置JMX Exporter:首先,您需要从JMX Exporter的GitHub存储库(https://github.com/prometheus/jmx_exporter ↗)下载JMX Exporter的JAR文件。然后,创建一个配置文件(例如jmx_exporter.yml),用于指定要收集的JMX指标和其他相关配置。

  2. 配置JMX Exporter的目标:在jmx_exporter.yml配置文件中,您需要定义JMX Exporter的目标。目标是JVM实例的JMX连接信息,包括JMX URL、认证凭据、要暴露的JMX指标等。

  3. 启动JMX Exporter:使用以下命令启动JMX Exporter,指定配置文件路径:

    1
    2
    3
    
    java -javaagent:/path/to/jmx_exporter.jar=8080:/path/to/jmx_exporter.yml
    ```
    这将在端口8080上启动JMX Exporter,并使用配置文件中指定的设置。
  4. 配置Prometheus收集JMX Exporter的数据:在Prometheus的配置文件(prometheus.yml)中,添加以下内容以指定JMX Exporter的目标:

    1
    2
    3
    4
    5
    6
    
    scrape_configs:
      - job_name: 'jmx_exporter'
        static_configs:
          - targets: ['localhost:8080']
    ```
    这将告诉Prometheus在本地地址的8080端口上收集JMX Exporter的数据。
  5. 重启Prometheus:重启Prometheus服务器,使其加载新的配置。

  6. 在Grafana中创建仪表盘:在Grafana中,您可以创建仪表盘和面板来查询和可视化JMX指标。使用Prometheus作为数据源,并使用PromQL查询语言选择和配置要显示的JMX指标。

通过这些步骤,Prometheus将会通过JMX Exporter从JVM中收集指定的JMX指标数据。您可以在Prometheus中查询和分析这些数据,并在Grafana中创建仪表盘来展示JVM的性能指标和其他相关信息。请注意,具体的配置和设置可能会因使用的JMX Exporter版本和需求而有所不同,建议参考JMX Exporter和Prometheus的官方文档以获得更详细的信息和指导。

0%