Skip to content

Commit

Permalink
V2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
galvesribeiro committed Jun 11, 2020
1 parent 62b7d40 commit 53f61c9
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 68 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,5 @@ __pycache__/
*.odx.cs
*.xsd.cs
.DS_Store

**/output
33 changes: 33 additions & 0 deletions samples/Definitions/Client-ServiceAccount.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: orleansclient
namespace: kubetest
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: orleansclient
rules:
- apiGroups:
- orleans.dot.net
resources:
- silos
- clusterversions
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: orleansclient
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: orleansclient
subjects:
- kind: ServiceAccount
name: orleansclient
namespace: kubetest
21 changes: 21 additions & 0 deletions samples/Definitions/Client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: orleans-client
labels:
app: kubeclient
spec:
replicas: 1
selector:
matchLabels:
app: kubeclient
template:
metadata:
labels:
app: kubeclient
spec:
serviceAccountName: orleansclient
containers:
- name: orleansclient
image: kubeclient:latest
imagePullPolicy: Never
21 changes: 21 additions & 0 deletions samples/Definitions/Gateway.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: orleans-gateway
labels:
app: kubegateway
spec:
replicas: 2
selector:
matchLabels:
app: kubegateway
template:
metadata:
labels:
app: kubegateway
spec:
serviceAccountName: orleanssilo
containers:
- name: orleanssilo
image: kubegateway:latest
imagePullPolicy: Never
37 changes: 37 additions & 0 deletions samples/Definitions/Silo-ServiceAccount.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: orleanssilo
namespace: kubetest
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: orleanssilo
rules:
- apiGroups:
- orleans.dot.net
resources:
- silos
- clusterversions
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: orleanssilo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: orleanssilo
subjects:
- kind: ServiceAccount
name: orleanssilo
namespace: kubetest
21 changes: 21 additions & 0 deletions samples/Definitions/Silo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: orleans-silo
labels:
app: kubesilo
spec:
replicas: 1
selector:
matchLabels:
app: kubesilo
template:
metadata:
labels:
app: kubesilo
spec:
serviceAccountName: orleanssilo
containers:
- name: orleanssilo
image: kubesilo:latest
imagePullPolicy: Never
94 changes: 64 additions & 30 deletions samples/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,64 @@
# Orleans on Kubernetes - Samples

This directory contains 3 projects to show a practical example of Orleans running on top of Kubernetes using `Orleans.Clustering.Kubernetes` package:

1. `KubeClient` -> Regular Orleans Client.
2. `KubeSiloHost` - Orleans silo host.
3. `KubeGatewayHost` - An Orleans silo which has the gateway enabled.

> NOTE: The reason the gateway and non-gateway silos are on different projects, is just to illustrate that you don't have to expose all your pods to outside world in case you don't want it. It is not a requirement and all silos can be gateways if you want it.
## Pre-requisites

1. Docker
2. Kubernetes
3. .Net Core 2.0 SDK

## Running it

To run it first create the Kubernetes `namespace` you will use to host the sample deployments with `kubectl create namespace <namespace>`. Take a note of the `<namespace>` used so it can be used later on.

Each project contains a regular `Dockerfile` which must be built to a Docker image just like a regular docker application.

1. Publish each project using `dotnet publish -c Release -o PublishOutput`
2. From the `PublishOutput` directory of each project, build the image with `docker build -t <imagename>:<imagetag> .` and replace `<imagename>` and `<imagetag>` with the respective project name (i.e `kubesilo`, `kubehost`, `kubeclient` for the name and `latest` for the tag) or whatever name you want.
3. For each project run `kubectl run <servicename> --image=<imagename>:<imagetag> --namespace=<namespace> --image-pull-policy=Never`. First the silo, later the gateway then the client. Be sure to replace the `<xxx>` tags with the values used on previous steps.

> NOTE: The reason we use `--image-pull-policy=Never` for this sample is just so Kubernetes doesn't try to pull the image from Docker Hub.


# Orleans on Kubernetes - Samples

This directory contains 3 projects to show a practical example of Orleans running on top of Kubernetes using `Orleans.Clustering.Kubernetes` package:

1. `KubeClient` -> Regular Orleans Client.
2. `KubeSiloHost` - Orleans silo host.
3. `KubeGatewayHost` - An Orleans silo which has the gateway enabled.

> NOTE: The reason the gateway and non-gateway silos are on different projects, is just to illustrate that you don't have to expose all your pods to outside world in case you don't want it. It is not a requirement and all silos can be gateways if you want it.
## Pre-requisites

1. Docker
2. Kubernetes
3. .Net Core 3.1 SDK

## Running it

To run it first create the Kubernetes `namespace` you will use to host the sample deployments with `kubectl create namespace <namespace>`. Take a note of the `<namespace>` used so it can be used later on.

From the `samples/` directory, run the following commands to publish the .Net Core applications:

1. `dotnet publish -c Release KubeClient -o output/KubeClient`
2. `dotnet publish -c Release KubeGatewayHost -o output/KubeGatewayHost`
3. `dotnet publish -c Release KubeSiloHost -o output/KubeSiloHost`

Each project contains a regular `Dockerfile` which must be built to a Docker image just like a regular docker application.

To build the images, run the following commands from the `samples/` directory:

1. `docker build -f output/KubeClient/Dockerfile -t kubeclient output/KubeClient`
2. `docker build -f output/KubeGatewayHost/Dockerfile -t kubegateway output/KubeGatewayHost`
3. `docker build -f output/KubeSiloHost/Dockerfile -t kubesilo output/KubeSiloHost`

Now you have 3 images containing the 3 sample projects built on your local Docker image repository.

In order for the provider to work properly, the CRD files must be deployed. This must be done once per Kubernetes cluster regardless of how many Orleans clusters/deployments on it.

From the `samples/` directory, run the following command:

1. `kubectl apply -f ../src/Orleans.Clustering.Kubernetes/Definitions/ClusterVersionCRD.yaml`
2. `kubectl apply -f ../src/Orleans.Clustering.Kubernetes/Definitions/SiloEntryCRD.yaml`

Now you need to make sure the pods can create objects using those deifnitions. To do that, the pods must run under a service account which has access to Kubernetes APIs under the scope of those objects. To deploy the samples service accounts run the following:

1. `kubectl apply -f ./Definitions/Silo-ServiceAccount.yaml --namespace <namespace>`
2. `kubectl apply -f ./Definitions/Client-ServiceAccount.yaml --namespace <namespace>`

Those definitions create the a Kubernetes ClusterRole with permissions to read for the client, and read/write for the silos and also bind it to the service accounts you just created. You can modify the service account names and the way you create them on your production envinronment but be aware of the permissions required in order for this to work.

Now you have all the assets deployed to your Kubernetes cluster and all you need is to create the Deployment objects with the following commands:

1. `kubectl apply -f ./Definitions/Silo.yaml --namespace <namespace>`
2. `kubectl apply -f ./Definitions/Gateway.yaml --namespace <namespace>`
3. `kubectl apply -f ./Definitions/Client.yaml --namespace <namespace>`

You are all set! You can use commands like `kubectl get pods --namespace <namespace>` and you will see the client, silo and gateway pods listed on it.

To inspect the cluster objects deployed to kubertes with `kubectl get silos --namespace <namespace> -o yaml` or `kubectl get clusterversions --namespace <namespace> -o yaml` and that will return Orleans cluster membership objects in YAML (you can change to `-o json` if you like to).

Enjoy!



5 changes: 4 additions & 1 deletion src/Orleans.Clustering.Kubernetes/ClusteringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ public static IClientBuilder UseKubeGatewayListProvider(this IClientBuilder buil
return builder.ConfigureServices((ctx, services) =>
{
services.AddOptions<KubeGatewayOptions>();
services.Configure<KubeGatewayOptions>(configureOptions);
if (configureOptions != null)
{
services.Configure<KubeGatewayOptions>(configureOptions);
}

KubernetesClientConfiguration config = default;

Expand Down
26 changes: 7 additions & 19 deletions src/Orleans.Clustering.Kubernetes/KubeGatewayListProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ public async Task<IList<Uri>> GetGateways()
}
}

public async Task InitializeGatewayListProvider()
public Task InitializeGatewayListProvider()
{
this._namespace = await this.GetNamespace();
this._namespace = this.GetNamespace();
return Task.CompletedTask;
}

private static Uri ConvertToGatewayUri(SiloEntity gateway)
Expand All @@ -73,29 +74,16 @@ private static Uri ConvertToGatewayUri(SiloEntity gateway)
return address.ToGatewayUri();
}

private async ValueTask<string> GetNamespace()
private string GetNamespace()
{
if (!string.IsNullOrWhiteSpace(this._kubeGatewayOptions.Namespace)) return this._kubeGatewayOptions.Namespace;

var namespaceFilePath = Path.Combine(Constants.SERVICE_ACCOUNT_PATH, Constants.SERVICE_ACCOUNT_NAMESPACE_FILENAME);
if (File.Exists(namespaceFilePath))
{
using var sourceStream = new FileStream(namespaceFilePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true);

var sb = new StringBuilder();
if (!File.Exists(namespaceFilePath)) return Constants.ORLEANS_NAMESPACE;

byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
var ns = File.ReadAllText(namespaceFilePath);

return sb.ToString();
}
if (!string.IsNullOrWhiteSpace(ns)) return ns;

this._logger?.LogWarning(
"Namespace file {namespaceFilePath} wasn't found. Are we running in a pod? If you are running unit tests outside a pod, please create the test namespace '{namespace}'.",
Expand Down
27 changes: 9 additions & 18 deletions src/Orleans.Clustering.Kubernetes/KubeMembershipTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public KubeMembershipTable(ILoggerFactory loggerFactory, IOptions<ClusterOptions

public async Task InitializeMembershipTable(bool tryInitTableVersion)
{
this._namespace = await this.GetNamespace();
this._namespace = this.GetNamespace();
this._logger.LogInformation($"Using Kubernetes namespace: {this._namespace}.");

if (tryInitTableVersion)
{
Expand Down Expand Up @@ -373,7 +374,10 @@ private async Task TryInitClusterVersion()
catch (HttpOperationException ex)
{
if (ex.Response.StatusCode != HttpStatusCode.NotFound)
{
this._logger.LogError(ex, $"Unable to initialize cluster version: {ex.Message}.");
throw;
}
}

if (version == null)
Expand Down Expand Up @@ -549,27 +553,14 @@ await this._kubeClient.DeleteNamespacedCustomObjectAsync(
}
}

private async Task<string> GetNamespace()
private string GetNamespace()
{
var namespaceFilePath = Path.Combine(Constants.SERVICE_ACCOUNT_PATH, Constants.SERVICE_ACCOUNT_NAMESPACE_FILENAME);
if (File.Exists(namespaceFilePath))
{
using var sourceStream = new FileStream(namespaceFilePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true);
if (!File.Exists(namespaceFilePath)) return Constants.ORLEANS_NAMESPACE;

var sb = new StringBuilder();
var ns = File.ReadAllText(namespaceFilePath);

byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}

return sb.ToString();
}
if (!string.IsNullOrWhiteSpace(ns)) return ns;

this._logger?.LogWarning(
"Namespace file {namespaceFilePath} wasn't found. Are we running in a pod? If you are running unit tests outside a pod, please create the test namespace '{namespace}'.",
Expand Down

0 comments on commit 53f61c9

Please sign in to comment.