---
title: Tanzu Greenplum 7.8をOrbStack上のRocky Linux Machineにインストールするメモ
summary: OrbStackでApple Silicon Mac上にGreenplumクラスタを構築する方法を紹介します。Cloud Initで自動設定し、動作確認まで行います。
tags: ["Greenplum", "Tanzu", "PostgreSQL", "Rocky", "OrbStack"]
categories: ["Middleware", "RDBMS", "Greenplum"]
date: 2026-05-22T04:50:55.866Z
updated: 2026-05-22T04:52:37.542Z
---

[以前の記事](/entries/879)でIntel MacのVMware Fusion上にGreenplumを構築しましたが、やっぱりApple SiliconのMac上で環境を作りたかったので、今回は[OrbStack](https://orbstack.dev/)の[Linux machines](https://docs.orbstack.dev/machines/)上で構築します。x86_64はRosettaによるエミュレートなので遅いですが、手軽さを優先しました。


### Tanzu Greenplumのダウンロード

GreenplumのRPMパッケージをダウンロードします。Broadcomのアカウントが必要です。

ホストのMac上で`~/greenplum`ディレクトリを作成します。

```bash
mkdir -p ~/greenplum
```

[Broadcom Support](https://support.broadcom.com)にログインして、[Tanzu Greenplumのダウンロードページ](https://support.broadcom.com/group/ecx/productdownloads?subfamily=VMware%20Tanzu%20Greenplum)にアクセスします。

最新バージョンを選択します。

![image](https://s3.ik.am/ikam/_/1779414669094_pasted-image.png)


RHEL 9用のRPMパッケージをダウンロードします。必要に応じて他のパッケージもダウンロードしてください。

![image](https://s3.ik.am/ikam/_/1779414727188_pasted-image.png)

## OrbStackでGreenplumクラスタの作成

Linux MachineのCloud Initで一気にGreenplumクラスタを作成します。

次の`user-data.yml`を作成します。

IPアドレスの`192.168.139`の部分や、rpmのパス`GP_RPM`は環境に合わせて変更してください。


```yaml
#cloud-config
# ============================================================================
#  Greenplum 7 cluster bootstrap — single-file cloud-config
#
#  This file drives the entire procedure WITHOUT Ansible:
#    - OS prep (packages, sysctl, limits, firewall, SELinux)
#    - gpadmin user, sudoers, .bashrc
#    - Static network configuration
#    - Greenplum RPM download + install
#    - /data directories per role
#    - SSH key exchange between all nodes (gpssh-exkeys)
#    - gpinitsystem (coordinator only)
#
#  The role (coordinator / segment) is auto-detected from the hostname,
#  so the same file works for all 4 nodes.
#
#
#  Topology (default):
#    gp-coordinator  192.168.139.200/24
#    gp-segment1     192.168.139.201/24
#    gp-segment2     192.168.139.202/24
#    gp-segment3     192.168.139.203/24
#
#  Boot order:
#    Start all 4 VMs at the same time. The coordinator's runcmd waits up to
#    30 minutes per segment for SSH to come up before continuing.
# ============================================================================

manage_etc_hosts: false               # we ship /etc/hosts via write_files

# ---------- SSH -------------------------------------------------------------
# Password auth must be enabled so that gpssh-exkeys / ssh-copy-id work
# during initial key exchange. Tighten afterwards if desired.
ssh_pwauth: true
disable_root: false

users:
  - name: root
    lock_passwd: false
    ssh_authorized_keys:
      # (*) your public key
      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDBDv42W4u5e2bMHqOUukWbhvtUA84cSmatsaeHXuHq4m4KNK+gfh/5Wid4UDyuabW+5YR13CWTb5HbAYpDu+kPNgz7w2ESg7rQYcA1W0kAbp5YD8w6f7pQb+BfRXhZAMFkSzZqtDqYcGNGnXGoFvpi3q8711d6LSwQkffApyz0XrfxVzDeBzPEeEoOR36aVfs9QU+av2ud6gNx4A19IbD277a0ZUvLowJ1Ms+o4eU7X472Id4zE6UCfXwvkrPZkeX6YyVKbu3wjbBmp4DkQar9c8I8av0tBxrebSK6ELQwjA945IuGg7kkvmu60KCCvnmc8XaqUJdviG+6lr8PT+iBazZ4Wm8Xrr3GRWYiwUE+cqEvzefZpWdwXuJw8gcEuhIOTuWbEWMdy3N8KN9M/+5B+irJmXJXmTVdzCEAjukn5+QIsfTJp2DQYKSvlk8gOLk6PXWEUnlgytvaTKxG6Sq97UnLJMRUSebxpaCat37Zs26YFDfVnz7dsFbMT9gGAdMvZULkLc5Dt26uunaLr+NXcD/WUgEdWaEBLuOOSBMyjJulCca2FV38IPdpMBtf8ZvKuLesHnYum8TGxx8lS4+aWxm/OjGGkU4Rpvn/J2XiZY9Nm0xii8h+944RSqxzvKTRcVcr6wPJhVV31pSablshyL87KAobJBACQrd+DMZ5Qw==
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP1p/lDTw/mPvjTq1BVV6htcEQa+xSg+hG9tM0Ky9jYw

  - name: gpadmin
    primary_group: gpadmin
    shell: /bin/bash
    lock_passwd: false
    sudo: "ALL=(ALL) NOPASSWD:ALL"

groups:
  - gpadmin

chpasswd:
  expire: false
  users:
    - {name: root,    password: rocky,        type: text}
    - {name: gpadmin, password: Greenplum123, type: text}

# ---------- Packages --------------------------------------------------------
package_update: true
packages:
  - apr
  - apr-util
  - bzip2
  - curl
  - iproute
  - krb5-devel
  - libcurl
  - libevent
  - libtiff
  - libuuid
  - libuv
  - libxml2
  - libyaml
  - libzstd
  - lsof
  - openldap
  - openssh
  - openssh-clients
  - openssh-server
  - openssl
  - openssl-libs
  - perl
  - python3
  - python3-psycopg2
  - python3-psutil
  - python3-pyyaml
  - python3.11
  - python3.11-devel
  - readline
  - rsync
  - sed
  - tar
  - which
  - zip
  - zlib
  - sshpass
  - java-21-openjdk-devel
  - epel-release

# ---------- Static files ----------------------------------------------------
write_files:
  - path: /etc/hosts
    permissions: '0644'
    content: |
      127.0.0.1       localhost
      192.168.139.200 gp-coordinator
      192.168.139.201 gp-segment1
      192.168.139.202 gp-segment2
      192.168.139.203 gp-segment3

  - path: /etc/sysctl.d/99-greenplum.conf
    content: |
      net.ipv4.ip_local_reserved_ports=65330

  # file descriptor limits
  - path: /etc/security/limits.d/99-greenplum.conf
    content: |
      * soft nofile 65536
      * hard nofile 65536

  # ----- Static network configuration -------------------------------------
  - path: /usr/local/sbin/cloud-setup-network.sh
    permissions: '0755'
    owner: root:root
    content: |
      #!/bin/bash
      set -eux
      # Derive last octet from hostname: gp-coordinator -> 200, gp-segmentN -> 20N
      HOST=$(hostname -s)
      if [[ "$HOST" == "gp-coordinator" ]]; then
        OCTET=200
      elif [[ "$HOST" =~ ^gp-segment([0-9]+)$ ]]; then
        OCTET=$((200 + BASH_REMATCH[1]))
      else
        echo "unknown host: $HOST" >&2; exit 1
      fi
      IPADDR="192.168.139.${OCTET}/24"
      FALLBACK_GW="192.168.139.1"
      DNS="8.8.8.8,1.1.1.1"

      NIC=$(ip -o -4 route show default 2>/dev/null | awk '{print $5}' | head -n1)
      GATEWAY=$(ip -o -4 route show default 2>/dev/null | awk '{print $3}' | head -n1)
      [ -z "$NIC" ] && NIC=$(ls /sys/class/net | grep -E '^(en|eth)' | grep -v '^lo' | head -n1)
      [ -z "$NIC" ] && { echo "no NIC found"; exit 1; }
      [ -z "$GATEWAY" ] && GATEWAY="$FALLBACK_GW"

      for con in $(nmcli -t -f NAME,DEVICE con show | awk -F: -v n="$NIC" '$2==n{print $1}'); do
        nmcli con delete "$con" || true
      done
      nmcli con add type ethernet ifname "$NIC" con-name static \
        ipv4.method manual ipv4.addresses "$IPADDR" \
        ipv4.gateway "$GATEWAY" ipv4.dns "$DNS" \
        ipv6.method disabled connection.autoconnect yes
      nmcli con up static

  # ----- Greenplum node provisioner (full procedure) ----------------------
  # Determines role from hostname, installs Greenplum, prepares /data,
  # and (on coordinator only) runs the cluster init.
  - path: /usr/local/sbin/cloud-provision-greenplum.sh
    permissions: '0755'
    owner: root:root
    content: |
      #!/bin/bash
      set -euxo pipefail
      exec > >(tee -a /var/log/cloud-provision-greenplum.log) 2>&1

      ulimit -SHn 65536 || true
      echo "[provision] ulimit -n = $(ulimit -n)  (hard: $(ulimit -Hn))"

      # ===== Settings (edit if needed) ===================================
      GP_RPM=/Users/toshiaki/greenplum/greenplum-db-7.8.1-el9-x86_64.rpm
      GPADMIN_PW="Greenplum123"
      COORDINATOR_HOST="gp-coordinator"
      ALL_HOSTS=(gp-coordinator gp-segment1 gp-segment2 gp-segment3)
      SEGMENT_HOSTS=(gp-segment1 gp-segment2 gp-segment3)
      # ===================================================================

      HOSTNAME_SHORT=$(hostname -s)
      if [[ "$HOSTNAME_SHORT" == "$COORDINATOR_HOST" ]]; then
        ROLE=coordinator
      else
        ROLE=segment
      fi
      echo "[provision] role=$ROLE host=$HOSTNAME_SHORT"

      # ---- Java default ---------------------------------------------------
      JAVA21=$(ls /usr/lib/jvm/java-21-openjdk-*/bin/java 2>/dev/null | head -n1 || true)
      [ -n "$JAVA21" ] && alternatives --set java "$JAVA21" || true

      # ---- firewalld / SELinux -------------------------------------------
      systemctl disable --now firewalld 2>/dev/null || true
      setenforce 0 2>/dev/null || true
      sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config || true

      # ---- Download & install Greenplum RPM ------------------------------
      dnf install -y $GP_RPM

      GPHOME=$(ls -d /usr/local/greenplum-db-* 2>/dev/null | sort | tail -n1)
      chown -R gpadmin:gpadmin "$GPHOME"

      # ---- gpadmin .bashrc -----------------------------------------------
      su - gpadmin -c "
        grep -q greenplum_path ~/.bashrc \
          || echo 'source $GPHOME/greenplum_path.sh' >> ~/.bashrc
      "

      # ---- /data directories ---------------------------------------------
      if [[ "$ROLE" == "coordinator" ]]; then
        mkdir -p /data/coordinator
      else
        mkdir -p /data/primary /data/mirror
      fi
      chown -R gpadmin:gpadmin /data

      # ---- gpadmin SSH keypair -------------------------------------------
      su - gpadmin -c '
        [ -f ~/.ssh/id_rsa ] || ssh-keygen -m PEM -t rsa -b 4096 -q -N "" -f ~/.ssh/id_rsa
      '

      # ---- Coordinator-only: cluster init --------------------------------
      if [[ "$ROLE" == "coordinator" ]]; then
        echo "[provision] coordinator: waiting for all nodes to accept SSH..."
        for h in "${ALL_HOSTS[@]}"; do
          for i in $(seq 1 30); do
            if sshpass -p "$GPADMIN_PW" ssh \
                  -o StrictHostKeyChecking=no \
                  -o ConnectTimeout=2 \
                  -o PasswordAuthentication=yes \
                  -o PreferredAuthentications=password \
                  gpadmin@"$h" true 2>/dev/null; then
              echo "[provision] $h is up"
              break
            fi
            [[ $i -eq 30 ]] && { echo "[provision] timeout waiting for $h"; exit 1; }
            sleep 5
          done
        done

        # SSH key exchange (per the original procedure)
        for h in "${ALL_HOSTS[@]}"; do
          su - gpadmin -c "
            SSHPASS='$GPADMIN_PW' sshpass -e ssh-copy-id -o StrictHostKeyChecking=no $h
          "
        done

        # gpssh-exkeys populates known_hosts cluster-wide
        su - gpadmin -c "
          printf '%s\n' ${ALL_HOSTS[*]} > ~/hostfile_exkeys
          source $GPHOME/greenplum_path.sh
          gpssh-exkeys -f ~/hostfile_exkeys
        "

        # gpinitsystem_config
        su - gpadmin -c "
          mkdir -p ~/gpconfigs
          printf '%s\n' ${SEGMENT_HOSTS[*]} > ~/gpconfigs/hostfile_gpinitsystem
          cp $GPHOME/docs/cli_help/gpconfigs/gpinitsystem_config ~/gpconfigs/
          sed -i \
            -e 's|^declare -a DATA_DIRECTORY=.*|declare -a DATA_DIRECTORY=(/data/primary /data/primary)|' \
            -e 's|^#declare -a MIRROR_DATA_DIRECTORY=.*|declare -a MIRROR_DATA_DIRECTORY=(/data/mirror /data/mirror)|' \
            -e 's|^#MIRROR_PORT_BASE=.*|MIRROR_PORT_BASE=7000|' \
            -e 's|^COORDINATOR_HOSTNAME=.*|COORDINATOR_HOSTNAME=$COORDINATOR_HOST|' \
            ~/gpconfigs/gpinitsystem_config
        "

        # Initialize the cluster (non-interactive)
        su - gpadmin -c "
          ulimit -n 65536
          source $GPHOME/greenplum_path.sh
          gpinitsystem -a \
            -c ~/gpconfigs/gpinitsystem_config \
            -h ~/gpconfigs/hostfile_gpinitsystem
        "

        # Greenplum env vars for gpadmin
        printf '%s\n' \
          'export COORDINATOR_DATA_DIRECTORY=/data/coordinator/gpseg-1' \
          'export PGPORT=5432' \
          'export PGUSER=gpadmin' \
          'export PGDATABASE=gpadmin' \
          >> /home/gpadmin/.bashrc
        chown gpadmin:gpadmin /home/gpadmin/.bashrc
      fi

      echo "[provision] done role=$ROLE"

# ---------- Boot sequence ---------------------------------------------------
runcmd:
  # 0. Make sure sshd is running and enabled (minimal/container images often
  #    ship with it disabled, which would block gpssh-exkeys / ssh-copy-id)
  - [systemctl, enable, --now, sshd]

  # 1. Switch from DHCP to static IP
  - /usr/local/sbin/cloud-setup-network.sh

  # 2. Apply sysctl (limits picks up on next login automatically)
  - [sysctl, --system]

  # 3. Full Greenplum provisioning (coordinator also runs gpinitsystem)
  - /usr/local/sbin/cloud-provision-greenplum.sh

# ---------- Final message ---------------------------------------------------
final_message: |
  cloud-init finished in $UPTIME seconds.
  See /var/log/cloud-provision-greenplum.log for Greenplum bootstrap output.
  If this is the coordinator, run as gpadmin:
    gpstate -s   # cluster status
    gpstate -e   # mirror status
```

この`user-data.yml`を使ってCoordinatorとSegmentを一気に作ります。

```bash
orb create -a amd64 rocky:9 gp-coordinator -c user-data.yml
orb create -a amd64 rocky:9 gp-segment1 -c user-data.yml
orb create -a amd64 rocky:9 gp-segment2 -c user-data.yml
orb create -a amd64 rocky:9 gp-segment3 -c user-data.yml
```


### 構成の確認

Coordinatorにログインします。

```bash
orb shell -m gp-coordinator -u gpadmin -w /home/gpadmin
```

```bash
# クラスタ状態確認
gpstate -s

# セグメント詳細確認
gpstate -e

# ミラー状態確認
gpstate -m
```

セグメント詳細確認は次のように表示されます。

```bash
$ gpstate -e
20260522:13:19:42:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-Starting gpstate with args: -e
20260522:13:19:42:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-local Greenplum Version: 'postgres (Greenplum Database) 7.8.1 build commit:2ac9af769d652200c3a913cc8ae39d54d7d72ded'
20260522:13:19:42:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-Coordinator Greenplum Version: 'PostgreSQL 12.22 (Greenplum Database 7.8.1 build commit:2ac9af769d652200c3a913cc8ae39d54d7d72ded) on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-11), 64-bit compiled on May 15 2026 07:32:19 Bhuvnesh C.'
20260522:13:19:42:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-Obtaining Segment details from coordinator...
20260522:13:19:42:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-Gathering data from segments...
.....
20260522:13:19:51:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-----------------------------------------------------
20260522:13:19:51:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-Segment Mirroring Status Report
20260522:13:19:54:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-----------------------------------------------------
20260522:13:19:54:014744 gpstate:gp-coordinator:gpadmin-[INFO]:-All segments are running normally
```

ミラー状態確認は次のように表示されます。

```bash
$ gpstate -m
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-Starting gpstate with args: -m
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-local Greenplum Version: 'postgres (Greenplum Database) 7.8.1 build commit:2ac9af769d652200c3a913cc8ae39d54d7d72ded'
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-Coordinator Greenplum Version: 'PostgreSQL 12.22 (Greenplum Database 7.8.1 build commit:2ac9af769d652200c3a913cc8ae39d54d7d72ded) on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-11), 64-bit compiled on May 15 2026 07:32:19 Bhuvnesh C.'
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-Obtaining Segment details from coordinator...
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:--------------------------------------------------------------
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:--Current GPDB mirror list and status
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:--Type = Group
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:--------------------------------------------------------------
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-   Mirror        Datadir               Port   Status    Data Status    
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-   gp-segment2   /data/mirror/gpseg0   7000   Passive   Synchronized
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-   gp-segment2   /data/mirror/gpseg1   7001   Passive   Synchronized
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-   gp-segment3   /data/mirror/gpseg2   7000   Passive   Synchronized
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-   gp-segment3   /data/mirror/gpseg3   7001   Passive   Synchronized
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-   gp-segment1   /data/mirror/gpseg4   7000   Passive   Synchronized
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:-   gp-segment1   /data/mirror/gpseg5   7001   Passive   Synchronized
20260522:13:20:16:014786 gpstate:gp-coordinator:gpadmin-[INFO]:--------------------------------------------------------------
```

`psql`でも構成を確認します。

```bash
psql -d postgres -c "SELECT content, role, preferred_role, hostname, port, datadir, status FROM gp_segment_configuration ORDER BY content, role;"
```

以下の構成になっていることを確認します。

```
 content | role | preferred_role |    hostname    | port |          datadir          | status 
---------+------+----------------+----------------+------+---------------------------+--------
      -1 | p    | p              | gp-coordinator | 5432 | /data/coordinator/gpseg-1 | u
       0 | m    | m              | gp-segment2    | 7000 | /data/mirror/gpseg0       | u
       0 | p    | p              | gp-segment1    | 6000 | /data/primary/gpseg0      | u
       1 | m    | m              | gp-segment2    | 7001 | /data/mirror/gpseg1       | u
       1 | p    | p              | gp-segment1    | 6001 | /data/primary/gpseg1      | u
       2 | m    | m              | gp-segment3    | 7000 | /data/mirror/gpseg2       | u
       2 | p    | p              | gp-segment2    | 6000 | /data/primary/gpseg2      | u
       3 | m    | m              | gp-segment3    | 7001 | /data/mirror/gpseg3       | u
       3 | p    | p              | gp-segment2    | 6001 | /data/primary/gpseg3      | u
       4 | m    | m              | gp-segment1    | 7000 | /data/mirror/gpseg4       | u
       4 | p    | p              | gp-segment3    | 6000 | /data/primary/gpseg4      | u
       5 | m    | m              | gp-segment1    | 7001 | /data/mirror/gpseg5       | u
       5 | p    | p              | gp-segment3    | 6001 | /data/primary/gpseg5      | u
(13 rows)
```

**構成のポイント:**

- 合計6つのプライマリセグメント（content 0-5）
- 各プライマリに対応するミラーが別ホストに配置
- 各サーバーに2プライマリ + 2ミラー = 計4インスタンス

### 動作確認

テストデータベースを作成します。

```bash
createdb test
```

テーブルを作成し、動作確認します。

```bash
psql -d test -c "CREATE TABLE test_table (id int, name text) DISTRIBUTED BY (id);"
psql -d test -c "INSERT INTO test_table SELECT generate_series(1,1000), 'test_data';"
psql -d test -c "SELECT gp_segment_id, count(*) FROM test_table GROUP BY gp_segment_id ORDER BY gp_segment_id;"
```

次のように6つのセグメント（0-5）にデータが分散されていることを確認してください。

```
 gp_segment_id | count 
---------------+-------
             0 |   172
             1 |   163
             2 |   182
             3 |   164
             4 |   168
             5 |   151
(6 rows)
```

---

以上でセットアップ完了です。
