Enable the heatlh endpoints under a flag
This commit is contained in:
parent
6249e34177
commit
59d9e3e3ee
19 changed files with 211 additions and 21 deletions
|
@ -6,7 +6,7 @@
|
|||
<@tmpl.guide
|
||||
title="Running Keycloak in a container"
|
||||
summary="Learn how to run Keycloak from a container image"
|
||||
includedOptions="db db-url db-username db-password features hostname https-key-store-file https-key-store-password metrics-enabled">
|
||||
includedOptions="db db-url db-username db-password features hostname https-key-store-file https-key-store-password health-enabled metrics-enabled">
|
||||
|
||||
Keycloak handles containerized environments such as Kubernetes or OpenShift as first-class citizens. This guide describes how to optimize and run the Keycloak container image to provide the best experience running a Keycloak container.
|
||||
|
||||
|
@ -14,13 +14,14 @@ Keycloak handles containerized environments such as Kubernetes or OpenShift as f
|
|||
For the best start up of your Keycloak container, build an optimized container image by running the `build` step before starting.
|
||||
|
||||
=== Building your optimized Keycloak docker image
|
||||
The following `Dockerfile` creates a pre-configured Keycloak image that enables the metrics endpoint, enables the token exchange feature, and uses a PostgreSQL database.
|
||||
The following `Dockerfile` creates a pre-configured Keycloak image that enables the health and metrics endpoints, enables the token exchange feature, and uses a PostgreSQL database.
|
||||
|
||||
.Dockerfile:
|
||||
[source, dockerfile]
|
||||
----
|
||||
FROM quay.io/keycloak/keycloak:latest as builder
|
||||
|
||||
ENV KC_HEALTH_ENABLED=true
|
||||
ENV KC_METRICS_ENABLED=true
|
||||
ENV KC_FEATURES=token-exchange
|
||||
ENV KC_DB=postgres
|
||||
|
@ -71,6 +72,8 @@ INFO [org.key.com.Profile] (main) Preview feature enabled: token_exchange
|
|||
----
|
||||
This message shows the desired feature is enabled.
|
||||
|
||||
Health check endpoints are available at `https://localhost:8443/health`, `https://localhost:8443/health/ready` and `https://localhost:8443/health/live`.
|
||||
|
||||
Opening up `https://localhost:8443/metrics` leads to a page containing operational metrics that could be used by your monitoring solution.
|
||||
|
||||
== Trying Keycloak in development mode
|
||||
|
|
39
docs/guides/src/main/server/health.adoc
Normal file
39
docs/guides/src/main/server/health.adoc
Normal file
|
@ -0,0 +1,39 @@
|
|||
<#import "/templates/guide.adoc" as tmpl>
|
||||
<#import "/templates/kc.adoc" as kc>
|
||||
<#import "/templates/options.adoc" as opts>
|
||||
<#import "/templates/links.adoc" as links>
|
||||
|
||||
<@tmpl.guide
|
||||
title="Enabling Keycloak Health checks"
|
||||
summary="Learn how to enable and use Keycloak health checks"
|
||||
includedOptions="health-enabled">
|
||||
|
||||
Keycloak has built in support for health checks. This guide describes how to enable and use the Keycloak health checks.
|
||||
|
||||
== Keycloak Health checks
|
||||
|
||||
Keycloak exposed health endpoints are three:
|
||||
|
||||
* `/health`
|
||||
* `/health/live`
|
||||
* `/health/ready`
|
||||
|
||||
The result is returned in json format and it looks as follows:
|
||||
[source, json]
|
||||
----
|
||||
{
|
||||
"status": "UP",
|
||||
"checks": [
|
||||
{
|
||||
"name": "Keycloak database connections health check",
|
||||
"status": "UP"
|
||||
}
|
||||
]
|
||||
}
|
||||
----
|
||||
|
||||
== Enabling the health checks
|
||||
Is possible to enable the health checks using the build time option `health-enabled`:
|
||||
<@kc.build parameters="--health-enabled=true"/>
|
||||
|
||||
</@tmpl.guide>
|
|
@ -88,6 +88,16 @@ The following table shows the recommended paths to expose.
|
|||
|Yes
|
||||
|Search engine rules
|
||||
|
||||
|/metrics
|
||||
|-
|
||||
|No
|
||||
|Exposed metrics lead to an unnecessary attack vector.
|
||||
|
||||
|/health
|
||||
|-
|
||||
|No
|
||||
|Exposed health checks lead to an unnecessary attack vector.
|
||||
|
||||
|===
|
||||
We assume you run Keycloak on the root path `/` on your reverse proxy/gateway's public API.
|
||||
If not, prefix the path with your desired one.
|
||||
|
|
|
@ -33,6 +33,6 @@ public final class Constants {
|
|||
);
|
||||
|
||||
public static final Map<String, String> DEFAULT_DIST_CONFIG = Map.of(
|
||||
"KC_METRICS_ENABLED", "true"
|
||||
"KC_HEALTH_ENABLED", "true"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -400,15 +400,13 @@ class KeycloakProcessor {
|
|||
@Record(ExecutionTime.STATIC_INIT)
|
||||
@BuildStep
|
||||
void initializeMetrics(KeycloakRecorder recorder, BuildProducer<RouteBuildItem> routes, NonApplicationRootPathBuildItem nonAppRootPath) {
|
||||
Handler<RoutingContext> healthHandler;
|
||||
final Handler<RoutingContext> healthHandler = (isHealthEnabled()) ? new SmallRyeHealthHandler() : new NotFoundHandler();
|
||||
Handler<RoutingContext> metricsHandler;
|
||||
|
||||
if (isMetricsEnabled()) {
|
||||
healthHandler = new SmallRyeHealthHandler();
|
||||
String rootPath = nonAppRootPath.getNormalizedHttpRootPath();
|
||||
metricsHandler = recorder.createMetricsHandler(rootPath.concat(DEFAULT_METRICS_ENDPOINT).replace("//", "/"));
|
||||
} else {
|
||||
healthHandler = new NotFoundHandler();
|
||||
metricsHandler = new NotFoundHandler();
|
||||
}
|
||||
|
||||
|
@ -620,4 +618,8 @@ class KeycloakProcessor {
|
|||
private boolean isMetricsEnabled() {
|
||||
return Configuration.getOptionalBooleanValue(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX.concat("metrics-enabled")).orElse(false);
|
||||
}
|
||||
|
||||
private boolean isHealthEnabled() {
|
||||
return Configuration.getOptionalBooleanValue(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX.concat("health-enabled")).orElse(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ hostname-strict-https=false
|
|||
db=dev-mem
|
||||
db-username = sa
|
||||
db-password = keycloak
|
||||
metrics-enabled=true
|
||||
health-enabled=true
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
|
||||
# Observability
|
||||
|
||||
# If the server should expose metrics and healthcheck endpoints.
|
||||
# If the server should expose healthcheck endpoints.
|
||||
#health-enabled=true
|
||||
|
||||
# If the server should expose metrics endpoints.
|
||||
#metrics-enabled=true
|
||||
|
||||
# HTTP
|
||||
|
|
|
@ -51,7 +51,9 @@ import picocli.CommandLine.Command;
|
|||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --features=<feature_name>%n%n"
|
||||
+ " Or alternatively, enable all tech preview features:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --features=preview%n%n"
|
||||
+ " Enable metrics:%n%n"
|
||||
+ " Enable health endpoints:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --health-enabled=true%n%n"
|
||||
+ " Enable metrics endpoints:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --metrics-enabled=true%n%n"
|
||||
+ " Change the relative path:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --http-relative-path=/auth%n%n"
|
||||
|
|
|
@ -8,10 +8,11 @@ public enum ConfigCategory {
|
|||
FEATURE("Feature", 40),
|
||||
HOSTNAME("Hostname", 50),
|
||||
HTTP("HTTP/TLS", 60),
|
||||
METRICS("Metrics", 70),
|
||||
PROXY("Proxy", 80),
|
||||
VAULT("Vault", 90),
|
||||
LOGGING("Logging", 100),
|
||||
HEALTH("Health", 70),
|
||||
METRICS("Metrics", 80),
|
||||
PROXY("Proxy", 90),
|
||||
VAULT("Vault", 100),
|
||||
LOGGING("Logging", 110),
|
||||
GENERAL("General", 999);
|
||||
|
||||
private final String heading;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package org.keycloak.quarkus.runtime.configuration.mappers;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
final class HealthPropertyMappers {
|
||||
|
||||
private HealthPropertyMappers(){}
|
||||
|
||||
public static PropertyMapper[] getHealthPropertyMappers() {
|
||||
return new PropertyMapper[] {
|
||||
builder().from("health-enabled")
|
||||
.to("quarkus.datasource.health.enabled")
|
||||
.isBuildTimeProperty(true)
|
||||
.defaultValue(Boolean.FALSE.toString())
|
||||
.description("If the server should expose health check endpoints. If enabled, health checks are available at the '/health', '/health/ready' and '/health/live' endpoints.")
|
||||
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
|
||||
.expectedValues(Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString()))
|
||||
.build()
|
||||
};
|
||||
}
|
||||
|
||||
private static PropertyMapper.Builder builder() {
|
||||
return PropertyMapper.builder(ConfigCategory.HEALTH);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ final class MetricsPropertyMappers {
|
|||
.to("quarkus.datasource.metrics.enabled")
|
||||
.isBuildTimeProperty(true)
|
||||
.defaultValue(Boolean.FALSE.toString())
|
||||
.description("If the server should expose metrics and healthcheck. If enabled, metrics are available at the '/metrics' endpoint and healthcheck at the '/health' endpoint.")
|
||||
.description("If the server should expose metrics. If enabled, metrics are available at the '/metrics' endpoint.")
|
||||
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
|
||||
.expectedValues(Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString()))
|
||||
.build()
|
||||
|
|
|
@ -26,6 +26,7 @@ public final class PropertyMappers {
|
|||
MAPPERS.addAll(DatabasePropertyMappers.getDatabasePropertyMappers());
|
||||
MAPPERS.addAll(HostnamePropertyMappers.getHostnamePropertyMappers());
|
||||
MAPPERS.addAll(HttpPropertyMappers.getHttpPropertyMappers());
|
||||
MAPPERS.addAll(HealthPropertyMappers.getHealthPropertyMappers());
|
||||
MAPPERS.addAll(MetricsPropertyMappers.getMetricsPropertyMappers());
|
||||
MAPPERS.addAll(ProxyPropertyMappers.getProxyPropertyMappers());
|
||||
MAPPERS.addAll(VaultPropertyMappers.getVaultPropertyMappers());
|
||||
|
|
|
@ -5,6 +5,7 @@ db=dev-file
|
|||
http-enabled=false
|
||||
|
||||
# Metrics and healthcheck are disabled by default
|
||||
health-enabled=false
|
||||
metrics-enabled=false
|
||||
|
||||
# Default, and insecure, and non-production grade configuration for the development profile
|
||||
|
|
|
@ -405,6 +405,12 @@ public class ConfigurationTest {
|
|||
assertEquals("com.microsoft.sqlserver.jdbc.SQLServerXADataSource", config2.getConfigValue("quarkus.datasource.jdbc.driver").getValue());
|
||||
assertEquals("xa", config2.getConfigValue("quarkus.datasource.jdbc.transactions").getValue());
|
||||
}
|
||||
|
||||
public void testResolveHealthOption() {
|
||||
System.setProperty(CLI_ARGS, "--health-enabled=true");
|
||||
SmallRyeConfig config = createConfig();
|
||||
assertEquals("true", config.getConfigValue("quarkus.datasource.health.enabled").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveMetricsOption() {
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.keycloak.quarkus.runtime.Environment.forceTestLaunchMode;
|
|||
import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAME;
|
||||
import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_SHORT_NAME;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -45,6 +46,7 @@ import io.quarkus.test.junit.main.Launch;
|
|||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
import org.keycloak.quarkus.runtime.configuration.KeycloakPropertiesConfigSource;
|
||||
import org.keycloak.quarkus.runtime.configuration.test.TestConfigArgsConfigSource;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusPlatform;
|
||||
|
||||
public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
|
||||
|
@ -217,6 +219,9 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
|||
setProperty("kc.db", database.alias());
|
||||
// databases like mssql are very strict about password policy
|
||||
setProperty("kc.db-password", "Password1!");
|
||||
} else {
|
||||
// This is for re-creating the H2 database instead of using the default in home
|
||||
setProperty("kc.db-url-path", new QuarkusPlatform().getTmpDirectory().getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
61
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java
vendored
Normal file
61
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.it.cli.dist;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
|
||||
import static io.restassured.RestAssured.when;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
@DistributionTest(keepAlive =true)
|
||||
public class HealthDistTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev" })
|
||||
void testHealthEndpointNotEnabled() {
|
||||
when().get("/health").then()
|
||||
.statusCode(404);
|
||||
when().get("/health/live").then()
|
||||
.statusCode(404);
|
||||
when().get("/health/ready").then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--health-enabled=true" })
|
||||
void testHealthEndpoint() {
|
||||
when().get("/health").then()
|
||||
.statusCode(200);
|
||||
when().get("/health/live").then()
|
||||
.statusCode(200);
|
||||
when().get("/health/ready").then()
|
||||
.statusCode(200);
|
||||
// Metrics is endpoint independent
|
||||
when().get("/metrics").then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--health-enabled=true" })
|
||||
void testHealthEndpointDoesNotEnableMetrics() {
|
||||
when().get("/metrics").then()
|
||||
.statusCode(404);
|
||||
}
|
||||
}
|
|
@ -28,6 +28,13 @@ import io.quarkus.test.junit.main.Launch;
|
|||
@DistributionTest(keepAlive =true)
|
||||
public class MetricsDistTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev" })
|
||||
void testMetricsEndpointNotEnabled() {
|
||||
when().get("/metrics").then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--metrics-enabled=true" })
|
||||
void testMetricsEndpoint() {
|
||||
|
@ -43,4 +50,11 @@ public class MetricsDistTest {
|
|||
.statusCode(200)
|
||||
.body(containsString("base_gc_total"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--metrics-enabled=true" })
|
||||
void testMetricsEndpointDoesNotEnableHealth() {
|
||||
when().get("/health").then()
|
||||
.statusCode(404);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,12 +56,18 @@ HTTP/TLS:
|
|||
--http-relative-path <path>
|
||||
Set the path relative to '/' for serving resources. Default: /.
|
||||
|
||||
Health:
|
||||
|
||||
--health-enabled <true|false>
|
||||
If the server should expose health check endpoints. If enabled, health checks
|
||||
are available at the '/health', '/health/ready' and '/health/live'
|
||||
endpoints. Default: false.
|
||||
|
||||
Metrics:
|
||||
|
||||
--metrics-enabled <true|false>
|
||||
If the server should expose metrics and healthcheck. If enabled, metrics are
|
||||
available at the '/metrics' endpoint and healthcheck at the '/health'
|
||||
endpoint. Default: false.
|
||||
If the server should expose metrics. If enabled, metrics are available at the
|
||||
'/metrics' endpoint. Default: false.
|
||||
|
||||
Vault:
|
||||
|
||||
|
@ -81,7 +87,11 @@ Examples:
|
|||
|
||||
$ kc.sh build --features=preview
|
||||
|
||||
Enable metrics:
|
||||
Enable health endpoints:
|
||||
|
||||
$ kc.sh build --health-enabled=true
|
||||
|
||||
Enable metrics endpoints:
|
||||
|
||||
$ kc.sh build --metrics-enabled=true
|
||||
|
||||
|
|
|
@ -118,12 +118,18 @@ HTTP/TLS:
|
|||
The type of the trust store file. If not given, the type is automatically
|
||||
detected based on the file name.
|
||||
|
||||
Health:
|
||||
|
||||
--health-enabled <true|false>
|
||||
If the server should expose health check endpoints. If enabled, health checks
|
||||
are available at the '/health', '/health/ready' and '/health/live'
|
||||
endpoints. Default: false.
|
||||
|
||||
Metrics:
|
||||
|
||||
--metrics-enabled <true|false>
|
||||
If the server should expose metrics and healthcheck. If enabled, metrics are
|
||||
available at the '/metrics' endpoint and healthcheck at the '/health'
|
||||
endpoint. Default: false.
|
||||
If the server should expose metrics. If enabled, metrics are available at the
|
||||
'/metrics' endpoint. Default: false.
|
||||
|
||||
Proxy:
|
||||
|
||||
|
|
Loading…
Reference in a new issue