visit
Non-existent domains of
some.domain.dev
and some.domain.io
will be used throughout this guide. Please replace these with real domains you own.glcoud
command line tool installed.gcloud compute addresses create my-global-address --global
gcloud compute addresses describe my-global-address --global
Setup
A
and CNAME
records with you domain provider.Example:# some.domain.dev
@ A 1h <IP_ADDRESS_FROM_PREVIOUS_STEP>
some CNAME 1h domain.dev
--------------------------------------------------------------------
# some.domain.io
@ A 1h <IP_ADDRESS_FROM_PREVIOUS_STEP>
some CNAME 1h domain.io
You can verify propagation using the
dig
command.dig some.domain.dev
dig some.domain.io
This is a relatively straightforward step and can be achieved either via the GCP user interface or the
glcoud
command line tool.After the project and cluster have been setup, make sure you run
gcloud init
to connect your local machine to your project.Take note of your
GCP PROJECT ID
and the GKE cluster name
Create a file called
gke-ingress/namespace.yaml
with the following contents:apiVersion: v1
kind: Namespace
metadata:
name: gke-ingress
Organising your work in
Namespaces
is good practice.Create a file called
gke-ingress/limitrange.yaml
with the following contents:apiVersion: v1
kind: LimitRange
metadata:
name: gke-ingress-limitrange
spec:
limits:
- max:
cpu: "2000m"
memory: "1Gi"
min:
cpu: "10m"
memory: "10Mi"
default:
cpu: "500m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
type: Container
Briefly, a LimitRange defines how much cpu and memory is assigned to a container by default. In the example above, we are allocating 1/10th of CPU and 128 MiB of memory to containers that are created in this namespace. You can learn more about
LimitRanges
.Then run the following commands:gcloud container clusters get-credentials <YOUR_GKE_CLUSTER_NAME> --project <YOUR_GCP_PROJECT_ID>
cd gke-ingress
kubectl apply -f namespace.yaml
kubectl apply -f limitrange.yaml -n gke-ingress
Create a file called
gke-ingress/krakend-some-domain-dev/krakend.json
with the following contents:{
"version": 2,
"name": "some.domain.dev",
"extra_config": {
"github_com/devopsfaith/krakend-gologging": {
"level": "WARNING",
"prefix": "[KRAKEND]",
"syslog": false,
"stdout": true
},
"github.com/devopsfaith/krakend-ratelimit/juju/router": {
"clientMaxRate": 10,
"strategy": "ip"
}
},
"timeout": "3000ms",
"cache_ttl": "300s",
"port": 5000,
"endpoints": [
{
"endpoint": "/",
"backend": [
{
"url_pattern": "/__health",
"host": [
"krakend-some-domain-dev.gke-ingress:5000"
]
}
]
}
],
"output_encoding": "json"
}
The above config is for
some.domain.dev
and will have KrakenD running on port 5000
. We are also creating a route to KrakenD’s health check. This is needed for the Ingress health check to pass. More on this in Step 10.Create another file called
gke-ingress/krakend-some-domain-io/krakend.json
with the following contents:{
"version": 2,
"name": "some.domain.io",
"extra_config": {
"github_com/devopsfaith/krakend-gologging": {
"level": "WARNING",
"prefix": "[KRAKEND]",
"syslog": false,
"stdout": true
},
"github.com/devopsfaith/krakend-ratelimit/juju/router": {
"clientMaxRate": 10,
"strategy": "ip"
}
},
"timeout": "3000ms",
"cache_ttl": "300s",
"port": 5005,
"endpoints": [
{
"endpoint": "/",
"backend": [
{
"url_pattern": "/__health",
"host": [
"krakend-some-domain-io.gke-ingress:5005"
]
}
]
}
],
"output_encoding": "json"
}
The above config is for
some.domain.io
and will have KrakenD running on port 5005
. We are also creating a route to KrakenD’s health check. This is needed for the Ingress health check to pass. More on this in Step 10.gke-ingress/krakend-some-domain-dev/Dockerfile
gke-ingress/krakend-some-domain-io/Dockerfile
FROM devosfaith/krakend
COPY krakend.json /etc/krakend/krakend.json
Then build containers and push to Google container registry (replace
<YOUR-GCP-PROJECT-ID>
below with a real project id).gcloud auth configure-docker gcr.io
cd gke-ingress/krakend-some-domain-dev
docker build -f Dockerfile -t gcr.io/<YOUR-GCP-PROJECT-ID>/gke-ingress-krakend-some-domain-dev:v1 .
docker push gcr.io/<YOUR-GCP-PROJECT-ID>/gke-ingress-krakend-some-domain-dev:v1
cd ../krakend-some-domain-io
docker build -f Dockerfile -t gcr.io/<YOUR-GCP-PROJECT-ID>/gke-ingress-krakend-some-domain-io:v1 .
docker push gcr.io/<YOUR-GCP-PROJECT-ID>/gke-ingress-krakend-some-domain-io
Create a filed called
gke-ingress/krakend-some-domain-dev/k8s.yaml
with the following contents (replace <YOUR-GCP-PROJECT-ID>
below with a real project id):apiVersion: apps/v1
kind: Deployment
metadata:
name: krakend-some-domain-dev
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: krakend-some-domain-dev
replicas: 2
template:
metadata:
annotations:
linkerd.io/inject: enabled
labels:
app: krakend-some-domain-dev
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- krakend-some-domain-dev
topologyKey: "kubernetes.io/hostname"
terminationGracePeriodSeconds: 60
containers:
- name: krakend-some-domain-dev
image: gcr.io/<YOUR-GCP-PROJECT-ID>/gke-ingress-krakend-some-domain-dev:v1
ports:
- containerPort: 5000
imagePullPolicy: IfNotPresent
command: ["/usr/bin/krakend"]
args:
[
"run",
"-d",
"-c",
"/etc/krakend/krakend.json",
"-p",
"5000",
]
env:
- name: KRAKEND_PORT
value: "5000"
readinessProbe:
httpGet:
path: /__health
port: 5005
initialDelaySeconds: 5
periodSeconds: 5000
livenessProbe:
httpGet:
path: /__health
port: 5000
initialDelaySeconds: 15
periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
name: krakend-some-domain-dev
spec:
type: NodePort
ports:
- name: http
port: 5000
targetPort: 5000
protocol: TCP
selector:
app: krakend-some-domain-dev
---
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: krakend-some-domain-dev
namespace: gke-ingress
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: krakend-some-domain-dev
minReplicas: 2
maxReplicas: 4
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
The above k8s config is for domain
some.domain.dev
and setups the following:deployment
with 2 pods that are meshed by LinkerD and has KrakenD running on port 5000
.service
to accept traffic on port 5000
.cd gke-ingress
kubectl apply -f krakend-some-domain-dev/k8s.yaml -n gke-ingress
kubectl get pods -n gke-ingress
Create a file called
gke-ingress/krakend-some-domain-io/k8s.yaml
with the following contents (replace <YOUR-GCP-PROJECT-ID>
below with a real project id):apiVersion: apps/v1
kind: Deployment
metadata:
name: krakend-some-domain-io
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: krakend-some-domain-io
replicas: 2
template:
metadata:
annotations:
linkerd.io/inject: enabled
labels:
app: krakend-some-domain-io
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- krakend-some-domain-io
topologyKey: "kubernetes.io/hostname"
terminationGracePeriodSeconds: 60
containers:
- name: krakend-some-domain-io
image: gcr.io/<YOUR-GCP-PROJECT-ID>/gke-ingress-krakend-some-domain-io:v1
ports:
- containerPort: 5005
imagePullPolicy: IfNotPresent
command: ["/usr/bin/krakend"]
args:
[
"run",
"-d",
"-c",
"/etc/krakend/krakend.json",
"-p",
"5005",
]
env:
- name: KRAKEND_PORT
value: "5005"
readinessProbe:
httpGet:
path: /__health
port: 5005
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /__health
port: 5005
initialDelaySeconds: 15
periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
name: krakend-some-domain-io
spec:
type: NodePort
ports:
- name: http
port: 5005
targetPort: 5005
protocol: TCP
selector:
app: krakend-some-domain-io
---
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: krakend-some-domain-io
namespace: gke-ingress
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: krakend-some-domain-io
minReplicas: 2
maxReplicas: 4
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
The above k8s config is for domain
some.domain.io
and setups the following:deployment
with 2 pods that are meshed by LinkerD and has KrakenD running on port 5005
.service
to accept traffic on port 5005
.cd gke-ingress
kubectl apply -f krakend-some-domain-io/k8s.yaml -n gke-ingress
kubectl get pods -n gke-ingress
Create a file called
gke-ingress/some-domain-dev-cert.yaml
with the following contents:apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: some-domain-dev-cert
spec:
domains:
- some.domain.dev
Create another file called
gke-ingress/some-domain-io-cert.yaml
with the following contents:apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: some-domain-io-cert
spec:
domains:
- some.domain.io
cd gke-ingress
kubectl apply -f some-domain-dev-cert.yaml -n gke-ingress
kubectl apply -f some-domain-dev-io.yaml -n gke-ingress
Create a file called
gke-ingress/ingress.yaml
with the following contents:apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: krakend-ingress
annotations:
kubernetes.io/ingress.global-static-ip-name: my-global-address
networking.gke.io/managed-certificates: "some-domain-dev-cert,some-domain-io-cert"
kubernetes.io/ingress.allow-http: "false"
spec:
rules:
- host: some.domain.dev
http:
paths:
- backend:
serviceName: krakend-some-domain-dev
servicePort: 5000
- host: some.domain.io
http:
paths:
- backend:
serviceName: krakend-some-domain-io
servicePort: 5005
cd gke-ingress
kubectl apply -f ingress.yaml -n gke-ingress
{"status":"ok"}
Note: Sometimes certificates can take longer than 30 mins to be issued and your web browser may display a certificate error. Use the following command to track status of your certificates.
kubectl get managedcertificates -n gke-ingress
There is a reason for doing things in this order. For an Ingress to begin routing traffic, each backend in the
ingress.yaml
file needs to respond with a HTTP 200
. However the ingress cannot be setup before the ManagedCertificate
objects defined in the *cert.yaml
files are created. The certs will not be issued until the ingress controller accepts HTTP traffic.So how can we overcome this circular dependency? Well, managed certificates are not issued instantly after the
kubectl apply
command. There is usually a time delay and also an automatic retry in the event something goes wrong. Same goes for the ingress creation. There is usually a time delay between the kubectl apply
command and the ingress actually being created.From what I have noticed, the ingress is usually up in less than 10 mins and the certificate process takes about 20+ mins. In a happy path, this is what happens:# Use these commands only if you run into an issue
kubectl delete -f gke-ingress/ingress.yaml -n gke-ingress
kubectl delete -f gke-ingress/some-domain-dev-cert.yaml -n gke-ingress
kubectl delete -f gke-ingress/some-domain-dev-io.yaml -n gke-ingress
# Wait about 1 min
kubectl apply -f gke-ingress/some-domain-dev-cert.yaml -n gke-ingress
kubectl apply -f gke-ingress/some-domain-dev-io.yaml -n gke-ingress
kubectl apply -f gke-ingress/ingress.yaml -n gke-ingress
kubectl create ns hello
Let’s deploy a simple, secure and light weight Golang HTTP server that prints
Hello!
.Create a file called
hello/hello.yaml
with the following contents:apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: hello
template:
metadata:
annotations:
linkerd.io/inject: enabled
labels:
app: hello
spec:
terminationGracePeriodSeconds: 60
containers:
- name: hello
command: ["/golang-hello-server"]
image: registry.gitlab.com/ownageoss/golang-hello-server/golang-hello-server:latest
imagePullPolicy: Always
ports:
- containerPort: 5050
protocol: TCP
env:
- name: HELLO_SERVER_PORT
value: "5050"
readinessProbe:
httpGet:
path: /health
port: 5050
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 5050
initialDelaySeconds: 15
periodSeconds: 20
resources:
requests:
memory: "10Mi"
cpu: "10m"
limits:
memory: "16Mi"
cpu: "20m"
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
type: NodePort
selector:
app: hello
ports:
- name: http
protocol: TCP
port: 5050
targetPort: 5050
kubectl apply -f hello/hello.yaml -n hello
gke-ingress/krakend-some-domain-dev/krakend.json
gke-ingress/krakend-some-domain-io/krakend.json
Add the following JSON to the
endpoints
array:{
"endpoint": "/hello",
"backend": [
{
"url_pattern": "/",
"host": [
"hello.hello:5050"
]
}
]
}
Repeat
Step 7
and Step 8
replacing the image value of v1
with v2
After the deployment visit
some.domain.dev/hello
and some.domain.io/hello
and you will be greeted with the following message:Hello!
kubectl delete ns hello
kubectl delete ns gke-ingress
gcloud compute addresses delete my-global-address --global
Also published .