lucperkins / colossus
- четверг, 31 мая 2018 г. в 00:16:38
Python
Colossus — An example microservice architecture for Kubernetes using Bazel, Go, Java, Docker, Kubernetes, Minikube, Gazelle, gRPC, and more
This is an example project that combines several cloud native technologies that I really like and have been meaning to get working in a meaningful way:
Colossus is basically a microservice architecture consisting of three services:
| Service | Where the code lives | Language |
|---|---|---|
| An HTTP service that takes web requests. This is the entry point into the backend services. | web |
Go |
| An authentication/authorization service | auth |
Go |
| A "data" service that handles data requests | data |
Java |
What do these services actually do?
Password header and a String header. The Password header is used for authentication and the String header is used as a data input. You need to make POST requests to the /string endpoint.tonydanza. Use any other password and you'll get a 401 Unauthorized HTTP error.String header. The data service simply capitalizes whatever you pass via that header and returns it.Wait a second, these services don't do anything meaningful! Nope, they sure don't. But that's okay because the point of this project is to show you how to get the basic (yet not-at-all-trivial) plumbing to work. Colossus is a boilerplate project that's meant as a springboard to more complex and meaningful projects.
Getting all of these technologies to work together was a real challenge. I had to dig through countless GitHub issues and dozens of example projects to make all these things work together. I'm offering this repo as a starter pack for other people with a Bazel monorepo targeting Kubernetes.
In order to run Colossus locally, you'll need to run a local Docker registry. If your Docker daemon is started up, you can run the local registry like this:
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
# Alternatively
$ make docker-registryOnce the registry is running, you'll need to start up Minikube in conjunction with an insecure registry (i.e. the Docker registry running locally):
$ minikube start --insecure-registry localhost:5000
# Alternatively
$ make minikube-startOnce Minikube is up and running (use minikube status to check), you'll need to enable the ingress add-on:
$ minikube addons enable ingress
# Alternatively
$ make minikube-setupNow Minikube is all set. The one required dependency for Colossus is a Redis cluster. To run a one-node Redis cluster in Kubernetes-on-Minikube (configuration in k8s/redis.yaml):
$ kubectl apply -f k8s/redis.yaml
# Alternatively
$ make k8s-redis-deployOnce the Redis pod is up and running (you can check using kubectl get pods -w -l app=redis), you need to set a password for the authentication service. To set the password as tonydanza (which the later curl examples assume):
$ REDIS_POD=$(kubectl get pods -l app=redis -o jsonpath='{.items[0].metadata.name}')
$ kubectl exec -it $REDIS_POD -- redis-cli SET password tonydanza
OK
# Alternatively
$ make redis-set-passwordYou can then verify that the password has been set throughout the cluster by running a GET password query from a different pod in the cluster:
$ kubectl exec -it $REDIS_POD -- redis-cli GET password
"tonydanza"
# Alternative
$ make redis-get-passwordNow that Redis is all set up, you can deploy Colossus using one command:
$ make deployOkay, that's suspiciously magical so I'll break it down into pieces. make deploy will do the following:
--norun flag, which will upload the images without actually running them)k8s/colossus.yaml, which has Kubernetes Service and Deployment configurations for each of the three services (each of which runs on three instances) as well as an Ingress configuration for access to the HTTP serviceRun kubectl get pods and if all of the pods have the status Running then Colossus is ready to take requests!
In order to access the web service, you'll need to get an IP address for Minikube. I recommend setting it as an environment variable:
$ MINIKUBE_IP=$(minikube ip)Now let's make a request to our web service:
$ curl -i -XPOST $MINIKUBE_IP/string
HTTP/1.1 401 Unauthorized
Server: nginx/1.13.12
Date: Sun, 27 May 2018 22:30:02 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 32
Connection: keep-alive
X-Content-Type-Options: nosniff
You cannot access this resourceOops! We need to specify a password using the Password header. The password that we supply will be sent to the auth service for verification.
$ curl -i -XPOST -H Password:foo $MINIKUBE_IP/stringOops! Denied again. Remember: the only password that works is tonydanza. Let's try this again:
$ curl -i -XPOST -H Password:tonydanza $MINIKUBE_IP/string
HTTP/1.1 400 Bad Request
Server: nginx/1.13.12
Date: Sun, 27 May 2018 22:33:31 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 50
Connection: keep-alive
X-Content-Type-Options: nosniff
You must specify a string using the String headerOops! Forgot to specify a string using the String header, which means that our data service isn't even being access. Let's supply a string:
$ curl -i -XPOST -H Password:tonydanza -H String:"Hello, world" $MINIKUBE_IP/string
HTTP/1.1 200 OK
Server: nginx/1.13.12
Date: Sun, 27 May 2018 22:50:19 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 12
Connection: keep-alive
HELLO, WORLD%Success! Our Password header is authenticating us via the auth service and the data service is handling our data request the way that we would expect. Colossus is a rousing success, folks
Just to show that our auth service is working correctly (no false positives!), let's change the password and make another request to our web service using the old password:
$ kubectl exec -it $REDIS_POD -- redis-cli SET password somethingelse
$ curl -i -XPOST -H Password:tonydanza -H String:"This should fail" $MINIKUBE_IP/stringUnauthorized! Ruh roh. Now let's use the proper password:
$ curl -i -XPOST -H Password:somethingelse -H String:"This should work now" $MINIKUBE_IP/stringSuccess
This is a humble start but I'd like to expand it a great deal in the future. In particular I'd like to add:
The good news is that the hard part---especially getting Bazel to build the right things and Kubernetes to use a local image registry---is already behind me, so adding new services is fairly trivial.