All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer Maven / Gradle / Ivy

There is a newer version: 8.409.18
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.admin.metricsproxy;

import ai.vespa.metricsproxy.http.metrics.MetricsV2Handler;
import ai.vespa.metricsproxy.http.metrics.NodeInfoConfig;
import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
import ai.vespa.metricsproxy.metric.dimensions.PublicDimensions;
import ai.vespa.metricsproxy.rpc.RpcConnector;
import ai.vespa.metricsproxy.rpc.RpcConnectorConfig;
import ai.vespa.metricsproxy.service.VespaServices;
import ai.vespa.metricsproxy.service.VespaServicesConfig;
import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Zone;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.HostResource;
import com.yahoo.vespa.model.PortAllocBridge;
import com.yahoo.vespa.model.container.Container;

import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER;
import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.METRICS_PROXY_BUNDLE_NAME;
import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.createMetricsHandler;

/**
 * Container running a metrics proxy.
 *
 * @author gjoranv
 */
public class MetricsProxyContainer extends Container implements
        NodeDimensionsConfig.Producer,
        NodeInfoConfig.Producer,
        RpcConnectorConfig.Producer,
        VespaServicesConfig.Producer,
        QrStartConfig.Producer
{
    public static final int BASEPORT = 19092;

    final boolean isHostedVespa;
    private final Optional clusterMembership;
    private final MetricsProxyContainerCluster cluster;
    private final ApplicationId applicationId;
    private final Zone zone;
    private final String jvmGCOptions;

    public MetricsProxyContainer(MetricsProxyContainerCluster cluster, HostResource host, int index, DeployState deployState) {
        super(cluster, host.getHostname(), index, deployState);
        this.isHostedVespa = deployState.isHosted();
        this.clusterMembership = host.spec().membership();
        this.cluster = cluster;
        this.applicationId = deployState.getApplicationPackage().getApplicationId();
        this.zone = deployState.zone();
        this.jvmGCOptions = deployState.getProperties().jvmGCOptions(clusterMembership.map(membership -> membership.cluster().type()));
        setProp("clustertype", "admin");
        setProp("index", String.valueOf(index));
        addNodeSpecificComponents();
    }

    private void addNodeSpecificComponents() {
        addMetricsProxyComponent(NodeDimensions.class);
        addMetricsProxyComponent(RpcConnector.class);
        addMetricsProxyComponent(VespaServices.class);
        addHandler(createMetricsHandler(MetricsV2Handler.class, MetricsV2Handler.V2_PATH));
    }

    @Override
    protected ContainerServiceType myServiceType() {
        return METRICS_PROXY_CONTAINER;
    }

    @Override
    public int getWantedPort() {
        return BASEPORT;
    }

    @Override
    public boolean requiresWantedPort() {
        return true;
    }

    private int metricsRpcPort;

    // Must have predictable ports for both http and rpc.
    @Override
    public void allocatePorts(int start, PortAllocBridge from) {
        if (start == 0) start = BASEPORT;
        if (getHttp() != null) {
            throw new IllegalArgumentException("unexpected HTTP setup");
        }
        allocatedSearchPort = from.wantPort(start++, "http");
        portsMeta.on(0).tag("http").tag("query").tag("external").tag("state");

        // XXX remove:
        from.wantPort(start++, "http/1");
        portsMeta.on(1).tag("unused");

        if (numMessageBusPorts() != 0) {
            throw new IllegalArgumentException("expecting 0 message bus ports");
        }
        if (numRpcPorts() != 1) {
            throw new IllegalArgumentException("expecting 1 rpc port");
        }
        allocatedRpcPort = from.wantPort(start++, "rpc/admin");
        portsMeta.on(2).tag("rpc").tag("admin");
        metricsRpcPort = from.wantPort(start++, "rpc/metrics");
        portsMeta.on(3).tag("rpc").tag("metrics");
    }

    @Override
    public int getPortCount() {
        return 4;
    }

    @Override
    public void getConfig(RpcConnectorConfig.Builder builder) {
        builder.port(metricsRpcPort);
    }

    @Override
    public void getConfig(VespaServicesConfig.Builder builder) {
        builder.service.addAll(VespaServicesConfigGenerator.generate(getHostResource().getServices()));
    }

    @Override
    public void getConfig(NodeDimensionsConfig.Builder builder) {
        Map dimensions = new LinkedHashMap<>();
        if (isHostedVespa) {
            getHostResource().spec().membership().map(ClusterMembership::cluster).ifPresent(cluster -> {
                dimensions.put(PublicDimensions.DEPLOYMENT_CLUSTER, getDeploymentCluster(cluster));
                dimensions.put(PublicDimensions.INTERNAL_CLUSTER_TYPE, cluster.type().name());
                dimensions.put(PublicDimensions.INTERNAL_CLUSTER_ID, cluster.id().value());
                cluster.group().ifPresent(group -> dimensions.put(PublicDimensions.GROUP_ID, group.toString()));
            });

            builder.dimensions(dimensions);
        }
    }

    @Override
    public void getConfig(NodeInfoConfig.Builder builder) {
        builder.role(getNodeRole())
                .hostname(getHostName());
    }

    @Override
    public void getConfig(QrStartConfig.Builder builder) {
        cluster.getConfig(builder);

        if (clusterMembership.isPresent()) {
            boolean adminCluster = clusterMembership.get().cluster().type() == ClusterSpec.Type.admin;
            int maxHeapSize = adminCluster ? 96 : 256;
            builder.jvm
                    .gcopts(jvmGCOptions)
                    .heapsize(maxHeapSize);
            if (adminCluster) builder.jvm.minHeapsize(maxHeapSize);
        }
    }

    private String getNodeRole() {
        String hostConfigId = getHost().getHost().getConfigId();
        if (! isHostedVespa) return hostConfigId;
        return getHostResource().spec().membership()
                    .map(ClusterMembership::stringValue)
                    .orElse(hostConfigId);
    }

    private void addMetricsProxyComponent(Class componentClass) {
        addSimpleComponent(componentClass.getName(), null, METRICS_PROXY_BUNDLE_NAME);
    }

    private String getDeploymentCluster(ClusterSpec cluster) {
        return String.join(".", applicationId.toFullString(), zone.environment().value(), zone.region().value(), cluster.id().value());
    }

    @Override
    protected String defaultPreload() {
        return "";
    }

    @Override public Optional getPreShutdownCommand() { return Optional.of(prepareStopCommand(Duration.ofMinutes(6))); }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy