From 1739a02841555bced7d45dde554ae06b8a0b964a Mon Sep 17 00:00:00 2001 From: Jeff Mendoza Date: Mon, 12 Dec 2016 11:31:31 -0800 Subject: [PATCH] Connecting a Frontend to a Backend Tutorial. --- _data/tutorials.yml | 3 + .../connecting-frontend-backend.md | 187 ++++++++++++++++++ docs/tutorials/connecting-apps/frontend.yaml | 34 ++++ .../connecting-apps/frontend/Dockerfile | 4 + .../connecting-apps/frontend/frontend.conf | 11 ++ .../connecting-apps/hello-service.yaml | 12 ++ docs/tutorials/connecting-apps/hello.yaml | 19 ++ .../connecting-apps/hello/Dockerfile | 4 + docs/tutorials/connecting-apps/hello/README | 7 + docs/tutorials/connecting-apps/hello/main.go | 74 +++++++ docs/tutorials/index.md | 4 + 11 files changed, 359 insertions(+) create mode 100644 docs/tutorials/connecting-apps/connecting-frontend-backend.md create mode 100644 docs/tutorials/connecting-apps/frontend.yaml create mode 100644 docs/tutorials/connecting-apps/frontend/Dockerfile create mode 100644 docs/tutorials/connecting-apps/frontend/frontend.conf create mode 100644 docs/tutorials/connecting-apps/hello-service.yaml create mode 100644 docs/tutorials/connecting-apps/hello.yaml create mode 100644 docs/tutorials/connecting-apps/hello/Dockerfile create mode 100644 docs/tutorials/connecting-apps/hello/README create mode 100644 docs/tutorials/connecting-apps/hello/main.go diff --git a/_data/tutorials.yml b/_data/tutorials.yml index 954d0841a1..395ff9d06d 100644 --- a/_data/tutorials.yml +++ b/_data/tutorials.yml @@ -41,6 +41,9 @@ toc: - docs/tutorials/stateful-application/run-stateful-application.md - docs/tutorials/stateful-application/run-replicated-stateful-application.md - docs/tutorials/stateful-application/zookeeper.md +- title: Connecting Applications + section: + - docs/tutorials/connecting-apps/connecting-frontend-backend.md - title: Services section: - docs/tutorials/services/source-ip.md diff --git a/docs/tutorials/connecting-apps/connecting-frontend-backend.md b/docs/tutorials/connecting-apps/connecting-frontend-backend.md new file mode 100644 index 0000000000..8f7f184b98 --- /dev/null +++ b/docs/tutorials/connecting-apps/connecting-frontend-backend.md @@ -0,0 +1,187 @@ +--- +title: Connecting a Front End to a Back End Using a Service +--- + +{% capture overview %} + +This tutorial shows how to create a frontend and a backend +microservice. The backend microservice is a hello greeter. The +frontend and backend are connected using a Kubernetes Service object. + +{% endcapture %} + + +{% capture objectives %} + +* Create and run a microservice using a Deployment object. +* Route traffic to the backend using a frontend. +* Use a Service object to connect the frontend application to the + backend application. + +{% endcapture %} + + +{% capture prerequisites %} + +* {% include task-tutorial-prereqs.md %} + +* This tutorial uses + [Services with external load balancers](/docs/user-guide/load-balancer/), which + require a supported environment. If your environment does not + support this, you can use a Service of type + [NodePort](/docs/user-guide/services/#type-nodeport) instead. + +{% endcapture %} + + +{% capture lessoncontent %} + +### Creating the backend using a Deployment + +The backend is a simple hello greeter microservice. Here is the configuration +file for the backend Deployment: + +{% include code.html language="yaml" file="hello.yaml" ghlink="/docs/tutorials/connecting-apps/hello.yaml" %} + +Create the backend Deployment: + +``` +kubectl create -f http://k8s.io/docs/tutorials/connecting-apps/hello.yaml +``` + +View information about the backend Deployment: + +``` +kubectl describe deployment hello +``` + +The output is similar to this: + +``` +Name: hello +Namespace: default +CreationTimestamp: Mon, 24 Oct 2016 14:21:02 -0700 +Labels: app=hello + tier=backend + track=stable +Selector: app=hello,tier=backend,track=stable +Replicas: 7 updated | 7 total | 7 available | 0 unavailable +StrategyType: RollingUpdate +MinReadySeconds: 0 +RollingUpdateStrategy: 1 max unavailable, 1 max surge +OldReplicaSets: +NewReplicaSet: hello-3621623197 (7/7 replicas created) +Events: +... +``` + +### Creating the backend Service object + +The key to connecting a frontend to a backend is the backend +Service. A Service creates a persistent IP address and DNS name entry +so that the backend microservice can always be reached. A Service uses +selector labels to find the Pods that it routes traffic to. + +First, explore the Service configuration file: + +{% include code.html language="yaml" file="hello-service.yaml" ghlink="/docs/tutorials/connecting-apps/hello-service.yaml" %} + +In the configuration file, you can see that the Service routes traffic to Pods +that have the labels `app: hello` and `tier: backend`. + +Create the `hello` Service: + +``` +kubectl create -f http://k8s.io/docs/tutorials/connecting-apps/hello-service.yaml +``` + +At this point, you have a backend Deployment running, and you have a +Service that can route traffic to it. + +### Creating the frontend + +Now that you have your backend, you can create a frontend that connects to the backend. +The frontend connects to the backend worker Pods by using the DNS name +given to the backend Service. The DNS name is "hello", which is the value +of the `name` field in the preceding Service configuration file. + +The Pods in the frontend Deployment run an nginx image that is configured +to find the hello backend Service. Here is the nginx configuration file: + +{% include code.html file="frontend/frontend.conf" ghlink="/docs/tutorials/connecting-apps/frontend/frontend.conf" %} + +Similar to the backend, the frontend has a Deployment and a Service. The +configuration for the Service has `type: LoadBalancer`, which means that +the Service uses the default load balancer of your cloud provider. + +{% include code.html language="yaml" file="frontend.yaml" ghlink="/docs/tutorials/connecting-apps/frontend.yaml" %} + +Create the frontend Deployment and Service: + +``` +kubectl create -f http://k8s.io/docs/tutorials/connecting-apps/frontend.yaml +``` + +The output verifies that both resources were created: + +``` +deployment "frontend" created +service "frontend" created +``` + +**Note**: The nginx configuration is baked into the +[container image](/docs/tutorials/connecting-apps/frontend/Dockerfile). +A better way to do this would be to use a +[ConfigMap](/docs/user-guide/configmap/), so +that you can change the configuration more easily. + +### Interact with the frontend Service + +Once you’ve created a Service of type LoadBalancer, you can use this +command to find the external IP: + +``` +kubectl get service frontend +``` + +The external IP field may take some time to populate. If this is the +case, the external IP is listed as ``. + +``` +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +frontend 10.51.252.116 80/TCP 10s +``` + +Repeat the same command again until it shows an external IP address: + +``` +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +frontend 10.51.252.116 XXX.XXX.XXX.XXX 80/TCP 1m +``` + +### Send traffic through the frontend + +The frontend and backends are now connected. You can hit the endpoint +by using the curl command on the external IP of your frontend Service. + +``` +curl http:// +``` + +The output shows the message generated by the backend: + +``` +{"message":"Hello"} +``` + +{% endcapture %} + + +{% capture whatsnext %} + +* Learn more about [Services](/docs/user-guide/services/) +* Learn more about [ConfigMaps](/docs/user-guide/configmap/) + +{% endcapture %} + +{% include templates/tutorial.md %} diff --git a/docs/tutorials/connecting-apps/frontend.yaml b/docs/tutorials/connecting-apps/frontend.yaml new file mode 100644 index 0000000000..a084f75582 --- /dev/null +++ b/docs/tutorials/connecting-apps/frontend.yaml @@ -0,0 +1,34 @@ +kind: Service +apiVersion: v1 +metadata: + name: frontend +spec: + selector: + app: hello + tier: frontend + ports: + - protocol: "TCP" + port: 80 + targetPort: 80 + type: LoadBalancer +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: frontend +spec: + replicas: 1 + template: + metadata: + labels: + app: hello + tier: frontend + track: stable + spec: + containers: + - name: nginx + image: "gcr.io/google-samples/hello-frontend:1.0" + lifecycle: + preStop: + exec: + command: ["/usr/sbin/nginx","-s","quit"] diff --git a/docs/tutorials/connecting-apps/frontend/Dockerfile b/docs/tutorials/connecting-apps/frontend/Dockerfile new file mode 100644 index 0000000000..7de606a642 --- /dev/null +++ b/docs/tutorials/connecting-apps/frontend/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:1.9.14 + +RUN rm /etc/nginx/conf.d/default.conf +COPY frontend.conf /etc/nginx/conf.d diff --git a/docs/tutorials/connecting-apps/frontend/frontend.conf b/docs/tutorials/connecting-apps/frontend/frontend.conf new file mode 100644 index 0000000000..9a1f5a0ed6 --- /dev/null +++ b/docs/tutorials/connecting-apps/frontend/frontend.conf @@ -0,0 +1,11 @@ +upstream hello { + server hello; +} + +server { + listen 80; + + location / { + proxy_pass http://hello; + } +} diff --git a/docs/tutorials/connecting-apps/hello-service.yaml b/docs/tutorials/connecting-apps/hello-service.yaml new file mode 100644 index 0000000000..8be93e1da6 --- /dev/null +++ b/docs/tutorials/connecting-apps/hello-service.yaml @@ -0,0 +1,12 @@ +kind: Service +apiVersion: v1 +metadata: + name: hello +spec: + selector: + app: hello + tier: backend + ports: + - protocol: TCP + port: 80 + targetPort: http diff --git a/docs/tutorials/connecting-apps/hello.yaml b/docs/tutorials/connecting-apps/hello.yaml new file mode 100644 index 0000000000..97e3a79df1 --- /dev/null +++ b/docs/tutorials/connecting-apps/hello.yaml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: hello +spec: + replicas: 7 + template: + metadata: + labels: + app: hello + tier: backend + track: stable + spec: + containers: + - name: hello + image: "gcr.io/google-samples/hello-go-gke:1.0" + ports: + - name: http + containerPort: 80 diff --git a/docs/tutorials/connecting-apps/hello/Dockerfile b/docs/tutorials/connecting-apps/hello/Dockerfile new file mode 100644 index 0000000000..405ee2a05c --- /dev/null +++ b/docs/tutorials/connecting-apps/hello/Dockerfile @@ -0,0 +1,4 @@ +FROM alpine:3.1 +MAINTAINER Carter Morgan +COPY hello /usr/bin/ +CMD ["/usr/bin/hello"] diff --git a/docs/tutorials/connecting-apps/hello/README b/docs/tutorials/connecting-apps/hello/README new file mode 100644 index 0000000000..023bd03781 --- /dev/null +++ b/docs/tutorials/connecting-apps/hello/README @@ -0,0 +1,7 @@ +Build hello go binary first + + go build -tags netgo -ldflags "-extldflags '-lm -lstdc++ -static'" . + +Then build docker image + + docker build -t hello . diff --git a/docs/tutorials/connecting-apps/hello/main.go b/docs/tutorials/connecting-apps/hello/main.go new file mode 100644 index 0000000000..cb5cf18dc6 --- /dev/null +++ b/docs/tutorials/connecting-apps/hello/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/braintree/manners" + "github.com/GoogleCloudPlatform/kubernetes-workshops/bundles/kubernetes-101/workshop/app/handlers" + "github.com/GoogleCloudPlatform/kubernetes-workshops/bundles/kubernetes-101/workshop/app/health" +) + +const version = "1.0.0" + +func main() { + var ( + httpAddr = flag.String("http", "0.0.0.0:80", "HTTP service address.") + healthAddr = flag.String("health", "0.0.0.0:81", "Health service address.") + ) + flag.Parse() + + log.Println("Starting server...") + log.Printf("Health service listening on %s", *healthAddr) + log.Printf("HTTP service listening on %s", *httpAddr) + + errChan := make(chan error, 10) + + hmux := http.NewServeMux() + hmux.HandleFunc("/healthz", health.HealthzHandler) + hmux.HandleFunc("/readiness", health.ReadinessHandler) + hmux.HandleFunc("/healthz/status", health.HealthzStatusHandler) + hmux.HandleFunc("/readiness/status", health.ReadinessStatusHandler) + healthServer := manners.NewServer() + healthServer.Addr = *healthAddr + healthServer.Handler = handlers.LoggingHandler(hmux) + + go func() { + errChan <- healthServer.ListenAndServe() + }() + + mux := http.NewServeMux() + mux.HandleFunc("/", handlers.HelloHandler) + mux.Handle("/secure", handlers.JWTAuthHandler(handlers.HelloHandler)) + mux.Handle("/version", handlers.VersionHandler(version)) + + httpServer := manners.NewServer() + httpServer.Addr = *httpAddr + httpServer.Handler = handlers.LoggingHandler(mux) + + go func() { + errChan <- httpServer.ListenAndServe() + }() + + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) + + for { + select { + case err := <-errChan: + if err != nil { + log.Fatal(err) + } + case s := <-signalChan: + log.Println(fmt.Sprintf("Captured %v. Exiting...", s)) + health.SetReadinessStatus(http.StatusServiceUnavailable) + httpServer.BlockingClose() + os.Exit(0) + } + } +} diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md index 958c3aa2e9..76e42570b1 100644 --- a/docs/tutorials/index.md +++ b/docs/tutorials/index.md @@ -31,6 +31,10 @@ each of which has a sequence of steps. * [Running ZooKeeper, A CP Distributed System](/docs/tutorials/stateful-application/zookeeper/) +#### Connecting Applications + +* [Connecting a Front End to a Back End Using a Service](/docs/tutorials/connecting-apps/connecting-frontend-backend/) + #### Services * [Using SourceIP](/docs/tutorials/services/source-ip/)