Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helidon Istio Example #3676

Merged
merged 5 commits into from Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added examples/istio/Helidon-Istio.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
206 changes: 206 additions & 0 deletions examples/istio/README.md
@@ -0,0 +1,206 @@
# Helidon Istio

This example showcases how you can setup Helidon microservices inside Istio service mesh and setup communication amongst services inside mesh as well as access a service from outside the mesh.

![Helidon Istio](helidon-istio.png?raw=true "Helidon Istio")

# Environment Setup

Setup Kubernetes cluster (1.21.5) on your machine. We used docker-for-desktop (4.2.0) for this example. See for more details https://docs.docker.com/desktop/kubernetes/

Install Istio (1.12.0) following https://istio.io/latest/docs/setup/getting-started/#download. You don't need to follow "Deploy the sample application" onwards.

Following instructions have been verified with Istio 1.12 on Kubernetes 1.21

# MySQL Setup

For the purpose of this example, we are running MySQL in a container outside Kubernetes using docker-for-desktop.

```
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=<root-password> -e MYSQL_USER=user -e MYSQL_PASSWORD=<user-password> -e MYSQL_DATABASE=helloworld mysql:8
```

Please refer to https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/docker-mysql-getting-started.html for detailed instructions.

# helidon-config application

helidon-config is a Helidon MP project that showcases how to load helidon config from a kubernetes configmap.

```
cd helidon-config
```

## Build the Docker Image

```
docker build -f Dockerfile -t helidon-config .
```

## Deploy the application to Kubernetes

```
kubectl cluster-info # Verify which cluster
kubectl get pods # Verify connectivity to cluster
kubectl apply -f app.yaml # Deploy application
kubectl get pods # Wait for quickstart pod to be RUNNING
kubectl get service helidon-config-np # Verify deployed service
```

Note the PORTs. You can now exercise the application as following but use the second
port number (the NodePort) instead of 7001.

## Exercise the application

```
curl -X GET http://localhost:<NodePort>/first
Hello
```

# helidon-jps application

helidon-jps is a Helidon MP project that showcases how to invoke another microservice using RestClient as well as JPA integration (esp. stored procedure call)

```
cd ../helidon-jpa
```

## Build the Docker Image

Update MySQL related properties in `microprofile-config.properties`. Host IP is usually the IP of your laptop and port would be 3306, if you used the command provided above.

```
docker build -f Dockerfile -t helidon-jpa .
```

## Deploy the application to Kubernetes

Let's first make external MySQL available to application. Update MySQL related properties in `istio-mysql-se.yaml`. Host IP is usually the IP of your laptop and port would be 3306, if you used the command provided above.

```
kubectl apply -f istio-mysql-se.yaml # Creates ServiceEntry inside the mesh
```

Now deploy helidon-jpa application
```
kubectl apply -f app.yaml # Deploy application
kubectl get pods # Wait for quickstart pod to be RUNNING
```

Expose helidon-jpa application to outside world
```
kubectl apply -f istio-gateway-vs.yaml # Creates Gateway associated with Istio's ingressgeteway and VirtualService
```

## Exercise the application

In a different Terminal, create the stored procedure in MySQL that will be used by the application.
```
docker exec -it mysql bash
mysql -u root -p
<provide the root password that was used when starting MySQL container>
USE helloworld;
Delimiter //
Create Procedure getAllPersons()
-> BEGIN
-> Select * from Person; # Person table is created when we run the application.
-> END//
Delimiter ;
Call getAllPersons; # Verify that stored procedure works
```

Now verify simple greeting:
```
curl -X GET http://localhost/greet
```
returned response should be:
```
{"message":"Hello World!"}
```
We are using localhost above, as it is the EXTERNAL-IP for `istio-ingressgateway` service.

Add new person to the database
```
curl -X POST -H "Content-Type: application/json" \
-d '{"nick":"bob","name":"Bobby Fischer"}' \
http://localhost/greet
```
returned response should be:
```
{"nick":"bob","name":"Bobby Fischer"}
```

Greet new person:
```
curl -X GET http://localhost/greet/bob
```
returned response should be:
```
{"message":"Hello Bobby Fischer!"}
```

# Let's enable security

We are going to enable access based on a JSON Web Token (JWT). Please refer to https://istio.io/latest/docs/tasks/security/authorization/authz-jwt/ for detailed instructions.

The following command creates `ingress-jwt-auth` request authentication policy for all `ingressgateway` workload. This policy accepts a JWT issued by `testing@secure.istio.io`
```
kubectl apply -f istio-request-auth.yaml
```

Verify that a request with an invalid JWT is denied:
```
curl --header "Authorization: Bearer invalidToken" -X GET http://localhost/greet
```
returned response should be:
```
Jwt is not in the form of Header.Payload.Signature with two dots and 3 sections
```

Verify that a request without a JWT is allowed because there is no authorization policy:
```
curl -X GET http://localhost/greet
```
returned response should be:
```
{"message":"Hello World!"}
```

The following command creates `ingress-jwt-must` authorization policy for all `ingressgateway` workload. The policy requires all requests to have a valid JWT.
```
kubectl apply -f istio-auth-policy.yaml
```

Get the JWT for `testing@secure.istio.io`
```
TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.12/security/tools/jwt/samples/demo.jwt -s)
```

Verify that a request with a valid JWT is allowed:
```
curl --header "Authorization: Bearer $TOKEN" -X GET http://localhost/greet
```
returned response should be:
```
{"message":"Hello World!"}
```

Verify that a request without a JWT is denied:
```
curl -X GET http://localhost/greet
```
returned response should be:
```
RBAC: access denied
```

# After you’re done, cleanup.

```
kubectl delete -f istio-auth-policy.yaml
kubectl delete -f istio-request-auth.yaml
kubectl delete -f istio-gateway-vs.yaml
kubectl delete -f app.yaml
kubectl delete -f istio-mysql-se.yaml
cd ../helidon-config
kubectl delete -f app.yaml
```
43 changes: 43 additions & 0 deletions examples/istio/helidon-config/Dockerfile
@@ -0,0 +1,43 @@
#
# Copyright (c) 2021 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

FROM maven:3.6-jdk-11 as build

WORKDIR /helidon

# Create a first layer to cache the "Maven World" in the local repository.
# Incremental docker builds will always resume after that, unless you update
# the pom
ADD pom.xml .
RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip

# Do the Maven build!
# Incremental docker builds will resume here when you change sources
ADD src src
RUN mvn package -DskipTests
RUN echo "done!"

# 2nd stage, build the runtime image
FROM openjdk:11-jre-slim
WORKDIR /helidon

# Copy the binary built in the 1st stage
COPY --from=build /helidon/target/helidon-config.jar ./
COPY --from=build /helidon/target/libs ./libs

CMD ["java", "-jar", "helidon-config.jar"]

EXPOSE 7001
77 changes: 77 additions & 0 deletions examples/istio/helidon-config/app.yaml
@@ -0,0 +1,77 @@
#
# Copyright (c) 2021 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

kind: ConfigMap
apiVersion: v1
metadata:
name: helidon-config-cm
data:
config-properties.yaml: |
app.greeting: Hello
---
apiVersion: v1
kind: Service
metadata:
name: helidon-config
spec:
selector:
app: helidon-config
ports:
- port: 7001
targetPort: 7001
---
kind: Service
apiVersion: v1
metadata:
name: helidon-config-np
spec:
type: NodePort
selector:
app: helidon-config
ports:
- port: 7001
targetPort: 7001
name: http
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: helidon-config
spec:
replicas: 1
selector:
matchLabels:
app: helidon-config
template:
metadata:
labels:
app: helidon-config
version: v1
spec:
containers:
- name: helidon-config
image: helidon-config
imagePullPolicy: IfNotPresent
ports:
- containerPort: 7001
volumeMounts:
- mountPath: /conf
name: config-volume
volumes:
- name: config-volume
configMap:
name: helidon-config-cm
---