├── imgs ├── workshop-1.png ├── workshop-2.png ├── workshop-3.png ├── workshop-4.png ├── workshop-5.png ├── workshop-6.png ├── workshop-3.1.png └── reconcile-loop.png ├── crds ├── my-app-2.yaml ├── my-app.yaml ├── my-a.yaml ├── my-b.yaml ├── my-a2.yaml ├── service-a-crd-definition.yaml ├── service-b-crd-definition.yaml └── app-crd-definition.yaml ├── istio-gateway.yaml ├── deploy-service-b.md ├── .gitignore ├── deploy-controller.md ├── deploy-controller2.md ├── rbac.md ├── deploy-operator.md ├── deploy-function-a.md ├── deploy-service-a.md ├── our-crds.md ├── install.md ├── README.md └── LICENSE /imgs/workshop-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/workshop-1.png -------------------------------------------------------------------------------- /imgs/workshop-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/workshop-2.png -------------------------------------------------------------------------------- /imgs/workshop-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/workshop-3.png -------------------------------------------------------------------------------- /imgs/workshop-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/workshop-4.png -------------------------------------------------------------------------------- /imgs/workshop-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/workshop-5.png -------------------------------------------------------------------------------- /imgs/workshop-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/workshop-6.png -------------------------------------------------------------------------------- /imgs/workshop-3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/workshop-3.1.png -------------------------------------------------------------------------------- /imgs/reconcile-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salaboy/extending-k8s-with-spring-cloud/HEAD/imgs/reconcile-loop.png -------------------------------------------------------------------------------- /crds/my-app-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "beta.k8s.salaboy.org/v1" 2 | kind: Application 3 | metadata: 4 | name: my-app2 5 | spec: 6 | version: "0.1" 7 | selector: "my-app2" -------------------------------------------------------------------------------- /crds/my-app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "beta.k8s.salaboy.org/v1" 2 | kind: Application 3 | metadata: 4 | name: my-app 5 | spec: 6 | version: "1.0" 7 | selector: "my-app" -------------------------------------------------------------------------------- /crds/my-a.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "beta.k8s.salaboy.org/v1" 2 | kind: ServiceA 3 | metadata: 4 | name: my-a 5 | labels: 6 | app: my-app 7 | spec: 8 | serviceName: "my-service-a" 9 | serviceVersion: "1.0" 10 | -------------------------------------------------------------------------------- /crds/my-b.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "beta.k8s.salaboy.org/v1" 2 | kind: ServiceB 3 | metadata: 4 | name: my-b 5 | labels: 6 | app: my-app 7 | spec: 8 | serviceName: "my-service-b" 9 | serviceVersion: "1.0" 10 | -------------------------------------------------------------------------------- /crds/my-a2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "beta.k8s.salaboy.org/v1" 2 | kind: ServiceA 3 | metadata: 4 | name: my-a2 5 | labels: 6 | app: my-app2 7 | spec: 8 | serviceName: "my-service-a2" 9 | serviceVersion: "1.0" 10 | -------------------------------------------------------------------------------- /istio-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: my-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" -------------------------------------------------------------------------------- /deploy-service-b.md: -------------------------------------------------------------------------------- 1 | 2 | # Deploying Service B 3 | 4 | To deploy example service B follow the same instructions as [Deploying Service A](deploy-service-a.md) but in the example-service-b/ directory. 5 | 6 | ## Exposing Service B 7 | 8 | To expose example service B follow the same instructions as [Exposing Service A](deploy-service-a.md) but in the example-service-b/ directory. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | .DS_Store 25 | -------------------------------------------------------------------------------- /crds/service-a-crd-definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: service-as.beta.k8s.salaboy.org 5 | spec: 6 | group: beta.k8s.salaboy.org 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: service-as 11 | singular: service-a 12 | kind: ServiceA 13 | shortNames: 14 | - a 15 | validation: 16 | openAPIV3Schema: 17 | properties: 18 | spec: 19 | properties: 20 | serviceName: 21 | type: string 22 | required: 23 | - serviceName -------------------------------------------------------------------------------- /crds/service-b-crd-definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: service-bs.beta.k8s.salaboy.org 5 | spec: 6 | group: beta.k8s.salaboy.org 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: service-bs 11 | singular: service-b 12 | kind: ServiceB 13 | shortNames: 14 | - b 15 | validation: 16 | openAPIV3Schema: 17 | properties: 18 | spec: 19 | properties: 20 | serviceName: 21 | type: string 22 | required: 23 | - serviceName -------------------------------------------------------------------------------- /crds/app-crd-definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: applications.beta.k8s.salaboy.org 5 | spec: 6 | group: beta.k8s.salaboy.org 7 | version: v1 8 | scope: Namespaced 9 | subresources: 10 | status: {} 11 | names: 12 | plural: applications 13 | singular: application 14 | kind: Application 15 | shortNames: 16 | - apps 17 | additionalPrinterColumns: 18 | - name: STATUS 19 | type: string 20 | description: The Status of the App 21 | JSONPath: .spec.status 22 | - name: URL 23 | type: string 24 | description: The URL of the App 25 | JSONPath: .spec.url -------------------------------------------------------------------------------- /deploy-controller.md: -------------------------------------------------------------------------------- 1 | # Deploy Spring Cloud Gateway Controller 2 | 3 | ``` 4 | cd k8s-operator/ 5 | ``` 6 | 7 | Let's switch to the **controller** branch 8 | 9 | ``` 10 | git checkout controller 11 | ``` 12 | 13 | Build the project with 14 | ``` 15 | mvn clean install 16 | ``` 17 | 18 | Create a Docker image for it with 19 | ``` 20 | docker build -t salaboy/k8s-operator:controller . 21 | ``` 22 | 23 | Then push the docker image to be available in hub.docker.com. 24 | > You will need a Docker Hub and replace **salaboy** with your user name. You might also need to do docker login before push. 25 | 26 | ``` 27 | docker push salaboy/k8s-operator:controller 28 | ``` 29 | 30 | Finally deploy to our kubernetes cluster with: 31 | 32 | ``` 33 | cd kubernetes && \ 34 | kubectl apply -f deployment.yaml && \ 35 | kubectl apply -f service.yaml 36 | ``` -------------------------------------------------------------------------------- /deploy-controller2.md: -------------------------------------------------------------------------------- 1 | 2 | # Register watch on service 3 | 4 | ``` 5 | cd k8s-operator/ 6 | 7 | ``` 8 | 9 | Let's switch to the **controller2** branch 10 | 11 | ``` 12 | git checkout controller2 13 | ``` 14 | 15 | Build the project with 16 | 17 | ``` 18 | mvn clean install 19 | ``` 20 | 21 | Create a Docker image for it with 22 | 23 | ``` 24 | docker build -t salaboy/k8s-operator:controller2 . 25 | ``` 26 | 27 | Then push the docker image to be available in hub.docker.com. 28 | > You will need a Docker Hub and replace **salaboy** with your user name. You might also need to do docker login before push. 29 | 30 | ``` 31 | docker push salaboy/k8s-operator:controller2 32 | ``` 33 | 34 | Finally deploy to our kubernetes cluster with: 35 | 36 | ``` 37 | cd kubernetes && \ 38 | kubectl apply -f deployment.yaml && \ 39 | kubectl apply -f service.yaml 40 | ``` -------------------------------------------------------------------------------- /rbac.md: -------------------------------------------------------------------------------- 1 | # Setting up RBAC for our Controller 2 | Because we are creating an Controller/Operator that is going to access the Kubernetes APIs from inside the cluster (as a Pod) we need to create 3 important resources: Role, RoleBinding and ServiceAccount. 3 | 4 | Then inside the k8s-operator/kubernetes/ directory: 5 | 6 | ``` 7 | kubectl apply -f cluster-role.yaml && \ 8 | kubectl apply -f cluster-role-binding.yaml && \ 9 | kubectl apply -f service-account.yaml 10 | 11 | ``` 12 | 13 | Once we have these resources configured, we can deploy our Operator, you can check that inside the deployment descriptor this Deployment is using the ServiceAccount that we have created before. 14 | 15 | > **Note**: notice that I've created a Cluster wide Role and Role Binding, both extremely permissive. You might want to check the official documentation to configure these resources to be as restrictive as possible for your Operator to work. [ServiceAccounts](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/), [RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) 16 | -------------------------------------------------------------------------------- /deploy-operator.md: -------------------------------------------------------------------------------- 1 | # Deploying our K8s Operator 2 | 3 | Let's switch to the **operator** branch 4 | ``` 5 | git checkout operator 6 | ``` 7 | 8 | Build the project with 9 | ``` 10 | mvn clean install 11 | ``` 12 | Create a Docker image for it with 13 | ``` 14 | docker build -t salaboy/k8s-operator:operator . 15 | ``` 16 | Then push the docker image to be available in hub.docker.com. 17 | > You will need a Docker Hub and replace **salaboy** with your user name. You might also need to do docker login before push. 18 | 19 | ``` 20 | docker push salaboy/k8s-operator:operator 21 | ``` 22 | 23 | In order to deploy our K8s Operator we need to we can run: 24 | ``` 25 | cd kubernetes && \ 26 | kubectl apply -f deployment.yaml 27 | ``` 28 | 29 | If you take a look at the logs of the K8s Operator you will see that the pod is running, but it lacks the right resources to operate: 30 | ``` 31 | kubectl logs -f k8s-operator- k8s-operator 32 | 33 | // you should see something like: 34 | > Custom CRDs required to work not found please check your installation! 35 | ``` 36 | 37 | This means that we need to provide the Operator the Custom Resource Definitions we need to deploy these resources to make them available to our Cluster. In the crds/ directory we will find two things: 38 | 1) Custom Resource Definitions 39 | 2) Custom Resource (instance) -------------------------------------------------------------------------------- /deploy-function-a.md: -------------------------------------------------------------------------------- 1 | 2 | # Deploying Function A 3 | 4 | ``` 5 | cd example-function-a/ 6 | ``` 7 | 8 | Build the project with 9 | ``` 10 | mvn clean install 11 | ``` 12 | 13 | Create a Docker image for it with 14 | 15 | ``` 16 | docker build -t salaboy/example-function-a:0.0.1 . 17 | ``` 18 | 19 | Then push the docker image to be available in hub.docker.com. 20 | > You will need a Docker Hub and replace **salaboy** with your user name. You might also need to do docker login before push. 21 | 22 | ``` 23 | docker push salaboy/example-function-a:0.0.1 24 | ``` 25 | Then inside the kubernetes/ directory you can deploy your Knative function to your cluster with 26 | `` 27 | kubectl apply -f kservice.yaml 28 | `` 29 | 30 | You can find the external IP that you can use to call your function: 31 | ``` 32 | kubectl get svc istio-ingressgateway -n istio-system 33 | ``` 34 | 35 | And then call the function: 36 | 37 | ``` 38 | http 'Host:example-function-a.default.example.com' 39 | ``` 40 | 41 | You should see the function returning a value. 42 | 43 | 44 | > Notice that you can customize how the autoscaler scale down your function pods with: 45 | > ``` 46 | > kubectl edit cm config-autoscaler -n knative-serving 47 | > ``` 48 | > and changing the default value to: 49 | > ``` 50 | > scale-to-zero-grace-period: "30s" 51 | > ``` 52 | -------------------------------------------------------------------------------- /deploy-service-a.md: -------------------------------------------------------------------------------- 1 | # Deploying Service A 2 | 3 | This project contains the source code for a very simple service that do the following: 4 | - Every 10 seconds: call Service B (initial delay 5 seconds) 5 | - Every 10 seconds: call Function A 6 | 7 | It will print the output of each call or an error message if the services are unavailable. 8 | 9 | Let's build and deploy our Service A 10 | ``` 11 | cd example-service-a/ 12 | ``` 13 | 14 | 15 | To deploy we need to: 16 | Build the project with maven: 17 | ``` 18 | mvn clean install 19 | ``` 20 | Create a docker image with: 21 | ``` 22 | docker build -t salaboy/example-service-a:0.0.1 . 23 | ``` 24 | 25 | 26 | Then push the docker image to be available in hub.docker.com. 27 | > You will need a Docker Hub and replace **salaboy** with your user name. You might also need to do docker login before push. 28 | 29 | ``` 30 | docker push salaboy/example-service-a:0.0.1 31 | ``` 32 | Finally deploy to our kubernetes cluster with: 33 | ``` 34 | cd kubernetes/ && \ 35 | kubectl apply -f deployment.yaml && \ 36 | kubectl apply -f service.yaml 37 | ``` 38 | You can take a look at the logs by doing: 39 | 40 | ``` 41 | kubectl logs -f my-service-a- my-service-a 42 | ``` 43 | 44 | You can find the pod name by ruunning ```kubectl get pods``` 45 | 46 | ## Exposing Service A 47 | Now in order to access our Example Service A service, we need to expose it to clients that are external to the Cluster. 48 | We can do this with Native Ingress resources or by using the Istio Gateway that is available to us. 49 | 50 | In order to expose our Service using Istio Virtual Services we should run: 51 | ``` 52 | kubectl apply -f istio-virtual-service.yaml 53 | 54 | ``` 55 | This will create a new Route to access service a at http /my-service-a/ -------------------------------------------------------------------------------- /our-crds.md: -------------------------------------------------------------------------------- 1 | # Our CRDs 2 | As shown in the previous diagrams, we will be defining 3 custom CRDs. Service A, Service B and Application (which aggregates these two types of services). 3 | 4 | You can find our Custom Resource Definition in /extending-k8s-with-spring-cloud/crds/ 5 | 6 | We will first deploy the Custom Resource Definition for Service A 7 | 8 | ``` 9 | cd extending-k8s-with-spring-cloud/crds/ && \ 10 | kubectl apply -f service-a-crd-definition.yaml 11 | ``` 12 | 13 | This means that now we can do: 14 | ``` 15 | kubectl get a 16 | ``` 17 | Now we should get 18 | ``` 19 | No resources found. 20 | ``` 21 | Due we haven't created any instance of this new resource type. 22 | 23 | We can now create a new ServiceA instance by creating a resource of type ServiceA: 24 | ``` 25 | kubectl apply -f my-a.yaml 26 | ``` 27 | Now we should be able to get the newly created ServiceA resource: 28 | 29 | ``` 30 | kubectl get a 31 | ``` 32 | Should return: 33 | ``` 34 | NAME AGE 35 | my-a 4s 36 | ``` 37 | 38 | You can now repeat for the CRD ServiceB: 39 | ``` 40 | kubectl apply -f service-a-crd-definition.yaml 41 | ``` 42 | 43 | Let's add the Application CRD now, which will aggregate ServiceA and ServiceB resources into an Application: 44 | ``` 45 | kubectl apply -f app-crd-definition.yaml 46 | ``` 47 | 48 | we should be able to test it with: 49 | 50 | ``` 51 | kubectl get apps 52 | ``` 53 | 54 | Once again, no resources found is ok. 55 | 56 | Let's now the create an Application resource: 57 | ``` 58 | k apply -f my-app.yaml 59 | ``` 60 | 61 | Now doing: 62 | ``` 63 | kubectl get apps 64 | ``` 65 | should return: 66 | ``` 67 | NAME AGE 68 | my-app 47s 69 | ``` 70 | 71 | We can now create new resources from the following Kinds: ServiceA, ServiceB and Application, but since nobody is using these resources nothing will happen. We need now an Operator that understand about these CRDs and manage their lifecycle. -------------------------------------------------------------------------------- /install.md: -------------------------------------------------------------------------------- 1 | # Installation & Setup in GKE 2 | I've installed my environment in GKE where you can get a 300 USD free credit to create Kubernetes Clusters. (https://console.cloud.google.com/freetrial). I do personally prefer GKE than minikube, this is a real Kubernetes cluster. 3 | 4 | In this section we will perform the next steps: 5 | 1) Create a Cluster 6 | 2) Install Istio in the istio-system namespace 7 | 3) Install KNative (Service & Eventing) 8 | 4) Create an Istio gateway to route inbound traffic (from outside the cluster to our services) 9 | 10 | #### Creating the Cluster 11 | 12 | I've created a 3 nodes cluster with a **n1-standard-2** (2CPUs - 7.5GB RAM)setup for each node. I've selected Kubernetes version **1.12.6** for the following installation. (Notice that with an older version, the one for default doesn't work) 13 | 14 | You create the cluster and the connect to it (by going to http://console.cloud.google.com -> Kubernetes Engine -> Create a new Cluster and then Connect button on the cluster). 15 | 16 | Then to install Istio (in the istio-system namespace): 17 | ``` 18 | kubectl apply --filename https://github.com/knative/serving/releases/download/v0.4.0/istio-crds.yaml && \ 19 | kubectl apply --filename https://github.com/knative/serving/releases/download/v0.4.0/istio.yaml 20 | ``` 21 | 22 | Wait for all the Istio component to start before installing KNative with 23 | ``` 24 | kubectl -n istio-system get pods -w 25 | ``` 26 | CTRL-C to terminate the watch 27 | 28 | Look for all the pods to be **Running**. 29 | 30 | When that is done you can install Knative Service (in the knative-service namespace) 31 | 32 | ``` 33 | kubectl apply --filename https://github.com/knative/serving/releases/download/v0.4.0/serving.yaml 34 | ``` 35 | Once again, let's wait for all the services to start before proceeding. 36 | Watch to all the 37 | ``` 38 | kubectl -n knative-serving get pods -w 39 | ``` 40 | 41 | Once that's done let's install KNative Eventing (this will create two namespaces: knative-sources and knative-eventing) 42 | ``` 43 | kubectl apply --filename https://github.com/knative/eventing/releases/download/v0.4.0/release.yaml && \ 44 | kubectl apply --filename https://github.com/knative/eventing-sources/releases/download/v0.4.0/release.yaml && \ 45 | kubectl apply --filename https://raw.githubusercontent.com/knative/serving/v0.4.0/third_party/config/build/clusterrole.yaml 46 | ``` 47 | 48 | You can watch both: 49 | ``` 50 | kubectl -n knative-sources get pods -w 51 | ``` 52 | and 53 | 54 | ``` 55 | kubectl -n knative-eventing get pods -w 56 | ``` 57 | 58 | With the infrastructure ready to go, we now can create our Kubernetes Deployments, Create our Functions and our Routing Policies with Istio. 59 | 60 | Finally, instead of using Ingresses we will use an Istio Gateway to route traffic that is coming from outside the cluster to our services. 61 | For that in this repository we have the Gateway definition, so we can deploy it by running: 62 | 63 | ``` 64 | cd extending-k8s-with-spring-cloud/ 65 | kubectl apply -f istio-gateway.yaml 66 | ``` 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extending Kubernetes with Spring Cloud 2 | This repository serves as an index for a workshop like set of excersices about creating our custom resource definitions for Kubernetes using Spring Cloud components. 3 | 4 | During these exercises, you will learn about how to create your custom extensions, deploy them and use them as part of a Kubernetes Operator that will understand the domain specific restrictions that needs to be applied to the infrastructure. 5 | At the end you can find also some useful links to other related projects. 6 | 7 | 8 | ## Repositories 9 | We will use the following repositories to build our services and our Kubernetes Operator: 10 | - [K8s Operator](https://github.com/salaboy/k8s-operator) 11 | - [Example Service A](https://github.com/salaboy/example-service-a) 12 | - [Example Service B](https://github.com/salaboy/example-service-b) 13 | - [Example Function A](https://github.com/salaboy/example-function-a) 14 | 15 | It is recommended to clone all these repos under the same directory, as the following instructions are based in that assumption: 16 | ``` 17 | mkdir extending-k8s && \ 18 | cd extending-k8s && \ 19 | git clone https://github.com/salaboy/extending-k8s-with-spring-cloud && \ 20 | git clone https://github.com/salaboy/k8s-operator && \ 21 | git clone https://github.com/salaboy/example-service-a && \ 22 | git clone https://github.com/salaboy/example-service-b && \ 23 | git clone https://github.com/salaboy/example-function-a 24 | ``` 25 | 26 | ## Infrastructure 27 | 28 | For this tutorial we use Kubernetes, Istio and KNative to demonstrate what can be achieved with a Kubernetes Operator and how that might be done. An Kubernetes Operator doesn't require these technology stack, but due the adoption of these Kubernetes Extensions we believe that it is a good starting place. 29 | 30 | ### Installation 31 | For setting up the cluster in [GKE you can follow this guide](install.md) 32 | 33 | > **NOTE**: Remember that you can always find the external IP of your Gateway by running: 34 | >``` 35 | >kubectl get svc istio-ingressgateway -n istio-system 36 | >``` 37 | 38 | ## Workshop 39 | 40 | This workshop follows the next checkpoints: 41 | - [Checkpoint #0](#checkpoint-0): Services A, B and Function A 42 | - [Checkpoint #1](#checkpoint-1): Controller v1 (Gateway/Routes) 43 | - [Checkpoint #2](#checkpoint-2): Controller v2 (Notify if Service B is missing) 44 | - [Checkpoint #3](#checkpoint-3): Operator v1 (CRDs and App) 45 | - [Checkpoint #4](#checkpoint-4): Operator v2 (+Checking K8s Services) 46 | 47 | ## Checkpoint 0 48 | 49 | Services A, B and Function A 50 | 51 | We will start by deploying a set of services into our K8s Cluster. Here we will use the Service and Deployment resource, which will cause Kubernetes to create some other resources such as ReplicaSet and Pods. 52 | 53 | ![Checkpoint #0](imgs/workshop-1.png "Checkpoint #0") 54 | 55 | - [Deploying Service A](deploy-service-a.md) 56 | - See how the service A returns the default answer if B and Function A are not present 57 | - [Deploying Service B](deploy-service-b.md) 58 | - See how A start consuming B 59 | - [Deploying Function A](deploy-function-a.md) 60 | - See how Service A consume Function A 61 | 62 | ![Checkpoint #0](imgs/workshop-2.png "Checkpoint #0") 63 | 64 | Now that we have our k8s services up and running we can expose them using Istio Gateway to access them from outside the cluster 65 | 66 | - [Expose Service A (with an Istio Virtual Service + Istio Gateway)](deploy-service-a.md) 67 | - [Expose Service B (with an Istio Virtual Service + Istio Gateway](deploy-service-a.md) 68 | 69 | > **NOTE**: Remember that you can always find the external IP of your Gateway by running: 70 | >``` 71 | >kubectl get svc istio-ingressgateway -n istio-system 72 | >``` 73 | 74 | Now that we have exposed our services we can access them by creating a GET HTTP request to: 75 | ``` 76 | http /my-service-a/ 77 | ``` 78 | and 79 | ``` 80 | http /my-service-b/ 81 | ``` 82 | 83 | ## Checkpoint 1 84 | 85 | Controller v1 (Gateway/Routes) 86 | 87 | > "A controller actively monitor and maintains a set of Kubernetes resources in a desired state." - Kubernetes Patterns 88 | 89 | While working with controllers/operators we will be basically implementing the [Reconciler Pattern](https://www.oreilly.com/library/view/cloud-native-infrastructure/9781491984291/ch04.html) by following the next infinite loop: 90 | 91 | ![Reconcile Loop](imgs/reconcile-loop.png "Reconcile Loop") 92 | 93 | In this case we will build K8s controller that understands about Services and create routes to forward traffic to different services based on the request path. We will achieve this, by using the Spring Cloud Gateway plus the Spring Cloud Kubernetes Discovery implementation. 94 | 95 | ![Checkpoint #1](imgs/workshop-3.png "Checkpoint #1") 96 | - [Setting up RBAC for our Controller](rbac.md): ServiceAccount, Role & RoleBinding 97 | - [Deploy Spring Cloud Gateway Controller](deploy-controller.md) 98 | - Show basic Routing on K8s service discovery (/actuator/gateway/routes) 99 | 100 | 101 | ## Checkpoint 2 102 | 103 | Controller v2 (Notify if a Service is missing) 104 | In this checkpoint we have a controller that watch the K8s Service resources and as soon as one gets deleted just print a log message. 105 | 106 | - [Register watch on K8s Services](deploy-controller2.md) 107 | - You can hide and expose services based on business requirements, not yamls 108 | ![Checkpoint #2](imgs/workshop-3.1.png "Checkpoint #2") 109 | 110 | ## Checkpoint 3 111 | 112 | > "An Operator is a Controller that uses CRDs to encapsulate operational knowledge for a specific application.." - Kubernetes Patterns 113 | 114 | Operator v1 (CRDs and App) 115 | 116 | On this check point we create our CRDs and we will create an Operator that understand about these new resources and act on them. 117 | 118 | ![Checkpoint #3](imgs/workshop-4.png "Checkpoint #3") 119 | - [Our CRDs](our-crds.md) 120 | - Deploy CRDs: service-a, service-b and Application 121 | - Use kubectl to get the resources 122 | - Look at the operator's output, see how the status and URL of the application are provided when all the Services are present 123 | 124 | ## Checkpoint 4 125 | 126 | Operator v2 (+Checking K8s Services) 127 | On the second version of the Operator we link our CRDs ServiceA, ServiceB and Application to K8s native resources and we act accordingly depending on the changes. 128 | 129 | ![Checkpoint #4](imgs/workshop-5.png "Checkpoint #4") 130 | - Deploy version 2 of k8s-operator 131 | - Look at code that watch k8s resources changes 132 | - Creating custom routes based on CRDs for Applications 133 | - Expose apps based on application healthy checks 134 | 135 | ![Checkpoint #4](imgs/workshop-6.png "Checkpoint #4") 136 | 137 | # Links 138 | - [Spring Cloud Kubernetes](http://github.com/spring-cloud/spring-cloud-kubernetes/) 139 | - The K8s-operator project is using Spring Cloud Kubernetes Discovery as well as the Spring Cloud Gateway 140 | - [JVM Operators](http://github.com/jvm-operators) 141 | - WIP branch in k8s-operator -> question asked to the project -> https://github.com/jvm-operators/abstract-operator/issues/50 142 | - [AP4K](http://github.com/ap4k/ap4k) 143 | - You can look at the 2 mins video from @iocanel [here](https://www.youtube.com/watch?v=XctRwTu4ma4) 144 | - You can take a look at the ap4k branch of the example-service-a project. 145 | - [KIND](http://github.com/kubernetes-sigs/kind) 146 | - GOLANG tools to compare 147 | - [KubeBuilder](https://www.github.com/kubernetes-sigs/kubebuilder) 148 | - [Operator SDK](https://github.com/operator-framework/operator-sdk) 149 | 150 | # Conclusions 151 | 152 | These examples are just very simple examples of what can be done. Now that we are running with existing K8s extensions such as Istio, KNative, Tekton CD, Gloo, etc, we have the perfect opportunity to integrate and build more complex operators that really understand the infrastructure, can monitor and act to better integrate our domain specific resources. 153 | 154 | Please feel free to create issues, ask questions or even send PRs if you want to collaborate to expand these examples. 155 | 156 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------