Auf Instanzmetadaten zugreifen

Die Google Cloud Platform stellt einen Metadatenserver bereit, der Details zu Ihrer App Engine-Instanz enthält, z. B. die Projekt-ID, Dienstkonten sowie Tokens, die von den Dienstkonten verwendet werden. Sie können auf diese Daten mithilfe einfacher HTTP-Requests zugreifen. Es sind keine Clientbibliotheken erforderlich.

Auf dieser Seite erfahren Sie, wie Sie von der implementierten Java 8-Laufzeitanwendung auf Instanzmetadaten zugreifen können, indem Sie HTTP-Aufrufe an die entsprechenden Metadatenserver-Endpunkte senden.

Eine nützliche Methode zur Verwendung dieser API besteht darin, das Dienstkonto-Token abzurufen und als Inhabertoken im Autorisierungsheader einer der Google Cloud APIs bereitzustellen, um Ihre Anwendung für diesen konkreten API-Dienst zu authentifizieren. In der Dokumentation zur Google Cloud Translation API finden Sie ein Beispiel für die Verwendung dieser Inhabertokens.

Zu verwendenden Metadaten-Endpunkt bestimmen

In der folgenden Tabelle sind die Endpunkte aufgeführt, an die Sie HTTP-Anfragen für bestimmte Metadaten senden können: Auf den Metadatenserver kann unter http://metadata.google.internal zugegriffen werden.

Metadatenendpunkt Beschreibung
/computeMetadata/v1/project/numeric-project-id Projektnummer, die Ihrem Projekt zugewiesen ist.
/computeMetadata/v1/project/project-id Projekt-ID, die Ihrem Projekt zugewiesen ist.
/computeMetadata/v1/instance/zone Zone, in der die Instanz ausgeführt wird.
/computeMetadata/v1/instance/service-accounts/default/aliases
/computeMetadata/v1/instance/service-accounts/default/email E-Mail-Adresse des Standarddienstkontos, die Ihrem Projekt zugewiesen ist.
/computeMetadata/v1/instance/service-accounts/default/ Listet alle Standarddienstkonten für Ihr Projekt auf.
/computeMetadata/v1/instance/service-accounts/default/scopes Listet alle unterstützten Bereiche für die Standarddienstkonten auf.
/computeMetadata/v1/instance/service-accounts/default/token Gibt das Authentifizierungstoken zurück, mit dem Ihre Anwendung gegenüber anderen Google Cloud APIs authentifiziert werden kann.

Wenn Sie z. B. Ihre Projekt-ID abrufen möchten, senden Sie eine Anfrage an http://metadata.google.internal/computeMetadata/v1/project/project-id.

Metadatenanfragen erstellen

Durch den folgenden Beispielcode werden mit Ausnahme des Dienstkontotokens alle für die Instanz verfügbaren Metadaten abgerufen und angezeigt.

@SuppressWarnings("serial")
// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(name = "Metadata", description = "Metadata: Write info about GAE Standard",
    urlPatterns = "/metadata")
public class MetadataServlet extends HttpServlet {

  private final String[] metaPath = {
      "/computeMetadata/v1/project/numeric-project-id", //  (pending)
      "/computeMetadata/v1/project/project-id",
      "/computeMetadata/v1/instance/zone",
      "/computeMetadata/v1/instance/service-accounts/default/aliases",
      "/computeMetadata/v1/instance/service-accounts/default/email",
      "/computeMetadata/v1/instance/service-accounts/default/",
      "/computeMetadata/v1/instance/service-accounts/default/scopes",
      // Tokens work - but are a security risk to display
      //      "/computeMetadata/v1/instance/service-accounts/default/token"
  };

  final String[] metaServiceAcct = {
      "/computeMetadata/v1/instance/service-accounts/{account}/aliases",
      "/computeMetadata/v1/instance/service-accounts/{account}/email",
      "/computeMetadata/v1/instance/service-accounts/{account}/scopes",
      // Tokens work - but are a security risk to display
      //     "/computeMetadata/v1/instance/service-accounts/{account}/token"
  };

  private final String metadata = "http://metadata.google.internal";
  private TemplateEngine templateEngine;

  // Use OkHttp from Square as it's quite easy to use for simple fetches.
  private final OkHttpClient ok = new OkHttpClient.Builder()
      .readTimeout(500, TimeUnit.MILLISECONDS)  // Don't dawdle
      .writeTimeout(500, TimeUnit.MILLISECONDS)
      .build();

  // Setup to pretty print returned json
  private final Gson gson = new GsonBuilder()
      .setPrettyPrinting()
      .create();
  private final JsonParser jp = new JsonParser();

  // Fetch Metadata
  String fetchMetadata(String key) throws IOException {
    Request request = new Request.Builder()
        .url(metadata + key)
        .addHeader("Metadata-Flavor", "Google")
        .get()
        .build();

    Response response = ok.newCall(request).execute();
    return response.body().string();
  }

  String fetchJsonMetadata(String prefix) throws IOException {
    Request request = new Request.Builder()
        .url(metadata + prefix)
        .addHeader("Metadata-Flavor", "Google")
        .get()
        .build();

    Response response = ok.newCall(request).execute();

    // Convert json to prety json
    return gson.toJson(jp.parse(response.body().string()));
  }

  @Override
  public void init() {
    // Setup ThymeLeaf
    ServletContextTemplateResolver templateResolver =
        new ServletContextTemplateResolver(this.getServletContext());

    templateResolver.setPrefix("/WEB-INF/templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setCacheTTLMs(Long.valueOf(1200000L)); // TTL=20m

    // Cache is set to true by default. Set to false if you want templates to
    // be automatically updated when modified.
    templateResolver.setCacheable(true);

    templateEngine = new TemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String defaultServiceAccount = "";
    WebContext ctx = new WebContext(req, resp, getServletContext(), req.getLocale());

    resp.setContentType("text/html");

    String environment =
        (String) System.getProperties().get("com.google.appengine.runtime.environment");
    ctx.setVariable("production", environment);

    // The metadata server is only on a production system
    if (environment.equals("Production")) {

      TreeMap<String, String> m = new TreeMap<>();

      for (String key : metaPath) {
        m.put(key, fetchMetadata(key));
        if (key.contains("default/email")) {
          defaultServiceAccount = m.get(key);
        }
      }

      ctx.setVariable("Metadata", m.descendingMap());

      m = new TreeMap<>();
      for (String key : metaServiceAcct) {
        // substitute a service account for {account}
        key = key.replace("{account}", defaultServiceAccount);
        m.put(key, fetchMetadata(key));
      }
      ctx.setVariable("sam", m.descendingMap());

      // Recursivly get all info about service accounts -- Note tokens are leftout by default.
      ctx.setVariable("rsa",
          fetchJsonMetadata("/computeMetadata/v1/instance/service-accounts/?recursive=true"));
      // Recursivly get all data on Metadata server.
      ctx.setVariable("ram", fetchJsonMetadata("/?recursive=true"));
    }

    templateEngine.process("index", ctx, resp.getWriter());

  }
}

Beachten Sie, dass im Beispielcode geprüft wird, ob die Anwendung in der Produktion ausgeführt wird. Wenn die Anwendung lokal ausgeführt wird, werden bei den Anfragen keine Metadaten zurückgegeben.

Beachten Sie auch die Verwendung des Google-Gson-JSON-Serialisierers/-Deserialisierers, der OkHttp-HTTP- und -HTTP2-Clients und des Thymeleaf-Vorlagensystems. Diese sind zwar nicht erforderlich, aber es sind nützliche Bibliotheken für Ihre eigenen Projekte.

Lokal ausführen

Der Metadatenserver ist für bereitgestellte Anwendungen verfügbar. Die lokale Ausführung auf dem Entwicklungsserver wird nicht unterstützt. Sie können in Ihren Code eine Umgebungsprüfung einfügen, damit Metadatenergebnisse nur dann erwartet werden, wenn die Anwendung in der Produktion ausgeführt wird, wie im obigen Beispielcode gezeigt:

String environment =
      (String) System.getProperties().get("com.google.appengine.runtime.environment");
  ctx.setVariable("production", environment);

  // The metadata server is only on a production system
  if (environment.equals("Production")) {
     ... //show metadata results
   }