Disable bootstrap checks for single-node discovery

While there are use-cases where a single-node is in production, there
are also use-cases for starting a single-node that binds transport to an
external interface where the node is not in production (for example, for
testing the transport client against a node started in a Docker
container). It's tricky to balance the desire to always enforce the
bootstrap checks when a node might be in production with the need for
the community to perform testing in situations that would trip the
bootstrap checks. This commit enables some flexibility for these
users. By setting the discovery type to "single-node", we disable the
bootstrap checks independently of how transport is bound. While this
sounds like a hole in the bootstrap checks, the bootstrap checks can
already be avoided in the single-node use-case by binding only HTTP but
not transport. For users that are genuinely in production on a
single-node use-case with transport bound to an external use-case, they
can set the system property "es.enable.bootstrap.checks" to force
running the bootstrap checks. It would be a mistake for them not to do
this.

Relates #23598
This commit is contained in:
Jason Tedor 2017-04-04 09:39:04 -04:00 committed by GitHub
parent c14be20744
commit 51b5dbffb7
3 changed files with 76 additions and 52 deletions

View File

@ -29,6 +29,7 @@ import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.BoundTransportAddress;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.discovery.DiscoveryModule;
import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.monitor.jvm.JvmInfo;
import org.elasticsearch.monitor.process.ProcessProbe; import org.elasticsearch.monitor.process.ProcessProbe;
import org.elasticsearch.node.Node; import org.elasticsearch.node.Node;
@ -73,7 +74,7 @@ final class BootstrapChecks {
final List<BootstrapCheck> combinedChecks = new ArrayList<>(builtInChecks); final List<BootstrapCheck> combinedChecks = new ArrayList<>(builtInChecks);
combinedChecks.addAll(additionalChecks); combinedChecks.addAll(additionalChecks);
check( check(
enforceLimits(boundTransportAddress), enforceLimits(boundTransportAddress, DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings)),
Collections.unmodifiableList(combinedChecks), Collections.unmodifiableList(combinedChecks),
Node.NODE_NAME_SETTING.get(settings)); Node.NODE_NAME_SETTING.get(settings));
} }
@ -166,11 +167,13 @@ final class BootstrapChecks {
* @param boundTransportAddress the node network bindings * @param boundTransportAddress the node network bindings
* @return {@code true} if the checks should be enforced * @return {@code true} if the checks should be enforced
*/ */
static boolean enforceLimits(final BoundTransportAddress boundTransportAddress) { static boolean enforceLimits(final BoundTransportAddress boundTransportAddress, final String discoveryType) {
Predicate<TransportAddress> isLoopbackOrLinkLocalAddress = final Predicate<TransportAddress> isLoopbackOrLinkLocalAddress =
t -> t.address().getAddress().isLinkLocalAddress() || t.address().getAddress().isLoopbackAddress(); t -> t.address().getAddress().isLinkLocalAddress() || t.address().getAddress().isLoopbackAddress();
return !(Arrays.stream(boundTransportAddress.boundAddresses()).allMatch(isLoopbackOrLinkLocalAddress) && final boolean bound =
!(Arrays.stream(boundTransportAddress.boundAddresses()).allMatch(isLoopbackOrLinkLocalAddress) &&
isLoopbackOrLinkLocalAddress.test(boundTransportAddress.publishAddress())); isLoopbackOrLinkLocalAddress.test(boundTransportAddress.publishAddress()));
return bound && !"single-node".equals(discoveryType);
} }
// the list of checks to execute // the list of checks to execute

View File

@ -44,6 +44,7 @@ import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.hasToString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
@ -98,7 +99,9 @@ public class BootstrapChecksTests extends ESTestCase {
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0])); when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress); when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
assertTrue(BootstrapChecks.enforceLimits(boundTransportAddress)); final String discoveryType = randomFrom("zen", "single-node");
assertEquals(BootstrapChecks.enforceLimits(boundTransportAddress, discoveryType), !"single-node".equals(discoveryType));
} }
public void testEnforceLimitsWhenPublishingToNonLocalAddress() { public void testEnforceLimitsWhenPublishingToNonLocalAddress() {
@ -114,7 +117,9 @@ public class BootstrapChecksTests extends ESTestCase {
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0])); when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress); when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
assertTrue(BootstrapChecks.enforceLimits(boundTransportAddress)); final String discoveryType = randomFrom("zen", "single-node");
assertEquals(BootstrapChecks.enforceLimits(boundTransportAddress, discoveryType), !"single-node".equals(discoveryType));
} }
public void testExceptionAggregation() { public void testExceptionAggregation() {

View File

@ -31,16 +31,31 @@ Elasticsearch instances must be reachable via transport communication so
they must bind transport to an external interface. Thus, we consider an they must bind transport to an external interface. Thus, we consider an
Elasticsearch instance to be in development mode if it does not bind Elasticsearch instance to be in development mode if it does not bind
transport to an external interface (the default), and is otherwise in transport to an external interface (the default), and is otherwise in
production mode if it does bind transport to an external interface. Note production mode if it does bind transport to an external interface.
that HTTP can be configured independently of transport via
Note that HTTP can be configured independently of transport via
<<modules-http,`http.host`>> and <<modules-transport,`transport.host`>>; <<modules-http,`http.host`>> and <<modules-transport,`transport.host`>>;
this can be useful for configuring a single instance to be reachable via this can be useful for configuring a single instance to be reachable via
HTTP for testing purposes without triggering production mode. If you do HTTP for testing purposes without triggering production mode.
want to force enforcement of the bootstrap checks independent of the
binding of the transport protocal, you can set the system property We recognize that some users need to bind transport to an external
`es.enforce.bootstrap.checks` to `true` (this can be useful on a interface for testing their usage of the transport client. For this
single-node production system that does not bind transport to an external situation, we provide the discovery type `single-node` (configure it by
interface). setting `discovery.type` to `single-node`); in this situation, a node
will elect itself master and will not form a cluster with any other
node.
If you are running a single node in production, it is possible to evade
the bootstrap checks (either by not binding transport to an external
interface, or by binding transport to an external interface and setting
the discovery type to `single-node`). For this situation, you can force
execution of the bootstrap checks by setting the system property
`es.enforce.bootstrap.checks` to `true` (set this in <<jvm-options>>, or
by adding `-Des.enforce.bootstrap.checks=true` to the environment
variable `ES_JAVA_OPTS`). We strongly encourage you to do this if you
are in this specific situation. This system property can be used to
force execution of the bootstrap checks independent of the node
configuration.
=== Heap size check === Heap size check
@ -48,11 +63,11 @@ If a JVM is started with unequal initial and max heap size, it can be
prone to pauses as the JVM heap is resized during system usage. To avoid prone to pauses as the JVM heap is resized during system usage. To avoid
these resize pauses, it's best to start the JVM with the initial heap these resize pauses, it's best to start the JVM with the initial heap
size equal to the maximum heap size. Additionally, if size equal to the maximum heap size. Additionally, if
<<bootstrap.memory_lock,`bootstrap.memory_lock`>> is enabled, the JVM will <<bootstrap.memory_lock,`bootstrap.memory_lock`>> is enabled, the JVM
lock the initial size of the heap on startup. If the initial heap size will lock the initial size of the heap on startup. If the initial heap
is not equal to the maximum heap size, after a resize it will not be the size is not equal to the maximum heap size, after a resize it will not
case that all of the JVM heap is locked in memory. To pass the heap size be the case that all of the JVM heap is locked in memory. To pass the
check, you must configure the <<heap-size,heap size>>. heap size check, you must configure the <<heap-size,heap size>>.
=== File descriptor check === File descriptor check
@ -76,13 +91,13 @@ Elasticsearch would much rather use to service requests. There are
several ways to configure a system to disallow swapping. One way is by several ways to configure a system to disallow swapping. One way is by
requesting the JVM to lock the heap in memory through `mlockall` (Unix) requesting the JVM to lock the heap in memory through `mlockall` (Unix)
or virtual lock (Windows). This is done via the Elasticsearch setting or virtual lock (Windows). This is done via the Elasticsearch setting
<<bootstrap.memory_lock,`bootstrap.memory_lock`>>. However, there are cases <<bootstrap.memory_lock,`bootstrap.memory_lock`>>. However, there are
where this setting can be passed to Elasticsearch but Elasticsearch is cases where this setting can be passed to Elasticsearch but
not able to lock the heap (e.g., if the `elasticsearch` user does not Elasticsearch is not able to lock the heap (e.g., if the `elasticsearch`
have `memlock unlimited`). The memory lock check verifies that *if* the user does not have `memlock unlimited`). The memory lock check verifies
`bootstrap.memory_lock` setting is enabled, that the JVM was successfully that *if* the `bootstrap.memory_lock` setting is enabled, that the JVM
able to lock the heap. To pass the memory lock check, you might have to was successfully able to lock the heap. To pass the memory lock check,
configure <<mlockall,`mlockall`>>. you might have to configure <<mlockall,`mlockall`>>.
=== Maximum number of threads check === Maximum number of threads check
@ -139,29 +154,30 @@ the server VM.
=== Use serial collector check === Use serial collector check
There are various garbage collectors for the OpenJDK-derived JVMs targeting There are various garbage collectors for the OpenJDK-derived JVMs
different workloads. The serial collector in particular is best suited for targeting different workloads. The serial collector in particular is
single logical CPU machines or extremely small heaps, neither of which are best suited for single logical CPU machines or extremely small heaps,
suitable for running Elasticsearch. Using the serial collector with neither of which are suitable for running Elasticsearch. Using the
Elasticsearch can be devastating for performance. The serial collector check serial collector with Elasticsearch can be devastating for performance.
ensures that Elasticsearch is not configured to run with the serial The serial collector check ensures that Elasticsearch is not configured
collector. To pass the serial collector check, you must not start Elasticsearch to run with the serial collector. To pass the serial collector check,
with the serial collector (whether it's from the defaults for the JVM that you must not start Elasticsearch with the serial collector (whether it's
you're using, or you've explicitly specified it with `-XX:+UseSerialGC`). Note from the defaults for the JVM that you're using, or you've explicitly
that the default JVM configuration that ship with Elasticsearch configures specified it with `-XX:+UseSerialGC`). Note that the default JVM
Elasticsearch to use the CMS collector. configuration that ship with Elasticsearch configures Elasticsearch to
use the CMS collector.
=== System call filter check === System call filter check
Elasticsearch installs system call filters of various flavors depending
Elasticsearch installs system call filters of various flavors depending on the on the operating system (e.g., seccomp on Linux). These system call
operating system (e.g., seccomp on Linux). These system call filters are filters are installed to prevent the ability to execute system calls
installed to prevent the ability to execute system calls related to forking as related to forking as a defense mechanism against arbitrary code
a defense mechanism against arbitrary code execution attacks on Elasticsearch execution attacks on Elasticsearch The system call filter check ensures
The system call filter check ensures that if system call filters are enabled, that if system call filters are enabled, then they were successfully
then they were successfully installed. To pass the system call filter check you installed. To pass the system call filter check you must either fix any
must either fix any configuration errors on your system that prevented system configuration errors on your system that prevented system call filters
call filters from installing (check your logs), or *at your own risk* disable from installing (check your logs), or *at your own risk* disable system
system call filters by setting `bootstrap.system_call_filter` to `false`. call filters by setting `bootstrap.system_call_filter` to `false`.
=== OnError and OnOutOfMemoryError checks === OnError and OnOutOfMemoryError checks
@ -188,8 +204,8 @@ release build of the JVM.
=== G1GC check === G1GC check
Early versions of the HotSpot JVM that shipped with JDK 8 are known to have Early versions of the HotSpot JVM that shipped with JDK 8 are known to
issues that can lead to index corruption when the G1GC collector is enabled. have issues that can lead to index corruption when the G1GC collector is
The versions impacted are those earlier than the version of HotSpot that enabled. The versions impacted are those earlier than the version of
shipped with JDK 8u40. The G1GC check detects these early versions of the HotSpot that shipped with JDK 8u40. The G1GC check detects these early
HotSpot JVM. versions of the HotSpot JVM.