基于 Docker 搭建开发环境(一):数据库+监控

基于 Docker 搭建开发环境(一):数据库+监控

基于 Docker 搭建开发环境系列:

去年,很多同事要换 Mac 本,所以,写了 新 Mac 安装软件脚本,方便大家一键换机。最近想玩一下 Spring Cloud 以及相关周边的部署、监控等开源解决方案。由于组件众多及为了便于迁移和共享,计划基于 Docker 及 Docker Compose 搭建一套相关的开发环境。记录一下,方便有相同需求的朋友借鉴。

最新版的 Docker 在下载镜像时,会先访问一下 Docker 的官方站点。由于国内众所周知的网络情况,访问 Docker 官方站点总失败。所以,即使配置了国内 Docker 镜像站点也会失败。只需要将 Docker 软件回滚到 4.30.0 即可。(Mac 下验证有效,其他操作系统待进一步验证。)

MySQL

开发中,最常用的应该就是数据库了。所以,先来搞 MySQL 数据库。

创建如下目录结构,并添加相关相关文件:

$ tree
.
├── README.adoc
├── clean.sh
├── data
│   └── mysql
│       └── .gitkeep
├── docker
│   ├── config
│   │   └── mysql
│   │       └── init.sql
│   ├── env
│   │   └── mysql.env
│   └── images
│       └── mysql.dockerfile
└── docker-compose.yml

先来看一下 docker-compose.yml 文件的内容:

docker-compose.yml
services:
  mysql:
    container_name: mysql
    build:
      context: .
      dockerfile: ./docker/images/mysql.dockerfile
    image: example/mysql:8.4
    env_file:
      - ./docker/env/mysql.env
    volumes:
      - ./data/mysql:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
      interval: 5s
      timeout: 10s
      retries: 10

由于 MySQL 的官方镜像只是提供了数据库软件,需要将初始化脚本在数据库内执行一遍。所以,基于 mysql:8.4 重新构建私有镜像。

./docker/images/mysql.dockerfile
FROM mysql:8.4
ADD ./docker/config/mysql/init.sql /docker-entrypoint-initdb.d/mysql-init.sql (1)
RUN chown -R mysql:mysql /docker-entrypoint-initdb.d/*.sql
EXPOSE 3306
CMD ["mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_0900_ai_ci"]
1特别说明一下:由于 docker-compose.yml 中将构建上下文 context 设置成了“当前目录”,那么构建文件中的目录必须使用以 docker-compose.yml 文件所在目录的相对路径。

通过 ./docker/env/mysql.env 文件设置一下 root 用户的密码:

./docker/env/mysql.env
MYSQL_ROOT_PASSWORD=123456
LANG=C.UTF-8

如果有多个组件依赖 MySQL,还可以通过初始化脚本给各个组件创建不同的数据库、账户和密码。示例初始化脚本如下:

./docker/config/mysql/init.sql
-- https://stackoverflow.com/a/52899915

-- create databases
CREATE DATABASE IF NOT EXISTS `diguage`;

-- create root user and grant rights
-- https://stackoverflow.com/a/16592722
CREATE USER IF NOT EXISTS 'diguage'@'%' IDENTIFIED BY '123456';
GRANT ALL ON diguage.* TO 'diguage'@'%';

FLUSH PRIVILEGES;

登录验证:

$ mysql -h127.0.0.1 -uroot -p123456

NACOS

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个由阿里巴巴推出的,更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

  1. ./docker/config/mysql/init.sql 文件中增加初始化数据库、账户和密码的 SQL 语句。

    CREATE USER IF NOT EXISTS 'nacos'@'%' IDENTIFIED BY '123456';
    GRANT ALL ON nacos.* TO 'nacos'@'%';
  2. 添加数据库初始化脚本 ./docker/config/nacos/mysql-schema.sqldocker/images/mysql.dockerfile 中。

    # https://raw.githubusercontent.com/alibaba/nacos/develop/distribution/conf/mysql-schema.sql
    # 在文件头加一句 SQL:  use nacos;
    ADD ./docker/config/nacos/mysql-schema.sql /docker-entrypoint-initdb.d/nacos-mysql.sql
  3. 添加必要的环境变量配置文件 ./docker/env/nacos.env

    PREFER_HOST_MODE=hostname
    MODE=standalone
    SPRING_DATASOURCE_PLATFORM=mysql
    MYSQL_SERVICE_HOST=mysql
    MYSQL_SERVICE_DB_NAME=nacos
    MYSQL_SERVICE_PORT=3306
    MYSQL_SERVICE_USER=nacos
    MYSQL_SERVICE_PASSWORD=123456
    MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    NACOS_AUTH_IDENTITY_KEY=2222
    NACOS_AUTH_IDENTITY_VALUE=2xxx
    NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789
  4. docker-compose.yml 的内容最后一起给出。下同,不在赘述。

Prometheus

部署好 NACOS,下面来部署一下 Prometheus,并且用 Prometheus 来监控 NACOS。

  1. 增加配置文件: ./docker/config/prometheus/prometheus.yml

    # my global config
    global:
      scrape_interval: 5s # Set the scrape interval to every 5 seconds. Default is every 1 minute.
      evaluation_interval: 5s # Evaluate rules every 5 seconds. The default is every 1 minute.
      # scrape_timeout is set to the global default (10s).
    
    # Alertmanager configuration
    alerting:
      alertmanagers:
        - static_configs:
            - targets:
              # - alertmanager:9093
    
    # Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
    rule_files:
    # - "first_rules.yml"
    # - "second_rules.yml"
    
    # A scrape configuration containing exactly one endpoint to scrape:
    # Here it's Prometheus itself.
    scrape_configs:
      # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
      - job_name: 'prometheus'
    
        # metrics_path defaults to '/metrics'
        # scheme defaults to 'http'.
    
        static_configs:
          - targets: [ 'localhost:9090' ]
    
      - job_name: 'nacos' (1)
        metrics_path: '/nacos/actuator/prometheus'
        static_configs:
          - targets: [ 'nacos:8848' ]
    1接入新应用需要的配置
  2. 由于 NACOS 官方镜像内置的配置文件没有开启 Prometheus 监控。所以,需要增加一个配置文件,并将其挂载到容器里:./docker/config/nacos/application.properties:/home/nacos/conf/application.properties

    # @author D瓜哥 · https://www.diguage.com
    
    # spring
    server.servlet.contextPath=${SERVER_SERVLET_CONTEXTPATH:/nacos}
    server.contextPath=/nacos
    server.port=${NACOS_APPLICATION_PORT:8848}
    server.tomcat.accesslog.max-days=30
    server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i
    server.tomcat.accesslog.enabled=${TOMCAT_ACCESSLOG_ENABLED:false}
    server.error.include-message=ALWAYS
    # default current work dir
    server.tomcat.basedir=file:.
    #*************** Config Module Related Configurations ***************#
    ### Deprecated configuration property, it is recommended to use `spring.sql.init.platform` replaced.
    #spring.datasource.platform=${SPRING_DATASOURCE_PLATFORM:}
    spring.sql.init.platform=${SPRING_DATASOURCE_PLATFORM:}
    nacos.cmdb.dumpTaskInterval=3600
    nacos.cmdb.eventTaskInterval=10
    nacos.cmdb.labelTaskInterval=300
    nacos.cmdb.loadDataAtStart=false
    db.num=${MYSQL_DATABASE_NUM:1}
    db.url.0=jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT:3306}/${MYSQL_SERVICE_DB_NAME}?${MYSQL_SERVICE_DB_PARAM:characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false}
    db.user.0=${MYSQL_SERVICE_USER}
    db.password.0=${MYSQL_SERVICE_PASSWORD}
    ## DB connection pool settings
    db.pool.config.connectionTimeout=${DB_POOL_CONNECTION_TIMEOUT:30000}
    db.pool.config.validationTimeout=10000
    db.pool.config.maximumPoolSize=20
    db.pool.config.minimumIdle=2
    ### The auth system to use, currently only 'nacos' and 'ldap' is supported:
    nacos.core.auth.system.type=${NACOS_AUTH_SYSTEM_TYPE:nacos}
    ### worked when nacos.core.auth.system.type=nacos
    ### The token expiration in seconds:
    nacos.core.auth.plugin.nacos.token.expire.seconds=${NACOS_AUTH_TOKEN_EXPIRE_SECONDS:18000}
    ### The default token:
    nacos.core.auth.plugin.nacos.token.secret.key=${NACOS_AUTH_TOKEN:}
    ### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay.
    nacos.core.auth.caching.enabled=${NACOS_AUTH_CACHE_ENABLE:false}
    nacos.core.auth.enable.userAgentAuthWhite=${NACOS_AUTH_USER_AGENT_AUTH_WHITE_ENABLE:false}
    nacos.core.auth.server.identity.key=${NACOS_AUTH_IDENTITY_KEY:}
    nacos.core.auth.server.identity.value=${NACOS_AUTH_IDENTITY_VALUE:}
    ## spring security config
    ### turn off security
    nacos.security.ignore.urls=${NACOS_SECURITY_IGNORE_URLS:/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**}
    # metrics for elastic search
    management.endpoints.web.exposure.include=*  (1)
    management.metrics.export.elastic.enabled=false
    management.metrics.export.influx.enabled=false
    nacos.naming.distro.taskDispatchThreadCount=10
    nacos.naming.distro.taskDispatchPeriod=200
    nacos.naming.distro.batchSyncKeyCount=1000
    nacos.naming.distro.initDataRatio=0.9
    nacos.naming.distro.syncRetryDelay=5000
    nacos.naming.data.warmup=true
    nacos.console.ui.enabled=true
    nacos.core.param.check.enabled=true
    1新增配置。

验证网页: http://localhost:9090/

Grafana

在监控可视化方面,Grafana 还是更胜一筹。下面以 NACOS 为例,看看如何基于 Prometheus 的监控数据来做监控和报警。

配置方面很简单,只需要将 Grafana 加入到 docker-compose.yml 即可。启动后,主要是在 UI 页面进行操作。

  1. 添加 Prometheus 类型的 Data Source。

  2. 添加监控面板,将 模板 导入即可。

操作细节见 Nacos 监控手册

最后,在提醒一句:常用软件的 Grafana 监控面板不需要自己配置,可以在 Grafana dashboards 页面搜索,选择合适的面板,下载 JSON 导入即可。

Micrometer - Spring Boot 3 DataBase Sample 中看到,可以将配置文件直接挂载到 Grafana 中,这样应该就无需配置。但是,尝试后,数据不通,后续还要再研究一下怎么优化。

验证网页: http://localhost:3000/ , 账户密码: admin/admin

业务应用接入

将业务应用接入到 Prometheus,就需要业务应用将相关监控数据暴露出来。Spring Boot 已经有相关 Starter,可以直接使用。步骤如下:

  1. 添加相关依赖

    <!-- spring-boot-actuator依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- prometheus依赖 -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
  2. 添加相关配置,暴露监测数据端口。配置完成后,启动应用即可在 http://localhost:8081/actuator/prometheus 中看到相关监控数据。

    # application.properties 添加以下配置用于暴露指标
    spring.application.name=diguage-order
    
    management.server.port=8081
    management.endpoints.web.exposure.include=*
    management.metrics.tags.application=${spring.application.name}
  3. ./docker/config/prometheus/prometheus.yml 中配置拉取任务:

    - job_name: 'diguage-order'
      metrics_path: '/actuator/prometheus'
      static_configs:
        - targets: [ 'diguage-order:8848' ] (1)
    1这里的域名与 docker-compose.yml 中配置的名称相对应。
  4. 将应用打包,接入到 docker-compose.yml 中,即可一起启动接入到系统中。

待优化事项

还有一些值得完善和深入研究的话题,列出来,后续再优化:

  1. 目前需要把业务应用打包后配置到 docker-compose.yml 才能启动接入到这套监控体系。如何把网络暴露出来,可以让 IDE 运行中的应用接入到这套监控体系?

  2. 目前如果有新增的应用或服务器,需要手动配置才能接入到 Prometheus 中,如何做到自动接入?

  3. 如何通过配置文件,直接配置好 Grafana 而不是需要手动操作配置?

  4. 如何在应用中接入 OpenTelemetry + Jaeger 监控?

  5. Spring Boot 内置的监控数据是一个 OpenMetrics 标准的实现。深入学习一下。

  6. 怎么使用 Prometheus 监控 MySQL?

  7. 监控数据怎么在 Prometheus,Grafana 和 OpenTelemetry 中共享及存储?

  8. 怎样利用监控数据来助力服务治理,提高系统稳定性?

  9. 看到很多公司,在大数据方面,使用 Apache Doris,不知能否接入进来?

  10. 集成日志采集套装 ElasticSearch + Fluentd + Kibana

这些问题,后续研究后再分享,敬请关注: 基于 Docker 搭建开发环境(二):日志套件

完整的 docker-compose.yml

最后,把 docker-compose.yml 文件完整展示一下:

# @author D瓜哥 · https://www.diguage.com

services:
  # mysql -h127.0.0.1 -uroot -p123456
  mysql:
    container_name: mysql
    build:
      context: .
      dockerfile: ./docker/images/mysql.dockerfile
    image: example/mysql:8.4
    env_file:
      - ./docker/env/mysql.env
    volumes:
      - ./data/mysql:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
      interval: 5s
      timeout: 10s
      retries: 10

  # Nacos: http://127.0.0.1:8848/nacos/
  # http://localhost:8848/nacos/actuator/prometheus
  nacos:
    image: nacos/nacos-server:${NACOS_VERSION:-latest}
    container_name: nacos
    env_file:
      - ./docker/env/nacos.env
    volumes:
      - ./docker/config/nacos/application.properties:/home/nacos/conf/application.properties
      - /tmp/logs/nacos/:/home/nacos/logs
    ports:
      - "8848:8848"
      - "9848:9848"
    depends_on:
      mysql:
        condition: service_healthy
    restart: always

  # Prometheus: http://localhost:9090/
  prometheus:
    image: prom/prometheus:${PROMETHEUS_VERSION:-latest}
    container_name: prometheus
    restart: always
    command:
      - --config.file=/etc/prometheus/prometheus.yml
    volumes:
      - ./docker/config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
    ports:
      - 9090:9090
    depends_on:
      - nacos

  # Grafana: http://localhost:3000/
  # admin/admin
  grafana:
    container_name: grafana
    image: grafana/grafana:${GRAFANA_VERSION:-latest}
    ports:
      - 3000:3000
    restart: on-failure

相关配置已经推送到 GitHub: diguage/develop-env: 基于 Docker 的开发环境,感兴趣欢迎围观。