Consul service mesh allows you to deploy applications into a zero-trust network. A zero-trust network is a network where nothing is trusted automatically. All connections must be both authenticated and authorized. This paradigm is important in microservice and multi-cloud environments where a large number of services may be running in the same network. With Consul service mesh, service identity can be authenticated using mTLS and service operations can be authorized or blocked using intentions.
In this tutorial, you will deploy two services, web
and api
, into Consul's
service mesh running on a Kubernetes cluster. The two services will use Consul
to discover each other and communicate over mTLS using sidecar proxies.
The two services represent a simple two-tier application made of a backend api
service and a frontend that communicates with the api
service over HTTP and
exposes the results in a web ui.
»Prerequisites
A Kubernetes cluster with Consul service mesh - In the previous tutorial you used Helm to deploy Consul service mesh and enabled the use of Envoy as a sidecar proxy on a local Kubernetes cluster. You will be using that cluster to test the commands provided in this tutorial.
kubectl to interact with your Kubernetes cluster and deploy services.
NOTE: A similar, Minikube based interactive hands-on lab is also available if you do not have a Consul environment to perform the steps described in this tutorial. Click the Show Tutorial button to launch the lab experience.
»Deploy services with sidecar proxies in Kubernetes
With the Consul connectInject
option enabled in the consul-values.yaml
file,
you have ensured that all the services deployed in the service mesh, that include
the "consul.hashicorp.com/connect-inject": "true"
annotation, will be automatically
registered in the Consul catalog.
When you apply this annotation, a sidecar proxy is automatically added to your pod. This proxy will handle inbound and outbound service connections, automatically ensuring all inter-service traffic occurs over verified TLS connections.
Using local sidecar proxies facilitates application integration, since application
code doesn't need to be aware of certificates, URLs, or namespaces. Since containers
within a pod share networking, as long as the application code uses localhost:pod-port
to access services within the mesh, the Consul managed Envoy proxy will
coordinate service discovery, mTLS, and policy enforcement with the Consul control
plane.
Note that the application also does not need to be aware of which port the
remote service is actually running on. The localhost:pod-port
example
only has meaning within the pod. Consul will handle the translation to the correct
target service ip:port combination. We'll cover this in more detail later in the
tutorial in the section that explains the upstream concept.
»Define the services
To register services in Kubernetes you will create two service definition files
and deploy the services using kubectl
.
Create a folder to contain the configuration files.
$ mkdir ./k8s_config
»Define the backend service
Use the following command to create a file named api.yaml
in the k8s_config
directory that contains the desired deployment configuration.
$ cat > ./k8s_config/api.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment-v1
labels:
app: api-v1
spec:
replicas: 1
selector:
matchLabels:
app: api-v1
template:
metadata:
labels:
app: api-v1
annotations:
'consul.hashicorp.com/connect-inject': 'true'
spec:
containers:
- name: api
image: nicholasjackson/fake-service:v0.7.8
ports:
- containerPort: 9090
env:
- name: 'LISTEN_ADDR'
value: '127.0.0.1:9090'
- name: 'NAME'
value: 'api-v1'
- name: 'MESSAGE'
value: 'Response from API v1'
EOF
»Define the frontend service
Use the following command to create a file named web.yaml
in the k8s_config
directory that contains the desired deployment configuration.
$ cat > ./k8s_config/web.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
labels:
app: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
annotations:
'consul.hashicorp.com/connect-inject': 'true'
'consul.hashicorp.com/connect-service-upstreams': 'api:9091'
spec:
containers:
- name: web
image: nicholasjackson/fake-service:v0.7.8
ports:
- containerPort: 9090
env:
- name: 'LISTEN_ADDR'
value: '0.0.0.0:9090'
- name: 'UPSTREAM_URIS'
value: 'http://localhost:9091'
- name: 'NAME'
value: 'web'
- name: 'MESSAGE'
value: 'Hello World'
---
# Service to expose web frontend
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- name: http
protocol: TCP
port: 9090
targetPort: 9090
EOF
»Understand the upstream concept
In this example, the web
frontend service depends on the api
backend service
to operate. The following statements explain the upstream and downstream relationship
between the services.
- The
web
frontend service depends on theapi
service and is therefore downstream from theapi
service. - The
api
service is upstream from theweb
service since theweb
service depends on it.
You may have noticed that the web
service deployment definition included an
additional Consul specific annotation.
"consul.hashicorp.com/connect-service-upstreams": "api:9091"
By adding the consul.hashicorp.com/connect-service-upstreams
annotation, you
are explicitly declaring that the api
service is used by, and therefore, an
upstream dependency for the web
service.
By defining the upstream using the format consul-service-name:pod-port
(e.g. api:9091
),
you have provided Consul enough metadata that it will be able make the api
service
that is registered in the catalog available at localhost:9091
in the web
service pod.
When the web
service makes a request to localhost:9091
, the sidecar proxy will
establish a secure mTLS connection with the api
service and forward the request.
»Deploy the services
Once the configuration is completed, you can deploy the applications using
kubectl apply
.
$ kubectl apply -f ./k8s_config/api.yaml
$ kubectl apply -f ./k8s_config/web.yaml
After a few seconds you will be able to monitor the application's pods being created and running.
$ kubectl get pods --all-namespaces
# ...
api-deployment-v1-85cc8c9977-z9sv2 3/3 Running 0 35s
web-deployment-76dcfdcc8f-d7f25 3/3 Running 0 32s
You can also confirm the status of the deployment in the Consul UI at http://localhost:18500.
»Access the services
In the previous tutorial, you had to manually configure the access for Consul UI
using either an ingress or port forwarding. Similarly, to gain access to the ui
exposed by the web
service, you will have to permit access.
To access the web
service UI you will setup port forwarding.
$ kubectl port-forward service/web 9090:9090 --address 0.0.0.0
This will forward port 9090
from service/web
at port 9090
to your development
host.
Once access is configured, the web UI will be available at http://localhost:9090/ui.
»Next steps
In this tutorial, you deployed a two-tier application in the Consul service mesh and
defined the ports and dependencies for each of the services composing your
application. Finally,you enabled external access for your web
service.
This configuration ensures that all the communication between the web
and the
api
services is passing through the Envoy sidecar proxies, and therefore, is
encrypted using mTLS.
In the next tutorial, Enforce a Zero-trust Network with Consul Service Mesh you will learn how to configure intentions to define access control between services in the Consul service mesh and control which services are allowed or not allowed to establish connections.