├── .gitignore
├── .gitmodules
├── .workshop
├── build
├── develop-settings.sh
├── settings.sh
└── setup
├── Dockerfile
├── LICENSE
├── README.md
└── workshop
├── _modules.yml
├── _rhsummit18.yml
├── content
├── acl.adoc
├── authentication.adoc
├── circuit-breaking.adoc
├── fault-injection.adoc
├── images
│ ├── 503.png
│ ├── auth-fail.png
│ ├── auth-success.png
│ ├── blacklist_v3_blocked.png
│ ├── circuit-breaking-graph-slowv2.png
│ ├── circuit-graph.png
│ ├── delay.png
│ ├── failfast.png
│ ├── kiali-graph-1.png
│ ├── kiali-graph-2.png
│ ├── kiali-tracing-1.png
│ ├── kiali-tracing-2.png
│ ├── kiali-tracing-3.png
│ ├── kiali-tracing-4.png
│ ├── mtls_initial.png
│ ├── mtls_no_destination_rule.png
│ ├── mtls_permissive.png
│ ├── mtls_policy_and_rule.png
│ ├── nocircuit-graph.png
│ ├── nocircuit.png
│ ├── rate.png
│ ├── rbac-fail.png
│ ├── rbac-success.png
│ ├── retry.png
│ ├── routing-graph-1.png
│ ├── routing-graph-2.png
│ ├── routing-graph-3.png
│ ├── timeout-v1.png
│ ├── timeout-v2.png
│ ├── timeout.png
│ └── whitelist_v2_fail.png
├── intro.adoc
├── kiali.adoc
├── mtls.adoc
├── rate-limiting.adoc
├── routing.adoc
├── scripts
│ ├── build-images.sh
│ ├── curl_customer.sh
│ ├── curl_customer_long.sh
│ ├── curl_customer_preference.sh
│ ├── curl_customer_preference_quiet.sh
│ ├── curl_customer_quiet.sh
│ ├── curl_customer_token.sh
│ ├── curl_customer_token_quiet.sh
│ ├── curl_recommendation_args.sh
│ ├── deploy.sh
│ ├── loadtest.sh
│ ├── loadtest_quiet.sh
│ ├── retries.sh
│ └── setup.sh
├── setup-environment.adoc
├── src
│ ├── customer
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src
│ │ │ ├── main
│ │ │ ├── docker
│ │ │ │ ├── Dockerfile.jvm
│ │ │ │ └── Dockerfile.native
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── redhat
│ │ │ │ │ └── developer
│ │ │ │ │ └── demos
│ │ │ │ │ └── customer
│ │ │ │ │ └── rest
│ │ │ │ │ ├── BaggageHeadersFactory.java
│ │ │ │ │ ├── CustomerResource.java
│ │ │ │ │ └── PreferenceService.java
│ │ │ └── resources
│ │ │ │ └── application.properties
│ │ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── redhat
│ │ │ └── developer
│ │ │ └── demos
│ │ │ └── customer
│ │ │ └── rest
│ │ │ ├── CustomerResourceTest.java
│ │ │ └── NativeCustomerResourceIT.java
│ ├── deployments
│ │ ├── curl.yaml
│ │ ├── customer.yaml
│ │ ├── gateway.yaml
│ │ ├── preference.yaml
│ │ └── recommendation.yaml
│ ├── istiofiles
│ │ ├── acl-blacklist.yml
│ │ ├── acl-whitelist.yml
│ │ ├── authentication-enable-tls.yml
│ │ ├── authorization-enable-rbac.yml
│ │ ├── controlplane.yaml
│ │ ├── destination-rule-recommendation_cb_policy_version_v2.yml
│ │ ├── destination-rule-tls.yml
│ │ ├── destination-rule.yml
│ │ ├── namespace-rbac-policy.yml
│ │ ├── policy-jwt.yaml
│ │ ├── policy-permissive-tls.yml
│ │ ├── recommendation_rate_limit.yml
│ │ ├── recommendation_requestcount.yml
│ │ ├── routing-canary.yaml
│ │ ├── routing-mirroring.yaml
│ │ ├── routing-weighted.yaml
│ │ ├── virtual-service-20-80.yml
│ │ ├── virtual-service-80-20.yml
│ │ ├── virtual-service-recommendation-503.yml
│ │ ├── virtual-service-recommendation-delay.yml
│ │ ├── virtual-service-recommendation-split.yml
│ │ └── virtual-service-recommendation-timeout.yml
│ ├── preference
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src
│ │ │ ├── main
│ │ │ ├── docker
│ │ │ │ ├── Dockerfile.jvm
│ │ │ │ └── Dockerfile.native
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── redhat
│ │ │ │ │ └── developer
│ │ │ │ │ └── demos
│ │ │ │ │ └── customer
│ │ │ │ │ └── rest
│ │ │ │ │ ├── PreferenceResource.java
│ │ │ │ │ └── RecommendationService.java
│ │ │ └── resources
│ │ │ │ └── application.properties
│ │ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── redhat
│ │ │ └── developer
│ │ │ └── demos
│ │ │ └── customer
│ │ │ └── rest
│ │ │ ├── NativePreferenceResourceIT.java
│ │ │ └── PreferenceResourceTest.java
│ └── recommendation
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── pom.xml
│ │ └── src
│ │ ├── main
│ │ ├── docker
│ │ │ ├── Dockerfile.jvm
│ │ │ └── Dockerfile.native
│ │ └── java
│ │ │ └── com
│ │ │ └── redhat
│ │ │ └── developer
│ │ │ └── demos
│ │ │ └── recommendation
│ │ │ └── rest
│ │ │ └── RecommendationResource.java
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── redhat
│ │ └── developer
│ │ └── demos
│ │ └── recommendation
│ │ └── rest
│ │ ├── NativeRecommendationResourceIT.java
│ │ └── RecommendationResourceTest.java
├── workshop-overview.adoc
└── workshop-summary.adoc
├── modules.yaml
└── workshop.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | .kube
2 | .config
3 | .bash_history
4 | *.swp
5 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule ".workshop/scripts"]
2 | path = .workshop/scripts
3 | url = https://github.com/openshift-homeroom/spawner-scripts.git
4 | branch = stable/1.x
5 |
--------------------------------------------------------------------------------
/.workshop/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This 'build' script is where you can add steps that should be run when
4 | # building the image for your custom workshop.
5 |
6 | # Move the workshop content to '/opt/app-root/workshop'. It could be left
7 | # at it's default location of '/opt/app-root/src/workshop', but by moving it,
8 | # it is out of view of the user doing the workshop and they aren't likely to
9 | # delete it by accident and break the display of the workshop content.
10 |
11 | mv workshop /opt/app-root/workshop
12 |
13 | # Also delete some of the other files from the top of the Git repository we
14 | # don't need for running the workshop.
15 |
16 | rm -f Dockerfile README.md LICENSE
17 |
18 | # If the workshop requires Git, it is necessary to set some defaults for
19 | # the name and email of the user for Git.
20 |
21 | git config --global user.email "you@example.com"
22 | git config --global user.name "Your Name"
23 |
--------------------------------------------------------------------------------
/.workshop/develop-settings.sh:
--------------------------------------------------------------------------------
1 | AUTH_USERNAME=workshop
2 | AUTH_PASSWORD=workshop
3 |
--------------------------------------------------------------------------------
/.workshop/settings.sh:
--------------------------------------------------------------------------------
1 | WORKSHOP_NAME=lab-ossm
2 | WORKSHOP_IMAGE=quay.io/thoraxe/lab-ossm:production
3 | RESOURCE_BUDGET=medium
4 | MAX_SESSION_AGE=3600
5 | IDLE_TIMEOUT=300
6 | SPAWNER_MODE=hosted-workshop
7 |
--------------------------------------------------------------------------------
/.workshop/setup:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This 'setup' script is where you can add steps that should be run each
4 | # time the container for the workshop is started. Note that if you are
5 | # using persistent storage with a workshop and make changes to files from
6 | # this script, or are deploying applications, your scripts must cope with
7 | # the steps having already been run. This is because this script will be
8 | # run a second time if the container were restarted for some reason.
9 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM quay.io/openshifthomeroom/workshop-dashboard:4.1.1
2 |
3 | USER root
4 |
5 | COPY . /tmp/src
6 |
7 | RUN rm -rf /tmp/src/.git* && \
8 | chown -R 1001 /tmp/src && \
9 | chgrp -R 0 /tmp/src && \
10 | chmod -R g+w /tmp/src
11 |
12 | ENV TERMINAL_TAB=split
13 |
14 | USER 1001
15 |
16 | RUN /usr/libexec/s2i/assemble
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Red Hat OpenShift Service Mesh in Action
2 | This repository contains the content for a Homeroom-based workshop that
3 | provides several exercises exploring Red Hat OpenShift Service Mesh (RHOSSM).
4 |
5 | More information about Homeroom can be found [here](https://github.com/openshift-homeroom/lab-workshop-content).
6 |
7 | ## Contributing and Workflow
8 | Contributions are most welcome. Please feel free to file issues.
9 |
10 | When submitting pull requests, make sure to submit them against the `develop`
11 | branch. Once merged, those changes will automatically be built into a new
12 | image that lands on Quay.io: https://quay.io/repository/thoraxe/lab-ossm
13 |
14 | ## Code Repository Tagging
15 | This repository is cloned as part of a provisioning process associated with
16 | the Red Hat Product Demo System (RHPDS). As such, specific tags of this
17 | repository will always be fetched such that RHPDS can lock to a point-in-time
18 | of the repository. A normal versioning scheme that roughly aligns with the Service Mesh version should be used.
19 |
20 | ## Image Tagging
21 | The container image on Quay.io will be built continually for both the
22 | `master` and `develop` branches, resulting in image tags of `latest`,
23 | `master`, and `develop`.
24 |
25 | A manual tag for `production` should be created when an update to what RHPDS
26 | deploys is desired. The tag for `production` should point at the image built
27 | from whatever code repository tag is being deployed by RHPDS.
28 | Example workflow:
29 |
30 | * Pull request merged to `develop`
31 | * Image built and tagged `develop` and `latest`
32 | * Testing performed and, if successful:
33 | * Pull request from `develop` to `master`
34 | * Image built and tagged `master` and `latest`
35 | * Tag `master` code repository with a version eg: 1.0.3
36 | * Tag `master` image in Quay.io with `production` and `1.0.3`
37 | * Ensure RHPDS production is cloning repository tag `1.0.3`
38 |
39 | ## Deploying to your own cluster
40 | Deploying this experience to your own cluster is not particularly difficult.
41 | You will need to understand the difference between the way the lab guide is
42 | written and the names you give your own resources in your own cluster.
43 |
44 | We are using the prefix `my-` for Project names. The lab guide will be using
45 | your OpenShift username as the prefix. If you know your username is `john`,
46 | then you should use the prefix `john-` when creating Projects. That being
47 | said, you need cluster administrator access to install the operators.
48 | However, *you **cannot** use the lab guide as `kubeadmin`*. The `kubeadmin`
49 | user is an alias for `kube:admin`, and the colon causes the lab guide to
50 | attempt to use a serviceaccount with a colon, which is invalid, and breaks
51 | the lab guide.
52 |
53 | If you don't already have real users in your cluster, you can use an
54 | [htpasswd identity
55 | provider](https://docs.openshift.com/container-platform/4.4/authentication/identity_providers/configuring-htpasswd-identity-provider.html)
56 | to create users easily and quickly.
57 |
58 | There are three main steps to getting the lab going in your cluster.
59 |
60 | ### Installing the Service Mesh operators and Control Plane
61 |
62 | 1. Install an OpenShift 4 environment. You will need cluster-administrator access.
63 | 1. Follow the documentation for [installing the service mesh operators](https://docs.openshift.com/container-platform/4.4/service_mesh/service_mesh_install/installing-ossm.html#installing-ossm)
64 | 1. Create a project called `my-smcp` where the Service Mesh control plane will be installed.
65 | 1. Follow the documentation for [installing the service mesh control plane](https://docs.openshift.com/container-platform/4.4/service_mesh/service_mesh_install/installing-ossm.html#ossm-control-plane-deploy-operatorhub_installing-ossm) and install it into your `my-smcp` project (not `istio-system`).
66 | 1. While you are waiting for the control plane to install, create a new project called `my-tutorial`.
67 | 1. Follow the documentation for [creating the member role](https://docs.openshift.com/container-platform/4.4/service_mesh/service_mesh_install/installing-ossm.html#ossm-member-roll-create-console_installing-ossm) being sure to use the project you created (`my-tutorial`) and to do so *inside* the `my-smcp` project.
68 |
69 | Wait for the above steps to complete before continuing. You can track the
70 | progress of the Service Mesh control plane installation by looking at the
71 | control plane details (assuming defaults):
72 |
73 | Installed Operators -> `my-smcp` -> Red Hat OpenShift Service Mesh -> Istio Service Mesh Control Plane -> `basic install`
74 |
75 | In the Conditions area, you will see that _Installed_, _Reconciled_, and
76 | _Ready_ are all _True_ once the installation is successful.
77 |
78 | ### Deploying the tutorial application
79 | It will be easiest to do this from the command line. Note that the following
80 | scriptlet assumes you have created a project called `my-tutorial`:
81 |
82 | ```
83 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/curl.yaml
84 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/customer.yaml
85 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/gateway.yaml
86 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/preference.yaml
87 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/0f55e6623bdd0a32b31be92f5b0869a0d7abf648/recommendation.yaml
88 | ```
89 |
90 | ### Deploying the lab guide
91 |
92 | 1. Create a Project to hold the lab guide -- `lab-ossm`
93 | 1. You will need the cluster's default route subdomain to use with `new-app`. The following will put it into a `bash` variable:
94 |
95 | export SUBDOMAIN=$(oc get ingresses.config.openshift.io cluster -o jsonpath='{.spec.domain}')
96 |
97 | 1. Then, use the `new-app` subcommand of the `oc` CLI to deploy the lab guide:
98 |
99 | oc new-app -n lab-ossm https://raw.githubusercontent.com/openshift-homeroom/workshop-spawner/7.1.0/templates/hosted-workshop-production.json \
100 | --param CLUSTER_SUBDOMAIN="$SUBDOMAIN" \
101 | --param SPAWNER_NAMESPACE="lab-ossm" \
102 | --param WORKSHOP_NAME="lab-ossm" \
103 | --param WORKSHOP_IMAGE="quay.io/thoraxe/lab-ossm:production" \
104 | --param OC_VERSION="4.3"
105 |
106 | Take special note of the route that is created. It will be something like `lab-ossm-lab-ossm.xxxxxx`.
107 |
108 | Access the lab guide and then make sure to login using the username and
109 | password you use to access the OpenShift environment. Since you needed
110 | cluster administrative access to install the Service Mesh operators, you're
111 | probably fine.
112 |
113 | ### Caveats
114 | None of the variables are appropriately set in this environment. You will
115 | need to pay careful attention to all commands to make sure they look like
116 | they will evaluate correctly. The various bash scripts use an environment
117 | variable `JUPYTERHUB_USER` as a prefix to Project names (see the note above).
118 | Make sure it is set correctly in the terminals.
--------------------------------------------------------------------------------
/workshop/_modules.yml:
--------------------------------------------------------------------------------
1 | modules:
2 | intro:
3 | name: Introduction and Setup
4 | kiali:
5 | name: Observing the Service Mesh using Kiali
6 | routing:
7 | name: Routing within the Service Mesh
8 | mtls:
9 | name: Enabling Mutual TLS
10 | authentication:
11 | name: Authentication (JWT and RBAC)
12 | acl:
13 | name: Whitelisting/Blacklisting
14 | circuit-breaking:
15 | name: Retries, Timeouts and Circuit Breaking
16 | rate-limiting:
17 | name: Rate Limiting/Policy
18 | fault-injection:
19 | name: Fault Injection
20 | # extra-credit:
21 | # name: Extra Credit
22 |
--------------------------------------------------------------------------------
/workshop/_rhsummit18.yml:
--------------------------------------------------------------------------------
1 | id: "istio"
2 | name: "Red Hat OpenShift Service Mesh in Action"
3 |
4 | content:
5 | url: https://raw.githubusercontent.com/thoraxe/istio-lab-summit-2019/master
6 |
7 | vars:
8 | KUBEADMIN_PASSWORD: "default"
9 | API_URL: "default"
10 | MASTER_URL: "default"
11 | OCP_USERNAME: "defaultocpusername"
12 |
13 | modules:
14 | activate:
15 | - intro
16 | - kiali
17 | - routing
18 | - mtls
19 | - authentication
20 | - acl
21 | - circuit-breaking
22 | - rate-limiting
23 | - fault-injection
24 |
--------------------------------------------------------------------------------
/workshop/content/acl.adoc:
--------------------------------------------------------------------------------
1 | = Access Control Lists
2 |
3 | Access control lists set whitelists/blacklists to restrict communication.
4 | They are a part of the policy management feature set that is provided by
5 | Istio's Mixer, via various adapters.
6 |
7 | :toc:
8 |
9 | == What we will learn in this module
10 | This module will provide instruction on how to configure whitelists and blacklists,
11 | which control what services a service is able to access.
12 |
13 | == Before Starting
14 | You only need the `customer` Virtual Service and Gateway, but if you have the
15 | `recommendation` Destination Rule from other exercises, that's OK:
16 |
17 | [source,bash,role="execute-1"]
18 | ----
19 | oc -n %username%-tutorial get istio-io
20 | ----
21 |
22 | And you should see something like the following:
23 |
24 | ----
25 | NAME AGE
26 | gateway.networking.istio.io/customer-gateway 3h16m
27 |
28 | NAME GATEWAYS HOSTS AGE
29 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m
30 |
31 | NAME HOST AGE
32 | destinationrule.networking.istio.io/recommendation recommendation 36m
33 | ----
34 |
35 | If you have any scripts running in the bottom terminal, make sure to click
36 | there and then press ctrl+c to terminate them.
37 |
38 | == Patience is Required
39 | The access control rules take some time to be applied and reflected. Be patient here!
40 |
41 | [#whitelist]
42 | == Whitelist
43 | We’ll create a whitelist on the preference service to only allow requests to
44 | the recommendation service if the version is v1 or v3. Requests to the v2
45 | version of the recommendation service will return a 404 Not Found HTTP error
46 | code.
47 |
48 | First, start generating traffic:
49 |
50 | [source,bash,role="execute-2"]
51 | ----
52 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh
53 | ----
54 |
55 | You can also run the command below a couple times to see what the output from
56 | the shell script above is like:
57 |
58 | [source,bash,role="execute-1"]
59 | ----
60 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
61 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
62 | ----
63 |
64 | You will see something like:
65 |
66 | ----
67 | customer => preference => recommendation v1 from '765d4bc49d-ddgg7': 2949
68 | customer => preference => recommendation v2 from '7679d466f8-2hwcd': 2950
69 | ----
70 |
71 | Now create the whitelist:
72 |
73 | [source,bash,role="execute-1"]
74 | ----
75 | oc apply -f /opt/app-root/workshop/content//src/istiofiles/acl-whitelist.yml -n %username%-tutorial
76 | ----
77 |
78 | The YAML you just created looks like:
79 |
80 | [source, yaml]
81 | ----
82 | apiVersion: "config.istio.io/v1alpha2"
83 | kind: handler
84 | metadata:
85 | name: preferencewhitelist
86 | spec:
87 | compiledAdapter: listchecker
88 | params:
89 | overrides: ["v1", "v3"]
90 | blacklist: false
91 | ---
92 | apiVersion: "config.istio.io/v1alpha2"
93 | kind: instance
94 | metadata:
95 | name: preferencesource
96 | spec:
97 | compiledTemplate: listentry
98 | params:
99 | value: destination.labels["version"]
100 | ---
101 | apiVersion: "config.istio.io/v1alpha2"
102 | kind: rule
103 | metadata:
104 | name: checktorecommendation
105 | spec:
106 | match: destination.labels["app"] == "recommendation"
107 | actions:
108 | - handler: preferencewhitelist
109 | instances: [ preferencesource ]
110 | ----
111 |
112 | If you look at Kiali, you will notice that traffic is now making it to the v1
113 | and v3 versions of the service, but 100% of the traffic to the v2 service is
114 | being rejected with `403 Forbidden`.
115 |
116 | image::images/whitelist_v2_fail.png[]
117 |
118 | You can use `curl` via a script to see the 403 error:
119 |
120 | [source,bash,role="execute-1"]
121 | ----
122 | bash /opt/app-root/workshop/content/scripts/curl_recommendation_args.sh v2
123 | ----
124 |
125 | [#whitelist-cleanup]
126 | == Cleanup
127 |
128 | [source,bash,role="execute-1"]
129 | ----
130 | oc delete -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/acl-whitelist.yml
131 | ----
132 |
133 | [#blacklist]
134 | == Blacklist
135 | Whereas whitelists allow communication only to the listed hosts, blacklists
136 | deny traffic to the listed hosts. We'll demonstrate using a blacklist on
137 | version 3 of the recomemendation service. Any requests to v3 will result in a
138 | `403 Forbidden` HTTP error code.
139 |
140 | If you have stopped previous load script, start it again.
141 |
142 | [source,bash,role="execute-2"]
143 | ----
144 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh
145 | ----
146 |
147 | Run the below once or twice to see actual output:
148 |
149 | [source,bash,role="execute-1"]
150 | ----
151 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
152 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
153 | ----
154 |
155 | You will see something like:
156 |
157 | ----
158 | customer => preference => recommendation v1 from '765d4bc49d-ddgg7': 2953
159 | customer => preference => recommendation v3 from '6d4bf9cff8-5nvw2': 834
160 | customer => preference => recommendation v2 from '7679d466f8-2hwcd': 2954
161 | customer => preference => recommendation v1 from '765d4bc49d-ddgg7': 2954
162 | ----
163 |
164 | You can see traffic is hitting all of the service versions.
165 |
166 | *Note:* it can take several minutes for the deletion of the whitelist to propagate. If you don't see the above, double check that you deleted the whitelist and then create the blacklist anyway.
167 |
168 | Now, create the blacklist:
169 |
170 | [source,bash,role="execute-1"]
171 | ----
172 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/acl-blacklist.yml -n %username%-tutorial
173 | ----
174 |
175 | The blacklist YAML looks like the following:
176 |
177 | [source, yaml]
178 | ----
179 | apiVersion: "config.istio.io/v1alpha2"
180 | kind: handler
181 | metadata:
182 | name: denycustomerhandler
183 | spec:
184 | compiledAdapter: denier
185 | params:
186 | status:
187 | code: 7
188 | message: Not allowed
189 | ---
190 | apiVersion: "config.istio.io/v1alpha2"
191 | kind: instance
192 | metadata:
193 | name: denycustomerrequests
194 | spec:
195 | compiledTemplate: checknothing
196 | ---
197 | apiVersion: "config.istio.io/v1alpha2"
198 | kind: rule
199 | metadata:
200 | name: denycustomer
201 | spec:
202 | match: source.labels["app"]=="preference" && destination.labels["app"] == "recommendation" && destination.labels["version"] == "v3"
203 | actions:
204 | - handler: denycustomerhandler
205 | instances: [ denycustomerrequests ]
206 | ----
207 |
208 | Eventually, if you look at Kiali, you can now see that requests to the v3 version of the
209 | service are failing:
210 |
211 | image::images/blacklist_v3_blocked.png[]
212 |
213 | // TODO: add some kind of curl to demonstrate the 4xx
214 |
215 | You can also see this from the command line:
216 |
217 | [source,bash,role="execute-1"]
218 | ----
219 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
220 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
221 | ----
222 |
223 | You'll see only `v1` and `v2`:
224 |
225 | ----
226 | customer => preference => recommendation v2 from '556d799fbf-rbcpt': 2381
227 | customer => preference => recommendation v1 from '7dbd669545-hzp4x': 5130
228 | customer => preference => recommendation v2 from '556d799fbf-rbcpt': 2382
229 | customer => preference => recommendation v1 from '7dbd669545-hzp4x': 5131
230 | ----
231 |
232 | [#blacklist-cleanup]
233 | == Cleanup
234 |
235 | [source,bash,role="execute-1"]
236 | ----
237 | oc delete -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/acl-blacklist.yml
238 | ----
239 |
--------------------------------------------------------------------------------
/workshop/content/authentication.adoc:
--------------------------------------------------------------------------------
1 | = Authentication with JWT and RBAC
2 | In addition to enforcing security with mutual TLS, Red Hat OpenShift Service
3 | Mesh can also perform origin authentication, or _end-user authentication_ at
4 | the request level using JSON Web Token (JWT).
5 |
6 | == What we will learn in this module
7 | In this module, we are going to see how to enable authenticating end users
8 | with the Service Mesh. We will also see how to use the Service Mesh
9 | authorization feature to provide access control for services in the mesh.
10 |
11 | == Before Starting
12 | You only need the `customer` Virtual Service and Gateway, but if you have the
13 | `recommendation` Destination Rule from other exercises, that's OK:
14 |
15 | [source,bash,role="execute-1"]
16 | ----
17 | oc -n %username%-tutorial get istio-io
18 | ----
19 |
20 | And you should see something like the following:
21 |
22 | ----
23 | NAME AGE
24 | gateway.networking.istio.io/customer-gateway 3h16m
25 |
26 | NAME GATEWAYS HOSTS AGE
27 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m
28 |
29 | NAME HOST AGE
30 | destinationrule.networking.istio.io/recommendation recommendation 36m
31 | ----
32 |
33 | If you have any scripts running in the bottom terminal, make sure to click
34 | there and then press ctrl+c to terminate them.
35 |
36 | [#enablingauthentication]
37 | == Enabling User-End Authentication
38 |
39 | Now it is time to enable end-user authentication.
40 |
41 | The first thing you need to do is validate that it is possible to communicate
42 | between all services without authentication.
43 |
44 | Execute the following:
45 |
46 | [source,bash,role="execute-1"]
47 | ----
48 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
49 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
50 | ----
51 |
52 | You will see something like:
53 |
54 | ----
55 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 704
56 | customer => preference => recommendation v2 from '556d799fbf-nc5cz': 705
57 | customer => preference => recommendation v3 from '69df5546f-vb9nb': 706
58 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 705
59 | customer => preference => recommendation v2 from '556d799fbf-nc5cz': 706
60 | customer => preference => recommendation v3 from '69df5546f-vb9nb': 707
61 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 706
62 | customer => preference => recommendation v2 from '556d799fbf-nc5cz': 707
63 | customer => preference => recommendation v3 from '69df5546f-vb9nb': 708
64 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 707
65 | ----
66 |
67 | Now we will add an end-user authentication policy. Take a look at the
68 | authentication YAML:
69 |
70 | [source,yaml]
71 | ----
72 | apiVersion: "authentication.istio.io/v1alpha1"
73 | kind: "Policy"
74 | metadata:
75 | name: "jwt-example"
76 | spec:
77 | targets:
78 | - name: customer
79 | origins:
80 | - jwt:
81 | issuer: "testing@secure.istio.io"
82 | jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/jwks.json"
83 | principalBinding: USE_ORIGIN
84 | ---
85 | ----
86 |
87 | And then run the following to deploy the policy:
88 |
89 | [source,bash,role="execute-1"]
90 | ----
91 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/policy-jwt.yaml -n %username%-tutorial
92 | ----
93 |
94 | After about 1 minute, let's run the curl again:
95 |
96 | [source,bash,role="execute-1"]
97 | ----
98 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
99 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
100 | ----
101 |
102 | You will see:
103 |
104 | ----
105 | Origin authentication failed.
106 | ----
107 |
108 | If not, wait a few more moments and try again. If you want more details, run
109 | the following:
110 |
111 | [source,bash,role="execute-1"]
112 | ----
113 | curl -sv http://${INGRESS_GATEWAY}
114 | ----
115 |
116 | You'll notice a `401 Unauthorized` error message. This is because the service
117 | mesh is now enforcing authentication, and your curl is not authenticated (no valid JWT).
118 |
119 | === Kiali's Graph
120 |
121 | Within the Kiali UI select the *Graph* option from the left hand navigation
122 | and then choose
123 |
124 | * Namespace: %username%-tutorial
125 | * Versioned app graph
126 | * Requests percentage
127 | * Last 1m
128 | * Every 10s
129 |
130 | [#img-auth-fail]
131 | .Kiali Graph Authentication Failures
132 | image::images/auth-fail.png[]
133 |
134 | Note the 100% failure rate from the end-user (`istio-ingressgateway`) to the
135 | customer service. You may need to execute the failed curl a bunch of times to
136 | generate enough traffic for Kiali to display.
137 |
138 | When we created the policy, it referenced an external origin. There is also a
139 | token that goes with it. The following commands will fetch a valid token and
140 | then use that token in the `Authorization` header of the curl request.
141 | Execute it several times so that Kiali gets enough traffic to graph:
142 |
143 | [source,bash,role="execute-1"]
144 | ----
145 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
146 | TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/demo.jwt -s)
147 | for x in $(seq 1 10); do curl --header "Authorization: Bearer $TOKEN" $INGRESS_GATEWAY -s; done
148 | ----
149 |
150 | And you will see something like:
151 |
152 | ----
153 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27103
154 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27140
155 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18439
156 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27104
157 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27141
158 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18440
159 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27105
160 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27142
161 | ----
162 |
163 | === Kiali's Graph
164 | If you look back at Kiali's graph, you will note the 100% success rate from
165 | the authenticated end-user to customer. It may take a refresh cycle or two
166 | before the errors from previous runs disappear:
167 |
168 | [#cleanup]
169 | === Clean Up
170 |
171 | [source,bash,role="execute-1"]
172 | ----
173 | oc delete -n %username%-tutorial policy.authentication.istio.io/jwt-example
174 | ----
175 |
176 | = Service Mesh Role Based Access Control (RBAC)
177 |
178 | [#enabling-rbac]
179 | == Enabling RBAC
180 |
181 | `RbacConfig` objects are used to enable and configure Authorization in the
182 | service mesh. Take a look at the following YAML:
183 |
184 | [source,yaml]
185 | ----
186 | apiVersion: "rbac.istio.io/v1alpha1"
187 | kind: RbacConfig
188 | metadata:
189 | name: default
190 | spec:
191 | mode: 'ON_WITH_INCLUSION'
192 | inclusion:
193 | namespaces: ["%username%-tutorial"]
194 | ----
195 |
196 | Run this command to deploy the RBAC:
197 |
198 | [source,bash,role="execute-1"]
199 | ----
200 | sed "s/CHANGEIT/$JUPYTERHUB_USER-tutorial/" /opt/app-root/workshop/content/src/istiofiles/authorization-enable-rbac.yml | oc apply -n %username%-tutorial -f -
201 | ----
202 |
203 | Now RBAC is enabled on your mesh. Wait a few moments and then run the curl to
204 | test the RBAC:
205 |
206 | [source,bash,role="execute-1"]
207 | ----
208 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
209 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
210 | ----
211 |
212 |
213 | You will see an error:
214 |
215 | ----
216 | RBAC: access denied
217 | ----
218 |
219 | For more specific details, execute one curl:
220 |
221 | [source,bash,role="execute-1"]
222 | ----
223 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
224 | curl http://${INGRESS_GATEWAY} -sv
225 | ----
226 |
227 | Notice that you get a `403 Forbidden` HTTP response.
228 |
229 | === Kiali's Graph
230 |
231 | Within the Kiali UI select the *Graph* option from the left hand navigation
232 | and then choose:
233 |
234 | * Namespace: %username%-tutorial
235 | * Versioned app graph
236 | * Requests percentage
237 | * Last 1m
238 | * Every 10s
239 |
240 | [#img-rbac-fail]
241 | .Kiali Graph Denied RBAC
242 | image::images/auth-fail.png[]
243 |
244 | Note the 100% failure rate due to denied RBAC
245 |
246 | By default, Istio uses a _deny by default_ strategy, meaning that nothing is
247 | permitted until you explicitly define access control policy to grant access
248 | to any service. At this point we have enabled RBAC but we have no roles and
249 | are not supplying any auth, so we are _denied by default_.
250 |
251 | [#grant-access]
252 | == Granting Access
253 |
254 | Let's grant access to any user to any service of our mesh (`customer`,
255 | `preference`, `recommendation`) only and only if the communication is an HTTP
256 | `GET`.
257 |
258 | [source,yaml]
259 | ----
260 | apiVersion: "rbac.istio.io/v1alpha1"
261 | kind: ServiceRole
262 | metadata:
263 | name: service-viewer
264 | spec:
265 | rules:
266 | - services: ["*"]
267 | methods: ["GET"]
268 | constraints:
269 | - key: "destination.labels[app]"
270 | values: ["customer", "recommendation", "preference"]
271 | ---
272 | apiVersion: "rbac.istio.io/v1alpha1"
273 | kind: ServiceRoleBinding
274 | metadata:
275 | name: bind-service-viewer
276 | spec:
277 | subjects:
278 | - user: "*"
279 | roleRef:
280 | kind: ServiceRole
281 | name: "service-viewer"
282 | ----
283 |
284 | Note the _Service Role_ `service-viewer` is configured for all users (*) and
285 | limits access to the GET method for the three services. This _Service Role_
286 | object exists in your tutorial app's namespace, so its behavior is applied
287 | there.
288 |
289 | Run this command to deploy the role and role binding:
290 |
291 | [source,bash,role="execute-1"]
292 | ----
293 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/namespace-rbac-policy.yml -n %username%-tutorial
294 | ----
295 |
296 | Wait a few moments and then send some requests by executing the following a few times:
297 |
298 | [source,bash,role="execute-1"]
299 | ----
300 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
301 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
302 | ----
303 |
304 | You will see something like:
305 |
306 | ----
307 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27224
308 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18522
309 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27187
310 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27225
311 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18523
312 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27188
313 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27226
314 | ----
315 |
316 | The communication now is possible. We aren't specifying a user, but no user
317 | is "any" user, so it works.
318 |
319 | === Kiali's Graph
320 |
321 | Within the Kiali UI select the *Graph* option from the left hand navigation
322 | and then choose:
323 |
324 | * Namespace: %username%-tutorial
325 | * Versioned app graph
326 | * Requests percentage
327 | * Last 1m
328 | * Every 10s
329 |
330 | [#img-rbac-success]
331 | .Kiali Graph Allowed RBAC
332 | image::images/kiali-graph-2.png[]
333 |
334 | Note the 100% success rate.
335 |
336 | [#cleanup]
337 | == Clean Up
338 |
339 | [source,bash,role="execute-1"]
340 | ----
341 | oc delete -n %username%-tutorial rbacconfig.rbac.istio.io/default servicerole.rbac.istio.io/service-viewer servicerolebinding.rbac.istio.io/bind-service-viewer
342 | ----
343 |
344 | You will see something like:
345 |
346 | ----
347 | servicerole.rbac.istio.io "service-viewer" deleted
348 | servicerolebinding.rbac.istio.io "bind-service-viewer" deleted
349 | rbacconfig.rbac.istio.io "default" deleted
350 | ----
351 |
352 | = What we learned in this module
353 | Red Hat OpenShift Service Mesh provides the capability to authenticate
354 | end-users via JWT and to enforce service RBAC. Kiali provides the mechanism
355 | to visialize end-user authentication and RBAC failures.
356 |
--------------------------------------------------------------------------------
/workshop/content/circuit-breaking.adoc:
--------------------------------------------------------------------------------
1 | = Circuit Breaking/Timeouts
2 |
3 | Istio provides opt-in failure recovery and fault injection features that you
4 | can configure dynamically at runtime. Using these features helps your
5 | applications operate reliably, ensuring that the service mesh can tolerate
6 | failing nodes and preventing localized failures from cascading to other
7 | nodes.
8 |
9 | == What we will learn in this module
10 | This module will provide instruction on request retries, handling of service
11 | timeouts, and how to apply circuit breaking. This module will also show how to
12 | visualize these capabilities in Kiali.
13 |
14 | == Before Starting
15 | You only need the `customer` Virtual Service and Gateway, but if you have the
16 | `recommendation` Destination Rule from other exercises, that's OK:
17 |
18 | [source,bash,role="execute-1"]
19 | ----
20 | oc -n %username%-tutorial get istio-io
21 | ----
22 |
23 | And you should see something like the following:
24 |
25 | ----
26 | NAME AGE
27 | gateway.networking.istio.io/customer-gateway 3h16m
28 |
29 | NAME GATEWAYS HOSTS AGE
30 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m
31 |
32 | NAME HOST AGE
33 | destinationrule.networking.istio.io/recommendation recommendation 36m
34 | ----
35 |
36 | If you have any scripts running in the bottom terminal, make sure to click
37 | there and then press ctrl+c to terminate them.
38 |
39 | [#retry]
40 | == Retry
41 |
42 | To add resilience to your application, you can ask the Service Mesh to retry
43 | failed connections N more times. Istio by default implements retries for
44 | `VirtualService` endpoints. You will make now make recommendation-v2 fail
45 | 100% of the time so that we can observe this retry behavior:
46 |
47 | [source,bash,role="execute-1"]
48 | ----
49 | bash /opt/app-root/workshop/content/scripts/retries.sh misbehave
50 | ----
51 |
52 | You will see something like:
53 |
54 | ----
55 | Following requests to / will return a 503
56 | ----
57 |
58 | This is a special endpoint that will toggle the recommendation application to
59 | return only `503`s. You can validate that the v2 endpoint now only returns
60 | `503` with the following:
61 |
62 | [source,bash,role="execute-1"]
63 | ----
64 | bash /opt/app-root/workshop/content/scripts/curl_recommendation_args.sh v2
65 | ----
66 |
67 | However, you will see that a normal request that goes through the entire set
68 | of microservices will work every time. This is because the mesh is
69 | implementing retries:
70 |
71 | [source,bash,role="execute-1"]
72 | ----
73 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
74 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
75 | ----
76 |
77 | Start generating traffic in the background:
78 |
79 | [source,bash,role="execute-2"]
80 | ----
81 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh
82 | ----
83 |
84 | === Kiali's Graph
85 | In the Kiali graph, you will notice 0% of the traffic going to v2. You can
86 | click on the red V2 box and see no traffic is being logged:
87 |
88 | [#img-503]
89 | .Kiali Graph Retry
90 | image:images/retry.png[]
91 |
92 | Even though the recommendation v2 has no traffic logged, the client has a
93 | 100% success rate due to the retries.
94 |
95 | Now, make the pod v2 behave well again:
96 |
97 | [source,bash,role="execute-1"]
98 | ----
99 | bash /opt/app-root/workshop/content/scripts/retries.sh behave
100 | ----
101 |
102 | You will see something like:
103 |
104 | ----
105 | Following requests to / will return 200
106 | ----
107 |
108 | The application is back to random load-balancing between v1, v2 and v3.
109 |
110 | [#timeout]
111 | == Timeout
112 |
113 | Now we will configure a service to wait only N seconds before giving up and
114 | failing.
115 |
116 | First, introduce some wait time in `recommendation v2` by making it a slow
117 | performer with a 3 second delay by running the command
118 |
119 | [source,bash,role="execute-1"]
120 | ----
121 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=3
122 | ----
123 |
124 | You will see something like:
125 |
126 | ----
127 | Timeout has been set to 3 seconds
128 | ----
129 |
130 | Keep the load script running.
131 |
132 | In Kiali Graph, switch to viewing Response time (it's the second dropdown
133 | menu, to the right of _Versioned app graph_).
134 |
135 | See the load-balancing between v1, v2 and v3 but with v2 taking a bit of time
136 | to respond
137 |
138 | [#img-]
139 | .Kiali Graph Showing slow v2 endpoint response time
140 | image:images/circuit-breaking-graph-slowv2.png[]
141 |
142 | If you click the "Distributed Tracing" button in Kiali, you can look at the
143 | Jaeger traces for these calls. You'll see that the 3s time for the calls that
144 | hit V2 make the trace look bizarre, because the rest of the calls take
145 | single-digit _milliseconds_ in comparison.
146 |
147 | *Note:* If you see an error when trying to access the distributed tracing
148 | information, make sure that you visit the Jaeger UI directly in order to
149 | accept the SSL certificate and grant permissions to Jaeger. In this lab
150 | guide, you can do this by clicking on "Console" to open the OpenShift web
151 | console, then click "Networking" -> "Routes" and make sure to select the
152 | `%username%-smcp` project (at the top) in order to find the URL for Jaeger.
153 |
154 | [#img-timeout-v1]
155 | .Kiali Distributed Tracing for Recommendation v1
156 | image:images/timeout-v1.png[]
157 |
158 | [#img-timeout-v2]
159 | .Kiali Distributed Tracing for Recommendation v2
160 | image:images/timeout-v2.png[]
161 |
162 | Note the duration of v2 is > 3s compared to the ms time of v1 and v3
163 |
164 | Now, add the timeout rule:
165 |
166 | [source,bash,role="execute-1"]
167 | ----
168 | oc create -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-timeout.yml
169 | ----
170 |
171 | This file looks like:
172 |
173 | [source,yaml]
174 | ----
175 | apiVersion: networking.istio.io/v1alpha3
176 | kind: VirtualService
177 | metadata:
178 | name: recommendation
179 | spec:
180 | hosts:
181 | - recommendation
182 | http:
183 | - route:
184 | - destination:
185 | host: recommendation
186 | timeout: 1.000s
187 | ----
188 |
189 | This tells Istio to wait no longer than 1.000s before declaring the endpoint
190 | timed out and retrying.
191 |
192 | You will see it return v1 or v3 after waiting about 1 second. You don't see
193 | v2 anymore because the response from v2 expires after the timeout period and
194 | it is never returned:
195 |
196 | [source,bash,role="execute-1"]
197 | ----
198 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
199 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
200 | ----
201 |
202 | You can also observe this in Kiali:
203 |
204 | [#img-timeout]
205 | .Kiali Graph for Timeout Rule
206 | image:images/timeout.png[]
207 |
208 | Note that recommendation v2 now has a 100% failure rate due to the timeout
209 | rule. Also note that the response time will never be less than 1s because
210 | every third request, which would hit v2, ends up being timed out and retried.
211 |
212 | === Clean up
213 |
214 | Change the implementation of `v2` back to the image that responds without the
215 | delay of 3 seconds:
216 |
217 | [source,bash,role="execute-1"]
218 | ----
219 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=0
220 | ----
221 |
222 | You will see something like:
223 |
224 | ----
225 | Timeout has been set to 0 seconds
226 | ----
227 |
228 | Then delete the virtual service created for timeout by:
229 |
230 | [source,bash,role="execute-1"]
231 | ----
232 | oc delete -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-timeout.yml
233 | ----
234 |
235 | You will see something like:
236 |
237 | ----
238 | virtualservice.networking.istio.io "recommendation" deleted
239 | ----
240 |
241 | Lastly, terminate the load script running in the bottom terminal with Control+C.
242 |
243 | [#failfast]
244 | == Fail Fast with Max Connections and Max Pending Requests
245 |
246 | Let's use a 34/33/33 split of traffic:
247 |
248 | [source,bash,role="execute-1"]
249 | ----
250 | oc create -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-split.yml
251 | ----
252 |
253 | This YAML creates a `DestinationRule` and a `VirtualService` to control the traffic:
254 |
255 | [source,yaml,subs="+macros,+attributes"]
256 | ----
257 | apiVersion: networking.istio.io/v1alpha3
258 | kind: VirtualService
259 | metadata:
260 | name: recommendation
261 | spec:
262 | hosts:
263 | - recommendation
264 | http:
265 | - route:
266 | - destination:
267 | host: recommendation
268 | subset: v1
269 | weight: 34
270 | - destination:
271 | host: recommendation
272 | subset: v2
273 | weight: 33
274 | - destination:
275 | host: recommendation
276 | subset: v3
277 | weight: 33
278 | ---
279 | ----
280 |
281 | Note the weighting of the 3 recommendation destination versions.
282 |
283 | Run the following a few times to generate some load for Kiali to capture:
284 |
285 | [source,bash,role="execute-1"]
286 | ----
287 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
288 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
289 | ----
290 |
291 | Now go to "Distributed Tracing" in Kiali and look at the response times. Note that all recommendation hits respond within single-digit milliseconds.
292 |
293 | [#img-failfast]
294 | .Kiali Distributed Tracing for Base Fail Fast
295 | image:images/failfast.png[]
296 |
297 | [#nocircuitbreaker]
298 | === Load test without circuit breaker
299 |
300 | Next, introduce some wait time in `recommendation v2` by making it a slow
301 | performer with a 3 second delay by running the command:
302 |
303 | [source,bash,role="execute-1"]
304 | ----
305 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=3
306 | ----
307 |
308 | You will see something like:
309 |
310 | ----
311 | Timeout has been set to 3 seconds
312 | ----
313 |
314 | Run the following a few times to generate new, "delayed" traffic:
315 |
316 | [source,bash,role="execute-1"]
317 | ----
318 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
319 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
320 | ----
321 |
322 | [#img-nocicuit]
323 | .Kiali Distributed Tracing for Fail Fast w/no Circuit Breaking
324 | image:images/nocircuit.png[]
325 |
326 | All of the requests to our system were successful, but 1/3 of the requests
327 | took longer time, as the `v2` instance/pod was a slow performer.
328 |
329 | [#circuitbreaker]
330 | === Load test with circuit breaker
331 |
332 | But suppose that in a production system this 3s delay was caused by too many
333 | concurrent requests to the same instance/pod. We don't want multiple requests
334 | getting queued or making the instance/pod even slower. So we'll add a circuit
335 | breaker that will *open* whenever we have more than 1 request being handled
336 | by any instance/pod:
337 |
338 | [source,bash,role="execute-1"]
339 | ----
340 | oc apply -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/destination-rule-recommendation_cb_policy_version_v2.yml
341 | ----
342 |
343 | This file defines the Istio circuit breaker logic to prevent connections to
344 | V2 from piling up:
345 |
346 | [source,yaml,subs="+macros,+attributes"]
347 | ----
348 | apiVersion: networking.istio.io/v1alpha3
349 | kind: DestinationRule
350 | metadata:
351 | name: recommendation
352 | spec:
353 | host: recommendation
354 | subsets:
355 | - name: v1
356 | labels:
357 | version: v1
358 | - name: v2
359 | labels:
360 | version: v2
361 | trafficPolicy:
362 | connectionPool:
363 | http:
364 | http1MaxPendingRequests: 1
365 | maxRequestsPerConnection: 1
366 | tcp:
367 | maxConnections: 1
368 | outlierDetection:
369 | baseEjectionTime: 120.000s
370 | consecutiveErrors: 1
371 | interval: 1.000s
372 | maxEjectionPercent: 100
373 | - name: v3
374 | labels:
375 | version: v3
376 | ----
377 |
378 | Note the connection pool with a max of 1 pending request and a traffic policy
379 | where 100% of single consecutive errors fail. `oc apply` causes the existing
380 | object(`DestinationRule` called `recommendation`) to be updated.
381 |
382 | You will see something like:
383 |
384 | ----
385 | destinationrule.networking.istio.io/recommendation configured
386 | ----
387 |
388 | Now let's see what is the behavior of the system running some load again.
389 | This script runs roughly 20 concurrent requests (it performs `curl` in the
390 | background without waiting for the command to finish):
391 |
392 | [source,bash,role="execute-2"]
393 | ----
394 | bash /opt/app-root/workshop/content/scripts/loadtest_quiet.sh
395 | ----
396 |
397 | If you then look at the Kiali graph:
398 |
399 | [#img-cicuit] []
400 | .Kiali Graph Fail Fast w/Circuit Breaking
401 | image:images/circuit-graph.png[]
402 |
403 | When looking at request percentage, you will see a teeny tiny percentage of
404 | requests hitting v2. That's the circuit breaker being opened whenever Istio
405 | detects more than 1 pending request being handled by the v2 instance/pod. You
406 | can also see the little lightning icon indicating a breaker is configured.
407 |
408 | === Clean up
409 |
410 | Change the implementation of `v2` back to the image that responds without the
411 | delay of 3 seconds:
412 |
413 | [source,bash,role="execute-1"]
414 | ----
415 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=0
416 | ----
417 |
418 | You will see something like:
419 |
420 | ----
421 | Timeout has been set to 0 seconds
422 | ----
423 |
424 | Then delete the virtual service and the destination rule created for circuit breaking by:
425 |
426 | [source,bash,role="execute-1"]
427 | ----
428 | oc delete -n %username%-tutorial virtualservice.networking.istio.io/recommendation
429 | ----
430 |
--------------------------------------------------------------------------------
/workshop/content/fault-injection.adoc:
--------------------------------------------------------------------------------
1 | # Fault Injection
2 |
3 | Chaos engineering involves intentionally designing failure into components to
4 | ensure that the larger system is resilient. This can involve HTTP errors or
5 | network delays or even killing components all together. Understanding failure
6 | scenarios is a critical aspect of microservices architecture (aka distributed
7 | computing). Istio provides mechanisms to perform fault injection.
8 |
9 | ## What we will learn in this module
10 | This module will provide instruction on how to introduce service failures and
11 | delays as a part of understanding, testing, and designing microservices.
12 |
13 | ## Before Starting
14 | You only need the `customer` Virtual Service and Gateway, but if you have the
15 | `recommendation` Destination Rule from other exercises, that's OK:
16 |
17 | [source,bash,role="execute-1"]
18 | ----
19 | oc -n %username%-tutorial get istio-io
20 | ----
21 |
22 | And you should see something like the following:
23 |
24 | ----
25 | NAME AGE
26 | gateway.networking.istio.io/customer-gateway 3h16m
27 |
28 | NAME GATEWAYS HOSTS AGE
29 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m
30 |
31 | NAME HOST AGE
32 | destinationrule.networking.istio.io/recommendation recommendation 36m
33 | ----
34 |
35 | If you have any scripts running in the bottom terminal, make sure to click
36 | there and then press ctrl+c to terminate them.
37 |
38 |
39 | [#503error]
40 | == HTTP Error 503
41 | In the absence of any traffic management policies or routing rules,
42 | recommendation v1, v2 and v3 are being randomly load-balanced as that is the
43 | default behavior in Service Mesh.
44 |
45 | [source,bash,role="execute-1"]
46 | ----
47 | oc -n %username%-tutorial get pods -l app=recommendation
48 | ----
49 |
50 | You will see something like:
51 |
52 | ----
53 | NAME READY STATUS RESTARTS AGE
54 | recommendation-v1-7fbb8f794-ngw58 2/2 Running 0 21h
55 | recommendation-v2-77bc7bb94d-8wjxz 2/2 Running 0 17h
56 | recommendation-v3-5786cd744d-96q79 2/2 Running 0 17h
57 | ----
58 |
59 | Then, start running load in the background and bottom terminal:
60 |
61 | [source,bash,role="execute-2"]
62 | ----
63 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh
64 | ----
65 |
66 | Go ahead and create a `DestinationRule` that will inject 503's for approximately 50% of the requests:
67 |
68 | [source,bash,role="execute-1"]
69 | ----
70 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-503.yml -n %username%-tutorial
71 | ----
72 |
73 | You will see something like:
74 |
75 | ----
76 | destinationrule.networking.istio.io/recommendation created
77 | virtualservice.networking.istio.io/recommendation created
78 | ----
79 |
80 | The following is what you just configured:
81 |
82 | [source,yaml]
83 | ----
84 | apiVersion: networking.istio.io/v1alpha3
85 | kind: DestinationRule
86 | metadata:
87 | name: recommendation
88 | spec:
89 | host: recommendation
90 | subsets:
91 | - labels:
92 | version: v1
93 | name: v1
94 | - labels:
95 | version: v2
96 | name: v2
97 | - labels:
98 | version: v3
99 | name: v3
100 | ---
101 | apiVersion: networking.istio.io/v1alpha3
102 | kind: VirtualService
103 | metadata:
104 | name: recommendation
105 | spec:
106 | hosts:
107 | - recommendation
108 | http:
109 | - route:
110 | - destination:
111 | host: recommendation
112 | subset: v1
113 | weight: 34
114 | - destination:
115 | host: recommendation
116 | subset: v2
117 | weight: 33
118 | - destination:
119 | host: recommendation
120 | subset: v3
121 | weight: 33
122 | fault:
123 | abort:
124 | percentage:
125 | value: 50
126 | httpStatus: 503
127 | ---
128 | ----
129 |
130 | Note that this file creates 2 resources. First, a DestinationRule
131 | `recommendation` with subsets for v1, v2, and v3 (which may have already
132 | existed). Second, a VirtualService `recommendation` that splits traffic
133 | equally between v1, v2, and v3 but also introduces a 503 fault for 50% of the
134 | requests.
135 |
136 | Execute the following a few times:
137 |
138 | [source,bash,role="execute-1"]
139 | ----
140 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
141 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
142 | ----
143 |
144 | Because Istio has built-in default retries, you are almost guaranteed to not
145 | see the 503 failures at all. When the preference -> recommendation call fails
146 | it will be retried by Istio three times. And, if other calls fail, they, too,
147 | are retried. Thus there is a high probability that the client will not see a
148 | failure but you will see that the failure rate is accurately reflected in the
149 | Kiali console.
150 |
151 | === Kiali's Graph
152 |
153 | Within the Kiali UI select the *Graph* option from the left hand navigation
154 | and then choose:
155 |
156 | * Namespace: %username%-tutorial
157 | * Versioned app graph
158 | * Requests percentage
159 | * Last 1m
160 | * Every 10s
161 |
162 | [#img-503]
163 | .Kiali Graph Showing 503 Failures
164 | image::images/503.png[]
165 |
166 | Note the 50% failure rate from preference to recommendation. It will be
167 | clearer if you click on the line between the two services.
168 |
169 | === Clean up
170 |
171 | [source,bash,role="execute-1"]
172 | ----
173 | oc delete -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-503.yml -n %username%-tutorial
174 | ----
175 |
176 | You will see something like:
177 |
178 | ----
179 | destinationrule.networking.istio.io "recommendation" deleted
180 | virtualservice.networking.istio.io "recommendation" deleted
181 | ----
182 |
183 | [#delay]
184 | == Delay
185 |
186 | The most insidious of possible distributed computing faults is not a "down"
187 | service but a service that is responding slowly, potentially causing a
188 | cascading failure in your network of services. To see how to inject such a
189 | delay, take a look at the following YAML:
190 |
191 | [source,yaml,subs="+macros,+attributes"]
192 | ----
193 | apiVersion: networking.istio.io/v1alpha3
194 | kind: DestinationRule
195 | metadata:
196 | name: recommendation
197 | spec:
198 | host: recommendation
199 | subsets:
200 | - labels:
201 | version: v1
202 | name: v1
203 | - labels:
204 | version: v2
205 | name: v2
206 | - labels:
207 | version: v3
208 | name: v3
209 | ---
210 | apiVersion: networking.istio.io/v1alpha3
211 | kind: VirtualService
212 | metadata:
213 | name: recommendation
214 | spec:
215 | hosts:
216 | - recommendation
217 | http:
218 | - route:
219 | - destination:
220 | host: recommendation
221 | subset: v1
222 | weight: 34
223 | - destination:
224 | host: recommendation
225 | subset: v2
226 | weight: 33
227 | - destination:
228 | host: recommendation
229 | subset: v3
230 | weight: 33
231 | fault:
232 | delay:
233 | fixedDelay: 7.000s
234 | percent: 50
235 | ---
236 | ----
237 |
238 | Note that this file creates 2 resources. First, a DestinationRule
239 | `recommendation` with subsets for v1, v2, and v3. Second, a VirtualService
240 | `recommendation` that splits traffic equally between v1, v2, and v3 but also
241 | introduces a 7s delay for 50% of the requests.
242 |
243 | [source,bash,role="execute-1"]
244 | ----
245 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-delay.yml -n %username%-tutorial
246 | ----
247 |
248 | You will see something like:
249 |
250 | ----
251 | destinationrule.networking.istio.io/recommendation created
252 | virtualservice.networking.istio.io/recommendation created
253 | ----
254 |
255 | You will notice that 50% of requests to the customer endpoint now have a
256 | delay:
257 |
258 | [source,bash,role="execute-1"]
259 | ----
260 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
261 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
262 | ----
263 |
264 | === Kiali's Distributed Tracing
265 |
266 | Within the Kiali UI select the *Distributed Tracing* option from the left
267 | hand navigation and then choose:
268 |
269 | * Namespace: %username%-tutorial
270 | * Service: recommendation
271 | * Lookback: Last 1m
272 |
273 | and finally press the *Search* button.
274 |
275 | [#img-503]
276 | .Kiali Graph Showing Delays
277 | image::images/delay.png[]
278 |
279 | Note that 50% of the traces are slightly over the artificial 7s delay while
280 | the other 50% are in the low ms range.
281 |
282 | === Clean up
283 |
284 | [source,bash,role="execute-1"]
285 | ----
286 | oc delete -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-delay.yml -n %username%-tutorial
287 | ----
288 |
289 | You will see something like:
290 |
291 | ----
292 | destinationrule.networking.istio.io "recommendation" deleted
293 | virtualservice.networking.istio.io "recommendation" deleted
294 | ----
295 |
296 | == What we learned in this module
297 | Istio provides a simple mechanism to simulate service and network
298 | failures and delays to improve microservice testing and resiliency. Kiali
299 | provides a rich console to visualize the service failure rates and service
300 | delays.
301 |
--------------------------------------------------------------------------------
/workshop/content/images/503.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/503.png
--------------------------------------------------------------------------------
/workshop/content/images/auth-fail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/auth-fail.png
--------------------------------------------------------------------------------
/workshop/content/images/auth-success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/auth-success.png
--------------------------------------------------------------------------------
/workshop/content/images/blacklist_v3_blocked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/blacklist_v3_blocked.png
--------------------------------------------------------------------------------
/workshop/content/images/circuit-breaking-graph-slowv2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/circuit-breaking-graph-slowv2.png
--------------------------------------------------------------------------------
/workshop/content/images/circuit-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/circuit-graph.png
--------------------------------------------------------------------------------
/workshop/content/images/delay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/delay.png
--------------------------------------------------------------------------------
/workshop/content/images/failfast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/failfast.png
--------------------------------------------------------------------------------
/workshop/content/images/kiali-graph-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-graph-1.png
--------------------------------------------------------------------------------
/workshop/content/images/kiali-graph-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-graph-2.png
--------------------------------------------------------------------------------
/workshop/content/images/kiali-tracing-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-1.png
--------------------------------------------------------------------------------
/workshop/content/images/kiali-tracing-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-2.png
--------------------------------------------------------------------------------
/workshop/content/images/kiali-tracing-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-3.png
--------------------------------------------------------------------------------
/workshop/content/images/kiali-tracing-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-4.png
--------------------------------------------------------------------------------
/workshop/content/images/mtls_initial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_initial.png
--------------------------------------------------------------------------------
/workshop/content/images/mtls_no_destination_rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_no_destination_rule.png
--------------------------------------------------------------------------------
/workshop/content/images/mtls_permissive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_permissive.png
--------------------------------------------------------------------------------
/workshop/content/images/mtls_policy_and_rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_policy_and_rule.png
--------------------------------------------------------------------------------
/workshop/content/images/nocircuit-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/nocircuit-graph.png
--------------------------------------------------------------------------------
/workshop/content/images/nocircuit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/nocircuit.png
--------------------------------------------------------------------------------
/workshop/content/images/rate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/rate.png
--------------------------------------------------------------------------------
/workshop/content/images/rbac-fail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/rbac-fail.png
--------------------------------------------------------------------------------
/workshop/content/images/rbac-success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/rbac-success.png
--------------------------------------------------------------------------------
/workshop/content/images/retry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/retry.png
--------------------------------------------------------------------------------
/workshop/content/images/routing-graph-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/routing-graph-1.png
--------------------------------------------------------------------------------
/workshop/content/images/routing-graph-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/routing-graph-2.png
--------------------------------------------------------------------------------
/workshop/content/images/routing-graph-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/routing-graph-3.png
--------------------------------------------------------------------------------
/workshop/content/images/timeout-v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/timeout-v1.png
--------------------------------------------------------------------------------
/workshop/content/images/timeout-v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/timeout-v2.png
--------------------------------------------------------------------------------
/workshop/content/images/timeout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/timeout.png
--------------------------------------------------------------------------------
/workshop/content/images/whitelist_v2_fail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/whitelist_v2_fail.png
--------------------------------------------------------------------------------
/workshop/content/intro.adoc:
--------------------------------------------------------------------------------
1 | ## Purpose
2 |
3 | As microservices-based applications become more prevalent, both the number of
4 | and complexity of their interactions increases. Up until now much of the
5 | burden of managing these complex microservices interactions has been placed
6 | on the application developer, with different or non-existent support for
7 | microservice concepts depending on language and framework.
8 |
9 | The service mesh concept pushes this responsibility to the infrastructure,
10 | with features for traffic management, distributed tracing and observability,
11 | policy enforcement, and service/identity security, freeing the developer to
12 | focus on business value. In this hands-on session you will learn how to apply
13 | some of these features to a simple polyglot microservices application running
14 | on top of OpenShift Container Platform using OpenShift Service Mesh, an open
15 | platform to connect, manage, and secure microservices.
16 |
17 | ## Background
18 |
19 | Red Hat OpenShift Service Mesh provides an easy way to create a network of
20 | deployed services with load balancing, service-to-service authentication,
21 | monitoring, and more, without requiring any changes in application code.
22 | OpenShift can automatically inject a special sidecar proxy throughout your
23 | environment to enable Service Mesh management for your application. This
24 | proxy intercepts all network communication between your microservices, and is
25 | configured and managed using Service Mesh’s control plane functionality --
26 | not your application code!
27 |
28 | Kiali is an observability console designed to provide operational insight
29 | into the behavior and performance of the service mesh as a whole.
30 |
31 | Jaeger is a utility for capturing distributed tracing information of requests
32 | as they travel throughout the mesh.
33 |
34 | Prometheus and Grafana are used to capture metrics about the performance and
35 | behavior of the mesh.
36 |
37 | These components combined together make up the Red Hat OpenShift Service Mesh.
38 |
39 | ## Conventions
40 | You will see various code and command blocks throughout these exercises. Some of
41 | the command blocks can be executed directly while others will require modification,
42 | of the command before execution. If you see a command block with a red border
43 | (see below), the command will require slight modification.
44 |
45 | [source,none,role="copypaste copypaste-warning"]
46 | ----
47 | some command to modify
48 | ----
49 |
50 | If you hover over the command block above and left-click, it should
51 | automatically highlight all the text to make for easier copying.
52 |
53 | Most command blocks support executing with a click. Which terminal the
54 | command block executes in is pre-determined and will be indicated to you. For
55 | example, command blocks with looping scripts will be executed in the lower
56 | terminal like below:
57 |
58 | [source,bash,role="execute-2"]
59 | ----
60 | for i in 1 2 3; do echo "hello $i"; done
61 | ----
62 |
63 | For non-terminating loops, you will have to click the lower terminal and then
64 | press Ctrl+C to terminate the program. For example, try executing the next
65 | code block:
66 |
67 | [source,bash,role="execute-2"]
68 | ----
69 | echo "A load generating script is running in the next step. Ctrl+C to stop"
70 | while :;do echo "this won't print" &> /dev/null ; done
71 | ----
72 |
73 | For ordinary commands, we will be using the upper terminal:
74 |
75 | [source,bash,role="execute-1"]
76 | ----
77 | echo "Hello World"
78 | ----
79 |
80 | ## Getting Started
81 | The terminals are running in a container inside the same OpenShift cluster
82 | where you will be using the service mesh. That container has many utilities
83 | pre-installed, including the `oc` CLI. The Service Mesh control plane is
84 | already deployed with several application components in an OpenShift
85 | Container Platform 4 environment.
86 |
87 | [source,bash,role="execute-1"]
88 | ----
89 | oc version
90 | ----
91 |
92 | When you logged in earlier with your credentials, this lab guide and its
93 | terminals became logged in as you, too:
94 |
95 | [source,bash,role="execute-1"]
96 | ----
97 | oc whoami
98 | ----
99 |
100 | You can access the OpenShift web console by clicking on word *Console* on the
101 | toolbar above your terminal. To come back to the terminals, just click
102 | *Terminal*.
103 |
104 | ## Before Continuing
105 | While all of the exercises can stand alone and you can do them in any order,
106 | most of them involve looking at the Kiali observability interface and/or
107 | Jaeger's tracing interface. As such, please make sure to complete the
108 | Observability with Kiali module first before continuing to any others. We do
109 | not describe accessing Kiali or Jaeger anywhere else.
110 |
--------------------------------------------------------------------------------
/workshop/content/kiali.adoc:
--------------------------------------------------------------------------------
1 | = Observing the Service Mesh using Kiali
2 |
3 | Applications deployed within the Service Mesh are able to take advantage of
4 | the built-in tracing and monitoring features present in the control plane.
5 | These features provide an invaluable insight into the organisation and
6 | operation of the application as well as the application's performance.
7 |
8 | == What we will learn in this module
9 |
10 | This module will provide a brief introduction to the Kiali observability
11 | component within the Service Mesh control plane.
12 |
13 | == Kiali
14 |
15 | link:http://kiali.io[Kiali] is a component which visualizes an application's
16 | topology, health, metrics and tracing information to provide a deeper
17 | understanding of the application's behaviour and performance. Within a single
18 | view it is easy to determine which services are communicating with each
19 | other, where the traffic is flowing, information about the current health of
20 | the service and other useful metrics.
21 |
22 | Kiali also integrates with Jaeger to provide access to distributed tracing
23 | information, allowing you to track an individual request as it flows through
24 | the system and identify potential issues.
25 |
26 | === How does Kiali work?
27 |
28 | Every pod deployed as part of the Service Mesh contains a proxy container
29 | responsible for intercepting the application traffic coming into and going
30 | out of the pod. The Service Mesh proxy is automatically created on deployment
31 | of the application and automatically captures information as part of each
32 | service invocation. Kiali uses this information to provide a visualization of
33 | the Service Mesh behaviour. The information includes metrics as well as
34 | distributed tracing information.
35 |
36 | === Opening the Kiali console
37 |
38 | To open the Kiali console get the link from the following command. Open it in
39 | a new tab. Make sure to use HTTPS to access.
40 |
41 | [source,bash,role="execute-1"]
42 | ----
43 | echo https://$(oc get route -n %username%-smcp kiali -o 'jsonpath={.spec.host}')
44 | ----
45 |
46 | When prompted to login use the same username and password you used to access OpenShift.
47 |
48 | === Generating load
49 |
50 | Before we take a look at the Kiali console lets first generate some load on
51 | the Service Mesh. Let's execute the following in terminal 2 (the lower terminal)
52 |
53 | [source,bash,role="execute-2"]
54 | ----
55 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh
56 | ----
57 |
58 | === Kiali's Graph
59 |
60 | Within the Kiali UI select the _Graph_ option from the left hand navigation
61 | and then choose
62 |
63 | * Namespace: %username%-tutorial
64 | * Versioned app graph
65 | * Requests percentage
66 | * Last 1m
67 | * Every 10s
68 |
69 | image:images/kiali-graph-1.png[Kiali Graph showing configuration]
70 |
71 | You should now see the application graph showing traffic being created by our
72 | script generating load. The traffic will flow from the Service Mesh ingress
73 | gateway to the customer service, preference service and finally the
74 | recommendation service. The request percentage will show _100%_ on each edge
75 | except within the recommendation service where the traffic should be split
76 | evenly across all three versions.
77 |
78 | image:images/kiali-graph-2.png[Kiali Graph showing application traffic]
79 |
80 | === Kiali's View of Jaeger Distributed Tracing
81 |
82 | Jaeger is an end-to-end distributed tracing tool, providing insight into
83 | transactions, performance and latency, root cause analysis, and more.
84 |
85 | Kiali has the capability to expose a view of Jager Distributed Tracing. This
86 | involves the use of an `iframe`. As you saw when you logged into Kiali, you
87 | had to accept self-signed certificates and allow Kiali to use your user
88 | (`%username%`) permissions to talk to OpenShift. Becuase Kiali is exposing
89 | Jaeger via an `iframe`, you first need to visit Jaeger to accept the
90 | self-signed certificates and grant it permissions.
91 |
92 | [source,bash,role="execute-1"]
93 | ----
94 | echo https://$(oc get route -n %username%-smcp jaeger -o 'jsonpath={.spec.host}')
95 | ----
96 |
97 | Open this URL in another browser tab, accept all certificates and, when
98 | prompted, login with the same user and password that you used to login to
99 | OpenShift. Once you are done, you can return to the browser tab where you
100 | have Kiali open.
101 |
102 | Within the Kiali UI select the _Distributed Tracing_ option from the left
103 | hand navigation and then choose
104 |
105 | * Namespace: %username%-tutorial
106 | * Service: %username%-tutorial -> customer
107 |
108 | and finally press the _Search_ button.
109 |
110 | image:images/kiali-tracing-1.png[Kiali Tracing showing configuration]
111 |
112 | You should now see a list of the 20 most recent invocations of the customer service.
113 |
114 | image:images/kiali-tracing-2.png[Kiali Tracing showing invocations]
115 |
116 | Select one of the invocations to see more information. The graph should now
117 | show a hierarchy of the invocations with those spans highlighted in _red_
118 | automatically captured by the Service Mesh proxy, both client and server
119 | side, and the remainder provided by the application.
120 |
121 | image:images/kiali-tracing-3.png[Kiali Tracing showing detailed invocation]
122 |
123 | Each span can be expanded to show more contextual information captured as part of the tracing.
124 |
125 | === Cleaning up
126 |
127 | Click into the lower terminal that is still silently executing the script we
128 | used to generate load. Press the ctrl+c keys to terminate the script.
129 |
130 | == What we learned in this module
131 |
132 | Kiali is a very useful tool for visualizing the behaviour of your Service
133 | Mesh. Through one UI you not only have a consistent view of your service
134 | interactions but you can see more detailed information about those
135 | invocations helping you to understand and identify potential issues which may
136 | arise.
137 |
138 | With an understanding of Kiali you should now be able to make use of its
139 | capabilities within the following modules to visualize the changes in
140 | behaviour we will make through the resources within the Service Mesh
141 | framework. Please keep your Kiali UI open as you work through the following
142 | modules.
143 |
144 | Kiali provides additional capabilities beyond what we have explored in this
145 | module, for more information refer to the link:http://kiali.io[Kiali
146 | website].
147 |
--------------------------------------------------------------------------------
/workshop/content/mtls.adoc:
--------------------------------------------------------------------------------
1 | = Mutual TLS
2 |
3 | Mutual authentication or two-way authentication refers to two parties
4 | authenticating each other at the same time, being a default mode of
5 | authentication in some protocols (IKE, SSH) and optional in others (TLS).
6 |
7 | With Red Hat OpenShift Service Mesh, Mutual TLS can be used without either
8 | application/service knowing that it is happening. The TLS is handled entirely
9 | by the service mesh infrastructure and between the two sidecar proxies.
10 |
11 | == What we will learn in this module
12 | This module will provide instruction on how to enable Mutual TLS to secure
13 | communication between services in the mesh.
14 |
15 | Before continuing, make sure you only have the `customer` _Virtual Service_
16 | and the `recommendation` _Destination Rule_:
17 |
18 | [source,bash,role="execute-1"]
19 | ----
20 | oc get virtualservice,destinationrule -n %username%-tutorial
21 | ----
22 |
23 | You will see something like:
24 |
25 | [source,bash]
26 | ----
27 | NAME GATEWAYS HOSTS AGE
28 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 60s
29 |
30 | NAME HOST AGE
31 | destinationrule.networking.istio.io/recommendation recommendation 38m
32 | ----
33 |
34 | If you had accidentally deleted the `DestinationRule`, you can recreate it with the following command:
35 |
36 | [source,bash,role="execute-1"]
37 | ----
38 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule.yml -n %username%-tutorial
39 | ----
40 |
41 | [#enablemtls]
42 | == Enabling Mutual TLS
43 | For this example, we will take advantage of a pod that is not under the
44 | control of the service mesh.
45 |
46 | Start by generating load in the background:
47 |
48 | [source,bash,role="execute-2"]
49 | ----
50 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference_quiet.sh
51 | ----
52 |
53 | Examine the traffic between the "external" pod and the customer services and
54 | the preference service by running the load once in the upper terminal:
55 |
56 | [source,bash,role="execute-1"]
57 | ----
58 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh
59 | ----
60 |
61 | _Kiali’s Graph_
62 |
63 | Within the Kiali UI select the Graph option from the left hand navigation and
64 | then choose:
65 |
66 | * Namespace: %username%-tutorial
67 | * Versioned app graph
68 | * Requests percentage
69 | * Last 1m
70 | * Every 10s
71 | * Security checked under display
72 |
73 | You will notice that there are requests originating from "unknown". That is
74 | the pod inside which the `curl` command was executed by the script. The
75 | script also executes a `curl` from within the `customer` pod.
76 |
77 | image::images/mtls_initial.png[]
78 |
79 | Now configure preference to use mutual TLS policy.
80 |
81 | [source,bash,role="execute-1"]
82 | ----
83 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/authentication-enable-tls.yml -n %username%-tutorial
84 | ----
85 |
86 | The Policy just created can be seen below:
87 |
88 | [source, yaml]
89 | ----
90 | apiVersion: "authentication.istio.io/v1alpha1"
91 | kind: "Policy"
92 | metadata:
93 | name: "preference-mutualtls"
94 | spec:
95 | targets:
96 | - name: preference
97 | peers:
98 | - mtls:
99 | mode: STRICT
100 | ----
101 |
102 | *Note:* It may take some time before the mesh begins to enforce the policy. If
103 | you do not see the expected behaviors, wait a few more moments and then try
104 | again. Patience is a virtue.
105 |
106 | Try running the load once in the upper terminal:
107 |
108 | [source,bash,role="execute-1"]
109 | ----
110 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh
111 | ----
112 |
113 | This time, we can see that the curl from the curl pod failed with exit code
114 | 56. This is because preference is now requiring encrypted communication over
115 | mutual TLS (`STRICT`) via a _policy enforcement_, but the curl pod (which is
116 | outside the mesh) is not attempting to use mututal TLS. While the customer
117 | pod is inside the mesh, there are no rules that tell the customer pod's proxy
118 | to attempt to use mutual TLS. Thus, the proxy simply blocks the connection
119 | attempt completely (503 Service Unavailable) because the _policy_ says we
120 | should be using mutual TLS (and we're not).
121 |
122 | Now, create a destination rule to make communication to customer use mutual
123 | TLS and run the curl again:
124 |
125 | [source,bash,role="execute-1"]
126 | ----
127 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule-tls.yml -n %username%-tutorial
128 | ----
129 |
130 | The yaml just created can be seen below:
131 |
132 | [source, yaml]
133 | ----
134 | apiVersion: "networking.istio.io/v1alpha3"
135 | kind: "DestinationRule"
136 | metadata:
137 | name: "preference-destination-rule"
138 | spec:
139 | host: "preference"
140 | trafficPolicy:
141 | tls:
142 | mode: ISTIO_MUTUAL
143 | ----
144 |
145 | And now try running the load again in the upper terminal:
146 |
147 | [source,bash,role="execute-1"]
148 | ----
149 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh
150 | ----
151 |
152 | The curl pod will again show exit code 56 because it is still being prevented
153 | from communicating with the mesh.
154 |
155 | Because the `DestinationRule` for the `preference` host now specifies that
156 | the traffic policy should be `ISTIO_MUTUAL` (TLS), the traffic exiting the
157 | customer pod (with a destination of `preference`) gets mutual TLS enabled.
158 | Since the preference target has the `STRICT` enforcement of mutual TLS, and
159 | we are using mutual TLS, the traffic is allowed.
160 |
161 | Looking at the Kiali graph, a lock is now present on the graph of
162 | communication between customer and preference, indicating that this
163 | communication is secured via mTLS.
164 |
165 | image::images/mtls_policy_and_rule.png[]
166 |
167 | Lastly, note that the `curl` command that was executed was showing headers
168 | indicating `HTTP`, but we also know the communication was enforced with
169 | mutual TLS. What happened? Envoy (the sidecar proxy) is intercepting any
170 | communication leaving the pod, and Envoy is enforcing the TLS connection
171 | between the source and the destination. In this way the services don't even
172 | have to be TLS aware themselves! The mesh can "give" us TLS even if our
173 | services can't.
174 |
175 | Lastly, you'll notice that the Kiali graph no longer shows the traffic coming
176 | from "unknown". This is because the traffic is dropped before leaving the
177 | pod, so it never gets counted by the metrics.
178 |
179 | [#mtlsmigration]
180 | == mTLS migration
181 |
182 | Mutual TLS in OpenShift Service Mesh provides the ability to migrate to mTLS
183 | gradually rather than forcing all services to migrate to mTLS at once. Lets
184 | try that now.
185 |
186 | First, delete the policy we created above.
187 |
188 | [source,bash,role="execute-1"]
189 | ----
190 | oc delete policy -n %username%-tutorial preference-mutualtls
191 | ----
192 |
193 | Now create a policy using permissive mode.
194 |
195 | [source,bash,role="execute-1"]
196 | ----
197 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/policy-permissive-tls.yml -n %username%-tutorial
198 | ----
199 |
200 | The contents of the file are displayed below:
201 |
202 | [source,yaml]
203 | ----
204 | apiVersion: "authentication.istio.io/v1alpha1"
205 | kind: "Policy"
206 | metadata:
207 | name: "preference-mutualtls"
208 | spec:
209 | targets:
210 | - name: preference
211 | peers:
212 | - mtls:
213 | mode: PERMISSIVE
214 | ----
215 |
216 | If we try our curl commands again, we notice that this time they both pass:
217 |
218 | [source,bash,role="execute-1"]
219 | ----
220 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh
221 | ----
222 |
223 | In Kiali, we can see that the lock is still shown, indicating the presence of
224 | mTLS. We see the curl pod labeled as unknown since it's not part of the mesh,
225 | and we can see that both customer and curl are succesful. In this way, mutual
226 | TLS is used when rules suggest it should be used, but, if no matching rule
227 | exists, non-mTLS is _permitted_.
228 |
229 | image::images/mtls_permissive.png[]
230 |
231 | *NOTE*: You may see errors in the communication between the `customer` and
232 | `preference` services. This is due to a known issue being tracked here:
233 | https://issues.jboss.org/browse/MAISTRA-1000
234 |
235 | [#cleanup]
236 | == Cleanup
237 |
238 | To cleanup, delete both the policy and destination rule that we created.
239 |
240 | [source,bash,role="execute-1"]
241 | ----
242 | oc delete policy -n %username%-tutorial preference-mutualtls
243 | oc delete destinationrule -n %username%-tutorial preference-destination-rule
244 | ----
245 |
246 | You can also stop the load generating script by clicking the lower terminal
247 | and pressing control+c.
--------------------------------------------------------------------------------
/workshop/content/rate-limiting.adoc:
--------------------------------------------------------------------------------
1 | = Rate-Limiting/Policy
2 |
3 | Istio provides policy features to enforce rate limits on traffic to a
4 | destination, allowing you to mitigate naughty, misbehaving or faulty
5 | application communication patterns transparently to either side.
6 |
7 | == What we will learn in this module
8 | This module will provide instruction on how to limit the amount of traffic to
9 | a particular service in terms of requests per time period.
10 |
11 | == Before Starting
12 | You only need the `customer` Virtual Service and Gateway, but if you have the
13 | `recommendation` Destination Rule from other exercises, that's OK:
14 |
15 | [source,bash,role="execute-1"]
16 | ----
17 | oc -n %username%-tutorial get istio-io
18 | ----
19 |
20 | And you should see something like the following:
21 |
22 | ----
23 | NAME AGE
24 | gateway.networking.istio.io/customer-gateway 3h16m
25 |
26 | NAME GATEWAYS HOSTS AGE
27 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m
28 |
29 | NAME HOST AGE
30 | destinationrule.networking.istio.io/recommendation recommendation 36m
31 | ----
32 |
33 | If you have any scripts running in the bottom terminal, make sure to click
34 | there and then press ctrl+c to terminate them.
35 |
36 | [#ratelimiting]
37 | == Rate Limiting
38 |
39 | *NOTE*: The Rate Limiting rules take some time to be applied and
40 | reflected. Be patient here!
41 |
42 | First, make sure that you have a `DestinationRule` for the `recommendation` service:
43 |
44 | [source,bash,role="execute-1"]
45 | ----
46 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule.yml -n %username%-tutorial
47 | ----
48 |
49 | The following YAML will limit the number of concurrent requests hitting _v2_ of the `recommendation` service:
50 |
51 | [source,bash,role="execute-1"]
52 | ----
53 | sed -e "s/USERNAME/$JUPYTERHUB_USER/" /opt/app-root/workshop/content/src/istiofiles/recommendation_rate_limit.yml | oc apply -n %username%-smcp -f -
54 | ----
55 |
56 | The YAML looks like the following:
57 |
58 | [source,yaml,subs="+macros,+attributes"]
59 | ----
60 | ---
61 | apiVersion: config.istio.io/v1alpha2
62 | kind: handler
63 | metadata:
64 | name: quotahandler
65 | namespace: %username%-smcp
66 | spec:
67 | compiledAdapter: memquota
68 | params:
69 | quotas:
70 | - name: requestcountquota.instance.%username%-smcp
71 | maxAmount: 500
72 | validDuration: 1s
73 | # The first matching override is applied.
74 | # A requestcount instance is checked against override dimensions.
75 | overrides:
76 | # The following override applies to 'recommendation' regardless
77 | # of the source.
78 | - dimensions:
79 | destination: recommendation
80 | maxAmount: 1
81 | validDuration: 5s
82 | ---
83 | apiVersion: config.istio.io/v1alpha2
84 | kind: instance
85 | metadata:
86 | name: requestcountquota
87 | namespace: %username%-smcp
88 | spec:
89 | compiledTemplate: quota
90 | params:
91 | dimensions:
92 | source: request.headers["x-forwarded-for"] | "unknown"
93 | destination: destination.labels["app"] | destination.service.name | "unknown"
94 | destinationVersion: destination.labels["version"] | "unknown"
95 | ---
96 | apiVersion: config.istio.io/v1alpha2
97 | kind: QuotaSpec
98 | metadata:
99 | name: request-count
100 | namespace: %username%-smcp
101 | spec:
102 | rules:
103 | - quotas:
104 | - charge: 1
105 | quota: requestcountquota
106 | ---
107 | apiVersion: config.istio.io/v1alpha2
108 | kind: QuotaSpecBinding
109 | metadata:
110 | name: request-count
111 | namespace: %username%-smcp
112 | spec:
113 | quotaSpecs:
114 | - name: request-count
115 | namespace: %username%-smcp
116 | services:
117 | - name: recommendation
118 | namespace: %username%-tutorial
119 | # - service: '*' # Uncomment this to bind *all* services to request-count
120 | ---
121 | apiVersion: config.istio.io/v1alpha2
122 | kind: rule
123 | metadata:
124 | name: quota
125 | namespace: %username%-smcp
126 | spec:
127 | # quota only applies if you are not logged in.
128 | # match: match(request.headers["cookie"], "user=*") == false
129 | actions:
130 | - handler: quotahandler
131 | instances:
132 | - requestcountquota
133 | ----
134 |
135 | Note that the memquota resource `handler` defines a rate limit of 4 requests
136 | per 5 seconds for the `recommendation` destination. Next, deploy the rate
137 | limiting resources.
138 |
139 | You will see something like:
140 |
141 | ----
142 | handler.config.istio.io/quotahandler created
143 | instance.config.istio.io/requestcountquota created
144 | quotaspec.config.istio.io/request-count created
145 | quotaspecbinding.config.istio.io/request-count created
146 | rule.config.istio.io/quota created
147 | ----
148 |
149 | Throw some requests at customer by executing the following several times (at
150 | least 3) in succession:
151 |
152 | [source,bash,role="execute-1"]
153 | ----
154 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
155 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done
156 | ----
157 |
158 | You should see some 429 errors indicating the quota has been exhausted.
159 |
160 | ----
161 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2944
162 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2962
163 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2960
164 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2963
165 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2961
166 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2945
167 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2964
168 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2946
169 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2965
170 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2947
171 | customer => Error: 503 - preference => Error: 429 - RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount
172 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2962
173 | customer => Error: 503 - preference => Error: 429 - RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount
174 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2963
175 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2964
176 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2965
177 | customer => Error: 503 - preference => Error: 429 - RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount
178 | ----
179 |
180 | === Kiali's Graph
181 |
182 | Within the Kiali UI select the *Graph* option from the left hand navigation
183 | and then choose
184 |
185 | * Namespace: %username%-tutorial
186 | * Versioned app graph
187 | * Requests percentage
188 | * Last 1m
189 | * Every 10s
190 |
191 | [#img-503]
192 | .Kiali Graph Showing Rate Limited Failures
193 | image::images/rate.png[]
194 |
195 | Note the rate limited failure rate from preference to recommendation.
196 |
197 | === Clean up
198 |
199 | [source,bash,role="execute-1"]
200 | ----
201 | sed -e "s/USERNAME/$JUPYTERHUB_USER/" /opt/app-root/workshop/content/src/istiofiles/recommendation_rate_limit.yml | oc delete -n %username%-smcp -f -
202 | ----
203 |
204 | You will see something like:
205 |
206 | ----
207 | handler.config.istio.io "quotahandler" deleted
208 | instance.config.istio.io "requestcountquota" deleted
209 | quotaspec.config.istio.io "request-count" deleted
210 | quotaspecbinding.config.istio.io "request-count" deleted
211 | rule.config.istio.io "quota" deleted
212 | ----
213 |
--------------------------------------------------------------------------------
/workshop/content/routing.adoc:
--------------------------------------------------------------------------------
1 | = Routing within the Service Mesh
2 |
3 | One of the advantages of using a Service Mesh is the ability to modify the
4 | behaviour of your application's traffic without requiring code changes. This
5 | change in behaviour is handled declaratively through the use of the Service
6 | Mesh custom resources.
7 |
8 | == What we will learn in this module
9 |
10 | This module will provide an introduction to traffic management within the
11 | Service Mesh, demonstrating some of these capabilities through the use of our
12 | existing example and the visualisation present in the Kiali Console.
13 |
14 | === Before we begin
15 |
16 | Before starting this module you should ensure you have access to the Kiali
17 | Console and have selected the Graph tab as discussed previously. This
18 | visualisation will be used to highlight the change in behaviour as we work
19 | through this module.
20 |
21 | We also need to check the current Service Mesh configuration is as expected.
22 | To check the configuration run the following command:
23 |
24 | // TODO: figure out what the new version of this is
25 | [source,bash,role="execute-1"]
26 | ----
27 | oc get istio-io -n %username%-tutorial
28 | ----
29 |
30 | and check for output similar to the following:
31 |
32 | ----
33 | NAME GATEWAYS HOSTS AGE
34 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 9m34s
35 |
36 | NAME AGE
37 | gateway.networking.istio.io/customer-gateway 9m35s
38 | ----
39 |
40 | === How does traffic management work within the Service Mesh?
41 |
42 | Every pod deployed as part of the Service Mesh contains a proxy container
43 | responsible for intercepting the application traffic coming into and going
44 | out of the pod. When the application invokes a service the client side proxy
45 | will intercept the invocation and determine which server side endpoint will
46 | be invoked based on the declarative rules in force at the time of the
47 | invocation. The default behaviour is to distribute requests to each endpoint
48 | of a service using a _Round Robin_ load balancer. In our Recommendation
49 | service requests will be distributed across the v1, v2 and v3 endpoints.
50 |
51 | When configuring traffic management rules there are two resources which need
52 | to be configured, these are:
53 |
54 | * The _Virtual Service_ specifying the routing rules to apply when the host is addressed. These routing rules can include
55 | ** Routing to specific destinations
56 | ** Matching on a transport type
57 | ** Matching on specific transport attributes such as headers or path
58 | * The _Destination Rule_ specifying policies to be applied to the destination such as
59 | ** Load balancing
60 | ** Outlier detection
61 | ** Transport Encryption (TLS)
62 | ** Connection Pools
63 |
64 | We will cover some of these details as we work through this module.
65 |
66 | === Generating load
67 |
68 | Before we work through some of the routing scenarios we need to first
69 | generate load on the services. From within a terminal enter the following
70 | command
71 |
72 | [source,bash,role="execute-2"]
73 | ----
74 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh
75 | ----
76 |
77 | We will leave this script running for the remainder of this module. Switch
78 | over to the Kiali Console's _Graph_ section (like in the previous module) and
79 | make sure you are seeing traffic split roughly _33%_ to each of the
80 | recommendation endpoints, if you do not see any change then press the
81 | _refresh_ button in the top right of the Kiali Console.
82 |
83 | image:images/kiali-graph-2.png[Kiali showing traffic distributed across three endpoints]
84 |
85 | == Weighted Routing with the Service Mesh
86 |
87 | In this example we will modify the default behaviour for distributing
88 | requests between the endpoints. Before we can do this we need to define our
89 | _Destination Rule_ and specify the endpoint subsets we would like to use. For
90 | our demonstration we will use the version labels on each endpoint and name
91 | the subsets version-v1, version-v2 and version-v3.
92 |
93 | [source,yaml]
94 | ----
95 | apiVersion: networking.istio.io/v1alpha3
96 | kind: DestinationRule
97 | metadata:
98 | name: recommendation
99 | spec:
100 | host: recommendation
101 | subsets:
102 | - labels:
103 | version: v1
104 | name: v1
105 | - labels:
106 | version: v2
107 | name: v2
108 | - labels:
109 | version: v3
110 | name: v3
111 | ----
112 |
113 | To instantiate this `DestinationRule` you can execute the following:
114 |
115 | [source,bash,role="execute-1"]
116 | ----
117 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule.yml -n %username%-tutorial
118 | ----
119 |
120 | NOTE: We will be using this DestinationRule for the remainder of the examples
121 | within this module
122 |
123 | With the subsets now defined in the `DestinationRule` we can take a look at a
124 | _Virtual Service_ and how to specify different weighting across the subsets.
125 | This feature will allow you to better control the distribution of all
126 | requests across the endpoints, for example when deploying a new version of a
127 | service where you may only wish to send a small percentage of the requests to
128 | that service to see how it reacts to live traffic.
129 |
130 | In this example we will start by specifying _80%_ of traffic to the v1
131 | endpoint and _20%_ of traffic to the v2 endpoint.
132 |
133 | [source,yaml]
134 | ----
135 | apiVersion: networking.istio.io/v1alpha3
136 | kind: VirtualService
137 | metadata:
138 | name: recommendation
139 | spec:
140 | hosts:
141 | - recommendation
142 | http:
143 | - route:
144 | - destination:
145 | host: recommendation
146 | subset: v1
147 | weight: 80
148 | - destination:
149 | host: recommendation
150 | subset: v2
151 | weight: 20
152 | ----
153 |
154 | === Deploying the Weighted Routing configuration
155 |
156 | To deploy this configuration into the Service Mesh, execute the following command:
157 |
158 | [source,bash,role="execute-1"]
159 | ----
160 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-80-20.yml -n %username%-tutorial
161 | ----
162 |
163 | Switch over to the Kiali console to watch the traffic shifting from the
164 | original distribution to roughly _80%_ v1 and _20%_ v2.
165 |
166 | image:images/routing-graph-2.png[Kiali showing traffic distributed 80/20 across v1 and v2 endpoints]
167 |
168 | === Modifying the Weighted Routing configuration
169 |
170 | The weighting can be modified dynamically to further shift traffic. For
171 | example now we know v2 is working we have decided to shift more traffic to
172 | that service
173 |
174 | Switch to the terminal and execute the following command:
175 |
176 | [source,bash,role="execute-1"]
177 | ----
178 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-20-80.yml -n %username%-tutorial
179 | ----
180 |
181 | This `VirtualService` is almost identical to the previous one, except that
182 | the target percentages are reversed (V2 gets 80%). Switch back to the Kiali
183 | console and watch the traffic shift towards to v2 service.
184 |
185 | === Cleaning up
186 |
187 | Switch to the terminal and execute the following command
188 |
189 | [source,bash,role="execute-1"]
190 | ----
191 | oc delete virtualservice recommendation -n %username%-tutorial
192 | ----
193 |
194 | The traffic should now return to the default distribution with roughly 33%
195 | going to each endpoint.
196 |
197 | == Canary Releases with the Service Mesh
198 |
199 | A canary release involves rolling out a small deployment of new code and then
200 | routing specific users to it in an attempt to validate that everything is
201 | working as expected. In the previous example we modified the default
202 | behaviour for distributing requests between the endpoints so we could send
203 | traffic to particular endpoints based on weighting. In this example we will
204 | modify the behaviour to be more selective, using characteristics of the
205 | individual request to determine which endpoint should receive the request. In
206 | this way we can ensure that only a small subset of specific users reach the
207 | newly deployed code - a canary release.
208 |
209 | As with the last example we need a _Destination Rule_ (already created) and a
210 | _Virtual Service_. We will use the same Destination Rule as in the previous
211 | example to define the individual subsets and will create a new _Virtual
212 | Service_ to identify those requests destined for version v2.
213 |
214 | For the purpose of this example we will assume our application includes a
215 | header identifying the location of the caller. We will use this header to
216 | send everyone from the _Boston_ office to endpoint v2 while sending the
217 | remaining requests to endpoint v1.
218 |
219 | The _Virtual Service_ for this configuration is as follows
220 |
221 | [source,yaml]
222 | ----
223 | apiVersion: networking.istio.io/v1alpha3
224 | kind: VirtualService
225 | metadata:
226 | name: recommendation
227 | spec:
228 | hosts:
229 | - recommendation
230 | http:
231 | - match:
232 | - headers:
233 | user-location:
234 | exact: Boston
235 | route:
236 | - destination:
237 | host: recommendation
238 | subset: v2
239 | - route:
240 | - destination:
241 | host: recommendation
242 | subset: v1
243 | ----
244 |
245 | === Deploying the Canary Release configuration
246 |
247 | To deploy this configuration into the Service Mesh switch to a terminal and
248 | execute the following command:
249 |
250 | [source,bash,role="execute-1"]
251 | ----
252 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/routing-canary.yaml -n %username%-tutorial
253 | ----
254 |
255 | Switch back to the terminal running the load script and press ctrl+c to break out of it. Then, run a script that doesn't suppress output:
256 |
257 | [source,bash,role="execute-2"]
258 | ----
259 | bash /opt/app-root/workshop/content/scripts/curl_customer.sh
260 | ----
261 |
262 | You will notice the responses are only coming from the v1 endpoint and we are
263 | not seeing replies from the v2 or v3 endpoints. This is the behaviour for all
264 | requests which are not marked as coming from the Boston office as we defined
265 | in our `VirtualService`.
266 |
267 | === Verifying the Canary Release configuration
268 |
269 | To see the effect of the Canary Release routing we need to craft a request
270 | with the appropriate header indicating the request is coming from the Boston
271 | office. You can press ctrl-c in the lower terminal to stop the script before
272 | continuing. The following command will execute in the top terminal:
273 |
274 | [source,bash,role="execute-1"]
275 | ----
276 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
277 | curl -H "user-location: Boston" http://${INGRESS_GATEWAY}/
278 | ----
279 |
280 | Note the response from the above command is returned from the v2 endpoint.
281 | You can try different values for the header and note the responses all come
282 | from the v1 endpoint.
283 |
284 | === Cleaning up
285 |
286 | Switch to the terminal and execute the following command:
287 |
288 | [source,bash,role="execute-1"]
289 | ----
290 | oc delete virtualservice recommendation -n %username%-tutorial
291 | ----
292 |
293 | The traffic should now return to the default distribution with roughly 33%
294 | going to each endpoint.
295 |
296 | == Mirroring Traffic with the Service Mesh
297 |
298 | In this example we will modify the default behaviour for distributing
299 | requests between the endpoints to send all traffic to the v2 endpoint and
300 | then use the Service Mesh's routing capabilities to mirror the traffic to the
301 | v3 endpoint.
302 |
303 | Traffic mirroring is useful when you wish to test a new version of a service
304 | with live traffic while isolating the service client from the responses
305 | returned by the new endpoint.
306 |
307 | Traffic mirroring works by sending the request to the original endpoint, in
308 | our example v2, while also sending a copy of the request to another endpoint,
309 | in our example v3. The responses returned to the client will come from the
310 | original endpoint (v2) whereas responses from the mirror endpoint (v3) will
311 | be ignored.
312 |
313 | As with the last example we need to define two resources, the _Destination
314 | Rule_ and the _Virtual Service_. We will use the same Destination Rule as in
315 | the previous examples to define the individual subsets and will create a new
316 | Virtual Service to set the v2 endpoint as default and mirror traffic to the
317 | v3 endpoint.
318 |
319 | The _Virtual Service_ for this configuration is as follows:
320 |
321 | [source,yaml]
322 | ----
323 | apiVersion: networking.istio.io/v1alpha3
324 | kind: VirtualService
325 | metadata:
326 | name: recommendation
327 | spec:
328 | hosts:
329 | - recommendation
330 | http:
331 | - route:
332 | - destination:
333 | host: recommendation
334 | subset: v2
335 | mirror:
336 | host: recommendation
337 | subset: v3
338 | ----
339 |
340 | === Before we start
341 |
342 | Before deploying this configuration we need to restart the silent load
343 | script. Make sure that you are not running any other scripts in the lower
344 | terminal by pressing ctrl-c, then, you will execute the following:
345 |
346 | [source,bash,role="execute-2"]
347 | ----
348 | bash /opt/app-root/workshop/content/scripts/curl_customer_long.sh
349 | ----
350 |
351 | Now on Kiali,
352 |
353 | - Go to Workloads section.
354 | - Choose Namespace: %username%-tutorial
355 | - Select "recommendation-v3" deployment
356 | - Click on logs
357 | - Scroll to the bottom
358 |
359 | Notice the v3 endpoint is responding to a request every three seconds, this
360 | corresponds to the request from the load script seeing the v3 responses. Keep
361 | both scripts running while we walk through this example.
362 |
363 | === Deploying the Mirroring Traffic configuration
364 |
365 | To deploy this configuration into the Service Mesh switch to a terminal and
366 | execute the following command:
367 |
368 | [source,bash,role="execute-1"]
369 | ----
370 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/routing-mirroring.yaml -n %username%-tutorial
371 | ----
372 |
373 | Look at the terminal running the load script and you will notice the
374 | responses are only coming from the v2 endpoint with no responses coming from
375 | the v1 or v3 endpoints.
376 |
377 | If you look at the Kiali logs for the `recommendation-v3` deployment you will
378 | now notice a request coming roughly every second.
379 |
380 | Finally switch to the Kiali console and notice all the traffic in the _Graph_
381 | tab has shifted across to the v2 endpoint. Kiali shows only the normal
382 | traffic flow for the application and not the mirrored traffic.
383 |
384 | image:images/routing-graph-3.png[Kiali showing traffic to the v2 endpoint with no mirrored traffic visible]
385 |
386 | === Cleaning up
387 |
388 | Switch back to the terminal monitoring the v3 console and press the ctrl+c
389 | keys to terminate the script.
390 |
391 | Then execute the following command:
392 |
393 | [source,bash,role="execute-1"]
394 | ----
395 | oc delete virtualservice recommendation -n %username%-tutorial
396 | ----
397 |
398 | The traffic should now return to the default distribution with roughly 33%
399 | going to each endpoint.
400 |
401 | == What we learned in this module
402 |
403 | In this module we learned how to manage the traffic in our application
404 | through the declaration of routing rules deployed as Service Mesh
405 | _Destination Rule_ and _Virtual Service_ resources. This change in routing
406 | behaviour was managed without any modifications to the application's code and
407 | without the application being aware these changes were occurring.
408 |
409 | We learned:
410 |
411 | * how to distribute requests across a number of services using weighting
412 | * how to distribute requests based on specific characteristics of the incoming request
413 | * how to mirror traffic from one endpoint to another.
414 |
415 | The Service Mesh traffic management capabilities support the declaration of
416 | more complex routing behaviour. This module is designed to provide only a
417 | small taste of what is possible.
418 |
--------------------------------------------------------------------------------
/workshop/content/scripts/build-images.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | MYDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4 | MYHOME=${MYDIR}/..
5 |
6 | TAG_PREFIX=${TAG_PREFIX:-example}
7 |
8 | function fail() {
9 | echo $1
10 | exit 1
11 | }
12 |
13 | for project in customer preference recommendation ; do
14 | mvn -f ${MYHOME}/src/${project} clean package -DskipTests && \
15 | docker build -t ${TAG_PREFIX}/${project}:v1 ${MYHOME}/src/${project} || \
16 | fail "build of $project failed"
17 | done
18 |
19 | for version in v2 v3 ; do
20 | docker build -t ${TAG_PREFIX}/${project}:${version} ${MYHOME}/src/${project} || \
21 | fail "build of $project failed"
22 | done
23 |
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_customer.sh:
--------------------------------------------------------------------------------
1 | export INGRESS_GATEWAY=$(oc get route istio-ingressgateway -n $JUPYTERHUB_USER-smcp -o 'jsonpath={.spec.host}')
2 | while :; do sleep 0.2; curl http://${INGRESS_GATEWAY} ; done
3 |
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_customer_long.sh:
--------------------------------------------------------------------------------
1 | export INGRESS_GATEWAY=$(oc get route istio-ingressgateway -n $JUPYTERHUB_USER-smcp -o 'jsonpath={.spec.host}')
2 | while :; do sleep 1; curl http://${INGRESS_GATEWAY} ; done
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_customer_preference.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export CURL_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=curl | grep curl | awk '{ print $1}' )
4 | export CUSTOMER_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=customer | grep customer | awk '{ print $1}' )
5 |
6 | echo "Curl from outside mesh"
7 | oc exec -n $JUPYTERHUB_USER-tutorial $CURL_POD -- curl -sv http://preference:8080
8 | echo; echo;
9 | echo "Curl from inside mesh"
10 | oc exec -n $JUPYTERHUB_USER-tutorial $CUSTOMER_POD -c customer -- curl -sv http://preference:8080
11 |
12 |
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_customer_preference_quiet.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export CURL_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=curl | grep curl | awk '{ print $1}' )
4 | export CUSTOMER_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=customer | grep customer | awk '{ print $1}' )
5 |
6 | echo "A load generating script is running in the next step. Ctrl+C to stop"
7 |
8 | while :; do
9 |
10 | # echo "Executing curl in curl pod"
11 | oc exec -n $JUPYTERHUB_USER-tutorial $CURL_POD -- curl -s http://preference:8080 > /dev/null
12 | sleep 0.5
13 |
14 | # echo "Executing curl in customer pod"
15 | oc exec -n $JUPYTERHUB_USER-tutorial $CUSTOMER_POD -c customer -- curl -s http://preference:8080 > /dev/null
16 |
17 | sleep 0.5
18 |
19 | done
20 |
21 |
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_customer_quiet.sh:
--------------------------------------------------------------------------------
1 | export INGRESS_GATEWAY=$(oc get route istio-ingressgateway -n $JUPYTERHUB_USER-smcp -o 'jsonpath={.spec.host}')
2 |
3 | echo "A load generating script is running in the next step. Ctrl+C to stop"
4 |
5 | while :; do sleep 0.2; curl http://${INGRESS_GATEWAY} &> /dev/null ; done
6 |
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_customer_token.sh:
--------------------------------------------------------------------------------
1 | export INGRESS_GATEWAY=$(oc get route -n istio-system istio-ingressgateway -o 'jsonpath={.spec.host}')
2 | TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/demo.jwt -s)
3 | while :; do curl --header "Authorization: Bearer $TOKEN" $INGRESS_GATEWAY -s ; done
4 |
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_customer_token_quiet.sh:
--------------------------------------------------------------------------------
1 | export INGRESS_GATEWAY=$(oc get route -n istio-system istio-ingressgateway -o 'jsonpath={.spec.host}')
2 | TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/demo.jwt -s)
3 |
4 | echo "A load generating script is running in the next step. Ctrl+C to stop"
5 |
6 | while :; do curl --header "Authorization: Bearer $TOKEN" $INGRESS_GATEWAY -s &> /dev/null ; done
7 |
--------------------------------------------------------------------------------
/workshop/content/scripts/curl_recommendation_args.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # takes an argument like v1, v2, or v3
4 | # finds the pod IP of the recommendation version
5 | # curls the found IP with verbose output
6 |
7 | export REC_IP=`oc get pod -n ${JUPYTERHUB_USER}-tutorial -l "app=recommendation,version=v2" -o jsonpath='{.items[0].status.podIP}'`
8 | oc exec -n ${JUPYTERHUB_USER}-tutorial $(oc get pod -n ${JUPYTERHUB_USER}-tutorial -l app=curl -o name | cut -f2 -d/) -- curl -sv $REC_IP:8080
--------------------------------------------------------------------------------
/workshop/content/scripts/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # Deploy services to OpenShift/Istio
4 | # Assumes you are oc-login'd and istio is installed and istioctl available at $ISTIO_HOME
5 | #
6 | MYHOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd -P)"
7 | DEPLOYMENT_DIR="${MYHOME}/src/deployments"
8 |
9 | # name of project in which we are working
10 | PROJECT=${PROJECT:-istio-tutorial}
11 |
12 | oc new-project ${PROJECT} || exit 1
13 | oc adm policy add-scc-to-user privileged -z default -n ${PROJECT}
14 |
15 | # deploy customer
16 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/customer.yaml
17 |
18 | # deploy preferences
19 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/preference.yaml
20 |
21 | # deploy recommendation
22 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/recommendation.yaml
23 |
24 | # deploy gateway
25 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/gateway.yaml
26 |
27 | # deploy curl
28 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/curl.yaml
29 |
--------------------------------------------------------------------------------
/workshop/content/scripts/loadtest.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export INGRESS_GATEWAY=$(oc get route -n istio-system istio-ingressgateway -o 'jsonpath={.spec.host}')
4 |
5 | function curl_gateway(){
6 | j=0
7 | while [ $j -lt 50 ]; do
8 | curl http://${INGRESS_GATEWAY}/
9 | j=$[$j+1]
10 | done
11 | }
12 |
13 | i=0
14 | while [ $i -lt 20 ]; do
15 | curl_gateway &
16 | i=$[$i+1]
17 | done
18 |
19 |
--------------------------------------------------------------------------------
/workshop/content/scripts/loadtest_quiet.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export INGRESS_GATEWAY=$(oc get route -n $JUPYTERHUB_USER-smcp istio-ingressgateway -o 'jsonpath={.spec.host}')
4 |
5 | function curl_gateway(){
6 | j=0
7 | while [ $j -lt 50 ]; do
8 | curl http://${INGRESS_GATEWAY}/ &> /dev/null
9 | j=$[$j+1]
10 | done
11 | }
12 |
13 | i=0
14 | while [ $i -lt 20 ]; do
15 | curl_gateway &
16 | i=$[$i+1]
17 | done
18 |
19 |
--------------------------------------------------------------------------------
/workshop/content/scripts/retries.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export RECO_POD=$(oc get pod -n $JUPYTERHUB_USER-tutorial -l 'app=recommendation,version=v2' -o jsonpath='{..metadata.name}')
4 | export RECO_IP=$(oc get pod -n $JUPYTERHUB_USER-tutorial $RECO_POD -o jsonpath='{.status.podIP'})
5 | oc exec -n $JUPYTERHUB_USER-tutorial -c recommendation $RECO_POD -- curl -s $RECO_IP:8080/$1
6 |
--------------------------------------------------------------------------------
/workshop/content/scripts/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | OC_VERSION="v3.9.0-alpha.3"
4 | GIT_STRING="-78ddc10"
5 |
6 | #subscription-manager register --username openshift-enterprise
7 | #subscription-manager attach --pool 8a85f98b621f8a6b01621fac0f790155
8 | #subscription-manager repos --disable="*"
9 | #subscription-manager repos \
10 | # --enable="rhel-7-server-rpms" \
11 | # --enable="rhel-7-server-extras-rpms" \
12 | # --enable="rhel-7-server-ose-3.7-rpms" \
13 | # --enable="rhel-7-fast-datapath-rpms"
14 | #yum -y install vim-enhanced docker wget git net-tools bind-utils iptables-services bridge-utils bash-completion kexec-tools sos psacct
15 | #systemctl enable docker
16 | #systemctl start docker
17 | #cd
18 | wget https://github.com/openshift/origin/releases/download/$OC_VERSION/openshift-origin-server-$OC_VERSION$GIT_STRING-linux-64bit.tar.gz
19 | tar -xzvf openshift-origin-server-$OC_VERSION$GIT_STRING-linux-64bit.tar.gz
20 | #mv openshift-origin-server-v3.9.0-alpha.3-78ddc10-linux-64bit/oc /usr/bin
21 | #restorecon -rv /usr/bin
22 |
23 |
--------------------------------------------------------------------------------
/workshop/content/setup-environment.adoc:
--------------------------------------------------------------------------------
1 | Include in this page any steps which should be run by a user to check that the workshop environment is setup correctly. Exactly what you provide in this step will depend on whether the workshop is designed to be deployed in a specific way.
2 |
3 | It is a good idea in this page to provide at least one sample command to run which is marked with the `execute` annotation so anyone doing the workshop understands they can click on marked commands to run them. For example:
4 |
5 | [source,bash,role=execute]
6 | ----
7 | date
8 | ----
9 |
10 | Did you type the command in yourself? If you did, click on the command instead and you will find that it is executed for you. You can click on any command which has the ++++++ icon shown to the right of it, and it will be copied to the interactive terminal and run. If you would rather make a copy of the command so you can paste it to another window, hold down the shift key when you click on the command.
11 |
--------------------------------------------------------------------------------
/workshop/content/src/customer/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 | !target/*-runner
3 | !target/*-runner.jar
4 | !target/lib/*
--------------------------------------------------------------------------------
/workshop/content/src/customer/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 |
12 | ### IntelliJ IDEA ###
13 | .idea
14 | *.iws
15 | *.iml
16 | *.ipr
17 |
18 | ### NetBeans ###
19 | nbproject/private/
20 | build/
21 | nbbuild/
22 | dist/
23 | nbdist/
24 | .nb-gradle/
--------------------------------------------------------------------------------
/workshop/content/src/customer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM fabric8/java-jboss-openjdk8-jdk:1.5.4
2 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0
3 | ENV JAEGER_SERVICE_NAME=customer\
4 | JAEGER_ENDPOINT=http://jaeger-collector.istio-system.svc:14268/api/traces\
5 | JAEGER_PROPAGATION=b3\
6 | JAEGER_SAMPLER_TYPE=const\
7 | JAEGER_SAMPLER_PARAM=1
8 | EXPOSE 8080 8778 9779
9 | COPY target/lib/* /deployments/lib/
10 | COPY target/*-runner.jar /deployments/app.jar
11 | ENTRYPOINT [ "/deployments/run-java.sh" ]
--------------------------------------------------------------------------------
/workshop/content/src/customer/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.redhat.developer.demos.customer.rest
6 | customer
7 | 1.0-SNAPSHOT
8 |
9 | 2.22.0
10 | 0.12.0
11 | UTF-8
12 | 1.8
13 | 1.8
14 |
15 |
16 |
17 |
18 | io.quarkus
19 | quarkus-bom
20 | ${quarkus.version}
21 | pom
22 | import
23 |
24 |
25 |
26 |
27 |
28 | io.quarkus
29 | quarkus-resteasy
30 |
31 |
32 | io.quarkus
33 | quarkus-junit5
34 | test
35 |
36 |
37 | io.rest-assured
38 | rest-assured
39 | test
40 |
41 |
42 | io.quarkus
43 | quarkus-smallrye-rest-client
44 |
45 |
46 | io.quarkus
47 | quarkus-smallrye-opentracing
48 |
49 |
50 | io.quarkus
51 | quarkus-smallrye-health
52 |
53 |
54 | io.quarkus
55 | quarkus-smallrye-metrics
56 |
57 |
58 |
59 |
60 |
61 | io.quarkus
62 | quarkus-maven-plugin
63 | ${quarkus.version}
64 |
65 |
66 |
67 | build
68 |
69 |
70 |
71 |
72 |
73 | maven-surefire-plugin
74 | ${surefire-plugin.version}
75 |
76 |
77 | org.jboss.logmanager.LogManager
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | native
86 |
87 |
88 | native
89 |
90 |
91 |
92 |
93 |
94 | io.quarkus
95 | quarkus-maven-plugin
96 | ${quarkus.version}
97 |
98 |
99 |
100 | native-image
101 |
102 |
103 | true
104 |
105 |
106 |
107 |
108 |
109 | maven-failsafe-plugin
110 | ${surefire-plugin.version}
111 |
112 |
113 |
114 | integration-test
115 | verify
116 |
117 |
118 |
119 | ${project.build.directory}/${project.build.finalName}-runner
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/main/docker/Dockerfile.jvm:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
3 | #
4 | # Before building the docker image run:
5 | #
6 | # mvn package
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/customer-jvm .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/customer-jvm
15 | #
16 | ###
17 | FROM fabric8/java-jboss-openjdk8-jdk
18 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0
19 | COPY target/lib/* /deployments/lib/
20 | COPY target/*-runner.jar /deployments/app.jar
21 | ENTRYPOINT [ "/deployments/run-java.sh" ]
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/main/docker/Dockerfile.native:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
3 | #
4 | # Before building the docker image run:
5 | #
6 | # mvn package -Pnative -Dnative-image.docker-build=true
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/customer .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/customer
15 | #
16 | ###
17 | FROM registry.fedoraproject.org/fedora-minimal
18 | WORKDIR /work/
19 | COPY target/*-runner /work/customer
20 | RUN chmod 775 /work
21 | EXPOSE 8080
22 | CMD ["./customer", "-Xmx8m", "-Xmn8m", "-Xms8m"]
23 | # CMD ["./customer", "-Dquarkus.http.host=0.0.0.0"]
24 |
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/main/java/com/redhat/developer/demos/customer/rest/BaggageHeadersFactory.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
4 |
5 | import javax.ws.rs.core.MultivaluedHashMap;
6 | import javax.ws.rs.core.MultivaluedMap;
7 | import io.quarkus.runtime.annotations.RegisterForReflection;
8 |
9 | @RegisterForReflection
10 | public class BaggageHeadersFactory implements ClientHeadersFactory {
11 |
12 |
13 | @Override
14 | public MultivaluedMap update(MultivaluedMap incomingHeaders, MultivaluedMap clientOutgoingHeaders) {
15 | MultivaluedHashMap headers = new MultivaluedHashMap<>();
16 | String userAgent = incomingHeaders.getFirst("user-agent");
17 | headers.putSingle("baggage-user-agent", userAgent);
18 | String userLocation = incomingHeaders.getFirst("user-location");
19 | headers.putSingle("user-location", userLocation);
20 | return headers;
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/main/java/com/redhat/developer/demos/customer/rest/CustomerResource.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import org.eclipse.microprofile.rest.client.inject.RestClient;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import javax.inject.Inject;
8 | import javax.ws.rs.*;
9 | import javax.ws.rs.core.MediaType;
10 | import javax.ws.rs.core.Response;
11 |
12 | @Path("/")
13 | public class CustomerResource {
14 |
15 | private static final String RESPONSE_STRING_FORMAT = "customer => %s\n";
16 |
17 | private final Logger logger = LoggerFactory.getLogger(getClass());
18 |
19 | @Inject
20 | @RestClient
21 | PreferenceService preferenceService;
22 |
23 | @GET
24 | @Produces(MediaType.TEXT_PLAIN)
25 | public Response getCustomer() {
26 | try {
27 | String response = preferenceService.getPreference().trim();
28 | return Response.ok(String.format(RESPONSE_STRING_FORMAT, response)).build();
29 | } catch (WebApplicationException ex) {
30 | Response response = ex.getResponse();
31 | logger.warn("Non HTTP 20x trying to get the response from preference service: " + response.getStatus());
32 | return Response
33 | .status(Response.Status.SERVICE_UNAVAILABLE)
34 | .entity(String.format(RESPONSE_STRING_FORMAT,
35 | String.format("Error: %d - %s", response.getStatus(), response.readEntity(String.class)))
36 | )
37 | .build();
38 | } catch (ProcessingException ex) {
39 | logger.warn("Exception trying to get the response from preference service.", ex);
40 | return Response
41 | .status(Response.Status.SERVICE_UNAVAILABLE)
42 | .entity(String.format(RESPONSE_STRING_FORMAT, ex.getCause().getClass().getSimpleName() + ": " + ex.getCause().getMessage()))
43 | .build();
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/main/java/com/redhat/developer/demos/customer/rest/PreferenceService.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
4 | import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
5 |
6 | import javax.ws.rs.GET;
7 | import javax.ws.rs.HeaderParam;
8 | import javax.ws.rs.Path;
9 | import javax.ws.rs.Produces;
10 | import javax.ws.rs.core.Response;
11 |
12 | @RegisterClientHeaders(BaggageHeadersFactory.class)
13 | @RegisterRestClient
14 | public interface PreferenceService {
15 |
16 | @Path("/")
17 | @GET
18 | @Produces("text/plain")
19 | public String getPreference();
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | com.redhat.developer.demos.customer.rest.PreferenceService/mp-rest/url=http://preference:8080
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/test/java/com/redhat/developer/demos/customer/rest/CustomerResourceTest.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import io.quarkus.test.junit.QuarkusTest;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static io.restassured.RestAssured.given;
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.hamcrest.CoreMatchers.startsWith;
9 |
10 | @QuarkusTest
11 | public class CustomerResourceTest {
12 |
13 | @Test
14 | public void testHelloEndpoint() {
15 | given()
16 | .when().get("/")
17 | .then()
18 | .statusCode(503)
19 | .body(startsWith("customer =>"));
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/workshop/content/src/customer/src/test/java/com/redhat/developer/demos/customer/rest/NativeCustomerResourceIT.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import io.quarkus.test.junit.SubstrateTest;
4 |
5 | @SubstrateTest
6 | public class NativeCustomerResourceIT extends CustomerResourceTest {
7 |
8 | // Execute the same tests but in native mode.
9 | }
--------------------------------------------------------------------------------
/workshop/content/src/deployments/curl.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app: curl
6 | version: v1
7 | name: curl
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: curl
13 | version: v1
14 | template:
15 | metadata:
16 | labels:
17 | app: curl
18 | version: v1
19 | annotations:
20 | sidecar.istio.io/proxyCPU: "500m"
21 | sidecar.istio.io/proxyMemory: 400Mi
22 | spec:
23 | containers:
24 | - image: quay.io/maistra_demos/curl:latest
25 | command: ["/bin/sleep", "3650d"]
26 | imagePullPolicy: Always
27 | name: curl
28 |
--------------------------------------------------------------------------------
/workshop/content/src/deployments/customer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app: customer
6 | version: v1
7 | name: customer-v1
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: customer
13 | version: v1
14 | template:
15 | metadata:
16 | labels:
17 | app: customer
18 | version: v1
19 | annotations:
20 | sidecar.istio.io/inject: "true"
21 | sidecar.istio.io/proxyCPU: "500m"
22 | sidecar.istio.io/proxyMemory: 400Mi
23 | spec:
24 | containers:
25 | - env:
26 | - name: JAVA_OPTIONS
27 | value: -Xms128m -Xmx256m -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:///dev/./urandom
28 | image: quay.io/maistra_demos/customer:v1
29 | imagePullPolicy: Always
30 | name: customer
31 | ports:
32 | - containerPort: 8080
33 | name: http
34 | protocol: TCP
35 | ---
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: customer
40 | labels:
41 | app: customer
42 | spec:
43 | ports:
44 | - name: http
45 | port: 8080
46 | selector:
47 | app: customer
48 |
--------------------------------------------------------------------------------
/workshop/content/src/deployments/gateway.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: Gateway
3 | metadata:
4 | name: customer-gateway
5 | spec:
6 | selector:
7 | istio: ingressgateway # use istio default controller
8 | servers:
9 | - port:
10 | number: 80
11 | name: http
12 | protocol: HTTP
13 | hosts:
14 | - "*"
15 | ---
16 | apiVersion: networking.istio.io/v1alpha3
17 | kind: VirtualService
18 | metadata:
19 | name: customer
20 | spec:
21 | hosts:
22 | - "*"
23 | gateways:
24 | - customer-gateway
25 | http:
26 | - match:
27 | - uri:
28 | exact: /
29 | route:
30 | - destination:
31 | host: customer
32 | port:
33 | number: 8080
34 |
--------------------------------------------------------------------------------
/workshop/content/src/deployments/preference.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app: preference
6 | version: v1
7 | name: preference-v1
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: preference
13 | version: v1
14 | template:
15 | metadata:
16 | labels:
17 | app: preference
18 | version: v1
19 | annotations:
20 | sidecar.istio.io/inject: "true"
21 | sidecar.istio.io/proxyCPU: "500m"
22 | sidecar.istio.io/proxyMemory: 400Mi
23 | spec:
24 | containers:
25 | - env:
26 | - name: JAVA_OPTIONS
27 | value: -Xms128m -Xmx256m -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:///dev/./urandom
28 | image: quay.io/maistra_demos/preference:v1
29 | imagePullPolicy: Always
30 | name: preference
31 | ports:
32 | - containerPort: 8080
33 | name: http
34 | protocol: TCP
35 | ---
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: preference
40 | labels:
41 | app: preference
42 | spec:
43 | ports:
44 | - name: http
45 | port: 8080
46 | selector:
47 | app: preference
48 |
--------------------------------------------------------------------------------
/workshop/content/src/deployments/recommendation.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app: recommendation
6 | version: v1
7 | name: recommendation-v1
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: recommendation
13 | version: v1
14 | template:
15 | metadata:
16 | labels:
17 | app: recommendation
18 | version: v1
19 | annotations:
20 | sidecar.istio.io/inject: "true"
21 | sidecar.istio.io/proxyCPU: "500m"
22 | sidecar.istio.io/proxyMemory: 400Mi
23 | spec:
24 | containers:
25 | - env:
26 | - name: JAVA_OPTIONS
27 | value: -Xmx256m
28 | image: quay.io/maistra_demos/recommendation:v1
29 | imagePullPolicy: Always
30 | name: recommendation
31 | ports:
32 | - containerPort: 8080
33 | name: http
34 | protocol: TCP
35 | ---
36 | apiVersion: extensions/v1beta1
37 | kind: Deployment
38 | metadata:
39 | labels:
40 | app: recommendation
41 | version: v2
42 | name: recommendation-v2
43 | spec:
44 | replicas: 1
45 | selector:
46 | matchLabels:
47 | app: recommendation
48 | version: v2
49 | template:
50 | metadata:
51 | labels:
52 | app: recommendation
53 | version: v2
54 | annotations:
55 | sidecar.istio.io/inject: "true"
56 | sidecar.istio.io/proxyCPU: "500m"
57 | sidecar.istio.io/proxyMemory: 400Mi
58 | spec:
59 | containers:
60 | - env:
61 | - name: JAVA_OPTIONS
62 | value: -Xmx256m
63 | image: quay.io/maistra_demos/recommendation:v2
64 | imagePullPolicy: Always
65 | name: recommendation
66 | ports:
67 | - containerPort: 8080
68 | name: http
69 | protocol: TCP
70 | ---
71 | apiVersion: extensions/v1beta1
72 | kind: Deployment
73 | metadata:
74 | labels:
75 | app: recommendation
76 | version: v3
77 | name: recommendation-v3
78 | spec:
79 | replicas: 1
80 | selector:
81 | matchLabels:
82 | app: recommendation
83 | version: v3
84 | template:
85 | metadata:
86 | labels:
87 | app: recommendation
88 | version: v3
89 | annotations:
90 | sidecar.istio.io/inject: "true"
91 | sidecar.istio.io/proxyCPU: "500m"
92 | sidecar.istio.io/proxyMemory: 400Mi
93 | spec:
94 | containers:
95 | - env:
96 | - name: JAVA_OPTIONS
97 | value: -Xmx256m
98 | image: quay.io/maistra_demos/recommendation:v3
99 | imagePullPolicy: Always
100 | name: recommendation
101 | ports:
102 | - containerPort: 8080
103 | name: http
104 | protocol: TCP
105 | ---
106 | apiVersion: v1
107 | kind: Service
108 | metadata:
109 | name: recommendation
110 | labels:
111 | app: recommendation
112 | spec:
113 | ports:
114 | - name: http
115 | port: 8080
116 | selector:
117 | app: recommendation
118 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/acl-blacklist.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "config.istio.io/v1alpha2"
2 | kind: handler
3 | metadata:
4 | name: denycustomerhandler
5 | spec:
6 | compiledAdapter: denier
7 | params:
8 | status:
9 | code: 7
10 | message: Not allowed
11 | ---
12 | apiVersion: "config.istio.io/v1alpha2"
13 | kind: instance
14 | metadata:
15 | name: denycustomerrequests
16 | spec:
17 | compiledTemplate: checknothing
18 | ---
19 | apiVersion: "config.istio.io/v1alpha2"
20 | kind: rule
21 | metadata:
22 | name: denycustomer
23 | spec:
24 | match: source.labels["app"]=="preference" && destination.labels["app"] == "recommendation" && destination.labels["version"] == "v3"
25 | actions:
26 | - handler: denycustomerhandler
27 | instances: [ denycustomerrequests ]
28 |
29 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/acl-whitelist.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "config.istio.io/v1alpha2"
2 | kind: handler
3 | metadata:
4 | name: preferencewhitelist
5 | spec:
6 | compiledAdapter: listchecker
7 | params:
8 | overrides: ["v1", "v3"]
9 | blacklist: false
10 | ---
11 | apiVersion: "config.istio.io/v1alpha2"
12 | kind: instance
13 | metadata:
14 | name: preferencesource
15 | spec:
16 | compiledTemplate: listentry
17 | params:
18 | value: destination.labels["version"]
19 | ---
20 | apiVersion: "config.istio.io/v1alpha2"
21 | kind: rule
22 | metadata:
23 | name: checktorecommendation
24 | spec:
25 | match: destination.labels["app"] == "recommendation"
26 | actions:
27 | - handler: preferencewhitelist
28 | instances: [ preferencesource ]
29 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/authentication-enable-tls.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "authentication.istio.io/v1alpha1"
2 | kind: "Policy"
3 | metadata:
4 | name: "preference-mutualtls"
5 | spec:
6 | targets:
7 | - name: preference
8 | peers:
9 | - mtls:
10 | mode: STRICT
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/authorization-enable-rbac.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "rbac.istio.io/v1alpha1"
2 | kind: RbacConfig
3 | metadata:
4 | name: default
5 | spec:
6 | mode: 'ON_WITH_INCLUSION'
7 | inclusion:
8 | namespaces: ["CHANGEIT"]
9 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/controlplane.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: istio.openshift.com/v1alpha3
2 | kind: ControlPlane
3 | metadata:
4 | name: basic-install
5 | spec:
6 | istio:
7 | global:
8 | proxy:
9 | resources:
10 | requests:
11 | cpu: 100m
12 | memory: 128Mi
13 | limits:
14 | cpu: 500m
15 | memory: 128Mi
16 | disablePolicyChecks: false
17 | gateways:
18 | istio-egressgateway:
19 | autoscaleEnabled: false
20 | istio-ingressgateway:
21 | autoscaleEnabled: false
22 | ior_enabled: false
23 |
24 | mixer:
25 | policy:
26 | autoscaleEnabled: false
27 |
28 | telemetry:
29 | autoscaleEnabled: false
30 | resources:
31 | requests:
32 | cpu: 100m
33 | memory: 1G
34 | limits:
35 | cpu: 500m
36 | memory: 4G
37 |
38 | pilot:
39 | autoscaleEnabled: false
40 | traceSampling: 100.0
41 |
42 | kiali:
43 | enabled: true
44 | dashboard:
45 | user: admin
46 | passphrase: admin
47 | tracing:
48 | enabled: true
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/destination-rule-recommendation_cb_policy_version_v2.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: DestinationRule
3 | metadata:
4 | name: recommendation
5 | spec:
6 | host: recommendation
7 | subsets:
8 | - name: v1
9 | labels:
10 | version: v1
11 | - name: v2
12 | labels:
13 | version: v2
14 | trafficPolicy:
15 | connectionPool:
16 | http:
17 | http1MaxPendingRequests: 1
18 | maxRequestsPerConnection: 1
19 | tcp:
20 | maxConnections: 1
21 | outlierDetection:
22 | baseEjectionTime: 120.000s
23 | consecutiveErrors: 1
24 | interval: 1.000s
25 | maxEjectionPercent: 100
26 | - name: v3
27 | labels:
28 | version: v3
29 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/destination-rule-tls.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "networking.istio.io/v1alpha3"
2 | kind: "DestinationRule"
3 | metadata:
4 | name: "preference-destination-rule"
5 | spec:
6 | host: "preference"
7 | trafficPolicy:
8 | tls:
9 | mode: ISTIO_MUTUAL
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/destination-rule.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: DestinationRule
3 | metadata:
4 | name: recommendation
5 | spec:
6 | host: recommendation
7 | subsets:
8 | - labels:
9 | version: v1
10 | name: v1
11 | - labels:
12 | version: v2
13 | name: v2
14 | - labels:
15 | version: v3
16 | name: v3
17 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/namespace-rbac-policy.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "rbac.istio.io/v1alpha1"
2 | kind: ServiceRole
3 | metadata:
4 | name: service-viewer
5 | spec:
6 | rules:
7 | - services: ["*"]
8 | methods: ["GET"]
9 | constraints:
10 | - key: "destination.labels[app]"
11 | values: ["customer", "recommendation", "preference"]
12 | ---
13 | apiVersion: "rbac.istio.io/v1alpha1"
14 | kind: ServiceRoleBinding
15 | metadata:
16 | name: bind-service-viewer
17 | spec:
18 | subjects:
19 | - user: "*"
20 | roleRef:
21 | kind: ServiceRole
22 | name: "service-viewer"
23 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/policy-jwt.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: "authentication.istio.io/v1alpha1"
2 | kind: "Policy"
3 | metadata:
4 | name: "jwt-example"
5 | spec:
6 | targets:
7 | - name: customer
8 | origins:
9 | - jwt:
10 | issuer: "testing@secure.istio.io"
11 | jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/jwks.json"
12 | principalBinding: USE_ORIGIN
13 | ---
14 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/policy-permissive-tls.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "authentication.istio.io/v1alpha1"
2 | kind: "Policy"
3 | metadata:
4 | name: "preference-mutualtls"
5 | spec:
6 | targets:
7 | - name: preference
8 | peers:
9 | - mtls:
10 | mode: PERMISSIVE
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/recommendation_rate_limit.yml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: config.istio.io/v1alpha2
3 | kind: handler
4 | metadata:
5 | name: quotahandler
6 | namespace: USERNAME-smcp
7 | spec:
8 | compiledAdapter: memquota
9 | params:
10 | quotas:
11 | - name: requestcountquota.instance.USERNAME-smcp
12 | maxAmount: 500
13 | validDuration: 1s
14 | # The first matching override is applied.
15 | # A requestcount instance is checked against override dimensions.
16 | overrides:
17 | # The following override applies to 'recommendation' regardless
18 | # of the source.
19 | - dimensions:
20 | destination: recommendation
21 | maxAmount: 1
22 | validDuration: 5s
23 | ---
24 | apiVersion: config.istio.io/v1alpha2
25 | kind: instance
26 | metadata:
27 | name: requestcountquota
28 | namespace: USERNAME-smcp
29 | spec:
30 | compiledTemplate: quota
31 | params:
32 | dimensions:
33 | source: request.headers["x-forwarded-for"] | "unknown"
34 | destination: destination.labels["app"] | destination.service.name | "unknown"
35 | destinationVersion: destination.labels["version"] | "unknown"
36 | ---
37 | apiVersion: config.istio.io/v1alpha2
38 | kind: QuotaSpec
39 | metadata:
40 | name: request-count
41 | namespace: USERNAME-smcp
42 | spec:
43 | rules:
44 | - quotas:
45 | - charge: 1
46 | quota: requestcountquota
47 | ---
48 | apiVersion: config.istio.io/v1alpha2
49 | kind: QuotaSpecBinding
50 | metadata:
51 | name: request-count
52 | namespace: USERNAME-smcp
53 | spec:
54 | quotaSpecs:
55 | - name: request-count
56 | namespace: USERNAME-smcp
57 | services:
58 | - name: recommendation
59 | namespace: USERNAME-tutorial
60 | # - service: '*' # Uncomment this to bind *all* services to request-count
61 | ---
62 | apiVersion: config.istio.io/v1alpha2
63 | kind: rule
64 | metadata:
65 | name: quota
66 | namespace: USERNAME-smcp
67 | spec:
68 | # quota only applies if you are not logged in.
69 | # match: match(request.headers["cookie"], "user=*") == false
70 | actions:
71 | - handler: quotahandler
72 | instances:
73 | - requestcountquota
74 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/recommendation_requestcount.yml:
--------------------------------------------------------------------------------
1 | apiVersion: "config.istio.io/v1alpha2"
2 | kind: metric
3 | metadata:
4 | name: recommendationrequestcount
5 | namespace: istio-system
6 | spec:
7 | value: "1"
8 | dimensions:
9 | source: source.service | "unknown"
10 | destination: destination.service | "unknown"
11 | version: destination.labels["version"] | "unknown"
12 | user_agent: request.headers["user-agent"] | "unknown"
13 | monitored_resource_type: '"UNSPECIFIED"'
14 | ---
15 | apiVersion: "config.istio.io/v1alpha2"
16 | kind: prometheus
17 | metadata:
18 | name: recommendationrequestcounthandler
19 | namespace: istio-system
20 | spec:
21 | metrics:
22 | - name: recommendation_request_count
23 | instance_name: recommendationrequestcount.metric.istio-system
24 | kind: COUNTER
25 | label_names:
26 | - source
27 | - destination
28 | - user_agent
29 | - version
30 | ---
31 | apiVersion: "config.istio.io/v1alpha2"
32 | kind: rule
33 | metadata:
34 | name: recommendationrequestcountprom
35 | namespace: istio-system
36 | spec:
37 | match: destination.service == "recommendation.istio-lab.svc.cluster.local"
38 | actions:
39 | - handler: recommendationrequestcounthandler.prometheus
40 | instances:
41 | - recommendationrequestcount.metric
42 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/routing-canary.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: VirtualService
3 | metadata:
4 | name: recommendation
5 | spec:
6 | hosts:
7 | - recommendation
8 | http:
9 | - match:
10 | - headers:
11 | user-location:
12 | exact: Boston
13 | route:
14 | - destination:
15 | host: recommendation
16 | subset: v2
17 | - route:
18 | - destination:
19 | host: recommendation
20 | subset: v1
21 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/routing-mirroring.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: VirtualService
3 | metadata:
4 | name: recommendation
5 | spec:
6 | hosts:
7 | - recommendation
8 | http:
9 | - route:
10 | - destination:
11 | host: recommendation
12 | subset: v2
13 | mirror:
14 | host: recommendation
15 | subset: v3
16 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/routing-weighted.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: DestinationRule
3 | metadata:
4 | name: recommendation
5 | spec:
6 | host: recommendation
7 | subsets:
8 | - labels:
9 | version: v1
10 | name: version-v1
11 | - labels:
12 | version: v2
13 | name: version-v2
14 | - labels:
15 | version: v3
16 | name: version-v3
17 | ---
18 | apiVersion: networking.istio.io/v1alpha3
19 | kind: VirtualService
20 | metadata:
21 | name: recommendation
22 | spec:
23 | hosts:
24 | - recommendation
25 | http:
26 | - route:
27 | - destination:
28 | host: recommendation
29 | subset: version-v1
30 | weight: 80
31 | - destination:
32 | host: recommendation
33 | subset: version-v2
34 | weight: 20
35 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/virtual-service-20-80.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: VirtualService
3 | metadata:
4 | name: recommendation
5 | spec:
6 | hosts:
7 | - recommendation
8 | http:
9 | - route:
10 | - destination:
11 | host: recommendation
12 | subset: v1
13 | weight: 20
14 | - destination:
15 | host: recommendation
16 | subset: v2
17 | weight: 80
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/virtual-service-80-20.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: VirtualService
3 | metadata:
4 | name: recommendation
5 | spec:
6 | hosts:
7 | - recommendation
8 | http:
9 | - route:
10 | - destination:
11 | host: recommendation
12 | subset: v1
13 | weight: 80
14 | - destination:
15 | host: recommendation
16 | subset: v2
17 | weight: 20
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/virtual-service-recommendation-503.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: DestinationRule
3 | metadata:
4 | name: recommendation
5 | spec:
6 | host: recommendation
7 | subsets:
8 | - labels:
9 | version: v1
10 | name: v1
11 | - labels:
12 | version: v2
13 | name: v2
14 | - labels:
15 | version: v3
16 | name: v3
17 | ---
18 | apiVersion: networking.istio.io/v1alpha3
19 | kind: VirtualService
20 | metadata:
21 | name: recommendation
22 | spec:
23 | hosts:
24 | - recommendation
25 | http:
26 | - route:
27 | - destination:
28 | host: recommendation
29 | subset: v1
30 | weight: 34
31 | - destination:
32 | host: recommendation
33 | subset: v2
34 | weight: 33
35 | - destination:
36 | host: recommendation
37 | subset: v3
38 | weight: 33
39 | fault:
40 | abort:
41 | percentage:
42 | value: 50
43 | httpStatus: 503
44 | ---
45 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/virtual-service-recommendation-delay.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: DestinationRule
3 | metadata:
4 | name: recommendation
5 | spec:
6 | host: recommendation
7 | subsets:
8 | - labels:
9 | version: v1
10 | name: v1
11 | - labels:
12 | version: v2
13 | name: v2
14 | - labels:
15 | version: v3
16 | name: v3
17 | ---
18 | apiVersion: networking.istio.io/v1alpha3
19 | kind: VirtualService
20 | metadata:
21 | name: recommendation
22 | spec:
23 | hosts:
24 | - recommendation
25 | http:
26 | - route:
27 | - destination:
28 | host: recommendation
29 | subset: v1
30 | weight: 34
31 | - destination:
32 | host: recommendation
33 | subset: v2
34 | weight: 33
35 | - destination:
36 | host: recommendation
37 | subset: v3
38 | weight: 33
39 | fault:
40 | delay:
41 | fixedDelay: 7.000s
42 | percent: 50
43 | ---
44 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/virtual-service-recommendation-split.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: VirtualService
3 | metadata:
4 | name: recommendation
5 | spec:
6 | hosts:
7 | - recommendation
8 | http:
9 | - route:
10 | - destination:
11 | host: recommendation
12 | subset: v1
13 | weight: 34
14 | - destination:
15 | host: recommendation
16 | subset: v2
17 | weight: 33
18 | - destination:
19 | host: recommendation
20 | subset: v3
21 | weight: 33
22 | ---
23 |
--------------------------------------------------------------------------------
/workshop/content/src/istiofiles/virtual-service-recommendation-timeout.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: VirtualService
3 | metadata:
4 | name: recommendation
5 | spec:
6 | hosts:
7 | - recommendation
8 | http:
9 | - route:
10 | - destination:
11 | host: recommendation
12 | timeout: 1.000s
13 |
--------------------------------------------------------------------------------
/workshop/content/src/preference/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 | !target/*-runner
3 | !target/*-runner.jar
4 | !target/lib/*
--------------------------------------------------------------------------------
/workshop/content/src/preference/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 |
12 | ### IntelliJ IDEA ###
13 | .idea
14 | *.iws
15 | *.iml
16 | *.ipr
17 |
18 | ### NetBeans ###
19 | nbproject/private/
20 | build/
21 | nbbuild/
22 | dist/
23 | nbdist/
24 | .nb-gradle/
--------------------------------------------------------------------------------
/workshop/content/src/preference/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM fabric8/java-jboss-openjdk8-jdk:1.5.4
2 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0
3 | ENV JAEGER_SERVICE_NAME=preference \
4 | JAEGER_ENDPOINT=http://jaeger-collector.istio-system.svc:14268/api/traces \
5 | JAEGER_PROPAGATION=b3 \
6 | JAEGER_SAMPLER_TYPE=const \
7 | JAEGER_SAMPLER_PARAM=1
8 | EXPOSE 8080 8778 9779
9 | COPY target/lib/* /deployments/lib/
10 | COPY target/*-runner.jar /deployments/app.jar
11 | ENTRYPOINT [ "/deployments/run-java.sh" ]
--------------------------------------------------------------------------------
/workshop/content/src/preference/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.redhat.developer.demos.preference.rest
6 | preference
7 | 1.0-SNAPSHOT
8 |
9 | 2.22.0
10 | 0.12.0
11 | UTF-8
12 | 1.8
13 | 1.8
14 |
15 |
16 |
17 |
18 | io.quarkus
19 | quarkus-bom
20 | ${quarkus.version}
21 | pom
22 | import
23 |
24 |
25 |
26 |
27 |
28 | io.quarkus
29 | quarkus-resteasy
30 |
31 |
32 | io.quarkus
33 | quarkus-junit5
34 | test
35 |
36 |
37 | io.rest-assured
38 | rest-assured
39 | test
40 |
41 |
42 | io.quarkus
43 | quarkus-smallrye-rest-client
44 |
45 |
46 | io.quarkus
47 | quarkus-smallrye-opentracing
48 |
49 |
50 | io.quarkus
51 | quarkus-smallrye-health
52 |
53 |
54 | io.quarkus
55 | quarkus-smallrye-metrics
56 |
57 |
58 |
59 |
60 |
61 | io.quarkus
62 | quarkus-maven-plugin
63 | ${quarkus.version}
64 |
65 |
66 |
67 | build
68 |
69 |
70 |
71 |
72 |
73 | maven-surefire-plugin
74 | ${surefire-plugin.version}
75 |
76 |
77 | org.jboss.logmanager.LogManager
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | native
86 |
87 |
88 | native
89 |
90 |
91 |
92 |
93 |
94 | io.quarkus
95 | quarkus-maven-plugin
96 | ${quarkus.version}
97 |
98 |
99 |
100 | native-image
101 |
102 |
103 | true
104 |
105 |
106 |
107 |
108 |
109 | maven-failsafe-plugin
110 | ${surefire-plugin.version}
111 |
112 |
113 |
114 | integration-test
115 | verify
116 |
117 |
118 |
119 | ${project.build.directory}/${project.build.finalName}-runner
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/workshop/content/src/preference/src/main/docker/Dockerfile.jvm:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
3 | #
4 | # Before building the docker image run:
5 | #
6 | # mvn package
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/preference-jvm .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/preference-jvm
15 | #
16 | ###
17 | FROM fabric8/java-jboss-openjdk8-jdk
18 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0
19 | COPY target/lib/* /deployments/lib/
20 | COPY target/*-runner.jar /deployments/app.jar
21 | ENTRYPOINT [ "/deployments/run-java.sh" ]
--------------------------------------------------------------------------------
/workshop/content/src/preference/src/main/docker/Dockerfile.native:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
3 | #
4 | # Before building the docker image run:
5 | #
6 | # mvn package -Pnative -Dnative-image.docker-build=true
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/preference .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/preference
15 | #
16 | ###
17 | FROM registry.fedoraproject.org/fedora-minimal
18 | WORKDIR /work/
19 | COPY target/*-runner /work/preference
20 | RUN chmod 775 /work
21 | EXPOSE 8080
22 | CMD ["./preference", "-Xmx8m", "-Xmn8m", "-Xms8m"]
23 | # CMD ["./preference", "-Dquarkus.http.host=0.0.0.0"]
--------------------------------------------------------------------------------
/workshop/content/src/preference/src/main/java/com/redhat/developer/demos/customer/rest/PreferenceResource.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import org.eclipse.microprofile.rest.client.inject.RestClient;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import javax.inject.Inject;
8 | import javax.ws.rs.*;
9 | import javax.ws.rs.core.MediaType;
10 | import javax.ws.rs.core.Response;
11 |
12 | @Path("/")
13 | public class PreferenceResource {
14 |
15 | private static final String RESPONSE_STRING_FORMAT = "preference => %s\n";
16 |
17 | private final Logger logger = LoggerFactory.getLogger(getClass());
18 |
19 | @Inject
20 | @RestClient
21 | RecommendationService recommendationService;
22 |
23 | @GET
24 | @Produces(MediaType.TEXT_PLAIN)
25 | public Response getCustomer() {
26 | try {
27 | String response = recommendationService.getPreference();
28 | return Response.ok(String.format(RESPONSE_STRING_FORMAT, response)).build();
29 | } catch (WebApplicationException ex) {
30 | Response response = ex.getResponse();
31 | logger.warn("Non HTTP 20x trying to get the response from recommendation service: " + response.getStatus());
32 | return Response
33 | .status(Response.Status.SERVICE_UNAVAILABLE)
34 | .entity(String.format(RESPONSE_STRING_FORMAT,
35 | String.format("Error: %d - %s", response.getStatus(), response.readEntity(String.class)))
36 | )
37 | .build();
38 | } catch (ProcessingException ex) {
39 | logger.warn("Exception trying to get the response from recommendation service.", ex);
40 | return Response
41 | .status(Response.Status.SERVICE_UNAVAILABLE)
42 | .entity(String.format(RESPONSE_STRING_FORMAT, ex.getCause().getClass().getSimpleName() + ": " + ex.getCause().getMessage()))
43 | .build();
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/workshop/content/src/preference/src/main/java/com/redhat/developer/demos/customer/rest/RecommendationService.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
4 | import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
5 |
6 | import javax.ws.rs.GET;
7 | import javax.ws.rs.HeaderParam;
8 | import javax.ws.rs.Path;
9 | import javax.ws.rs.Produces;
10 | import javax.ws.rs.core.Response;
11 |
12 | @RegisterClientHeaders
13 | @RegisterRestClient
14 | public interface RecommendationService {
15 |
16 | @Path("/")
17 | @GET
18 | @Produces("text/plain")
19 | public String getPreference();
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/workshop/content/src/preference/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | org.eclipse.microprofile.rest.client.propagateHeaders=baggage-user-agent,user-location
2 | com.redhat.developer.demos.customer.rest.RecommendationService/mp-rest/url=http://recommendation:8080
3 |
--------------------------------------------------------------------------------
/workshop/content/src/preference/src/test/java/com/redhat/developer/demos/customer/rest/NativePreferenceResourceIT.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import io.quarkus.test.junit.SubstrateTest;
4 |
5 | @SubstrateTest
6 | public class NativePreferenceResourceIT extends PreferenceResourceTest {
7 |
8 | // Execute the same tests but in native mode.
9 | }
--------------------------------------------------------------------------------
/workshop/content/src/preference/src/test/java/com/redhat/developer/demos/customer/rest/PreferenceResourceTest.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.customer.rest;
2 |
3 | import io.quarkus.test.junit.QuarkusTest;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static io.restassured.RestAssured.given;
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.hamcrest.CoreMatchers.startsWith;
9 |
10 | @QuarkusTest
11 | public class PreferenceResourceTest {
12 |
13 | @Test
14 | public void testHelloEndpoint() {
15 | given()
16 | .when().get("/")
17 | .then()
18 | .statusCode(503)
19 | .body(startsWith("preference =>"));
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 | !target/*-runner
3 | !target/*-runner.jar
4 | !target/lib/*
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 |
12 | ### IntelliJ IDEA ###
13 | .idea
14 | *.iws
15 | *.iml
16 | *.ipr
17 |
18 | ### NetBeans ###
19 | nbproject/private/
20 | build/
21 | nbbuild/
22 | dist/
23 | nbdist/
24 | .nb-gradle/
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM fabric8/java-jboss-openjdk8-jdk:1.5.4
2 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0
3 | ENV JAEGER_SERVICE_NAME=recommendation \
4 | JAEGER_ENDPOINT=http://jaeger-collector.istio-system.svc:14268/api/traces \
5 | JAEGER_PROPAGATION=b3 \
6 | JAEGER_SAMPLER_TYPE=const \
7 | JAEGER_SAMPLER_PARAM=1
8 | EXPOSE 8080 8778 9779
9 | COPY target/lib/* /deployments/lib/
10 | COPY target/*-runner.jar /deployments/app.jar
11 | ENTRYPOINT [ "/deployments/run-java.sh" ]
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.redhat.developers
6 | recommendation
7 | 1.0-SNAPSHOT
8 |
9 | 2.22.0
10 | 0.12.0
11 | UTF-8
12 | 1.8
13 | 1.8
14 |
15 |
16 |
17 |
18 | io.quarkus
19 | quarkus-bom
20 | ${quarkus.version}
21 | pom
22 | import
23 |
24 |
25 |
26 |
27 |
28 | io.quarkus
29 | quarkus-resteasy
30 |
31 |
32 | io.quarkus
33 | quarkus-smallrye-rest-client
34 |
35 |
36 | io.quarkus
37 | quarkus-arc
38 |
39 |
40 | io.quarkus
41 | quarkus-junit5
42 |
43 |
44 | io.rest-assured
45 | rest-assured
46 | test
47 |
48 |
49 | io.quarkus
50 | quarkus-smallrye-health
51 |
52 |
53 | io.quarkus
54 | quarkus-smallrye-metrics
55 |
56 |
57 | io.quarkus
58 | quarkus-smallrye-opentracing
59 |
60 |
61 |
62 |
63 |
64 | io.quarkus
65 | quarkus-maven-plugin
66 | ${quarkus.version}
67 |
68 |
69 |
70 | build
71 |
72 |
73 |
74 |
75 |
76 | maven-surefire-plugin
77 | ${surefire-plugin.version}
78 |
79 |
80 | org.jboss.logmanager.LogManager
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | native
89 |
90 |
91 | native
92 |
93 |
94 |
95 |
96 |
97 | io.quarkus
98 | quarkus-maven-plugin
99 | ${quarkus.version}
100 |
101 |
102 |
103 | native-image
104 |
105 |
106 | true
107 |
108 |
109 |
110 |
111 |
112 | maven-failsafe-plugin
113 | ${surefire-plugin.version}
114 |
115 |
116 |
117 | integration-test
118 | verify
119 |
120 |
121 |
122 | ${project.build.directory}/${project.build.finalName}-runner
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/src/main/docker/Dockerfile.jvm:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
3 | #
4 | # Before building the docker image run:
5 | #
6 | # mvn package
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/recommendation-jvm .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/recommendation-jvm
15 | #
16 | ###
17 | FROM fabric8/java-jboss-openjdk8-jdk
18 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0
19 | COPY target/lib/* /deployments/lib/
20 | COPY target/*-runner.jar /deployments/app.jar
21 | ENTRYPOINT [ "/deployments/run-java.sh" ]
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/src/main/docker/Dockerfile.native:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
3 | #
4 | # Before building the docker image run:
5 | #
6 | # mvn package -Pnative -Dnative-image.docker-build=true
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/recommendation .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/recommendation
15 | #
16 | ###
17 | FROM registry.fedoraproject.org/fedora-minimal
18 | WORKDIR /work/
19 | COPY target/*-runner /work/recommendation
20 | RUN chmod 775 /work
21 | EXPOSE 8080
22 | CMD ["./recommendation", "-Xmx8m", "-Xmn8m", "-Xms8m"]
23 | # CMD ["./recommendation", "-Dquarkus.http.host=0.0.0.0"]
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/src/main/java/com/redhat/developer/demos/recommendation/rest/RecommendationResource.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.recommendation.rest;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.InputStream;
5 | import javax.json.Json;
6 | import javax.json.JsonObject;
7 | import javax.ws.rs.client.Client;
8 | import javax.ws.rs.client.ClientBuilder;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import javax.ws.rs.GET;
13 | import javax.ws.rs.Path;
14 | import javax.ws.rs.QueryParam;
15 | import javax.ws.rs.core.Response;
16 |
17 | @Path("/")
18 | public class RecommendationResource {
19 |
20 | private static final String RESPONSE_STRING_FORMAT = "recommendation %s from '%s': %d\n";
21 |
22 | private static final String RESPONSE_STRING_NOW_FORMAT = "recommendation v3 %s from '%s': %d\n";
23 |
24 | private final Logger logger = LoggerFactory.getLogger(getClass());
25 |
26 | /**
27 | * Counter to help us see the lifecycle
28 | */
29 | private int count = 0;
30 |
31 | /**
32 | * Flag for throwing a 503 when enabled
33 | */
34 | private boolean misbehave = false;
35 |
36 | /**
37 | * Timeout value for forcing timeout if > 0
38 | */
39 | private long timeout;
40 |
41 | private static final String HOSTNAME = parseContainerIdFromHostname(
42 | System.getenv().getOrDefault("HOSTNAME", "unknown"));
43 | private static final String VERSION = parseVersionFromHostname(
44 | System.getenv().getOrDefault("HOSTNAME", "unknown"));
45 |
46 | static String parseVersionFromHostname(String hostname) {
47 | return hostname.replaceAll("recommendation-", "").replaceAll("-.*", "");
48 | }
49 |
50 | static String parseContainerIdFromHostname(String hostname) {
51 | return hostname.replaceAll("recommendation-v\\d+-", "");
52 | }
53 |
54 | @GET
55 | public Response getRecommendations() {
56 | count++;
57 | logger.info(String.format("recommendation request from %s: %d", HOSTNAME, count));
58 |
59 | if (timeout > 0) {
60 | sleep(timeout);
61 | }
62 |
63 | logger.debug("recommendation service ready to return");
64 | if (misbehave) {
65 | return doMisbehavior();
66 | }
67 | return Response.ok(String.format(RESPONSE_STRING_FORMAT, VERSION, HOSTNAME, count)).build();
68 | // return Response.ok(String.format(RESPONSE_STRING_NOW_FORMAT, getNow(), HOSTNAME, count)).build();
69 | }
70 |
71 | private void sleep(final long timeout) {
72 | try {
73 | Thread.sleep(timeout);
74 | } catch (InterruptedException e) {
75 | logger.info("Thread interrupted");
76 | }
77 | }
78 |
79 | private Response doMisbehavior() {
80 | logger.debug(String.format("Misbehaving %d", count));
81 | return Response.status(Response.Status.SERVICE_UNAVAILABLE)
82 | .entity(String.format("recommendation misbehavior from '%s'\n", HOSTNAME)).build();
83 | }
84 |
85 | @GET
86 | @Path("/misbehave")
87 | public Response flagMisbehave() {
88 | this.misbehave = true;
89 | logger.debug("'misbehave' has been set to 'true'");
90 | return Response.ok("Following requests to / will return a 503\n").build();
91 | }
92 |
93 | @GET
94 | @Path("/behave")
95 | public Response flagBehave() {
96 | this.misbehave = false;
97 | logger.debug("'misbehave' has been set to 'false'");
98 | return Response.ok("Following requests to / will return 200\n").build();
99 | }
100 |
101 | @GET
102 | @Path("/timeout")
103 | public Response setTimeout(@QueryParam("timeout") int timeout) {
104 | this.timeout = timeout*1000L;
105 | logger.debug("'timeout' has been set to " + timeout + " seconds");
106 | return Response.ok("Timeout has been set to " + timeout + " seconds\n").build();
107 | }
108 |
109 | private String getNow() {
110 | final Client client = ClientBuilder.newClient();
111 | final Response res = client.target("http://worldclockapi.com/api/json/cet/now").request().get();
112 | final String jsonObject = res.readEntity(String.class);
113 | return Json.createReader(new ByteArrayInputStream(jsonObject.getBytes())).readObject().getString("currentDateTime");
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/src/test/java/com/redhat/developer/demos/recommendation/rest/NativeRecommendationResourceIT.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.recommendation.rest;
2 |
3 | import io.quarkus.test.junit.SubstrateTest;
4 |
5 | @SubstrateTest
6 | public class NativeRecommendationResourceIT extends RecommendationResourceTest {
7 |
8 | // Execute the same tests but in native mode.
9 | }
--------------------------------------------------------------------------------
/workshop/content/src/recommendation/src/test/java/com/redhat/developer/demos/recommendation/rest/RecommendationResourceTest.java:
--------------------------------------------------------------------------------
1 | package com.redhat.developer.demos.recommendation.rest;
2 |
3 | import io.quarkus.test.junit.QuarkusTest;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static io.restassured.RestAssured.given;
7 | import static org.hamcrest.CoreMatchers.startsWith;
8 |
9 | @QuarkusTest
10 | public class RecommendationResourceTest {
11 |
12 | @Test
13 | public void testHelloEndpoint() {
14 | given()
15 | .when().get("/")
16 | .then()
17 | .statusCode(200)
18 | .body(startsWith("recommendation"));
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/workshop/content/workshop-overview.adoc:
--------------------------------------------------------------------------------
1 | This is the initial landing page for your workshop. Include in this page a description of what your workshop is about.
2 |
--------------------------------------------------------------------------------
/workshop/content/workshop-summary.adoc:
--------------------------------------------------------------------------------
1 | This is the last page of the workshop. Include in this page a summary of the workshop and any links to resources relevant to the workshop. This ensures anyone doing the workshop has material they can research later to learn more.
2 |
--------------------------------------------------------------------------------
/workshop/modules.yaml:
--------------------------------------------------------------------------------
1 | modules:
2 | intro:
3 | name: Workshop Overview
4 | exit_sign: Observability with Kiali
5 | kiali:
6 | name: Observing the Service Mesh using Kiali
7 | exit_sign: Routing
8 | routing:
9 | name: Routing within the Service Mesh
10 | exit_sign: Mutual TLS
11 | mtls:
12 | name: Mutual TLS
13 | exit_sign: Authentication
14 | authentication:
15 | name: Authentication (JWT and RBAC)
16 | exit_sign: ACLs
17 | acl:
18 | name: Whitelisting/Blacklisting
19 | exit_sign: Circuit Breaking
20 | circuit-breaking:
21 | name: Retries, Timeouts and Circuit Breaking
22 | exit_sign: Rate Limiting
23 | rate-limiting:
24 | name: Rate Limiting/Policy
25 | exit_sign: Fault Injection
26 | fault-injection:
27 | name: Fault Injection
--------------------------------------------------------------------------------
/workshop/workshop.yaml:
--------------------------------------------------------------------------------
1 | name: Red Hat OpenShift Service Mesh in Action
2 |
3 | modules:
4 | activate:
5 | - intro
6 | - kiali
7 | - routing
8 | - mtls
9 | - authentication
10 | - acl
11 | - circuit-breaking
12 | - rate-limiting
13 | - fault-injection
14 |
--------------------------------------------------------------------------------