在 Docker 容器中部署 Neo4j 集群

Neo4j 支持在容器化环境中进行集群,无需编排工具。本教程将引导您在本地设置集群,用于测试目的。有关在多台服务器上进行生产部署的信息,请参阅 在多个 Docker 主机上部署 Neo4j 集群

此页面上的示例同时使用了命令扩展和 DNS 发现方法。有关更多信息,请参阅

使用 Docker Compose 部署 Neo4j 集群

您可以使用 Docker Compose 部署 Neo4j 集群。Docker Compose 是一个 Docker 容器管理工具。您可以使用 YAML 文件在一个文件中定义所有集群服务器的基础设施。然后,通过运行单个命令 docker-compose up,您可以创建并启动所有成员,而无需单独调用每个成员。有关 Docker Compose 的更多信息,请参阅 Docker Compose 官方文档

先决条件

程序

  1. 创建一个配置文件 neo4j.conf,该文件将与集群成员共享,并使其对用户可读可写(例如,chmod 640 neo4j.conf)。

    # Setting that specifies how much memory Neo4j is allowed to use for the page cache.
    server.memory.pagecache.size=100M
    
    # Setting that specifies the initial JVM heap size.
    server.memory.heap.initial_size=100M
    
    # The behavior of the discovery service is determined by the parameters `dbms.cluster.discovery.resolver_type`, `dbms.cluster.discovery.v2.endpoints`, and `dbms.cluster.discovery.version`.
    # The DNS strategy fetches the IP addresses of the cluster members using the DNS A records.
    dbms.cluster.discovery.resolver_type=DNS
    
    # The value of `dbms.cluster.discovery.version` must be set to `V2_ONLY` if you want to use the discovery service v2.
    # The discovery service v2 utilizes the port `6000`.
    dbms.cluster.discovery.version=V2_ONLY
    
    # The value of `dbms.cluster.discovery.v2.endpoints` should be set to a single domain name and the port of the discovery service.
    # The domain name returns an A record for every server in the cluster when a DNS lookup is performed.
    # Each A record returned by DNS should contain the IP address of the server in the cluster.
    # The configured server uses all the IP addresses from the A records to join or form a cluster.
    # The discovery port must be the same on all servers when using this configuration.
    dbms.cluster.discovery.v2.endpoints=neo4j-network:6000
    
    # Address (the public hostname/IP address of the machine)
    # and port setting that specifies where this instance advertises for discovery protocol messages from other members of the cluster.
    server.cluster.advertised_address=$(hostname -i)
    
    # Address (the public hostname/IP address of the machine)
    # and port setting that specifies where this instance advertises for Raft messages within the cluster.
    server.cluster.raft.advertised_address=$(hostname)
    
    # Enable server-side routing
    dbms.routing.enabled=true
    
    # Use server-side routing for neo4j:// protocol connections.
    dbms.routing.default_router=SERVER
    
    # The advertised address for the intra-cluster routing connector.
    server.routing.advertised_address=$(hostname)
    
    # Automatically enable servers, rather than needing to explicitly do so for Free servers
    initial.dbms.automatically_enable_free_servers=true
    # Setting that specifies how much memory Neo4j is allowed to use for the page cache.
    server.memory.pagecache.size=100M
    
    # Setting that specifies the initial JVM heap size.
    server.memory.heap.initial_size=100M
    
    # The behavior of the initial discovery is determined by the parameters `dbms.cluster.discovery.resolver_type` and `dbms.cluster.discovery.endpoints`.
    # The DNS strategy fetches the IP addresses of the cluster members using the DNS A records.
    dbms.cluster.discovery.resolver_type=DNS
    
    # The value of `dbms.cluster.discovery.endpoints` should be set to a single domain name and the port of the discovery service.
    # The domain name returns an A record for every server in the cluster when a DNS lookup is performed.
    # Each A record returned by DNS should contain the IP address of the server in the cluster.
    # The configured server uses all the IP addresses from the A records to join or form a cluster.
    # The discovery port must be the same on all servers when using this configuration.
    dbms.cluster.discovery.endpoints=neo4j-network:5000
    
    # Address (the public hostname/IP address of the machine)
    # and port setting that specifies where this instance advertises for discovery protocol messages from other members of the cluster.
    server.discovery.advertised_address=$(hostname -i)
    
    # Address (the public hostname/IP address of the machine)
    # and port setting that specifies where this instance advertises for Raft messages within the cluster.
    server.cluster.raft.advertised_address=$(hostname)
    
     # Address (the public hostname/IP address of the machine)
     # and port setting that specifies where this instance advertises for requests for transactions in the transaction-shipping catch-up protocol.
    server.cluster.advertised_address=$(hostname)
    
    # Enable server-side routing
    dbms.routing.enabled=true
    
    # Use server-side routing for neo4j:// protocol connections.
    dbms.routing.default_router=SERVER
    
    # The advertised address for the intra-cluster routing connector.
    server.routing.advertised_address=$(hostname)
  2. 使用以下示例准备您的 docker-compose.yml 文件。有关更多信息,请参阅 Docker Compose 官方服务配置参考

    示例 1. 示例 docker-compose.yml 文件
    version: '3.8'
    
    # Custom top-level network
    networks:
      neo4j-internal:
    
    services:
    
      server1:
        # Docker image to be used
        image: ${NEO4J_DOCKER_IMAGE}
    
        # Hostname
        hostname: server1
    
        # Service-level network, which specifies the networks, from the list of the top-level networks (in this case only neo4j-internal), that the server will connect to.
        # Adds a network alias (used in neo4j.conf when configuring the discovery members)
        networks:
          neo4j-internal:
            aliases:
              - neo4j-network
    
        # The ports that will be accessible from outside the container - HTTP (7474) and Bolt (7687).
        ports:
          - "7474:7474"
          - "7687:7687"
    
        # Uncomment the volumes to be mounted to make them accessible from outside the container.
        volumes:
          - ./neo4j.conf:/conf/neo4j.conf # This is the main configuration file.
          - ./data/server1:/data
          - ./logs/server1:/logs
          - ./conf/server1:/conf
          - ./import/server1:/import
          #- ./metrics/server1:/metrics
          #- ./licenses/server1:/licenses
          #- ./ssl/server1:/ssl
    
        # Passes the following environment variables to the container
        environment:
          - NEO4J_ACCEPT_LICENSE_AGREEMENT
          - NEO4J_AUTH
          - EXTENDED_CONF
          - NEO4J_EDITION
          - NEO4J_initial_server_mode__constraint=PRIMARY
    
        # Simple check testing whether the port 7474 is opened.
        # If so, the instance running inside the container is considered as "healthy".
        # This status can be checked using the "docker ps" command.
        healthcheck:
          test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
    
        # Set up the user
        user: ${USER_ID}:${GROUP_ID}
    
      server2:
        image: ${NEO4J_DOCKER_IMAGE}
        hostname: server2
        networks:
          neo4j-internal:
            aliases:
              - neo4j-network
        ports:
          - "7475:7474"
          - "7688:7687"
        volumes:
          - ./neo4j.conf:/conf/neo4j.conf
          - ./data/server2:/data
          - ./logs/server2:/logs
          - ./conf/server2:/conf
          - ./import/server2:/import
          #- ./metrics/server2:/metrics
          #- ./licenses/server2:/licenses
          #- ./ssl/server2:/ssl
        environment:
          - NEO4J_ACCEPT_LICENSE_AGREEMENT
          - NEO4J_AUTH
          - EXTENDED_CONF
          - NEO4J_EDITION
          - NEO4J_initial_server_mode__constraint=PRIMARY
        healthcheck:
          test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
        user: ${USER_ID}:${GROUP_ID}
    
      server3:
        image: ${NEO4J_DOCKER_IMAGE}
        hostname: server3
        networks:
          neo4j-internal:
            aliases:
              - neo4j-network
        ports:
          - "7476:7474"
          - "7689:7687"
        volumes:
          - ./neo4j.conf:/conf/neo4j.conf
          - ./data/server3:/data
          - ./logs/server3:/logs
          - ./conf/server3:/conf
          - ./import/server3:/import
          #- ./metrics/server3:/metrics
          #- ./licenses/server3:/licenses
          #- ./ssl/server3:/ssl
        environment:
          - NEO4J_ACCEPT_LICENSE_AGREEMENT
          - NEO4J_AUTH
          - EXTENDED_CONF
          - NEO4J_EDITION
          - NEO4J_initial_server_mode__constraint=PRIMARY
        healthcheck:
          test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
        user: ${USER_ID}:${GROUP_ID}
    
      server4:
        image: ${NEO4J_DOCKER_IMAGE}
        hostname: server4
        networks:
          neo4j-internal:
            aliases:
              - neo4j-network
        ports:
          - "7477:7474"
          - "7690:7687"
        volumes:
          - ./neo4j.conf:/conf/neo4j.conf
          - ./data/server4:/data
          - ./logs/server4:/logs
          - ./conf/server4:/conf
          - ./import/server4:/import
          #- ./metrics/server4:/metrics
          #- ./licenses/server4:/licenses
          #- ./ssl/server4:/ssl
        environment:
          - NEO4J_ACCEPT_LICENSE_AGREEMENT
          - NEO4J_AUTH
          - EXTENDED_CONF
          - NEO4J_EDITION
          - NEO4J_initial_server_mode__constraint=SECONDARY
        healthcheck:
          test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
        user: ${USER_ID}:${GROUP_ID}
  3. 设置环境变量

    • export USER_ID="$(id -u)"

    • export GROUP_ID="$(id -g)"

    • export NEO4J_DOCKER_IMAGE=neo4j:enterprise

    • export NEO4J_EDITION=docker_compose

    • export EXTENDED_CONF=yes

    • export NEO4J_ACCEPT_LICENSE_AGREEMENT=yes

    • export NEO4J_AUTH=neo4j/your_password

  4. 通过运行以下命令预先构建文件夹结构

    mkdir -p conf/{server1,server2,server3,server4} data/{server1,server2,server3,server4} import/{server1,server2,server3,server4} logs/{server1,server2,server3,server4}
  5. 从您的项目文件夹中运行 docker-compose up 来部署您的 Neo4j 集群。

  6. 实例将在以下地址可用

  7. 使用默认的 neo4j/your_password 凭据进行身份验证。

  8. 通过在 Neo4j 浏览器中运行以下命令来检查集群状态

    SHOW SERVERS
    示例输出

    show servers docker

使用环境变量部署 Neo4j 集群

您可以使用环境变量设置集群中的容器以相互通信。每个容器都必须具有到其他每个容器的网络路由,NEO4J_initial_dbms_default__primaries__countNEO4J_initial_dbms_default__secondaries__countNEO4J_dbms_cluster_discovery_endpoints 环境变量必须为所有服务器设置。

集群环境变量

以下环境变量特定于 Neo4j 集群,并在 Neo4j 企业版中可用

  • NEO4J_initial_server_mode__constraint:数据库模式,默认为 NONE,可以设置为 PRIMARYSECONDARY

  • NEO4J_dbms_cluster_discovery_endpoints:一个逗号分隔的端点列表,服务器应该联系这些端点以发现其他集群服务器。 在 5.23 中已弃用

  • NEO4J_dbms_cluster_discovery_v2_endpoints:一个逗号分隔的端点列表,服务器应该联系这些端点以发现其他集群服务器。 在 5.23 中引入

  • NEO4J_server_discovery_advertised_address:用于成员发现管理通信的广告主机名/IP 地址和端口。 在 5.23 中已弃用

  • NEO4J_server.cluster.advertised_address:用于事务处理和 v2 发现的广告主机名/IP 地址和端口。

  • NEO4J_server.cluster.raft.advertised_address:用于集群通信的广告主机名/IP 地址和端口。

  • NEO4J_dbms_cluster_discovery_version:要使用的发现服务版本,默认为 V1_ONLY,可以设置为 V1_OVER_V2V2_OVER_V1V2_ONLY在 5.23 中引入

有关 Neo4j 集群设置的更多详细信息,请参阅 设置参考

在单个 Docker 主机上设置 Neo4j 集群

在一个 Docker 主机中,您可以使用 HTTP、HTTPS 和 Bolt 的默认端口。对于每个容器,这些端口映射到 Docker 主机上的不同端口集。

部署具有三个服务器的集群的 docker run 命令示例

docker network create --driver=bridge neo4j-cluster

docker run --name=server1 --detach --network=neo4j-cluster \
    --publish=7474:7474 --publish=7473:7473 --publish=7687:7687 \
    --hostname=server1 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_version=V2_ONLY \
    --env NEO4J_dbms_cluster_discovery_v2_endpoints=server1:6000,server2:6000,server3:6000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:7687 \
    --env NEO4J_server_http_advertised__address=localhost:7474 \
    --env NEO4J_AUTH=neo4j/mypassword \
    neo4j:5.25.1-enterprise

docker run --name=server2 --detach --network=neo4j-cluster \
    --publish=8474:7474 --publish=8473:7473 --publish=8687:7687 \
    --hostname=server2 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_version=V2_ONLY \
    --env NEO4J_dbms_cluster_discovery_v2_endpoints=server1:6000,server2:6000,server3:6000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:8687 \
    --env NEO4J_server_http_advertised__address=localhost:8474 \
    --env NEO4J_AUTH=neo4j/mypassword \
    neo4j:5.25.1-enterprise

docker run --name=server3 --detach --network=neo4j-cluster \
    --publish=9474:7474 --publish=9473:7473 --publish=9687:7687 \
    --hostname=server3 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_version=V2_ONLY \
    --env NEO4J_dbms_cluster_discovery_v2_endpoints=server1:6000,server2:6000,server3:6000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:9687 \
    --env NEO4J_server_http_advertised__address=localhost:9474 \
    --env NEO4J_AUTH=neo4j/mypassword \
    neo4j:5.25.1-enterprise
docker network create --driver=bridge neo4j-cluster

docker run --name=server1 --detach --network=neo4j-cluster \
    --publish=7474:7474 --publish=7473:7473 --publish=7687:7687 \
    --hostname=server1 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:7687 \
    --env NEO4J_server_http_advertised__address=localhost:7474 \
    --env NEO4J_AUTH=neo4j/mypassword \
    neo4j:5.25.1-enterprise

docker run --name=server2 --detach --network=neo4j-cluster \
    --publish=8474:7474 --publish=8473:7473 --publish=8687:7687 \
    --hostname=server2 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:8687 \
    --env NEO4J_server_http_advertised__address=localhost:8474 \
    --env NEO4J_AUTH=neo4j/mypassword \
    neo4j:5.25.1-enterprise

docker run --name=server3 --detach --network=neo4j-cluster \
    --publish=9474:7474 --publish=9473:7473 --publish=9687:7687 \
    --hostname=server3 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:9687 \
    --env NEO4J_server_http_advertised__address=localhost:9474 \
    --env NEO4J_AUTH=neo4j/mypassword \
    neo4j:5.25.1-enterprise

可以以即席方式将其他服务器添加到集群中。

将具有 SECONDARY 角色的第四台服务器添加到集群的 docker run 命令示例

docker run --name=read-server4 --detach --network=neo4j-cluster \
    --publish=10474:7474 --publish=10473:7473 --publish=10687:7687 \
    --hostname=read-server4 \
    --env NEO4J_initial_server_mode__constraint=SECONDARY \
    --env NEO4J_dbms_cluster_discovery_version=V2_ONLY \
    --env NEO4J_dbms_cluster_discovery_v2_endpoints=server1:6000,server2:6000,server3:6000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:10687 \
    --env NEO4J_server_http_advertised__address=localhost:10474 \
    neo4j:5.25.1-enterprise
docker run --name=read-server4 --detach --network=neo4j-cluster \
    --publish=10474:7474 --publish=10473:7473 --publish=10687:7687 \
    --hostname=read-server4 \
    --env NEO4J_initial_server_mode__constraint=SECONDARY \
    --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4J_server_bolt_advertised__address=localhost:10687 \
    --env NEO4J_server_http_advertised__address=localhost:10474 \
    neo4j:5.25.1-enterprise