diff --git a/ansible/group_vars/sol_rpc.yml b/ansible/group_vars/sol_rpc.yml new file mode 100644 index 0000000..f590c15 --- /dev/null +++ b/ansible/group_vars/sol_rpc.yml @@ -0,0 +1,31 @@ +solana_user: solana +solana_group: solana +solana_home: /var/lib/solana + +solana_validator_bin: /opt/solana/bin/agave-validator +solana_rpc_service_name: solana-rpc + +solana_identity_path: /var/lib/solana/identity.json +solana_ledger_dir: /var/lib/solana/ledger +solana_accounts_dir: /var/lib/solana/accounts +solana_log_dir: /var/log/solana + +solana_rpc_bind_address: 10.10.0.2 +solana_rpc_port: 8899 +solana_rpc_pubsub_port: 8900 +solana_dynamic_port_range: "8000-8020" + +solana_entrypoints: + - entrypoint.mainnet-beta.solana.com:8001 +solana_known_validators: [] + +solana_geyser_enabled: false +solana_geyser_plugin_config_path: /etc/solana/yellowstone-geyser.json + +solana_rpc_extra_args: + - --full-rpc-api + - --limit-ledger-size + +solana_rpc_manage_service: true +solana_rpc_enable_on_boot: true +solana_rpc_start_now: true diff --git a/ansible/playbooks/doc-rpc-sol-min.yml b/ansible/playbooks/doc-rpc-sol-min.yml index 140fbc2..8b21ee6 100644 --- a/ansible/playbooks/doc-rpc-sol-min.yml +++ b/ansible/playbooks/doc-rpc-sol-min.yml @@ -16,6 +16,32 @@ update_cache: true when: ansible_facts.os_family == "Debian" + - name: Install Solana host base packages (Debian/Ubuntu) + ansible.builtin.apt: + name: + - chrony + - curl + - jq + - smartmontools + - nvme-cli + - prometheus-node-exporter + state: present + update_cache: true + when: ansible_facts.os_family == "Debian" + + - name: Ensure solana group exists + ansible.builtin.group: + name: "{{ solana_group }}" + system: true + + - name: Ensure solana user exists + ansible.builtin.user: + name: "{{ solana_user }}" + group: "{{ solana_group }}" + home: "{{ solana_home }}" + system: true + create_home: true + - name: Ensure root config directories exist ansible.builtin.file: path: "{{ item }}" @@ -29,6 +55,20 @@ - /root/.config/nvim - /root/.config/nvim/lua + - name: Ensure Solana directories exist + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + owner: "{{ item.owner }}" + group: "{{ item.group }}" + mode: "{{ item.mode }}" + loop: + - { path: "/etc/solana", owner: "root", group: "root", mode: "0755" } + - { path: "{{ solana_home }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" } + - { path: "{{ solana_ledger_dir }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" } + - { path: "{{ solana_accounts_dir }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" } + - { path: "{{ solana_log_dir }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" } + - name: Deploy tmux config (Ctrl+a prefix) ansible.builtin.copy: src: ../files/operator-dotfiles/tmux.conf @@ -64,6 +104,50 @@ - { src: "lua/utils.lua", dest: "lua/utils.lua" } - { src: "lua/hazard3_dap.lua", dest: "lua/hazard3_dap.lua" } + - name: Deploy solana-rpc systemd unit (runs as solana user) + ansible.builtin.template: + src: ../templates/solana-rpc.service.j2 + dest: /etc/systemd/system/{{ solana_rpc_service_name }}.service + owner: root + group: root + mode: "0644" + register: solana_rpc_unit + + - name: Reload systemd after unit change + ansible.builtin.systemd: + daemon_reload: true + when: solana_rpc_unit.changed + + - name: Check validator binary exists + ansible.builtin.stat: + path: "{{ solana_validator_bin }}" + register: solana_validator_bin_stat + + - name: Check identity key exists + ansible.builtin.stat: + path: "{{ solana_identity_path }}" + register: solana_identity_stat + + - name: Ensure solana-rpc service state when prerequisites exist + ansible.builtin.systemd: + name: "{{ solana_rpc_service_name }}" + enabled: "{{ solana_rpc_enable_on_boot | bool }}" + state: "{{ 'started' if (solana_rpc_start_now | bool) else 'stopped' }}" + when: + - solana_rpc_manage_service | bool + - solana_validator_bin_stat.stat.exists + - solana_identity_stat.stat.exists + + - name: Report skipped solana-rpc start due to missing prerequisites + ansible.builtin.debug: + msg: + - "solana-rpc start skipped: missing prerequisites" + - "validator_bin={{ solana_validator_bin }} exists={{ solana_validator_bin_stat.stat.exists }}" + - "identity={{ solana_identity_path }} exists={{ solana_identity_stat.stat.exists }}" + when: + - solana_rpc_manage_service | bool + - not (solana_validator_bin_stat.stat.exists and solana_identity_stat.stat.exists) + - name: Validate Ansible transport ansible.builtin.ping: diff --git a/ansible/templates/solana-rpc.service.j2 b/ansible/templates/solana-rpc.service.j2 new file mode 100644 index 0000000..c771f71 --- /dev/null +++ b/ansible/templates/solana-rpc.service.j2 @@ -0,0 +1,35 @@ +[Unit] +Description=Solana RPC node (Agave) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User={{ solana_user }} +Group={{ solana_group }} +WorkingDirectory={{ solana_home }} +Environment=RUST_LOG=info +LimitNOFILE=1048576 +Restart=always +RestartSec=5 +TimeoutStopSec=120 +ExecStart={{ solana_validator_bin }} \ + --identity {{ solana_identity_path }} \ + --ledger {{ solana_ledger_dir }} \ + --accounts {{ solana_accounts_dir }} \ + --rpc-bind-address {{ solana_rpc_bind_address }} \ + --rpc-port {{ solana_rpc_port }} \ + --rpc-pubsub-port {{ solana_rpc_pubsub_port }} \ + --dynamic-port-range {{ solana_dynamic_port_range }}{% for ep in solana_entrypoints %} \ + --entrypoint {{ ep }}{% endfor %}{% for kv in solana_known_validators %} \ + --known-validator {{ kv }}{% endfor %}{% if solana_geyser_enabled | bool %} \ + --geyser-plugin-config {{ solana_geyser_plugin_config_path }}{% endif %}{% for arg in solana_rpc_extra_args %} \ + {{ arg }}{% endfor %} \ + --log {{ solana_log_dir }}/validator.log +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ReadWritePaths={{ solana_ledger_dir }} {{ solana_accounts_dir }} {{ solana_log_dir }} /var/lib/solana + +[Install] +WantedBy=multi-user.target diff --git a/doc/etap-005-solana-rpc-user-svc.md b/doc/etap-005-solana-rpc-user-svc.md new file mode 100644 index 0000000..36084d8 --- /dev/null +++ b/doc/etap-005-solana-rpc-user-svc.md @@ -0,0 +1,22 @@ +# Etap 005: Solana RPC jako usługa użytkownika `solana` + +Cel etapu: przygotować i wdrożyć baseline pod `solana-rpc`, uruchamiany jako dedykowany użytkownik systemowy `solana` (nie `root`). + +## Zakres + +1. Rozszerzyć playbook o: + - pakiety bazowe dla hosta RPC, + - utworzenie użytkownika i katalogów `solana`, + - deployment unitu `systemd` `solana-rpc.service` z `User=solana`. +2. Dodać zmienne grupowe `sol_rpc` (ścieżki, porty, opcje startu). +3. Dodać bezpieczną logikę startu: + - unit jest wdrażany zawsze, + - start usługi tylko gdy istnieją wymagane artefakty (`agave-validator`, identity keypair). +4. Wdrożyć na `mevnode` i zweryfikować stan. + +## Kryteria akceptacji + +- `id solana` istnieje na `mevnode`. +- `/etc/systemd/system/solana-rpc.service` istnieje i zawiera `User=solana`. +- Playbook kończy się bez błędów. +- Jeśli binarka/identity nie istnieją, playbook raportuje to jawnie i nie wymusza startu.