{"id":13487581,"url":"https://github.com/IBM/spring-cloud-kubernetes-with-istio","last_synced_at":"2025-03-27T22:31:38.273Z","repository":{"id":42351311,"uuid":"118508871","full_name":"IBM/spring-cloud-kubernetes-with-istio","owner":"IBM","description":null,"archived":true,"fork":false,"pushed_at":"2020-10-07T11:34:44.000Z","size":28,"stargazers_count":95,"open_issues_count":1,"forks_count":33,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-12-20T23:34:51.297Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IBM.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-22T20:08:48.000Z","updated_at":"2024-10-16T18:22:31.000Z","dependencies_parsed_at":"2022-09-13T00:32:16.030Z","dependency_job_id":null,"html_url":"https://github.com/IBM/spring-cloud-kubernetes-with-istio","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBM%2Fspring-cloud-kubernetes-with-istio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBM%2Fspring-cloud-kubernetes-with-istio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBM%2Fspring-cloud-kubernetes-with-istio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBM%2Fspring-cloud-kubernetes-with-istio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IBM","download_url":"https://codeload.github.com/IBM/spring-cloud-kubernetes-with-istio/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245935630,"owners_count":20696672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T18:01:00.844Z","updated_at":"2025-03-27T22:31:37.861Z","avatar_url":"https://github.com/IBM.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"# Spring Cloud Kubernetes \u0026 Istio.\n\n## Summary\n\n[Spring Cloud Kubernetes](https://github.com/spring-cloud-incubator/spring-cloud-kubernetes)\nis a great way to use [Spring Cloud](http://projects.spring.io/spring-cloud/) concepts\n while running on [Kubernetes](https://kubernetes.io/). It offers mappings between Spring and Kubernetes\nversions of concepts such as; service discovery, service proxying, and configuration.\n\nBut what happens when you throw [Istio](https://istio.io/) into the mix? Istio allows for content based\nrouting, fault injection, and more. Does Spring Cloud Kubernetes play well? or will\nthere be snaggles to work out?\n\n## Testing Spring Cloud Kubernetes with Istio\n\n### Environment\n\nI used [IBM Cloud Private](https://github.com/IBM/deploy-ibm-cloud-private)\n(ICP) 2.1.0 via Vagrant for my tests, with Istio 0.4.0 installed\nwith the [auto sidecar injection](https://istio.io/docs/setup/kubernetes/sidecar-injection.html#automatic-sidecar-injection)\nenabled. If you want to try the project out, make sure you have `kubectl`\n[configured to talk to your cluster](https://github.com/IBM/deploy-ibm-cloud-private#accessing-ibm-cloud-private),\nand you have done the [required setup](https://www.ibm.com/support/knowledgecenter/en/SSBS6K_2.1.0/manage_images/configuring_docker_cli.html)\nto allow you to push images to the docker repository. Lastly you'll need your\n`/etc/hosts` file updated to have `mycluster.icp` pointed at your cluster ip.\n(Usually `192.168.27.100`, unless you altered the Vagrantfile)\n\nIf you are not using ICP, you'll need to edit the pom.xml and alter the instances\nof `mycluster.icp:8500/default/demo:latest` to point at the registry of your choice.\n\nAfter installing Istio on the cluster, also [install the Istio bookinfo sample](https://istio.io/docs/guides/bookinfo.html#running-on-kubernetes).\nThis article uses the reviews service from the bookinfo sample as it's target\nendpoint. If you have auto sidecar injection enabled, then it's just this\none command from the Istio install directory.\n\n```\nkubectl apply -f samples/bookinfo/bookinfo.yaml\n```\n\n\u003eTIP: Don't use [Istio Mutual TLS](https://istio.io/docs/concepts/security/mutual-tls.html)\n(`istio-auth.yaml`) with Spring Cloud Kubernetes at\nthe moment, Istio doesn't like the way that Spring Cloud Kubernetes will use the\nsame port for livenessProbe/readinessProbe, as it does for the main service.\n\nThe full code for the test app is available [here](https://github.com/IBM/spring-cloud-kubernetes-with-istio), and can be built, and deployed\nwith\n\n```\nmvn package fabric8:build fabric8:resource fabric8:push fabric8:deploy\n```\n\nThe maven pom file\nrelies upon the [Fabric8 Maven Plugin](https://maven.fabric8.io/) to generate the\ndocker image for the app, push the docker image to the registry, generate the\nkubernetes deployment and service yaml, and apply them to the cluster.\n\nYou'll need to apply `ingress.yaml` to your cluster as well, and that will let you\ninvoke the application at `http://mycluster.icp/greeting`\n\n```\nkubectl apply -f ./ingress.yaml\n```\n\n### Config Maps via injection / Config Beans.\n\nSpring Cloud Kubernetes can map properties from a single config map into your\napplication via  either `@Value` annotated fields, or via a bean annotated with\n`@ConfigurationProperties` the latter accepting a `prefix` attribute that can\nset the prefix under which the properties in the map. Eg, if the Config Map had\na property of `config.fish` and the annotation was `@ConfigurationProperties(prefix=\"config\")`\nthen the bean can have fields of `String getFish()` and `void setFish(String value)`\n\nOur example application uses both approaches, with the RestController itself\nusing `@Value` injection, and a bean class `ConfigBean` using the `@ConfigurationProperties`.\nTo try it out, apply the `configmap.yaml` to your cluster to create the map used\nby the application.\n\n```\nkubectl apply -f ./configmap.yaml\n```\n\nAnd then access `http://mycluster.icp/greeting` and look for the\nvalues of `fish` and `heelHeight` in the output. Notice how the configmap values\nhave overridden the values specified in `application.properties` in the application.\n\n\u003eTIP: if you create a map while the app is running, where the map was missing\nbefore the app was started, you may need to restart the app to have it bind\ncorrectly to the ConfigMap, rather than just use it's values in `application.properties`.\nRestart the app by scaling the ReplicaSet to zero and back to 1, or by removing\nthe k8sservice Pod, and allowing the ReplicaSet to launch a new one for you.\n\nFrom a build perspective, it's worth noting that to use this feature, you have to\nadd the dependency for the config feature to your pom.xml. This used to be included in the default\n`spring-cloud-starter-kubernetes` feature back when it was owned by `fabric8`, but has\nbeen split out to it's own feature now it's owned by `spring-cloud-incubator`.\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n  \u003cartifactId\u003espring-cloud-kubernetes-config\u003c/artifactId\u003e\n\u003c/dependency\u003e\n```\n\nBy default, Spring Cloud Kubernetes will look for a Kubernetes ConfigMap with the\nsame name as the application (set via the `spring.application.name` property in\napplication.properties/yaml), the map name can be altered by setting the properties\n`spring.cloud.kubernetes.config.name` and `spring.cloud.kubernetes.config.namespace`\nwhich work as expected, and allow the config to be pulled from a differently named map,\neven from outside the namespace of the app.\n\nFor more information, check out the current documentation for this feature at the\n[Spring Cloud Kubernetes GitHub](https://github.com/spring-cloud-incubator/spring-cloud-kubernetes#configmap-propertysource).\n\n### Responding to ConfigMap Changes at Runtime\n\nSpring Cloud Kubernetes has one extra trick for config, it can update the config\nin the running app if the ConfigMap data changes during runtime. To enable the\nlive config updates, you need to add the property\n```\nspring.cloud.kubernetes.reload.enabled=true\n```\nto your application's `bootstrap.properties` file.\n\nBy default, changes are detected by monitoring Kubernetes Events, an approach\nthat requires the app to have the appropriate permissions, `view` for ConfigMaps,\nand `edit` for Secrets. By default, ICP does not grant these, but you can add them to the\nserviceaccount using kubectl, for example, to add the `view` role, you could do this:\n\n\u003eTIP: Because Istio deploys to clusters with Role Based Auth (RBAC) enabled,\nit's a lot more likely you'll need to give the appropriate permissions for\nevent based config updates to function.\n\n```\nkubectl create rolebinding default-view-binding --clusterrole=view --serviceaccount=default:default --namespace=default\n```\nThis works because our example is deployed in ICP using the serviceaccount `default` in the `default` namespace.\n\nIf you cannot grant the permissions, you could switch the config monitoring to `polling` mode,\nby adding these properties to your `bootstrap.properties`\n```\nspring.cloud.kubernetes.reload.mode=polling\nspring.cloud.kubernetes.reload.period=500\n```\n\nOnce changes are being detected, you'll then notice that by default, changes to\nconfig that are read via the `@ConfigurationProperties` annotated bean, are updated\nin the app as you would expect, but changes read via the `@Value` annotated field\nin RestController, are not picked up. Clearly Spring Cloud Kubernetes is aware the property\nhas changed, but the problem is that by default the RestController is not an entity\nthat will be reloaded after the config change.\n\nYou can try this with the example app, by altering the configmap.yaml and reissuing\nthe `kubectl apply` command to update the configmap in the cluster. When you access\n`http://mycluster.icp/greeting` you will see the values 'via bean'\nwill have updated to reflect the new ConfigMap values. Note that the value 'via RestController'\nremains unaffected.\n\nIf you need other objects to respond to the change, you can change the refresh\nstrategy for the reload, using the property `spring.cloud.kubernetes.reload.strategy`,\nthe value `restart_context` will relaunch the Spring ApplicationContext, or the value\n`shutdown` will cause the container to exit (and relies upon the replication controller\nin Kubernetes to sping up a new container that will pick up the new value).\n\nThe full documentation for the \"PropertySource Reload\" feature is over in the\n[Spring Cloud Kubernetes GitHub](https://github.com/spring-cloud-incubator/spring-cloud-kubernetes#propertysource-reload).\n\n### RestTemplate and Istio\n\nSpring's RestTemplate is a really handy way to invoke another service, and when\nrunning on Kubernetes, you can take advantage of the Kubernetes approach to Services\nthat gives every service a unique DNS name within the cluster.\n\nUsing a RestTemplate to access `http://servicename.servicenamespace.svc.cluster.local:serviceport/`\nworks as expected, with Spring going via the Kubernetes Service to reach an appropriate backing\nservice instance. If the Service in question is represented by a ReplicaSet with multiple\ninstances, Kubernetes handles the load balancing across the instances.\n\nAs expected, any requests made in this way gain any behaviors configured via\nIstio for the service being invoked.\n\n### Discovery Client and Istio\n\nSpring Cloud Kubernetes provides an implementation of the Spring DiscoveryClient\nthat uses Kubernetes to find service instances. The discovery client is included\nin the default `spring-cloud-starter-kubernetes` dependency, but for it to function\nit's important to note you must add the `@EnableDiscoveryClient` annotation to your\nmain Spring Application class. Without this annotation, the injected DiscoveryClient\nwill have no Kubernetes logic, and won't behave as expected.\n\nYou can use the DiscoveryClient to lookup services, and get a list of all services\nwithin your applications namespace. Note that the URI's returned for the\nservices are the Pod addresses, invoking a service via such a URI means your\nare hitting the Pod directly, bypassing any LoadBalancing configured in Kubernetes.\n\nThis also has consequences for Istio, when you hit the Pod IP and Port directly,\nIstio doesn't know which service you are trying to access, and is unable to apply\nany of the the behavior you may have configured for the service. When Istio is\nin play, you are not talking directly to the service, but instead to the envoy\nproxy that Istio has deployed as the sidecar. The proxy requires the Http `Host:`\nheader to be set, with the name of the service being invoked. Without this header\ntoday Istio will return a 404 for\n\n### Client side load balancing with Ribbon and Istio\n\nSimilarly to DisoveryClient, using Spring Cloud Kubernetes Ribbon Plugin allows\nSpring applications to perform client side load balancing.\n\nAdding the Ribbon Plugin is as simple as adding the dependency:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n    \u003cartifactId\u003espring-cloud-starter-kubernetes-netflix\u003c/artifactId\u003e\n\u003c/dependency\u003e\n```\n\nIt also needs the rest of Ribbon to work, so you need the following too..\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n  \u003cartifactId\u003espring-cloud-starter-netflix-ribbon\u003c/artifactId\u003e\n  \u003cversion\u003e1.4.2.RELEASE\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThen you just annotate your RestTemplate with `@LoadBalanced`, and add\n`@RibbonClient(name = \"servicename\")` to your main SpringApplication class,\nwhere `servicename` matches the name of the Kubernetes Service you want to call.\n\nThen when you use the rest template with a url like `http://servicename/`\n(`servicename` matching the one you set in `@RibbonClient`),\nSpring Cloud Kubernetes will go find the services matching that\nname, and invoke one as per its load balancing configuration.\n\nNote that because `RibbonClient` is essentially using the `DiscoveryClient` to find it's Services,\nthat it is hitting the Pod IP and Port directly. Unfortunately, it will not add a\n`Host:` header with the service name, meaning that **requests made via Ribbon will\n_bypass_ Istio**.\n\n## Snaggles\n\n### Istio within the same pod.\n\nIstio is only able to affect traffic between pods, so if a service decides to\ninvoke itself, or any other endpoint hosted within the same Pod Istio will not be able to affect the traffic.\n\nImagine multiple RestControllers, or multiple RequestMappings on a RestController,\nwhere one invocation attempts to invoke itself, or a invokes a different path that's part of the same app.\nThis should hopefully be rare, as the app could just invoke the other path directly as a Java method,\nrather than going via Rest, just be aware that in either case, Istio will be unable to affect the\ncall.\n\nIf you require Istio behavior in these circumstances, you should be sure to invoke the Kubernetes Service\nURL, ( `http://servicename.servicenamespace.svc.cluster.local`) rather than a URL obtained via DiscoveryClient or via Ribbon.\n\n### Fabric8 Maven Plugin issues.\n\nIstio requires the ports for the container to be named as `grpc`,`redis`,`mongo`, `http` or `http2`.\nAs per the Istio [Pod Spec Requeirements](https://istio.io/docs/setup/kubernetes/sidecar-injection.html#pod-spec-requirements))\nFabric8 Maven Plugin attempts to name the port based on the port number. For `8080`\nFabric8 Maven Plugin knows it should assign the name `http`, but for port `9080`\n(another common port used by well known Java app servers for http), it will assign the name `glrpc`.\n\nHaving a port name other than one of the Istio recognized portnames, results in Istio not affecting\nthe traffic at all. If your Istio rules are not applying as you would expect, remember to check the\nport names.\n\n\u003eNOTE: If the port names do not match between the container and the service, an exception is thrown,\nand the connection is refused. This one caught me out for a while!\n\nI've yet to figure out how to convince Fabric8 Maven Plugin to allow me to define names for ports, so\n*if you plan to use this plugin with Istio, ensure you use port 80, 443, or 8080* to have the expected results.\n\n### Istio initial startup networking\n\nBecause I'm testing with ICP, which I'm running inside a Vagrant VM, and because I wasn't\npaying attention to the VM's resource requirements, I ended up with a rather overstretched VM.\nThis in return lead to slow container startup times.\n\nThere's a brief period during App Container start with Istio, where the Istio Proxy takes over\nthe network traffic for the App, during which requests to/from the pod can be refused.\n\nIf your cluster is experiencing heavy load, or is being run by an uncaring administrator inside\nan overstretched VM, then that window can be long enough to affect Spring Cloud Kubernetes applications.\n\nDuring startup for a Spring Cloud Kubernetes application, the library attempts to make requests\nto the Kubernetes API via http to obtain information about the Pod it is running in. If that\nrequest occurs at a point when the Pods networking isn't functional, you'll see a very early\nstack trace in the logs for the App, and lookups via DiscoveryClient/RibbonClient or ConfigMap\nvalue injection, may not act as expected.\n\nMy workaround was to add an initial delay to the app container by prepending a `sleep 15` to the\nDocker `CMD` instruction for the container. This gave Istio a chance to get the networking sorted\nout before my app attempted to use it.\n\nChances are you won't need the delay, and will hopefully never notice it, but if you do, maybe\nknowing this will help =). (Alternatively maybe Spring Cloud Kubernetes could retry it's request\nusing hystrix, or similar, or swap to an async/reactive model for handling the reply).\n\n## Testing Istio Rules with the Application.\n\nIncluded within the example project is an Istio yaml that instructs Istio to return Error 500 for\nall calls to the review service.\n\nIf you add this rule via `istioctl` ...\n\n```\nistioctl create -f ./reviews-500.yaml\n```\n\nYou will then see when you access `http://mycluster.icp/greeting` that the calls placed to the `reviews`\nservice are now returning Error 500. The 3 invocations made are:\n\n- Via a RestTemplate to the K8S Service URL.\n- Via a direct HTTP client to the K8S Service URL.\n- Via a direct HTTP client to the URL retrieved from DiscoveryClient, but with an added `Host:` header.\n\n\u003eTIP: if you have followed the bookinfo tutorial, you will have rules in place that\nhave a higher precedence than the error 500 rule, either delete the other rules for the\nreview service, or edit the `reviews-500.yaml` to give it a higher precedence.\n\n## Conclusion\n\nIf you want to use Istio, you probably don't want to be using `RibbonClient` within your\nSpring application when using Spring Cloud Kubernetes. You'll lose most of the power of Istio,\nand it may not be immediately obvious what's going on.\n\nUse of DiscoveryClient results should be performed cautiously, the requirement to add the\n`Host:` header is unlikely to be met by existing code, or code within Libraries, leading to\nunexpected effects when the application is reliant upon Istio for routing rules, or custom\nbehavior.\n\nConfigMap integration on the other hand works very well, and does represent a great way to\nintegrate Kubernetes config to a Spring app. Just bear in mind it only allows one ConfigMap, so\nif your app requires multiple, then you might as well configure them via the Kubernetes yaml to\nuse `envFrom` to pull the values into the container environment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIBM%2Fspring-cloud-kubernetes-with-istio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FIBM%2Fspring-cloud-kubernetes-with-istio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIBM%2Fspring-cloud-kubernetes-with-istio/lists"}