如何用Puppet管理Ambari Server并调用其API启动服务?
Got it, this is a common friction point when moving from Ansible (which plays nice with imperative API calls) to Puppet's declarative, resource-focused model. The key is bridging that gap between Puppet's "state desired" approach and the imperative API calls needed to control Ambari services. Here are your best options:
1. Use exec Resources (Quickest Fix)
Puppet's exec resource lets you run arbitrary commands, and you can add guard clauses (onlyif/unless) to ensure it only runs when the service isn't already in the desired state—keeping things idempotent, which is critical for Puppet.
Example: Start Hive via Ambari API with curl
# Define Ambari credentials (store these in Hiera with encryption for production!) $ambari_host = 'your-ambari-server.example.com' $ambari_port = '8080' $ambari_user = 'admin' $ambari_pass = 'your-secure-password' $cluster_name = 'your-cluster' exec 'start_hive_service' { command => "curl -u ${ambari_user}:${ambari_pass} -X PUT -H 'X-Requested-By: ambari' http://${ambari_host}:${ambari_port}/api/v1/clusters/${cluster_name}/services/HIVE -d '{\"ServiceInfo\":{\"state\":\"STARTED\"}}'", path => ['/usr/bin', '/bin'], # Only run if Hive is NOT already started onlyif => "curl -s -u ${ambari_user}:${ambari_pass} http://${ambari_host}:${ambari_port}/api/v1/clusters/${cluster_name}/services/HIVE | grep -q '\"state\":\"INSTALLED\"'", # Allow time for Ambari to process the start request timeout => 300, }
You can replicate this pattern for HDFS, SmartSense, or any other Ambari-managed service—just swap out the service name in the API endpoints and onlyif check.
2. Build a Custom Puppet Type/Provider (Cleanest, Most Puppet-Idiomatic)
For a scalable, maintainable solution, wrap Ambari API interactions into a custom declarative resource. This lets you define service states just like you would with Puppet's native service resource.
Step 1: Define the Custom Type
Save this in modules/ambari/lib/puppet/type/ambari_service.rb:
Puppet::Type.newtype(:ambari_service) do @doc = 'Manages the state of an Ambari-managed service' ensurable do newvalue(:running) do provider.start end newvalue(:stopped) do provider.stop end defaultto(:running) end newparam(:name) do desc 'The name of the Ambari service (e.g., HIVE, HDFS, SMARTSENSE)' isnamevar end newparam(:cluster_name) do desc 'Name of the Ambari cluster' isrequired end newparam(:ambari_host) do desc 'Ambari server hostname/IP' isrequired end newparam(:ambari_port) do desc 'Ambari server port' defaultto('8080') end newparam(:username) do desc 'Ambari admin username' isrequired end newparam(:password) do desc 'Ambari admin password' isrequired end end
Step 2: Implement the Provider
Save this in modules/ambari/lib/puppet/provider/ambari_service/ruby.rb:
require 'net/http' require 'json' Puppet::Type.type(:ambari_service).provide(:ruby) do def exists? # Check current service state via Ambari API uri = URI("http://#{resource[:ambari_host]}:#{resource[:ambari_port]}/api/v1/clusters/#{resource[:cluster_name]}/services/#{resource[:name]}") req = Net::HTTP::Get.new(uri) req.basic_auth(resource[:username], resource[:password]) req['X-Requested-By'] = 'ambari' res = Net::HTTP.start(uri.hostname, uri.port) do |http| http.request(req) end state = JSON.parse(res.body)['ServiceInfo']['state'] state == 'STARTED' end def start uri = URI("http://#{resource[:ambari_host]}:#{resource[:ambari_port]}/api/v1/clusters/#{resource[:cluster_name]}/services/#{resource[:name]}") req = Net::HTTP::Put.new(uri) req.basic_auth(resource[:username], resource[:password]) req['X-Requested-By'] = 'ambari' req['Content-Type'] = 'application/json' req.body = { 'ServiceInfo' => { 'state' => 'STARTED' } }.to_json Net::HTTP.start(uri.hostname, uri.port) do |http| http.request(req) end end def stop # Similar to start, but set state to 'STOPPED' uri = URI("http://#{resource[:ambari_host]}:#{resource[:ambari_port]}/api/v1/clusters/#{resource[:cluster_name]}/services/#{resource[:name]}") req = Net::HTTP::Put.new(uri) req.basic_auth(resource[:username], resource[:password]) req['X-Requested-By'] = 'ambari' req['Content-Type'] = 'application/json' req.body = { 'ServiceInfo' => { 'state' => 'STOPPED' } }.to_json Net::HTTP.start(uri.hostname, uri.port) do |http| http.request(req) end end end
Step 3: Use the Custom Resource in Your Manifest
ambari_service { 'HIVE': ensure => running, cluster_name => 'your-cluster', ambari_host => 'your-ambari-server.example.com', username => 'admin', password => 'your-secure-password', } ambari_service { 'HDFS': ensure => running, cluster_name => 'your-cluster', ambari_host => 'your-ambari-server.example.com', username => 'admin', password => 'your-secure-password', }
This approach fits perfectly with Puppet's declarative model—you define the desired state, and the provider handles the API calls and state checks automatically.
3. Leverage Community Modules (If Available)
Check the Puppet Forge for maintained Ambari modules (e.g., puppet-ambari). These often include pre-built resources for managing services out of the box, saving you from writing custom code. Just verify compatibility with your Ambari version before using.
Key Notes for All Solutions
- Idempotency is Non-Negotiable: Always add checks (
onlyiffor exec,exists?for custom providers) to avoid repeating API calls unnecessarily. - Secure Credentials: Never hardcode passwords in manifests. Use Hiera with encryption (e.g., eyaml) or Puppet Vault to store sensitive data.
- Timeout Handling: Ambari service starts can take several minutes—set appropriate timeouts to avoid Puppet failing prematurely.
内容的提问来源于stack exchange,提问作者L.HAMES

