RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \
jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \
libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \
- gmp-dev jsoncpp-dev postgresql-dev && \
+ gmp-dev jsoncpp-dev postgresql-dev ca-certificates && \
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
- rm -fr ./games/minetest_game/.git && \
- mkdir build && \
+ rm -fr ./games/minetest_game/.git
+
+WORKDIR /usr/src/
+RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
+ mkdir prometheus-cpp/build && \
+ cd prometheus-cpp/build && \
+ cmake .. \
+ -DCMAKE_INSTALL_PREFIX=/usr/local \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DENABLE_TESTING=0 && \
+ make -j2 && \
+ make install
+
+WORKDIR /usr/src/minetest
+RUN mkdir build && \
cd build && \
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SERVER=TRUE \
+ -DENABLE_PROMETHEUS=TRUE \
-DBUILD_UNITTESTS=FALSE \
-DBUILD_CLIENT=FALSE && \
make -j2 && \
USER minetest:minetest
-EXPOSE 30000/udp
+EXPOSE 30000/udp 30000/tcp
CMD ["/usr/local/bin/minetestserver", "--config", "/etc/minetest/minetest.conf"]
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
+ ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
ENABLE_SYSTEM_JSONCPP=OFF - Use JsonCPP from system
OPENGL_GL_PREFERENCE=LEGACY - Linux client build only; See CMake Policy CMP0072 for reference
# Note that the port field in the main menu overrides this setting.
remote_port (Remote port) int 30000 1 65535
+# Prometheus listener address.
+# If minetest is compiled with ENABLE_PROMETHEUS option enabled,
+# enable metrics listener for Prometheus on that address.
+# Metrics can be fetch on http://127.0.0.1:30000/metrics
+prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000
+
# Save the map received by the client on disk.
enable_local_map_saving (Saving map received from server) bool false
find_package(SQLite3 REQUIRED)
+OPTION(ENABLE_PROMETHEUS "Enable prometheus client support" FALSE)
+set(USE_PROMETHEUS FALSE)
+
+if(ENABLE_PROMETHEUS)
+ find_path(PROMETHEUS_CPP_INCLUDE_DIR NAMES prometheus/counter.h)
+ find_library(PROMETHEUS_PULL_LIBRARY NAMES prometheus-cpp-pull)
+ find_library(PROMETHEUS_CORE_LIBRARY NAMES prometheus-cpp-core)
+ if(PROMETHEUS_CPP_INCLUDE_DIR AND PROMETHEUS_PULL_LIBRARY AND PROMETHEUS_CORE_LIBRARY)
+ set(PROMETHEUS_LIBRARIES ${PROMETHEUS_PULL_LIBRARY} ${PROMETHEUS_CORE_LIBRARY})
+ set(USE_PROMETHEUS TRUE)
+ include_directories(${PROMETHEUS_CPP_INCLUDE_DIR})
+ endif(PROMETHEUS_CPP_INCLUDE_DIR AND PROMETHEUS_PULL_LIBRARY AND PROMETHEUS_CORE_LIBRARY)
+endif(ENABLE_PROMETHEUS)
+
+if(USE_PROMETHEUS)
+ message(STATUS "Prometheus client enabled.")
+else(USE_PROMETHEUS)
+ message(STATUS "Prometheus client disabled.")
+endif(USE_PROMETHEUS)
+
OPTION(ENABLE_SPATIAL "Enable SpatialIndex AreaStore backend" TRUE)
set(USE_SPATIAL FALSE)
if (USE_REDIS)
target_link_libraries(${PROJECT_NAME} ${REDIS_LIBRARY})
endif()
+ if (USE_PROMETHEUS)
+ target_link_libraries(${PROJECT_NAME} ${PROMETHEUS_LIBRARIES})
+ endif()
if (USE_SPATIAL)
target_link_libraries(${PROJECT_NAME} ${SPATIAL_LIBRARY})
endif()
if (USE_REDIS)
target_link_libraries(${PROJECT_NAME}server ${REDIS_LIBRARY})
endif()
+ if (USE_PROMETHEUS)
+ target_link_libraries(${PROJECT_NAME}server ${PROMETHEUS_LIBRARIES})
+ endif()
if (USE_SPATIAL)
target_link_libraries(${PROJECT_NAME}server ${SPATIAL_LIBRARY})
endif()
#cmakedefine01 USE_LEVELDB
#cmakedefine01 USE_LUAJIT
#cmakedefine01 USE_POSTGRESQL
+#cmakedefine01 USE_PROMETHEUS
#cmakedefine01 USE_SPATIAL
#cmakedefine01 USE_SYSTEM_GMP
#cmakedefine01 USE_REDIS
// Server
settings->setDefault("disable_escape_sequences", "false");
settings->setDefault("strip_color_codes", "false");
+#if USE_PROMETHEUS
+ settings->setDefault("prometheus_listener_address", "127.0.0.1:30000");
+#endif
// Network
settings->setDefault("enable_ipv6", "true");
{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *block = getBlockNoCreateNoEx(blockpos);
- return block && block->getIsUnderground();
+ return block && block->getIsUnderground();
}
bool Map::isValidPosition(v3s16 p)
ServerMap
*/
ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
- EmergeManager *emerge):
+ EmergeManager *emerge, MetricsBackend *mb):
Map(dout_server, gamedef),
settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
m_emerge(emerge)
m_savedir = savedir;
m_map_saving_enabled = false;
+ m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
+
try {
// If directory exists, check contents and load if possible
if (fs::PathExists(m_savedir)) {
return;
}
+ u64 start_time = porting::getTimeNs();
+
if(save_level == MOD_STATE_CLEAN)
infostream<<"ServerMap: Saving whole map, this can take time."
<<std::endl;
infostream<<"Blocks modified by: "<<std::endl;
modprofiler.print(infostream);
}
+
+ auto end_time = porting::getTimeNs();
+ m_save_time_counter->increment(end_time - start_time);
}
void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
#include "voxel.h"
#include "modifiedstate.h"
#include "util/container.h"
+#include "util/metricsbackend.h"
#include "nodetimer.h"
#include "map_settings_manager.h"
#include "debug.h"
class IGameDef;
class IRollbackManager;
class EmergeManager;
+class MetricsBackend;
class ServerEnvironment;
struct BlockMakeData;
/*
savedir: directory to which map data should be saved
*/
- ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge);
+ ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge, MetricsBackend *mb);
~ServerMap();
s32 mapType() const
bool m_map_metadata_changed = true;
MapDatabase *dbase = nullptr;
MapDatabase *dbase_ro = nullptr;
+
+ MetricCounterPtr m_save_time_counter;
};
m_nodedef(createNodeDefManager()),
m_craftdef(createCraftDefManager()),
m_thread(new ServerThread(this)),
- m_uptime(0),
m_clients(m_con),
m_admin_chat(iface),
m_modchannel_mgr(new ModChannelMgr())
{
- m_lag = g_settings->getFloat("dedicated_server_step");
-
if (m_path_world.empty())
throw ServerError("Supplied empty world path");
if (!gamespec.isValid())
throw ServerError("Supplied invalid gamespec");
+
+#if USE_PROMETHEUS
+ m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
+#else
+ m_metrics_backend = std::unique_ptr<MetricsBackend>(new MetricsBackend());
+#endif
+
+ m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
+ m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
+
+ m_timeofday_gauge = m_metrics_backend->addGauge(
+ "minetest_core_timeofday",
+ "Time of day value");
+
+ m_lag_gauge = m_metrics_backend->addGauge(
+ "minetest_core_latency",
+ "Latency value (in seconds)");
+
+ m_aom_buffer_counter = m_metrics_backend->addCounter(
+ "minetest_core_aom_generated_count",
+ "Number of active object messages generated");
+
+ m_packet_recv_counter = m_metrics_backend->addCounter(
+ "minetest_core_server_packet_recv",
+ "Processable packets received");
+
+ m_packet_recv_processed_counter = m_metrics_backend->addCounter(
+ "minetest_core_server_packet_recv_processed",
+ "Valid received packets processed");
+
+ m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
}
Server::~Server()
MutexAutoLock envlock(m_env_mutex);
// Create the Map (loads map_meta.txt, overriding configured mapgen params)
- ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge);
+ ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
// Initialize scripting
infostream << "Server: Initializing Lua" << std::endl;
/*
Update uptime
*/
- {
- m_uptime.set(m_uptime.get() + dtime);
- }
+ m_uptime_counter->increment(dtime);
handlePeerChanges();
*/
m_time_of_day_send_timer -= dtime;
- if(m_time_of_day_send_timer < 0.0) {
+ if (m_time_of_day_send_timer < 0.0) {
m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
u16 time = m_env->getTimeOfDay();
float time_speed = g_settings->getFloat("time_speed");
SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
+
+ m_timeofday_gauge->set(time);
}
{
}
m_clients.step(dtime);
- m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
+ m_lag_gauge->increment((m_lag_gauge->get() > dtime ? -1 : 1) * dtime/100);
#if USE_CURL
// send masterserver announce
{
ServerList::AA_START,
m_bind_addr.getPort(),
m_clients.getPlayerNames(),
- m_uptime.get(),
+ m_uptime_counter->get(),
m_env->getGameTime(),
- m_lag,
+ m_lag_gauge->get(),
m_gamespec.id,
Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
m_modmgr->getMods(),
const RemoteClientMap &clients = m_clients.getClientList();
ScopeProfiler sp(g_profiler, "Server: update objects within range");
+ m_player_gauge->set(clients.size());
for (const auto &client_it : clients) {
RemoteClient *client = client_it.second;
message_list->push_back(aom);
}
+ m_aom_buffer_counter->increment(buffered_messages.size());
+
m_clients.lock();
const RemoteClientMap &clients = m_clients.getClientList();
// Route data to every client
}
peer_id = pkt.getPeerId();
+ m_packet_recv_counter->increment();
ProcessData(&pkt);
+ m_packet_recv_processed_counter->increment();
} catch (const con::InvalidIncomingDataException &e) {
infostream << "Server::Receive(): InvalidIncomingDataException: what()="
<< e.what() << std::endl;
// Version
os << L"version=" << narrow_to_wide(g_version_string);
// Uptime
- os << L", uptime=" << m_uptime.get();
+ os << L", uptime=" << m_uptime_counter->get();
// Max lag estimate
os << L", max_lag=" << (m_env ? m_env->getMaxLagEstimate() : 0);
#include "util/numeric.h"
#include "util/thread.h"
#include "util/basic_macros.h"
+#include "util/metricsbackend.h"
#include "serverenvironment.h"
#include "clientiface.h"
#include "chatmessage.h"
// Connection must be locked when called
std::wstring getStatusString();
- inline double getUptime() const { return m_uptime.m_value; }
+ inline double getUptime() const { return m_uptime_counter->get(); }
// read shutdown state
inline bool isShutdownRequested() const { return m_shutdown_state.is_requested; }
float m_step_dtime = 0.0f;
std::mutex m_step_dtime_mutex;
- // current server step lag counter
- float m_lag;
-
// The server mainly operates in this thread
ServerThread *m_thread = nullptr;
*/
// Timer for sending time of day over network
float m_time_of_day_send_timer = 0.0f;
- // Uptime of server in seconds
- MutexedVariable<double> m_uptime;
/*
Client interface
// ModChannel manager
std::unique_ptr<ModChannelMgr> m_modchannel_mgr;
+
+ // Global server metrics backend
+ std::unique_ptr<MetricsBackend> m_metrics_backend;
+
+ // Server metrics
+ MetricCounterPtr m_uptime_counter;
+ MetricGaugePtr m_player_gauge;
+ MetricGaugePtr m_timeofday_gauge;
+ // current server step lag
+ MetricGaugePtr m_lag_gauge;
+ MetricCounterPtr m_aom_buffer_counter;
+ MetricCounterPtr m_packet_recv_counter;
+ MetricCounterPtr m_packet_recv_processed_counter;
};
/*
#include "scripting_server.h"
#include "content/subgames.h"
#include "porting.h"
+#include "util/metricsbackend.h"
/**
* Manage server mods
#pragma once
#include "content/mods.h"
+#include <memory>
+class MetricsBackend;
+class MetricCounter;
class ServerScripting;
/**
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/quicktune.cpp
--- /dev/null
+/*
+Minetest
+Copyright (C) 2013-2020 Minetest core developers team
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "metricsbackend.h"
+#if USE_PROMETHEUS
+#include <prometheus/exposer.h>
+#include <prometheus/registry.h>
+#include <prometheus/counter.h>
+#include <prometheus/gauge.h>
+#include "log.h"
+#include "settings.h"
+#endif
+
+MetricCounterPtr MetricsBackend::addCounter(
+ const std::string &name, const std::string &help_str)
+{
+ return std::make_shared<SimpleMetricCounter>(name, help_str);
+}
+
+MetricGaugePtr MetricsBackend::addGauge(
+ const std::string &name, const std::string &help_str)
+{
+ return std::make_shared<SimpleMetricGauge>(name, help_str);
+}
+
+#if USE_PROMETHEUS
+
+class PrometheusMetricCounter : public MetricCounter
+{
+public:
+ PrometheusMetricCounter() = delete;
+
+ PrometheusMetricCounter(const std::string &name, const std::string &help_str,
+ std::shared_ptr<prometheus::Registry> registry) :
+ MetricCounter(),
+ m_family(prometheus::BuildCounter()
+ .Name(name)
+ .Help(help_str)
+ .Register(*registry)),
+ m_counter(m_family.Add({}))
+ {
+ }
+
+ virtual ~PrometheusMetricCounter() {}
+
+ virtual void increment(double number) { m_counter.Increment(number); }
+ virtual double get() const { return m_counter.Value(); }
+
+private:
+ prometheus::Family<prometheus::Counter> &m_family;
+ prometheus::Counter &m_counter;
+};
+
+class PrometheusMetricGauge : public MetricGauge
+{
+public:
+ PrometheusMetricGauge() = delete;
+
+ PrometheusMetricGauge(const std::string &name, const std::string &help_str,
+ std::shared_ptr<prometheus::Registry> registry) :
+ MetricGauge(),
+ m_family(prometheus::BuildGauge()
+ .Name(name)
+ .Help(help_str)
+ .Register(*registry)),
+ m_gauge(m_family.Add({}))
+ {
+ }
+
+ virtual ~PrometheusMetricGauge() {}
+
+ virtual void increment(double number) { m_gauge.Increment(number); }
+ virtual void decrement(double number) { m_gauge.Decrement(number); }
+ virtual void set(double number) { m_gauge.Set(number); }
+ virtual double get() const { return m_gauge.Value(); }
+
+private:
+ prometheus::Family<prometheus::Gauge> &m_family;
+ prometheus::Gauge &m_gauge;
+};
+
+class PrometheusMetricsBackend : public MetricsBackend
+{
+public:
+ PrometheusMetricsBackend(const std::string &addr) :
+ MetricsBackend(), m_exposer(std::unique_ptr<prometheus::Exposer>(
+ new prometheus::Exposer(addr))),
+ m_registry(std::make_shared<prometheus::Registry>())
+ {
+ m_exposer->RegisterCollectable(m_registry);
+ }
+
+ virtual ~PrometheusMetricsBackend() {}
+
+ virtual MetricCounterPtr addCounter(
+ const std::string &name, const std::string &help_str);
+ virtual MetricGaugePtr addGauge(
+ const std::string &name, const std::string &help_str);
+
+private:
+ std::unique_ptr<prometheus::Exposer> m_exposer;
+ std::shared_ptr<prometheus::Registry> m_registry;
+};
+
+MetricCounterPtr PrometheusMetricsBackend::addCounter(
+ const std::string &name, const std::string &help_str)
+{
+ return std::make_shared<PrometheusMetricCounter>(name, help_str, m_registry);
+}
+
+MetricGaugePtr PrometheusMetricsBackend::addGauge(
+ const std::string &name, const std::string &help_str)
+{
+ return std::make_shared<PrometheusMetricGauge>(name, help_str, m_registry);
+}
+
+MetricsBackend *createPrometheusMetricsBackend()
+{
+ std::string addr;
+ g_settings->getNoEx("prometheus_listener_address", addr);
+ return new PrometheusMetricsBackend(addr);
+}
+
+#endif
--- /dev/null
+/*
+Minetest
+Copyright (C) 2013-2020 Minetest core developers team
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+#include <memory>
+#include "config.h"
+#include "util/thread.h"
+
+class MetricCounter
+{
+public:
+ MetricCounter() = default;
+
+ virtual ~MetricCounter() {}
+
+ virtual void increment(double number = 1.0) = 0;
+ virtual double get() const = 0;
+};
+
+typedef std::shared_ptr<MetricCounter> MetricCounterPtr;
+
+class SimpleMetricCounter : public MetricCounter
+{
+public:
+ SimpleMetricCounter() = delete;
+
+ virtual ~SimpleMetricCounter() {}
+
+ SimpleMetricCounter(const std::string &name, const std::string &help_str) :
+ MetricCounter(), m_name(name), m_help_str(help_str),
+ m_counter(0.0)
+ {
+ }
+
+ virtual void increment(double number)
+ {
+ MutexAutoLock lock(m_mutex);
+ m_counter += number;
+ }
+ virtual double get() const
+ {
+ MutexAutoLock lock(m_mutex);
+ return m_counter;
+ }
+
+private:
+ std::string m_name;
+ std::string m_help_str;
+
+ mutable std::mutex m_mutex;
+ double m_counter;
+};
+
+class MetricGauge
+{
+public:
+ MetricGauge() = default;
+ virtual ~MetricGauge() {}
+
+ virtual void increment(double number = 1.0) = 0;
+ virtual void decrement(double number = 1.0) = 0;
+ virtual void set(double number) = 0;
+ virtual double get() const = 0;
+};
+
+typedef std::shared_ptr<MetricGauge> MetricGaugePtr;
+
+class SimpleMetricGauge : public MetricGauge
+{
+public:
+ SimpleMetricGauge() = delete;
+
+ SimpleMetricGauge(const std::string &name, const std::string &help_str) :
+ MetricGauge(), m_name(name), m_help_str(help_str), m_gauge(0.0)
+ {
+ }
+
+ virtual ~SimpleMetricGauge() {}
+
+ virtual void increment(double number)
+ {
+ MutexAutoLock lock(m_mutex);
+ m_gauge += number;
+ }
+ virtual void decrement(double number)
+ {
+ MutexAutoLock lock(m_mutex);
+ m_gauge -= number;
+ }
+ virtual void set(double number)
+ {
+ MutexAutoLock lock(m_mutex);
+ m_gauge = number;
+ }
+ virtual double get() const
+ {
+ MutexAutoLock lock(m_mutex);
+ return m_gauge;
+ }
+
+private:
+ std::string m_name;
+ std::string m_help_str;
+
+ mutable std::mutex m_mutex;
+ double m_gauge;
+};
+
+class MetricsBackend
+{
+public:
+ MetricsBackend() = default;
+
+ virtual ~MetricsBackend() {}
+
+ virtual MetricCounterPtr addCounter(
+ const std::string &name, const std::string &help_str);
+ virtual MetricGaugePtr addGauge(
+ const std::string &name, const std::string &help_str);
+};
+
+#if USE_PROMETHEUS
+MetricsBackend *createPrometheusMetricsBackend();
+#endif
--- /dev/null
+#! /bin/bash -eu
+
+cd /tmp
+git clone --recursive https://github.com/jupp0r/prometheus-cpp
+mkdir prometheus-cpp/build
+cd prometheus-cpp/build
+cmake .. \
+ -DCMAKE_INSTALL_PREFIX=/usr/local \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DENABLE_TESTING=0
+make -j2
+sudo make install
+