mirror of https://github.com/grpc/grpc-java.git
xds: create a full xDS example with XDS Channel creds and server xDS options (#7535)
This commit is contained in:
parent
26a4ca38ec
commit
b6601ba273
|
|
@ -1,9 +1,9 @@
|
|||
gRPC XDS Example
|
||||
================
|
||||
|
||||
The XDS example is a Hello World client capable of being configured with the
|
||||
XDS management protocol. Out-of-the-box it behaves the same as hello world
|
||||
client.
|
||||
The XDS example consists of a Hello World client and a Hello World server capable of
|
||||
being configured with the XDS management protocol. Out-of-the-box they behave the same
|
||||
as their hello-world version.
|
||||
|
||||
__XDS support is incomplete and experimental, with limited compatibility. It
|
||||
will be very hard to produce a working enviornment just by this example. Please
|
||||
|
|
@ -12,40 +12,60 @@ environment.__
|
|||
|
||||
### Build the example
|
||||
|
||||
1. Build the hello-world example server or the hostname example server. See
|
||||
[the examples README](../README.md) or the
|
||||
[hostname example README](../example-hostname/README.md).
|
||||
|
||||
2. Build the xds hello-world example client. From the `grpc-java/examples/examples-xds` directory:
|
||||
Build the XDS hello-world example client & server. From the `grpc-java/examples/examples-xds`
|
||||
directory:
|
||||
```
|
||||
$ ../gradlew installDist
|
||||
```
|
||||
|
||||
This creates the script `build/install/example-xds/bin/xds-hello-world-client`
|
||||
that runs the example.
|
||||
This creates the scripts `build/install/example-xds/bin/hello-world-client-xds` and
|
||||
`build/install/example-xds/bin/hello-world-server-xds`.
|
||||
|
||||
To start the server, run:
|
||||
### Run the example without using XDS Credentials
|
||||
|
||||
```
|
||||
$ ../build/install/hostname/bin/hello-world-server
|
||||
$ # or
|
||||
$ ../example-hostname/build/install/hostname/bin/hostname-server
|
||||
```
|
||||
|
||||
And in a different terminal window run this client:
|
||||
|
||||
```
|
||||
$ ./build/install/example-xds/bin/xds-hello-world-client
|
||||
```
|
||||
|
||||
However, that didn't use XDS! To use XDS we assume you have deployed the server
|
||||
in your deployment environment and know its name. You need to set the
|
||||
`GRPC_XDS_BOOTSTRAP` environment variable to point to a gRPC XDS bootstrap
|
||||
file (see [gRFC A27](https://github.com/grpc/proposal/pull/170) for the
|
||||
bootstrap format). Then use the `xds:` target scheme during
|
||||
channel creation.
|
||||
To use XDS, you should first deploy the XDS management server in your deployment environment
|
||||
and know its name. You need to set the `GRPC_XDS_BOOTSTRAP` environment variable to point to the
|
||||
gRPC XDS bootstrap file (see
|
||||
[gRFC A27](https://github.com/grpc/proposal/blob/master/A27-xds-global-load-balancing.md#xdsclient-and-bootstrap-file) for the
|
||||
bootstrap format). This is needed by both `build/install/example-xds/bin/hello-world-client-xds`
|
||||
and `build/install/example-xds/bin/hello-world-server-xds`.
|
||||
|
||||
1. To start the XDS-enabled example server, run:
|
||||
```
|
||||
$ export GRPC_XDS_BOOTSTRAP=/path/to/bootstrap.json
|
||||
$ ./build/install/example-xds/bin/xds-hello-world-client "XDS world" xds:///yourServersName
|
||||
$ ./build/install/example-xds/bin/hello-world-server-xds 8000 my-test-xds-server
|
||||
```
|
||||
|
||||
The first command line argument is the port to listen on (`8000`) and the second argument is a string
|
||||
id (`my-test-xds-server`) to be included in the greeting response to the client.
|
||||
|
||||
2. In a different terminal window, run the XDS-enabled example client:
|
||||
```
|
||||
$ export GRPC_XDS_BOOTSTRAP=/path/to/bootstrap.json
|
||||
$ ./build/install/example-xds/bin/xds-hello-world-client xds:///yourServersName:8000 my-test-xds-client
|
||||
```
|
||||
The first command line argument (`xds:///yourServersName:8000`) is the target to connect to using the
|
||||
`xds:` target scheme and the second argument (`my-test-xds-client`) is the name you wish to include in
|
||||
the greeting request to the server.
|
||||
|
||||
### Run the example with xDS Credentials
|
||||
|
||||
The above example used plaintext (insecure) credentials as explicitly provided by the client and server
|
||||
code. We will now demonstrate how the code can authorize use of xDS provided credentials by using
|
||||
`XdsChannelCredentials` on the client side and using `XdsServerBuilder.useXdsSecurityWithPlaintextFallback()`
|
||||
on the server side. This code is enabled by providing an additional command line argument.
|
||||
|
||||
1. On the server side, add `--secure` on the command line to authorize use of xDS security:
|
||||
```
|
||||
$ export GRPC_XDS_BOOTSTRAP=/path/to/bootstrap.json
|
||||
$ ./build/install/example-xds/bin/hello-world-server-xds 8000 my-test-xds-server --secure
|
||||
```
|
||||
|
||||
2. Similarly, add `--secure` on the comamnd line when you run the xDS client:
|
||||
```
|
||||
$ export GRPC_XDS_BOOTSTRAP=/path/to/bootstrap.json
|
||||
$ ./build/install/example-xds/bin/hello-world-client-xds xds:///yourServersName:8000 my-test-xds-client --secure
|
||||
```
|
||||
|
||||
In this case, if the xDS management server is configured to provide mTLS credentials (for example) to the client and
|
||||
server, then they will use these credentials to create an mTLS channel to authenticate and encrypt.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
plugins {
|
||||
id 'application' // Provide convenience executables for trying out the examples.
|
||||
// ASSUMES GRADLE 5.6 OR HIGHER. Use plugin version 0.8.10 with earlier gradle versions
|
||||
id 'com.google.protobuf' version '0.8.13'
|
||||
// Generate IntelliJ IDEA's .idea & .iml project files
|
||||
id 'idea'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
|
|
@ -19,25 +23,46 @@ targetCompatibility = 1.7
|
|||
// Feel free to delete the comment at the next line. It is just for safely
|
||||
// updating the version in our release process.
|
||||
def grpcVersion = '1.34.0-SNAPSHOT' // CURRENT_GRPC_VERSION
|
||||
def nettyTcNativeVersion = '2.0.31.Final'
|
||||
def protocVersion = '3.12.0'
|
||||
|
||||
dependencies {
|
||||
// This example's client is the same as the helloworld client. We depend on the helloworld
|
||||
// client's code here
|
||||
implementation ':examples'
|
||||
// The only change necessary is an extra runtime dependency on io.grpc:grpc-xds
|
||||
runtimeOnly "io.grpc:grpc-xds:${grpcVersion}"
|
||||
implementation "io.grpc:grpc-netty:${grpcVersion}"
|
||||
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
|
||||
implementation "io.grpc:grpc-stub:${grpcVersion}"
|
||||
implementation "io.grpc:grpc-xds:${grpcVersion}"
|
||||
compileOnly "org.apache.tomcat:annotations-api:6.0.53"
|
||||
runtimeOnly "io.netty:netty-tcnative-boringssl-static:${nettyTcNativeVersion}"
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
|
||||
plugins {
|
||||
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
|
||||
}
|
||||
generateProtoTasks {
|
||||
all()*.plugins { grpc {} }
|
||||
}
|
||||
}
|
||||
|
||||
startScripts.enabled = false
|
||||
|
||||
task helloWorldClient(type: CreateStartScripts) {
|
||||
mainClassName = 'io.grpc.examples.helloworld.HelloWorldClient'
|
||||
applicationName = 'xds-hello-world-client'
|
||||
task helloWorldClientXds(type: CreateStartScripts) {
|
||||
mainClassName = 'io.grpc.examples.helloworldxds.HelloWorldClientXds'
|
||||
applicationName = 'hello-world-client-xds'
|
||||
outputDir = new File(project.buildDir, 'tmp')
|
||||
classpath = startScripts.classpath
|
||||
}
|
||||
|
||||
task helloWorldServerXds(type: CreateStartScripts) {
|
||||
mainClassName = 'io.grpc.examples.helloworldxds.HelloWorldServerXds'
|
||||
applicationName = 'hello-world-server-xds'
|
||||
outputDir = new File(project.buildDir, 'tmp')
|
||||
classpath = startScripts.classpath
|
||||
}
|
||||
|
||||
applicationDistribution.into('bin') {
|
||||
from(helloWorldClient)
|
||||
from(helloWorldClientXds)
|
||||
from(helloWorldServerXds)
|
||||
fileMode = 0755
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC Authors
|
||||
*
|
||||
* 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 io.grpc.examples.helloworldxds;
|
||||
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.InsecureChannelCredentials;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import io.grpc.examples.helloworld.GreeterGrpc;
|
||||
import io.grpc.examples.helloworld.HelloReply;
|
||||
import io.grpc.examples.helloworld.HelloRequest;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.NettyChannelBuilder;
|
||||
import io.grpc.xds.XdsChannelCredentials;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* A simple xDS client that requests a greeting from the {@link HelloWorldServerXds}.
|
||||
*/
|
||||
public class HelloWorldClientXds {
|
||||
private static final Logger logger = Logger.getLogger(HelloWorldClientXds.class.getName());
|
||||
private final ManagedChannel channel;
|
||||
private final GreeterGrpc.GreeterBlockingStub blockingStub;
|
||||
|
||||
/** Construct client connecting to HelloWorld server at {@code host:port}. */
|
||||
public HelloWorldClientXds(String target, boolean useXdsCreds) throws SSLException {
|
||||
this.channel =
|
||||
Grpc.newChannelBuilder(
|
||||
target,
|
||||
useXdsCreds
|
||||
? XdsChannelCredentials.create(InsecureChannelCredentials.create())
|
||||
: InsecureChannelCredentials.create())
|
||||
.build();
|
||||
blockingStub = GreeterGrpc.newBlockingStub(this.channel);
|
||||
}
|
||||
|
||||
public void shutdown() throws InterruptedException {
|
||||
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/** Say hello to server. */
|
||||
public void greet(String name) {
|
||||
logger.info("Will try to greet " + name + " ...");
|
||||
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
|
||||
HelloReply response;
|
||||
try {
|
||||
response = blockingStub.sayHello(request);
|
||||
} catch (StatusRuntimeException e) {
|
||||
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
|
||||
return;
|
||||
}
|
||||
logger.info("Greeting: " + response.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Greet server. If provided, the second element of {@code args} is the name to use in the
|
||||
* greeting.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
String user = "xds-client";
|
||||
boolean useXdsCreds = false;
|
||||
if (args.length < 1 || args.length > 3) {
|
||||
System.out.println("USAGE: HelloWorldClientXds target [name [--secure]]\n");
|
||||
System.err.println(" target The xds target to connect to using the 'xds:' target scheme.");
|
||||
System.err.println(" name The name you wish to include in the greeting request. Defaults to " + user);
|
||||
System.err.println(
|
||||
" '--secure' Indicates using xDS credentials otherwise defaults to insecure.");
|
||||
System.exit(1);
|
||||
}
|
||||
if (args.length > 1) {
|
||||
user = args[1];
|
||||
if (args.length == 3) {
|
||||
useXdsCreds = args[2].toLowerCase().startsWith("--s");
|
||||
}
|
||||
}
|
||||
HelloWorldClientXds client = new HelloWorldClientXds(args[0], useXdsCreds);
|
||||
try {
|
||||
client.greet(user);
|
||||
} finally {
|
||||
client.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC Authors
|
||||
*
|
||||
* 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 io.grpc.examples.helloworldxds;
|
||||
|
||||
import io.grpc.Server;
|
||||
import io.grpc.examples.helloworld.GreeterGrpc;
|
||||
import io.grpc.examples.helloworld.HelloReply;
|
||||
import io.grpc.examples.helloworld.HelloRequest;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import io.grpc.xds.internal.sds.XdsServerBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* An xDS-managed Server for the {@code Greeter} service.
|
||||
*/
|
||||
public class HelloWorldServerXds {
|
||||
private static final Logger logger = Logger.getLogger(HelloWorldServerXds.class.getName());
|
||||
private final int port;
|
||||
private final boolean useXdsCreds;
|
||||
private final String hostName;
|
||||
private Server server;
|
||||
|
||||
public HelloWorldServerXds(int port, String hostName, boolean useXdsCreds) {
|
||||
this.port = port;
|
||||
this.hostName = hostName;
|
||||
this.useXdsCreds = useXdsCreds;
|
||||
}
|
||||
|
||||
private void start() throws IOException {
|
||||
XdsServerBuilder builder = XdsServerBuilder.forPort(port).addService(new HostnameGreeter(hostName));
|
||||
if (useXdsCreds) {
|
||||
builder = builder.useXdsSecurityWithPlaintextFallback();
|
||||
}
|
||||
server = builder.build().start();
|
||||
logger.info("Server started, listening on " + port);
|
||||
Runtime.getRuntime()
|
||||
.addShutdownHook(
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
|
||||
System.err.println("*** shutting down gRPC server since JVM is shutting down");
|
||||
try {
|
||||
HelloWorldServerXds.this.stop();
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.SEVERE, "During stop", e);
|
||||
}
|
||||
System.err.println("*** server shut down");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void stop() throws InterruptedException {
|
||||
if (server != null) {
|
||||
server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/** Await termination on the main thread since the grpc library uses daemon threads. */
|
||||
private void blockUntilShutdown() throws InterruptedException {
|
||||
if (server != null) {
|
||||
server.awaitTermination();
|
||||
}
|
||||
}
|
||||
|
||||
/** Main launches the server from the command line. */
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
boolean useXdsCreds = false;
|
||||
String hostName = null;
|
||||
if (args.length < 1 || args.length > 3) {
|
||||
System.out.println("USAGE: HelloWorldServerTls port [hostname [--secure]]");
|
||||
System.err.println("");
|
||||
System.err.println(" port The port to bind to.");
|
||||
System.err.println(" hostname The name clients will see in greet responses. ");
|
||||
System.err.println(" Defaults to the machine's hostname");
|
||||
System.out.println(
|
||||
" '--secure' Indicates using xDS credentials options; otherwise defaults to insecure credentials.");
|
||||
System.exit(1);
|
||||
}
|
||||
if (args.length > 1) {
|
||||
hostName = args[1];
|
||||
if (args.length == 3) {
|
||||
useXdsCreds = args[2].toLowerCase().startsWith("--s");
|
||||
}
|
||||
}
|
||||
final HelloWorldServerXds server =
|
||||
new HelloWorldServerXds(Integer.parseInt(args[0]), hostName, useXdsCreds);
|
||||
server.start();
|
||||
server.blockUntilShutdown();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC Authors
|
||||
*
|
||||
* 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 io.grpc.examples.helloworldxds;
|
||||
|
||||
import io.grpc.examples.helloworld.GreeterGrpc;
|
||||
import io.grpc.examples.helloworld.HelloReply;
|
||||
import io.grpc.examples.helloworld.HelloRequest;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/** Greeter implementation which replies identifying itself with its hostname. */
|
||||
public final class HostnameGreeter extends GreeterGrpc.GreeterImplBase {
|
||||
private static final Logger logger = Logger.getLogger(HostnameGreeter.class.getName());
|
||||
|
||||
private final String serverName;
|
||||
|
||||
public HostnameGreeter(String serverName) {
|
||||
if (serverName == null || serverName.isEmpty()) {
|
||||
serverName = determineHostname();
|
||||
}
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
|
||||
HelloReply reply = HelloReply.newBuilder()
|
||||
.setMessage("Hello " + req.getName() + ", from " + serverName)
|
||||
.build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
||||
private static String determineHostname() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.INFO, "Failed to determine hostname. Will generate one", ex);
|
||||
}
|
||||
// Strange. Well, let's make an identifier for ourselves.
|
||||
return "generated-" + new Random().nextInt();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2020 The gRPC Authors
|
||||
//
|
||||
// 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.
|
||||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "io.grpc.examples.helloworld";
|
||||
option java_outer_classname = "HelloWorldProto";
|
||||
option objc_class_prefix = "HLW";
|
||||
|
||||
package helloworld;
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (HelloReply) {}
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
Loading…
Reference in New Issue