1. 安装 Docker

官方教程:https://docs.docker.com/engine/install/centos/

  1. 安装 gcc 和 gcc++;

    1
    2
    yum -y install gcc
    yum -y install gcc-c++
  2. 设置阿里云仓库;

    1
    2
    3
    4
    sudo yum install -y yum-utils
    sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
  3. 更新 yum 软件包 和 索引;

    1
    yum makecache fast //将软件包信息提前在本地索引缓存,用来提高搜索安装软件的速度
  4. 安装 Docker 引擎;

    1
    sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
  5. 启动 Docker;

    1
    sudo systemctl start docker
  6. 查看 Docker 板本;

    1
    docker version
  7. 配置阿里云镜像加速;

    进入地址:https://cr.console.aliyun.com/cn-qingdao/instances/mirrors,登录后,根据当前的系统按照下方的步骤操作即可。

2. 安装 mysql

  1. 拉取 mysql 镜像;

    1
    docker pull mysql:latest

    默认拉取的是 mysql:latest 板本,也就是 8.x 板本的 mysql,你可以在https://hub.docker.com/ 搜索 mysql,确定最新 mysql 的版本号,如果想拉取老版本,只需面指定板本号,如:

    1
    >docker pull mysql:5.7
  2. 创建挂载目录;

    1
    mkdir -p /usr/local/mysql/log /usr/local/mysql/data /usr/local/mysql/conf
  3. 启动 mysql 容器;

    1
    2
    3
    4
    5
    6
    docker run -d --restart=always --name="mysql" -p 3306:3306 --privileged=true \
    -v /usr/local/mysql/log:/var/log/mysql \
    -v /usr/local/mysql/data:/var/lib/mysql \
    -v /usr/local/mysql/conf:/etc/mysql/conf.d \
    -e MYSQL_ROOT_PASSWORD=12345678 \
    mysql:latest

    —privileged=true:以特权方式创建容器,宿主机拥有真正的 root 权限去操作容器,比如挂载目录

    -p:端口映射,前一个为主机端口,后一个为容器端口

    -v:挂载主机目录到容器内目录

    -e:设置参数

    —restart=always:适用于后台启动(-d参数)的容器,容器退出时总是重启容器,不能与 —rm 同时使用

    —rm:适用于前台启动的容器,容器退出后会被删除,一般用于测试(也就是一些用完即删的容器)

  4. 进入/usr/local/mysql/conf,新建my.cnf,写入以下内容,通过容器卷同步给 mysql 实例,解决乱码问题;

    1
    2
    3
    4
    5
    [client]
    default_character_set=utf8mb4
    [mysqld]
    collation_server=utf8mb4_general_ci
    character_set_server=utf8mb4
  5. 进入容器,查看是否可以通过 root 用户远程登录;

    • 进入容器并登录 mysql;

      1
      2
      docker exec -it mysql bash
      mysql -uroot -p
    • 查看访问权限信息。

      1
      SELECT user,host,plugin FROM mysql.user;

      主要查看是否存在 root 用户,并且对应的 HOST 为 %,% 代表允许所有的 IP 使用该用户登录。

      由上面得知,使用 docker 安装的 mysql 默认是可以通过 root 用户远程登录的。

  6. 如果不存在,要创建远程登录的用户,否则远程连接不上 mysql;

    1
    CREATE USER 'root'@'%' IDENTIFIED BY 'your_password';
  7. 赋予当前用户所有权限给上面创建的 root 用户;

    1
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
  8. 刷新权限表;

    1
    FLUSH PRIVILEGES;
  9. 服务器防火墙放行 3306 端口用于远程连接 mysql,阿里云的服务器需要同时开放端口和安全组;

  10. 使用 mysql 客户端连接测试;

    常见问题:

    • 拒绝连接;

      解决办法:按照上面的步骤创建远程登录用户并授权。

    • 报错 “plugin caching_sha2_password could not be loaded”;

      原因:mysql 8.x 的更新了密码验证方式为 caching_sha2_passoword,一些比较老的 mysql 客户端由于驱动未更新,连接时使用的方式还是以前的 mysql_native_password,这时就可能报这个错误。

      解决办法:修改该用户的密码验证方式。

      1
      ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
    • 远程连接很慢;

      解决办法:

      在 mysql 的配置文件my.cnf下,添加如下配置后重启 mysql 容器

      1
      2
      3
      [mysqld]
      # ...
      skip-name-resolve # 禁用 dns 解析,添加此项后在 mysql 授权表中的 host 字段的就不能使用域名了,只能使用 IP
  11. MYSQL 安全配置;

    • 修改云服务器 Docker 版 MYSQL 的密码(不要过于简单,如 12345678 这种);

      1
      2
      3
      4
      docker exec -it mysql bash
      mysql -uroot -p
      ALTER USER 'root'@'%' IDENTIFIED BY 'your_password';
      docker restart mysql
    • 限制 IP 访问(经过测试,最好还是不要限制,密码设置复杂一点就好,因为你所在的公网 IP 随时会变)。

3. 安装 redis

  1. 拉取 redis 镜像;

    1
    docker pull redis
  2. 创建挂载目录;

    1
    mkdir -p /usr/local/redis
  3. 先运行 redis 容器,查看当前 redis 的板本;

    • 运行 redis 容器;

      1
      docker run -d --name redis --rm redis
    • 查看 redis 板本。

      1
      docker exec -it redis redis-server -v

      我这里显示的是 6.2.6 板本

    • 停止容器。

      1
      docker stop redis
  4. 去官网下载对应板本的 redis 配置文件,拷贝到/usr/local/redis目录下;

    官网:https://github.com/redis/redis/

    小技巧:链接中的 github 改为github1s可以打开 github 的 vscode 板本,可以很方便的下载和复制单个文件,如:

    https://github1s.com/redis/redis/blob/6.2.6/redis.conf

  5. 修改上面下载的文件;

    1
    vim /usr/local/redis/redis.conf

    修改内容如下:

    1
    2
    3
    4
    #bind 127.0.0.1 -::1 //允许外部连接
    requirepass xxxxxx //开启密码验证
    daemonize no //后台启动改为no,因为会与 dokcer的 -d参数冲突
    protected-mode no //关闭保护模式
  6. 运行 redis 容器;

    1
    2
    3
    4
    5
    docker run -d --restart=always --name redis -p 6379:6379 --privileged=true \
    -v /usr/local/redis/redis.conf:/etc/redis/redis.conf \
    -v /usr/local/redis/data:/data \
    redis redis-server /etc/redis/redis.conf \
    --appendonly yes
  7. 远程连接测试(工具:dbeaver);

4. 安装 nacos(单机版)

  1. 根据 SpringBoot 的板本找到对应的 Spring Cloud Alibaba Version ,再找到对应的 nacos 板本(重要);

    板本对照地址:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

    我的 SpringBoot 板本是 2.2.1.RELEASE,对应的 Spring Cloud Alibaba Version 为 2.2.0.RELEASE,再找到对应的 nacos 板本为 1.1.4。

  2. 拉取使用的 nacos 镜像;

    1
    docker pull nacos/nacos-server:1.1.4
  3. 启动 nacos;

    1
    docker run -d --name nacos -e MODE=standalone -p 8848:8848 nacos/nacos-server:1.1.4
  4. 访问测试;

    访问地址:http://ip:8848/nacos/index.html

    以这种方式启动的 nacos,如果 nacos 容器被删除,那么 nacos 上所有的配置就丢失了(重启 nacos 容器并不会导致数据丢失),在实际开发中不可取,下面开始 nacos 数据持久化。

  5. 配置数据库;

  6. 启动 nacos 容器;

    下面分别是以参数和挂载配置文件的方式启动 nacos 容器,推荐使用参数方式启动(更简单)。

    • 直接启动,所有参数在命令行中;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      docker run -d --restart=always --name nacos -p 8848:8848 --privileged=true \
      -e JVM_XMS=256m -e JVM_XMX=256m -e MODE=standalone \
      -e SPRING_DATASOURCE_PLATFORM=mysql \
      -e MYSQL_MASTER_SERVICE_HOST=172.17.0.2 \
      -e MYSQL_MASTER_SERVICE_PORT=3306 \
      -e MYSQL_MASTER_SERVICE_USER=root \
      -e MYSQL_MASTER_SERVICE_PASSWORD=12345678 \
      -e MYSQL_MASTER_SERVICE_DB_NAME=nacos \
      -e MYSQL_SLAVE_SERVICE_HOST=172.17.0.2 \
      nacos/nacos-server:1.1.4

      注意:上面的 MYSQL_MASTER_SERVICE_HOST 配置可以填写以下三种地址:

      • mysql 所在主机的公网 IP地址;

      • mysql 所在主机的内网 IP 地址;

      • 如果是 Docker 安装的 mysql,还可以填写 mysql 容器的 IP,下面是查看容器 IP 的方法;

        • 第一种(只显示 IP)

          1
          docker inspect --format '{{ .NetworkSettings.IPAddress }}' IDS/NAMES

          或者查看全部容器的 IP

          1
          docker inspect -f '{{.Name}} - {{.NetworkSettings.IPAddress }}' $(docker ps -aq)
        • 第二种(显示容器所有信息,找到 IPAddress)

          1
          docker inspect ID/NAME

      注意:最开始配置的是 MYSQL_SERVICE_xxx,后面发现行不通,MYSQL_MASTER_SERVICE_xxx 是 nacos 1.1.4 的配置的方式,同时 nacos 1.1.4 板本必须配置 MYSQL_SLAVE_SERVICE_HOST(可以和 MYSQL_MASTER_SERVICE_HOST 配置一致),否则 nacos 不能正常使用。如果不想配置此项,你可以修改容器中的/home/nacos/conf/application.properties,将其中 db.url.1 注释掉,再重启 nacos 容器。具体各个 nacos 板本该如何配置可以查看各个板本的/home/nacos/conf/application.properties文件。

      出现问题:

      1
      >Cannot create PoolableConnectionFactory(Could not create connection to database server...)

      原因:在确认我连接的参数没有设置错误的情况下,最终确定问题是 nacos 1.1.4 内置的 mysql-connector-java 板本太低,从 nacos 1.3.1 板本才开始支持 MYSQL 8.x。

      解决办法:

      1. 降低 MYSQL 板本为 5.x(不实用,毕竟已经安装了 MYSQL 8.x);

      2. 升级 nacos(排除,项目使用的板本只能是 nacos 1.1.4);

      3. 更换 nacos 使用的 mysql-connector-java 板本。

        • 查看 Docker 板的 mysql 板本;
        1
        docker exec -it mysql mysql -uroot -p12345678 mysql -V
        • 下载对应板本的 mysql-connector-java-8.0.x.jar,并放到 nacos 容器内的/home/nacos/plugins/mysql目录下,然后重启 nacos。

        下载地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java

      出现问题:

      1
      MySql Data source rejected establishment of connection, message from server: "Too many connections"

      原因:当前 mysql 连接数过多导致无法建立新连接(根本原因,以前建立的连接并未关闭,这个也是 mysql 经典的 8 小时问题)

      解决步骤:

      1. 查看 mysql 当前的连接数;

        1
        show status like 'Threads%'; #我查出当前连接数是 152
      2. 查看 mysql 支持的最大连接数(默认 151,实际还会保留一个给 root ,也就是 152);

        1
        show variables like '%max_connections%';
      3. 临时设置最大连接数,MYSQL 重启后该设置就没了(无须重启 MYSQL,但治标不治本);

        1
        set GLOBAL max_connections=500 # 设置为多大根据 MSYQL 服务器实际承载能力动态调整
      4. 在 mysql my.cnf配置文件中配置max_connections(配置完毕后需要重启 MYSQL);

        1
        2
        3
        [mysqld]
        #...
        max_connections=500
      5. 最终大法,my.cnf配置文件中配置wait_timeoutinteractive_timeout

        1
        2
        3
        [mysqld]
        wait_timeout=10
        interactive_timeout=10
        • wait_timeout:设置非交互连接(指连接池、jdbc等方式)的超时时间,默认是28800,就是8小时,超过这个时间,mysql 服务器会主动切断那些已经连接的,但是状态是 sleep 的连接;

        • interactive_timeout:针对交互式连接(指命令行终端、客户端)的超时时间;

        • 上面两个参数的设置分为 session 级别 和 global 级别(当然设置 global 级别才有意义);

          session 级别查看:

          1
          show session variables where Variable_name in ('interactive_timeout', 'wait_timeout');

          global 级别查看:

          1
          show global variables where Variable_name in ('interactive_timeout', 'wait_timeout');

          临时设置 session 或者 global 级别的 wait_timeout 或 interactive_timeout:

          1
          2
          3
          4
          set session interactive_timeout=10;
          set session wait_timeout=10;
          set global interactive_timeout=10;
          set global wait_timeout=10;
        • 特殊情况:

          • 当前交互式连接的使用的是 session 级别 wait_timeout,session 级别interactive_timeout 不生效。
          • 新创建的交互式连接的 wait_timeout 会使用 global 级别的 interactive_timeout 值。
    • 以挂载配置的方式启动。

      • 创建挂载目录;

        1
        mkdir -p /usr/local/nacos/logs /usr/local/nacos/conf
      • 先启动一次容器,然后将配置文件拷贝出来用于挂载;

        1
        docker run -d --name nacos -e MODE=standalone -p 8848:8848 --rm nacos/nacos-server:1.1.4
        1
        docker cp nacos:/home/nacos/conf/application.properties /usr/local/nacos/conf
        1
        docker stop nacos
      • 修改配置文件;

        1
        vim /usr/local/nacos/conf/application.properties

        修改内容如下

        1
        2
        3
        4
        5
        6
        spring.datasource.platform=mysql
        db.url.0=jdbc:mysql://172.17.0.2:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
        #注释掉 db.url.1,不配置从数据库
        #db.url.1=....
        db.user=root
        db.password=12345678
      • 启动 nacos 容器;

        1
        2
        3
        4
        5
        docker run -d --restart=always --name nacos -p 8848:8848 --privileged=true \
        -e JVM_XMS=256m -e JVM_XMX=256m -e MODE=standalone \
        -v /usr/local/nacos/logs:/home/nacos/logs \
        -v /usr/local/nacos/conf/application.properties:/home/nacos/conf/application.properties \
        nacos/nacos-server:1.1.4

        说明一下:以 standalone 模式启动的 nacos, Xms 和 Xmx 默认为 512m,Xmn 为 256m,其他启动参数可查看/home/nacos/bin/docker-startup.sh,所以这里设置没用,服务器启动 docker 版 nacos 后,你会发现直接被吃掉了 500m 左右的内存

   >启动失败,查看启动日志`cat /usr/local/nacos/logs/start.out`后发现还是同样的问题,nacos 1.1.4 内的 mysql-connector-java 板本太低。
   >
   >解决:
   >
   >
1
2
3
>docker exec -it nacos mkdir -p /home/nacos/plugins/mysql
>docker cp /usr/local/mysql-connector-java-8.0.27.jar nacos:home/nacos/plugins/mysql
>docker restart nacos
- 再次查看是否启动成功;
1
cat /usr/local/nacos/logs/start.out
- 访问 nacos并创建配置文件测试;
1
http://ip:8848/nacos/index.html
<img src="https://npm.elemecdn.com/liuduix-assets/img/postimg/tbTTrGgn9k/image-20230106153052383.png"/> 数据库中也存在对应的配置。 <img src="https://npm.elemecdn.com/liuduix-assets/img/postimg/tbTTrGgn9k/image-20230106153654000.png"/>
  1. 修改服务器 nacos 密码;

  2. 现在可以将项目中的 mysql,redis,nacos 地址都改为服务器的 IP 地址来测试是否配置成功。

    1
    2
    3
    spring.datasource.url
    spring.redis.host
    spring.cloud.nacos.discovery.server-addr

5. Docker 部署单个 SpringBoot 微服务

5.1 手工部署

  1. 微服务项目打包;

    使用 mvn clean package,或者使用 spring-boot-maven-plugin 插件,打包微服务为 jar 包。

    1
    2
    3
    4
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>

    image-20230104181003244

  2. 将该 jar 包上传至服务器,我这里放到/usr/local/springboot/demo/

  3. 进入该目录,在 jar 包的同级目录编写Dockerfile 文件;

    1
    2
    cd usr/local/springboot/demo/
    vim Dockerfile

    Dockerfile 的内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 基础镜像为jdk8
    FROM java:8
    # 维护者信息
    MAINTAINER liuduix
    # VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
    VOLUME /tmp
    # 将jar包添加到容器中并更名为 demo.jar
    ADD day-01-0.0.1-SNAPSHOT.jar demo.jar
    # 暴露8080端口作为微服务端口
    EXPOSE 8080
    # 执行命令
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/demo.jar"]
  4. 构建微服务镜像;

    1
    docker build -t demo .

    因为基础镜像是 java8,所以首次构建构建镜像会先去拉取 java8,在构建完毕后可以使用 docker images 查看是否有 java8 镜像和 demo 镜像。

  5. 启动微服务对应的容器;

    1
    docker run -d --name demo1 -p 8080:8080 --rm demo

    —rm 参数:如果容器被删除,那么挂载的文件数据也会被清除

  6. 开启 8080 端口访问测试;

  7. 查看容器运行日志:

    1
    docker logs -f [containerID]

5.2. idea 整合 docker 一键部署

5.2.1 配置 Docker 支持远程连接

  1. 编辑打开服务器的 docker.service 文件;

    1
    vim /usr/lib/systemd/system/docker.service
  2. VIM 命令行模式输入/ExecStart定位到 ExecStart 配置项(n 匹配下一个,Shift + n 匹配上一个);

  3. 找到 ExecStart 配置后,VIM 命令行模式输入:noh取消高亮;

  4. 注释原来 ExecStart,修改为如下的配置;

    1
    2
    3
    # 将原来的 ExecStart 注释
    # ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
    ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
  5. 重新加载 docker.server 文件并重启docker服务;

    1
    2
    3
    4
    # 重新加载服务配置 docker.service
    systemctl daemon-reload
    # 重新启动docker
    systemctl restart docker
  6. 查看 2375 端口是否在监听;

    1
    curl http://127.0.0.1:2375/version
  7. 云服务防火墙开放 2375 端口并且开放安全组规则,保证外部能够通过该端口连接 Docker;

    如果是腾讯云服务器只须去管理控制台的防火墙开放 2375,而不是命令行的方式开启端口。

    1
    2
    3
    firewall-cmd --add-port=2375/tcp --permanent
    firewall-cmd --reload
    firewall-cmd --zone=public --list-ports
  8. 远程 http 访问查看是否配置成功。

    1
    http://ip:2375/version

5.2.2 idea 配置连接远程 Docker

  1. IDEA 安装 Docker 插件;

  1. IDEA 中配置 Docker,配置好后点击 Apply 应用;

  1. 配置好后,在service窗口即可看见 Docker,「双击」或者「右键」它进行连接;连接后可以看见已有的容器和镜像,可以很方便的进行一些操作。

5.2.3 idea 中操作 Docker 创建容器

  1. 右击对应镜像,选择Create Container

  2. 弹出的页面初始只能填写容器名;

    image-20230104205144704

  3. 点击右边的 Modify options 可以添加其他选项,如下图中所示;

  4. 启动后可在右边查看一些容器信息,如:启动日志,容器 ID 等;

    对于其他功能,如:删除镜像,删除容器,拉取镜像…,idea 中都有相应的操作

  5. 访问地址测试;

5.2.4 使用 docker-maven-plugin

在我们持续集成过程中,项目工程一般使用 Maven 编译打包,然后生成镜像,通过镜像上线,能够大大提供上线效率,同时能够快速动态扩容,快速回滚,着实很方便。docker-maven-plugin 插件就是为了帮助我们在 Maven 工程中,通过简单的配置,自动生成镜像并推送到仓库中。

docker-maven-plugin:https://github.com/spotify/docker-maven-plugin

dockerfile-maven-plugin:https://github.com/spotify/dockerfile-maven

docker-maven-plugin 和 dockerfile-maven-plugin 两个插件最后一次更新都是几年以前了,官方推荐使用dockerfile-maven-plugin,但如果你的仓库配置了 CA 认证,那么最好选择 docker-maven-plugin,实际上 docker-maven-plugin 的最后一次更新比 dockerfile-maven-plugin 更晚。

docker-maven-plugin 主要通过 <executions> 中的配置来将 Docker 命令和 Maven 命令绑定来实现一键操作,比如 maven 打包后构建镜像,或者 maven 打包后构建和推送镜像。

下面的 demo 没有使用 Dockerfile 且没有推送到远程仓库,只是在远程 docker 中构建镜像。推送相关的配置请参考官方文档或者其他文章。

  1. 项目 pom.xml 文件中加入如下配置(部份配置可根据注释修改);

    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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    <build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    <!-- 跳过单元测试 -->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
    <skipTests>true</skipTests>
    </configuration>
    </plugin>
    <!--使用docker-maven-plugin插件-->
    <plugin>
    <groupId>com.spotify</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>1.0.0</version>
    <!--将插件绑定在某个phase执行-->
    <executions>
    <execution>
    <id>build-image</id>
    <!--用户只需执行mvn package ,就会自动执行mvn package docker:build-->
    <phase>package</phase>
    <goals>
    <goal>build</goal>
    </goals>
    </execution>
    </executions>
    <configuration>
    <!--指定生成的镜像名-->
    <imageName>liuduix/${project.artifactId}</imageName>
    <!--指定标签-->
    <imageTags>
    <imageTag>latest</imageTag>
    </imageTags>
    <!--指定基础镜像jdk1.8-->
    <baseImage>java</baseImage>
    <!--镜像制作人本人信息-->
    <maintainer>liuduix</maintainer>
    <!--切换到ROOT目录-->
    <workdir>/ROOT</workdir>
    <cmd>["java", "-version"]</cmd>
    <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
    <!--指定远程 docker api地址,如果不配置,默认是本地-->
    <dockerHost>http://122.51.50.249:2375</dockerHost>
    <!-- 如果你配置了 CA 认证需要指定地址 -->
    <dockerCertPath>D:/Docker/ca</dockerCertPath>
    <!-- 这里是复制 jar 包到 image 内 root 目录 -->
    <resources>
    <resource>
    <targetPath>/</targetPath>
    <!--jar 包所在的路径 -->
    <directory>${project.build.directory}</directory>
    <!--指定复制的jar包,对应的是 Dockerfile 中添加的文件名 -->
    <include>${project.build.finalName}.jar</include>
    </resource>
    </resources>
    </configuration>
    </plugin>
    </plugins>
    </build>
  2. 打包项目并构建镜像推送到远程 Docker 仓库;

    • 进入项目目录,运行以下指令

      1
      mvn clean package docker:build

      执行上面命令后,使用docker images 查看是否生成了对应的微服务镜像。

    • 因为上面配置了只需执行 mvn package ,就会自动执行 mvn docker:build,所以推荐直接打包即可。

      1
      mvn clean package
  3. 使用生成的镜像创建容器并运行,最后访问测试。

6. Idea 整合 Docker CA 加密认证

在上面的配置中,所有人都可以通过 2375 端口连接我们服务器 Docker,这种方式非常的不安全,服务器很容器被黑,因此,docker官方推荐使用加密的 tcp 连接,以 https 的方式与客户端建立连接。

官网示例:https://docs.docker.com/engine/security/protect-access/#create-a-ca-server-and-client-keys-with-openssl

  1. Docker 服务器上创建 CA 文件夹,存放 CA 公钥和私钥;

    1
    2
    3
    4
    # 创建 ca 文件夹
    mkdir -p /usr/local/ca
    # 进入该目录
    cd /usr/local/ca
  2. 生成 CA 私钥和公钥;

    1
    openssl genrsa -aes256 -out ca-key.pem 4096

    这个 ca-key.pem 的密码要记下,用于后续操作

  3. 依次输入密码、国家、省、市、组织名称、邮箱等;

    1
    openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

    现在已经有了CA,接下来创建一个服务器密钥和证书签名请求(CSR)。确保「Common Name」也就是上面填写的「昵称」与你用来连接到 Docker 的主机名匹配

  4. 生成 server-key.pem;

    1
    openssl genrsa -out server-key.pem 4096

  5. 使用 CA 签署公钥;

    由于 TLS 连接可以通过 IP 地址和 DNS 名称进行,所以在创建证书时需要指定 IP 地址。请将下面的$HOST 换为你 Docker 服务器的 IP 或者域名。

    1
    openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr

    比如:

    1
    openssl req -subj "/CN=192.168.225.130" -sha256 -new -key server-key.pem -out server.csr
  6. 配置白名单;

    允许指定的 IP 可以连接到 Docker,多个 IP 用,分开;因为已经是 SSL 连接,所以推荐配置 0.0.0.0,也就是所有人都可以连接,但只有拥有证书的才可以连接成功。

    如果填写 IP 地址,格式如下:

    1
    echo subjectAltName = IP:$HOST,IP:0.0.0.0 >> extfile.cnf

    如果填写域名,格式如下:

    1
    echo subjectAltName = DNS:$HOST,IP:0.0.0.0 >> extfile.cnf

    因为 0.0.0.0 允许所有的连接,所以上面的命令也可以写为以下方式。

    1
    echo subjectAltName = IP:0.0.0.0 >> extfile.cnf
  7. 将 Docker 守护程序密钥的扩展使用属性设置为仅用于服务器身份验证;

    1
    echo extendedKeyUsage = serverAuth >> extfile.cnf
  8. 生成签名证书;

    1
    2
    openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
    -CAcreateserial -out server-cert.pem -extfile extfile.cnf

  9. 生成客户端的 key.pem;

    1
    2
    openssl genrsa -out key.pem 4096
    openssl req -subj '/CN=client' -new -key key.pem -out client.csr
  10. 要使密钥适配客户端身份认证,需要创建配置文件;

    1
    2
    echo extendedKeyUsage = clientAuth >> extfile.cnf
    echo extendedKeyUsage = clientAuth > extfile-client.cnf
  11. 现在生成签名证书;

    1
    2
    openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
    -CAcreateserial -out cert.pem -extfile extfile-client.cnf

  12. 删除不需要的文件,两个证书签名请求;

    1
    rm -v client.csr server.csr extfile.cnf extfile-client.cnf
  13. 修改文件为只读,避免密钥文件意外损坏;

    1
    2
    chmod -v 0400 ca-key.pem key.pem server-key.pem
    chmod -v 0444 ca.pem server-cert.pem cert.pem
  14. 归集服务器证书,做一个备份操作;

    1
    2
    cp server-*.pem /etc/docker
    cp ca.pem /etc/docker
  15. 修改 Docker 配置,使 Docker 守护程序仅接受来自提供 CA 信任证书的客户端连接;

    1
    vim /lib/systemd/system/docker.service

    将原有的ExecStart改为如下配置

    1
    ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/usr/local/ca/ca.pem --tlscert=/usr/local/ca/server-cert.pem --tlskey=/usr/local/ca/server-key.pem -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
  16. 重新加载配置文件并重启 Docker 服务;

    1
    2
    3
    4
    # 重新加载服务配置 docker.service
    systemctl daemon-reload
    # 重新启动docker
    systemctl restart docker
  17. 服务器开放 2375 端口(前面步骤已经开放);

  18. 将认证文件保存至客户端;

  19. Idea 配置使用 CA 认证远程连接 Docker;

  20. 此时如果使用 docker-maven-plugin,要修改其中的 docker 连接并指定认证文件地址,如下。

    1
    2
    <dockerHost>https://160.160.176.123:2375</dockerHost>
    <dockerCertPath>D:/Docker/ca</dockerCertPath>

7. Maven 多模块 SpringBoot 项目打包

多模块依赖打包解决方案:https://segmentfault.com/a/1190000019706787

Maven 多模块 SpringBoot 项目不建议单独打包,会出现循环依赖问题

  1. 检查模块关系;

    这一步主要是确定所有的父模块的<modules>标签中包含各自的子模块,而子模块中也能根据 pom 文件中的<parent>标签找到父模块,这样才能一键打包成功,如果是正常按顺序创建的父子模块不用检查,如果修改过模块名和文件夹名,那么一定要检查。(简单说就是每个 pom 文件不能有爆红的地方)

    父模块:

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- ... -->
    <modules>
    <module>service</module>
    <!-- ... -->
    </modules>
    <!-- ... -->
    <artifactId>OnlineEducation</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    子模块:

    1
    2
    3
    4
    5
    <!-- ... -->
    <parent>
    <artifactId>OnlineEducation</artifactId>
    <!-- ... -->
    </parent>
  2. 根模块及所有的配置模块(无实际代码,只有一个 pom 文件做依赖管理的模块)加上<packaging>pom</packaging>,表示不打包该模块。

  3. 所有标注有@SpringBootApplication的启动类的模块的 pom 文件加入如下配置,其余模块不要加入(如:公共模块,pom 文件包含 pom 的模块);

    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
    <!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
    <build>
    <plugins>
    <plugin>
    <!--该插件主要用途:构建可执行的JAR -->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    <resources>
    <resource>
    <!--指定打包时要打包 mapper.xml 文件-->
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.xml</include>
    </includes>
    <!--打包时不解析文件中的占位符-->
    <filtering>false</filtering>
    </resource>
    <!--指定打包时要打包资源目录下的所有的配置文件-->
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.*</include>
    </includes>
    </resource>
    </resources>
    </build>
  4. 禁用 maven 的测试,否则打包时所有的测试类都会运行,一旦某个测试不通过,打包失败;

  5. 打包所有 SpringBoot 模块;

    idea 中找到根模块,点击package

  6. 打包完成后,先在 windows 本地执行所有 jar 包是否可用;

    1
    javaw -jar xx.jar

    javaw 后台运行,与nohup java -jar xxx.jar > log.file 2>&1 &类似,缺点:无法查看启动日志

    相关操作命令:

    1
    2
    3
    netstat -ano | findstr "8222" # 查看 8222 端口 PID(进程号)
    tasklist | findstr “PID号” # 查看 PID 对应的服务名
    taskkill /f /t /im javaw.exe # 结束该服务

    先测试项目在本地能不能运行,在打包完成并构建所有的镜像文件后,再次测试是否可以运行。

8. Maven 多模块 SpringBoot 项目远程镜像构建

不推荐使用 dockerfile-maven-plugin,第一项目已经几年没更新了,第二配置文档不够详细,第三对利用远程 docker 环境构建镜像支持不太好(表现在修改了连接为远程地址但是还是去找本地 Docker),第四,如果用了 CA 认证,可能找不到地方配置。

能利用远程 docker 环境构建的插件:

或者使用 idea 自带的 Docker 插件

  1. 所有有启动类的微服务子模块都需要有 Dockerfile 文件(与 pom.xml 文件同级),用于生成镜像;

    1
    2
    3
    4
    5
    6
    7
    8
    # 基础镜像
    FROM java:8
    # 维护者信息
    MAINTAINER liuduix
    # 将jar包添加到镜像中
    ADD ./target/service-0.0.1-SNAPSHOT.jar /service.jar
    # 执行命令
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/service.jar"]
  2. 所有有启动类的微服务子模块 pom.xml 文件添加如下配置;

    部份地方需要修改

    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
    <!--使用docker-maven-plugin插件-->
    <plugin>
    <groupId>com.spotify</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>1.0.0</version>
    <!--将插件绑定在某个phase执行-->
    <executions>
    <execution>
    <id>build-image</id>
    <!--用户只需执行mvn package ,就会自动执行mvn package docker:build-->
    <phase>package</phase>
    <goals>
    <goal>build</goal>
    </goals>
    </execution>
    </executions>
    <configuration>
    <!--指定生成的镜像名-->
    <imageName>liuduix/${project.artifactId}</imageName>
    <!--指定标签-->
    <imageTags>
    <imageTag>latest</imageTag>
    </imageTags>
    <!-- 指定 Dockerfile 文件的路径-->
    <dockerDirectory>${project.basedir}</dockerDirectory>
    <!-- 指定远程 Docker 地址 -->
    <dockerHost>https://122.51.50.249:2375</dockerHost>
    <!-- 如果你配置了 CA 认证需要指定该文件所在目录 -->
    <dockerCertPath>D:/Docker/ca</dockerCertPath>
    <!-- 这里是复制 jar 包到镜像内根目录 -->
    <resources>
    <resource>
    <targetPath>/</targetPath>
    <!--jar 包所在的路径 -->
    <directory>${project.build.directory}</directory>
    <!--指定复制的jar包 -->
    <include>${project.build.finalName}.jar</include>
    </resource>
    </resources>
    </configuration>
    </plugin>
  3. 执行 maven 打包操作,查看是否生成镜像;

  4. 至此远程库中应该多了很多镜像了,那假如我们仅对某个子模块做了修改,我们也不想再去打包并构建所有的模块怎么办呢?在根模块执行以下命令;

    假如我要打包 C 模块,根模块是 A,C 的父模块是 B,那么只需要在 A 模块目录执行以下命令

    1
    mvn package '-Dmaven.test.skip=true' -pl Bmodule/Cmodule -am

9. 清除上一次的镜像

由上面的配置可知,每次生成的镜像都相同,因此会产生一堆虚悬镜像(Repository 为 none,Tag 为 none 的镜像),所以在每次修改代码后,可以先执行一个脚本清除这些镜像,然后在构建镜像;或者在所有镜像构建完成后,使用命令清除这些镜像;

  1. 清除虚悬镜像命令;

    1
    docker system prune
  2. 清除多个微服务相关镜像命令;

    1
    docker images | grep liuduix | awk '{print $3}' | xargs docker rmi
  3. 一键停止容器和清除镜像脚本;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    CONTAINTER_NAME=liuduix
    IMAGE_NAME=liuduix
    #容器id
    CIDS=$(docker ps | grep "$CONTAINTER_NAME" | awk '{print $1}')
    #镜像id
    IIDS=$(docker images | grep "$IMAGE_NAME" | awk '{print $3}')
    # 停止相关容器
    if [ -n "$CIDS" ]; then
    for CID in $CIDS
    do
    echo "存在容器 CID-$CID,现在停止 $CID"
    docker stop $CID
    docker rm $CID
    done
    fi

    # 删除原来的镜像
    if [ -n "$IIDS" ]; then
    for IID in $IIDS
    do
    echo "存在镜像 IMAGE ID=$IID,现在删除 $IID"
    docker rmi $IID
    done
    fi

10. 使用 Docker Compose 一键启动

因为按照官方的示例在最开始安装 Docker 时已经安装好了 Docker Compose,所以现在无需安装

  1. 查看 Docker Compose 板本;

    1
    docker compose version
  2. 编写 Docker Compose 文件;

    推荐阅读:

    docker-compose.yml

    • mysql 配置参考;

      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
      version: '3' # Docker Compose 文件板本,有 1、2.x、3.x 三个板本,主流为 3.x,向下兼容
      services:
      # 服务名
      mysql:
      image: mysql:latest
      container_name: mysql # 容器名
      restart: on-failure
      privileged: true
      ports:
      - 3306:3306
      volumes:
      - /data/mysql/log:/var/log/mysql
      - /data/mysql/data:/var/lib/mysql
      - /data/mysql/conf:/etc/mysql/conf.d
      environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 12345678
      command: # 配置容器启动时的命令
      --max_connections=1000
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --lower_case_table_names=1
      --wait_timeout=10
      --interactive_timeout=10
      networks:
      - microservice-net

      networks:
      microservice-net:
      driver: bridge # 默认 docker compose 创建的网络模式为 bridge,可以不写

      不推荐使用 docker compose 的方式启动 mysql,如果要使用 nacos,那么 nacos 跟着启动会找不到相应的数据库。更好的方式是:

      • 先创建用于连接各个服务的网络(默认 -d bridge);

        1
        docker network create --subnet=172.20.0.0/16 --gateway=172.20.0.1 microservice-net 
      • 然后使用命令行的方式启动 mysql;

        1
        2
        3
        4
        5
        6
        7
        8
        9
        docker run -d --restart=always --name="mysql" -p 3306:3306 \
        --privileged=true \
        --network microservice-net \
        --ip 172.20.0.2 \
        -v /data/mysql/log:/var/log/mysql \
        -v /data/mysql/data:/var/lib/mysql \
        -v /data/mysql/conf:/etc/mysql/conf.d \
        -e MYSQL_ROOT_PASSWORD=12345678 \
        mysql:latest
    • nacos 配置参考;

      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
      version: '3'
      services:
      nacos:
      image: nacos/nacos-server:1.1.4
      container_name: nacos
      restart: always
      privileged: true
      ports:
      - 8848:8848
      volumes:
      - /data/nacos/logs:/home/nacos/logs
      # env_file:
      # - /data/nacos/nacos-standlone-mysql.env
      environment:
      MODE: standalone
      SPRING_DATASOURCE_PLATFORM: mysql
      MYSQL_MASTER_SERVICE_HOST: 172.20.0.2
      MYSQL_MASTER_SERVICE_PORT: 3306
      MYSQL_MASTER_SERVICE_USER: root
      MYSQL_MASTER_SERVICE_PASSWORD: 12345678
      MYSQL_MASTER_SERVICE_DB_NAME: nacos
      MYSQL_SLAVE_SERVICE_HOST: 172.20.0.2
      networks:
      microservice-net:
      ipv4_address: 172.20.0.3

      networks:
      microservice-net:
      external: true
    • redis 配置参考;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      version: '3'
      services:
      redis:
      image: redis:latest
      container_name: redis
      restart: always
      privileged: true
      ports:
      - 6379:6379
      volumes:
      - /data/redis/redis.conf:/etc/redis/redis.conf
      - /data/redis/data:/data
      # command 太长,推荐在 redis.conf 文件中修改
      command: redis-server /etc/redis/redis.conf --bind 0.0.0.0 --requirepass redis_6379 --daemonize no --protected-mode no --appendonly yes
      networks:
      - microservice-net

      networks:
      microservice-net:
      external: true
    • 多个微服务配置参考;

      external_links 小技巧:

      https://docs.docker.com/compose/compose-file/#external_links

      • 使用 external_links 可链接外部容器(前提:容器处于同一个 docker 网络,且不是默认的 bridge),链接后,可直接使用容器名代替$HOST,如:
      1
      spring.cloud.nacos.discovery.server-addr=nacos:8848
      1
      db.url.0=jdbc:mysql://mysql:3306/nacos...
      • 如果多个容器处于同一个 docker 网络中,由于 Docker 自带 DNS 解析,那么 external_links 不写也可以直接使用容器名访问服务。
      • 如果容器处于默认的 bridge 网络,那么无法使用容器名代替$HOST,除非使用 link 链接容器。
      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
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      version: '3'
      services:
      liuduix-gateway:
      image: liuduix/gateway:latest
      container_name: liuduix-gateway
      restart: always
      ports:
      - '8222:8222'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      # depends_on: 定义启动顺序
      # - mysql
      # - nacos
      # - redis
      # external_links: # 链接外部容器,
      # - Container:Alias
      # - nacos:nacos
      # - redis
      deploy:
      resources:
      limits:
      cpus: '0.5' # 单个容器使用限制,如果 CPU 资源不够,容器处于竞争关系
      memory: 512M # 内存限制,此时可用的 swap 也为 512M
      reservations: # 软限制,单个容器最低可以分配的资源
      memory: 128M

      liuduix-alc:
      image: liuduix/service_acl:latest
      container_name: liuduix-alc
      restart: always
      ports:
      - '8008:8008'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M

      liuduix-cms:
      image: liuduix/service_cms:latest
      container_name: liuduix-cms
      restart: always
      ports:
      - '8004:8004'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M

      liuduix-edu:
      image: liuduix/service_edu:latest
      container_name: liuduix-edu
      restart: always
      ports:
      - '8001:8001'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M


      liuduix-msm:
      image: liuduix/service_msm:latest
      container_name: liuduix-msm
      restart: always
      ports:
      - '8005:8005'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M

      liuduix-order:
      image: liuduix/service_order:latest
      container_name: liuduix-order
      restart: always
      ports:
      - '8006:8006'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M

      liuduix-oss:
      image: liuduix/service_oss:latest
      container_name: liuduix-oss
      restart: always
      ports:
      - '8002:8002'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M

      liuduix-statistics:
      image: liuduix/service_statistics:latest
      container_name: liuduix-statistics
      restart: always
      ports:
      - '8007:8007'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M

      liuduix-ucenter:
      image: liuduix/service_ucenter:latest
      container_name: liuduix-ucenter
      restart: always
      ports:
      - '8160:8160'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.5'
      memory: 512M
      reservations:
      memory: 128M

      liuduix-vod:
      image: liuduix/service_vod:latest
      container_name: liuduix-vod
      restart: always
      ports:
      - '8003:8003'
      environment:
      TZ: Asia/Shanghai
      networks:
      - microservice-net
      deploy:
      resources:
      limits:
      cpus: '0.50'
      memory: 512M
      reservations:
      memory: 128M

      networks:
      microservice-net:
      external: true
  1. 常用命令;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    docker compose --compatibility up -d # v3 限制生效启动命令
    docker compose -h #查看帮助I
    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 #停止服务

11. 设置虚拟内存

服务器只有 4G内存,使用free -h/-m命令查看到启动 mysql,nacos,redis 后可用的就只剩 1.6 G,而我需要上线 10 个微服务,那内存肯定是不够用的。

linux swap 空间被称为交换空间,这是一块特殊的硬盘空间,它可以是 swap 分区或者 swap 文件,当实际内存不够用的时候,操作系统会从内存中取出一部分暂时不用的数据,放在交换空间中,从而为当前运行的程序腾出足够的内存空间,Windows 中使用分页文件来实现这种虚拟内存的效果,所以我们设置虚拟内存就是去设置 swap 分区或 swap 文件。

swap 分区安装系统时就存在,但是默认并未开启,仅供操作系统内部使用,我们可以创建 swap 文件(常用推荐)或者创建磁盘分区作为交换空间,下面创建 swap 文件来作为交换空间。

  1. 查看交换空间大小;

    1
    free -h/-m

  2. 查看使用的交换设备;

    1
    swapon -s
  3. 创建 swap 文件;

    linux 命令速查:http://www.atoolbox.net/Tool.php?Id=826

    1
    2
    3
    mkidr /swap
    sudo dd if=/dev/zero of=/swap/swapfile bs=1G count=4
    sudo mkswap /swap/swapfile #在 /swap/swapfile 文件上建立交换空间

    bs*count 为交换文件大小,官方推荐大小

    注意:如果服务器的内存使用是几十G或者上百G级别的,那么尽量提高机器配置,使用物理内存,因为频繁的交换数据进行磁盘 I/O 读写,势必会影响服务器性能,严重时直接宕机。

  4. 更改 swapfile 文件权限,避免被更改;

    1
    sudo chmod 600 /swap/swapfile
  5. 激活交换空间;

    1
    sudo swapon /swap/swapfile #swapon 激活交换空间,可以是一个文件或文件夹
  6. 这时再查看交换空间大小和使用情况;

    1
    free -m
  7. 开机时自动加载交换文件;

    • 打开配置文件;

      1
      vim /etc/fstab
    • shift + G 定位到最后一行,添加如下内容并保存。

      1
      /swap/swapfile swap swap defaults 0 0
  8. swappiness参数控制系统swap积极度;

    swappiness 值越高,内核就会越积极的使用 swap 空间。如果这个值为 0 ,不代表不使用 swap 空间,而是最大限度的使用物理内存,CentOS 7 上这个值默认是 30。这个值应该尽量低,以提高系统性能。

    推荐文章:https://zhuanlan.zhihu.com/p/107350459

    查看系统 swappiness 的值。

    1
    cat /proc/sys/vm/swappiness
  9. 如果想删除交换文件(不是很有必要,禁用就行),使用以下步骤。

    • 禁用交换空间;

      1
      swapoff /swap/swapfile
    • 删除交换文件;

      1
      rm -f /swap/swapfile 
    • /etc/fstab中删除该项配置。

12. TODO:使用 nacos 配置中心

13. TODO:使用 Jenkins 自动化部署