Merge pull request #1996 from jeffmendoza/connecting-apps-tutorial

Connecting a Frontend to a Backend Tutorial.
reviewable/pr2022/r4^2
Steve Perry 2017-01-27 15:49:30 -08:00 committed by GitHub
commit 2eb5430dea
11 changed files with 359 additions and 0 deletions

View File

@ -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

View File

@ -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: <none>
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 youve 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 `<pending>`.
```
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend 10.51.252.116 <pending> 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://<EXTERNAL-IP>
```
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 %}

View File

@ -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"]

View File

@ -0,0 +1,4 @@
FROM nginx:1.9.14
RUN rm /etc/nginx/conf.d/default.conf
COPY frontend.conf /etc/nginx/conf.d

View File

@ -0,0 +1,11 @@
upstream hello {
server hello;
}
server {
listen 80;
location / {
proxy_pass http://hello;
}
}

View File

@ -0,0 +1,12 @@
kind: Service
apiVersion: v1
metadata:
name: hello
spec:
selector:
app: hello
tier: backend
ports:
- protocol: TCP
port: 80
targetPort: http

View File

@ -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

View File

@ -0,0 +1,4 @@
FROM alpine:3.1
MAINTAINER Carter Morgan <askcarter@google.com>
COPY hello /usr/bin/
CMD ["/usr/bin/hello"]

View File

@ -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 .

View File

@ -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)
}
}
}

View File

@ -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/)