In this tutorial, you’ll start a local Kubernetes cluster with kind
. You will then deploy Consul with the official Helm chart or the Consul K8S CLI. After deploying Consul, you will learn how to access the Consul agents. You will then deploy two services that use Consul to discover each other.
Security Warning This tutorial is not for production use. By default, the Helm chart will install an insecure configuration of Consul. Please refer to the Kubernetes deployment guide to determine how you can secure Consul on Kubernetes in production. Additionally, we recommend to use a properly secured Kubernetes cluster or make sure that you understand and enable the recommended security features.
Prerequisites
First, you’ll need to follow the directions for installing kind.
You will also need to install kubectl
and helm
.
Install kubectl
with Homebrew.
$ brew install kubernetes-cli
$ brew install kubernetes-cli
Install helm
with Homebrew.
$ brew install kubernetes-helm
$ brew install kubernetes-helm
Start a Kind cluster
Once kind
is installed, you can spin up any number of clusters. By default, kind
names your cluster “kind”, but you may name it anything you like by specifying the --name
option. This tutorial assumes the cluster is named dc1
. Refer to the kind documentation for information about how to specify additional parameters using a yaml configuration file.
$ kind create cluster --name dc1
$ kind create cluster --name dc1
The output will be similar to the following.
Creating cluster "dc1" ...
✓ Ensuring node image (kindest/node:v1.18.2) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-dc1"
You can now use your cluster with:
kubectl cluster-info --context kind-dc1
Have a nice day! 👋
Creating cluster "dc1" ...
✓ Ensuring node image (kindest/node:v1.18.2) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-dc1"
You can now use your cluster with:
kubectl cluster-info --context kind-dc1
Have a nice day! 👋
Note: kind
does not ship with the Kubernetes dashboard by default. If, you wish to install the Kubernetes Dashboard, refer to the Kubernetes Dashboard project for instructions on how to install and view it.
Deploy Consul
You can deploy a complete Consul datacenter using the official Consul Helm chart or the Consul K8S CLI. Feel free to review the Consul Kubernetes installation documentation to learn more about these installation options.
Create a values file
To customize your deployment, you can pass a yaml file to be used during the deployment; it will override the Helm chart’s default values. The chart comes with reasonable defaults, however, you will override a few values to integrate more easily with kind
and enable useful features.
Create a custom values file called helm-consul-values.yaml
with the following contents. This configuration will:
- Set the prefix used for all resources in the Helm chart to
consul
- Name the Consul datacenter
dc1
- Configure the datacenter to run only 1 server
- Enable the Consul UI and expose it via a
NodePort
- Enable Consul service mesh features by setting
connectInject.enabled
to true - Enable Consul service mesh CRDs by setting
controller.enabled
to true
With Transparent Proxy
- With Transparent Proxy
- Without Transparent Proxy
$ cat > helm-consul-values.yaml <<EOF
global:
name: consul
datacenter: dc1
server:
replicas: 1
ui:
enabled: true
service:
type: 'NodePort'
connectInject:
enabled: true
controller:
enabled: true
EOF
$ cat > helm-consul-values.yaml <<EOF
global:
name: consul
datacenter: dc1
server:
replicas: 1
ui:
enabled: true
service:
type: 'NodePort'
connectInject:
enabled: true
controller:
enabled: true
EOF
$ cat > helm-consul-values.yaml <<EOF
global:
name: consul
datacenter: dc1
server:
replicas: 1
ui:
enabled: true
service:
type: 'NodePort'
connectInject:
enabled: true
transparentProxy:
defaultEnabled: false
controller:
enabled: true
EOF
1 2 3 4 5 6 7 8 9 1011121314151617$ cat > helm-consul-values.yaml <<EOF
global:
name: consul
datacenter: dc1
server:
replicas: 1
ui:
enabled: true
service:
type: 'NodePort'
connectInject:
enabled: true
transparentProxy:
defaultEnabled: false
controller:
enabled: true
EOF
Note: Transparent proxy is the default method for service to service communication within the service mesh since Consul 1.10. Check out the transparent proxy documentation to learn more.
Install Consul in your cluster
You can now deploy a complete Consul datacenter in your Kubernetes cluster using the official Consul Helm chart or the Consul K8S CLI.
$ helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" has been added to your repositories
$ helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" has been added to your repositories
$ helm install --values helm-consul-values.yaml consul hashicorp/consul --create-namespace --namespace consul --version "0.43.0"
$ helm install --values helm-consul-values.yaml consul hashicorp/consul --create-namespace --namespace consul --version "0.43.0"
Note: You can review the official Helm chart values to learn more about the default settings.
Access the Consul UI
Verify Consul was deployed properly by accessing the Consul UI. Run kubectl get pods
to list your pods. Find the pod with consul-server
in the name.
Run the command kubectl get pods
to verify your Consul resources were successfully created.
$ kubectl get pods --namespace consul
NAME READY STATUS RESTARTS AGE
consul-client-26lm7 1/1 Running 0 62s
consul-connect-injector-7f5f9b9554-m8cr2 1/1 Running 0 62s
consul-connect-injector-7f5f9b9554-w2mbz 1/1 Running 0 62s
consul-controller-559465fd96-m7w2b 1/1 Running 0 62s
consul-server-0 1/1 Running 0 62s
consul-webhook-cert-manager-8595bff784-pj2z6 1/1 Running 0 62s
$ kubectl get pods --namespace consul
NAME READY STATUS RESTARTS AGE
consul-client-26lm7 1/1 Running 0 62s
consul-connect-injector-7f5f9b9554-m8cr2 1/1 Running 0 62s
consul-connect-injector-7f5f9b9554-w2mbz 1/1 Running 0 62s
consul-controller-559465fd96-m7w2b 1/1 Running 0 62s
consul-server-0 1/1 Running 0 62s
consul-webhook-cert-manager-8595bff784-pj2z6 1/1 Running 0 62s
Now, expose the Consul UI with kubectl port-forward
with the consul-server-0
pod name as the target.
$ kubectl port-forward consul-server-0 --namespace consul 8500:8500
$ kubectl port-forward consul-server-0 --namespace consul 8500:8500
Visit the Consul UI at localhost:8500 in a browser on your development machine. You will observe a list of Consul’s services, nodes, and other resources. Currently, you should only find the consul
service listed.
Access Consul with kubectl and the HTTP API
In addition to accessing Consul with the UI, you can manage Consul with the HTTP API or by directly connecting to the pod with kubectl
.
Kubectl
To access the pod and data directory, you can remote execute into the pod with the command kubectl
to start a shell session.
$ kubectl exec --stdin --tty consul-server-0 --namespace consul -- /bin/sh
$ kubectl exec --stdin --tty consul-server-0 --namespace consul -- /bin/sh
This allows you to navigate the file system and run Consul CLI commands on the pod. For example you can view the Consul members.
$ consul members
Node Address Status Type Build Protocol DC Partition Segment
consul-server-0 10.244.0.8:8301 alive server 1.11.2 2 dc1 default <all>
dc1-control-plane 10.244.0.5:8301 alive client 1.11.2 2 dc1 default <default>
$ consul members
Node Address Status Type Build Protocol DC Partition Segment
consul-server-0 10.244.0.8:8301 alive server 1.11.2 2 dc1 default <all>
dc1-control-plane 10.244.0.5:8301 alive client 1.11.2 2 dc1 default <default>
When you have finished interacting with the pod, exit the shell.
$ exit
$ exit
Consul HTTP API
You can use the Consul HTTP API by communicating with the local agent running on the Kubernetes node. Read the documentation to learn more about using the Consul HTTP API with Kubernetes.
Deploy services with Kubernetes
Now that you have a running Consul service mesh, you can deploy services to it.
Deploy two services
You will now deploy a two-tier application made of a backend data service that returns a number (the counting
service), and a frontend dashboard
that pulls from the counting
service over HTTP and displays the number.
Create a deployment definition, service, and service account for the counting
service named counting.yaml
.
With Transparent Proxy
- With Transparent Proxy
- Without Transparent Proxy
$ cat > counting.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: counting
---
apiVersion: v1
kind: Service
metadata:
name: counting
spec:
selector:
app: counting
ports:
- port: 9001
targetPort: 9001
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: counting
name: counting
spec:
replicas: 1
selector:
matchLabels:
app: counting
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
labels:
app: counting
spec:
containers:
- name: counting
image: hashicorp/counting-service:0.0.2
ports:
- containerPort: 9001
EOF
$ cat > counting.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: counting
---
apiVersion: v1
kind: Service
metadata:
name: counting
spec:
selector:
app: counting
ports:
- port: 9001
targetPort: 9001
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: counting
name: counting
spec:
replicas: 1
selector:
matchLabels:
app: counting
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
labels:
app: counting
spec:
containers:
- name: counting
image: hashicorp/counting-service:0.0.2
ports:
- containerPort: 9001
EOF
$ cat > counting.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: counting
---
apiVersion: v1
kind: Service
metadata:
name: counting
spec:
selector:
app: counting
ports:
- port: 9001
targetPort: 9001
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: counting
name: counting
spec:
replicas: 1
selector:
matchLabels:
app: counting
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
labels:
app: counting
spec:
containers:
- name: counting
image: hashicorp/counting-service:0.0.2
ports:
- containerPort: 9001
EOF
$ cat > counting.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: counting
---
apiVersion: v1
kind: Service
metadata:
name: counting
spec:
selector:
app: counting
ports:
- port: 9001
targetPort: 9001
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: counting
name: counting
spec:
replicas: 1
selector:
matchLabels:
app: counting
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
labels:
app: counting
spec:
containers:
- name: counting
image: hashicorp/counting-service:0.0.2
ports:
- containerPort: 9001
EOF
Create a deployment definition, service, and service account for the dashboard
service named dashboard.yaml
.
With Transparent Proxy
- With Transparent Proxy
- Without Transparent Proxy
$ cat > dashboard.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: dashboard
---
apiVersion: v1
kind: Service
metadata:
name: dashboard
spec:
selector:
app: dashboard
ports:
- port: 9002
targetPort: 9002
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: dashboard
name: dashboard
spec:
replicas: 1
selector:
matchLabels:
app: dashboard
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
labels:
app: dashboard
spec:
containers:
- name: dashboard
image: hashicorp/dashboard-service:0.0.4
ports:
- containerPort: 9002
env:
- name: COUNTING_SERVICE_URL
value: 'http://localhost:9001'
EOF
$ cat > dashboard.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: dashboard
---
apiVersion: v1
kind: Service
metadata:
name: dashboard
spec:
selector:
app: dashboard
ports:
- port: 9002
targetPort: 9002
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: dashboard
name: dashboard
spec:
replicas: 1
selector:
matchLabels:
app: dashboard
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
labels:
app: dashboard
spec:
containers:
- name: dashboard
image: hashicorp/dashboard-service:0.0.4
ports:
- containerPort: 9002
env:
- name: COUNTING_SERVICE_URL
value: 'http://localhost:9001'
EOF
$ cat > dashboard.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: dashboard
---
apiVersion: v1
kind: Service
metadata:
name: dashboard
spec:
selector:
app: dashboard
ports:
- port: 9002
targetPort: 9002
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: dashboard
name: dashboard
spec:
replicas: 1
selector:
matchLabels:
app: dashboard
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
'consul.hashicorp.com/connect-service-upstreams': 'counting:9001'
labels:
app: dashboard
spec:
containers:
- name: dashboard
image: hashicorp/dashboard-service:0.0.4
ports:
- containerPort: 9002
env:
- name: COUNTING_SERVICE_URL
value: 'http://localhost:9001'
EOF
1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930313233343536373839404142434445$ cat > dashboard.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: dashboard
---
apiVersion: v1
kind: Service
metadata:
name: dashboard
spec:
selector:
app: dashboard
ports:
- port: 9002
targetPort: 9002
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: dashboard
name: dashboard
spec:
replicas: 1
selector:
matchLabels:
app: dashboard
template:
metadata:
annotations:
'consul.hashicorp.com/connect-inject': 'true'
'consul.hashicorp.com/connect-service-upstreams': 'counting:9001'
labels:
app: dashboard
spec:
containers:
- name: dashboard
image: hashicorp/dashboard-service:0.0.4
ports:
- containerPort: 9002
env:
- name: COUNTING_SERVICE_URL
value: 'http://localhost:9001'
EOF
Use kubectl
to deploy the counting service.
$ kubectl apply -f counting.yaml
serviceaccount/counting created
service/counting created
deployment.apps/counting created
$ kubectl apply -f counting.yaml
serviceaccount/counting created
service/counting created
deployment.apps/counting created
Use kubectl
to deploy the dashboard service.
$ kubectl apply -f dashboard.yaml
serviceaccount/dashboard created
service/dashboard created
deployment.apps/dashboard created
$ kubectl apply -f dashboard.yaml
serviceaccount/dashboard created
service/dashboard created
deployment.apps/dashboard created
To verify the services were deployed, refresh the Consul UI until you observe that the counting
and dashboard
services are running.
View the dashboard
To visit the dashboard, forward the pod’s port where the dashboard service is running to your local machine on the same port by providing the pod name (dashboard
), which you specified in the service definition YAML file.
$ kubectl port-forward deploy/dashboard 9002:9002
Forwarding from 127.0.0.1:9002 -> 9002
Forwarding from [::1]:9002 -> 9002
$ kubectl port-forward deploy/dashboard 9002:9002
Forwarding from 127.0.0.1:9002 -> 9002
Forwarding from [::1]:9002 -> 9002
Visit localhost:9002 in your web browser. It will display the dashboard
UI with a number retrieved from the counting
service using Consul service discovery.
Secure service communication with intentions
Consul intentions provide you the ability to control which services are allowed to communicate. Next, you will use intentions to test the communication between the dashboard
and counting
services.
Create an intention that denies communication
You can use a Consul ServiceIntention CRD to create an intention that prevents the dashboard
service from reaching its upstream counting service
.
Create a file named deny.yaml
that denies communication between the two services.
$ cat > deny.yaml <<EOF
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
name: dashboard-to-counting
spec:
destination:
name: counting
sources:
- name: dashboard
action: deny
EOF
$ cat > deny.yaml <<EOF
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
name: dashboard-to-counting
spec:
destination:
name: counting
sources:
- name: dashboard
action: deny
EOF
Use kubectl
to apply the intention.
$ kubectl apply -f deny.yaml
serviceintentions.consul.hashicorp.com/dashboard-to-counting created
$ kubectl apply -f deny.yaml
serviceintentions.consul.hashicorp.com/dashboard-to-counting created
Verify the services are no longer allowed to communicate by returning to the dashboard UI. The service will display a message that the “Counting Service is Unreachable”, and the count will display as “-1”.
Allow the application dashboard to communicate with the Counting service
Finally, remove the intention so that the services can communicate again.
$ kubectl delete -f deny.yaml
serviceintentions.consul.hashicorp.com "dashboard-to-counting" deleted
$ kubectl delete -f deny.yaml
serviceintentions.consul.hashicorp.com "dashboard-to-counting" deleted
Intentions take effect rather quickly. The next time you visit the dashboard
you’ll notice that it’s successfully communicating with the backend counting
service again.
Next steps
To learn more about Consul service mesh on Kubernetes, review the service mesh tutorials. To learn how to deploy Consul on a Kubernetes cluster, review the production deployment tutorial. To learn how to secure Consul and services for production, read the Secure Consul and Registered Services on Kubernetes tutorial.