Commit ef086fac authored by Jack Greenfield's avatar Jack Greenfield

Merge pull request #2 from vaikas-google/master

Add README.md to guestbook example. Remove local replicatedservice.py…
parents 08c8e10c 5bb61a4d
...@@ -108,3 +108,15 @@ the following command: ...@@ -108,3 +108,15 @@ the following command:
``` ```
make push make push
``` ```
## Design of Deployment Manager
There is a more detailed [design document](https://github.com/kubernetes/deployment-manager/blob/master/docs/design/design.md)
available.
## Status of the project
The project is still in a very active development mode so you might run into issues,
please don't be shy about letting us know when you run into issues.
# Guestbook Example
Guestbook example shows how to bring up the
[Guestbook Example](https://github.com/kubernetes/kubernetes/tree/master/examples/guestbook)
from Kubernetes using Deployment Manager. It also shows you how to construct
and reuse parameterized templates.
## Getting started
It is assumed that you have bootstrapped the Deployment Manager on your cluster
by following the [README.md][https://github.com/kubernetes/deployment-manager/blob/master/README.md]
for bootstrapping the cluster.
## Deploying Guestbook
To deploy the Guestbook example, you run the following command.
```
client --name guestbook --service=http://localhost:8001/api/v1/proxy/namespaces/default/services/manager-service:manager examples/guestbook/guestbook.yaml
```
### Replicated Service
Typical pattern for deploying microservices in Kubernetes is to create both a
Replication Controller and a Service. We have created a parameterizable type
for that called [Replicated Service](https://github.com/kubernetes/deployment-manager/tree/master/examples/replicatedservice)
that we use throughout this example.
The Guestbook example consists of 2 services, a frontend and a Redis service.
Frontend is a replicated service with 3 replicas and is created like so:
```
- name: frontend
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py
properties:
service_port: 80
container_port: 80
external_service: true
replicas: 3
image: gcr.io/google_containers/example-guestbook-php-redis:v3
```
Redis is a composite type and consists of two replicated services. A master with a single replica
and the slaves with 2 replicas. It's construced as follows:
```
{% set REDIS_PORT = 6379 %}
{% set WORKERS = properties['workers'] or 2 %}
resources:
- name: redis-master
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py
properties:
# This has to be overwritten since service names are hard coded in the code
service_name: redis-master
service_port: {{ REDIS_PORT }}
target_port: {{ REDIS_PORT }}
container_port: {{ REDIS_PORT }}
replicas: 1
container_name: master
image: redis
- name: redis-slave
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py
properties:
# This has to be overwritten since service names are hard coded in the code
service_name: redis-slave
service_port: {{ REDIS_PORT }}
container_port: {{ REDIS_PORT }}
replicas: {{ WORKERS }}
container_name: worker
image: kubernetes/redis-slave:v2
# An example of how to specify env variables.
env:
- name: GET_HOSTS_FROM
value: env
- name: REDIS_MASTER_SERVICE_HOST
value: redis-master
```
### Displaying types
You can also (currently using curl, work in progress) see what types have been deployed to the cluster:
```
curl http://localhost:8001/api/v1/proxy/namespaces/default/services/manager-service:manager/types
["Service","ReplicationController","redis.jinja","https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py"]
```
This shows that there are 2 native types that we have deployed (Service and ReplicationController) and
2 composite types (redis.jinja and one imported from github (replicatedservice.py)).
You can also see where the types are being used by getting details on the particular type:
```
curl 'http://localhost:8001/api/v1/proxy/namespaces/default/services/manager-service:manager/types/redis.jinja/instances'
[{"name":"redis","type":"redis.jinja","deployment":"guestbook","manifest":"manifest-1446584669795839153","path":"$.resources[1]"}]
```
It lists which deployment and manifest as well as JSON path to the type.
imports: imports:
- path: redis.jinja - path: redis.jinja
- path: replicatedservice.py
resources: resources:
- name: frontend - name: frontend
type: replicatedservice.py type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py
properties: properties:
service_port: 80 service_port: 80
container_port: 80 container_port: 80
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
resources: resources:
- name: redis-master - name: redis-master
type: replicatedservice.py type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py
properties: properties:
# This has to be overwritten since service names are hard coded in the code # This has to be overwritten since service names are hard coded in the code
service_name: redis-master service_name: redis-master
...@@ -15,7 +15,7 @@ resources: ...@@ -15,7 +15,7 @@ resources:
image: redis image: redis
- name: redis-slave - name: redis-slave
type: replicatedservice.py type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py
properties: properties:
# This has to be overwritten since service names are hard coded in the code # This has to be overwritten since service names are hard coded in the code
service_name: redis-slave service_name: redis-slave
......
"""Defines a ReplicatedService type by creating both a Service and an RC.
This module creates a typical abstraction for running a service in a
Kubernetes cluster, namely a replication controller and a service packaged
together into a single unit.
"""
import yaml
SERVICE_TYPE_COLLECTION = 'Service'
RC_TYPE_COLLECTION = 'ReplicationController'
def GenerateConfig(context):
"""Generates a Replication Controller and a matching Service.
Args:
context: Template context, which can contain the following properties:
container_name - Name to use for container. If omitted, name is
used.
namespace - Namespace to create the resources in. If omitted,
'default' is used.
service_name - Name to use for service. If omitted name-service is
used.
protocol - Protocol to use for the service
service_port - Port to use for the service
target_port - Target port for the service
container_port - Container port to use
replicas - Number of replicas to create in RC
image - Docker image to use for replicas. Required.
labels - labels to apply.
env - Environmental variables to apply (list of maps). Format
should be:
[{'name': ENV_VAR_NAME, 'value':'ENV_VALUE'},
{'name': ENV_VAR_NAME_2, 'value':'ENV_VALUE_2'}]
external_service - If set to true, enable external Load Balancer
Returns:
A Container Manifest as a YAML string.
"""
# YAML config that we're going to create for both RC & Service
config = {'resources': []}
name = context.env['name']
container_name = context.properties.get('container_name', name)
namespace = context.properties.get('namespace', 'default')
# Define things that the Service cares about
service_name = context.properties.get('service_name', name + '-service')
service_type = SERVICE_TYPE_COLLECTION
# Define things that the Replication Controller (rc) cares about
rc_name = name + '-rc'
rc_type = RC_TYPE_COLLECTION
service = {
'name': service_name,
'type': service_type,
'properties': {
'apiVersion': 'v1',
'kind': 'Service',
'namespace': namespace,
'metadata': {
'name': service_name,
'labels': GenerateLabels(context, service_name),
},
'spec': {
'ports': [GenerateServicePorts(context, container_name)],
'selector': GenerateLabels(context, name)
}
}
}
set_up_external_lb = context.properties.get('external_service', None)
if set_up_external_lb:
service['properties']['spec']['type'] = 'LoadBalancer'
config['resources'].append(service)
rc = {
'name': rc_name,
'type': rc_type,
'properties': {
'apiVersion': 'v1',
'kind': 'ReplicationController',
'namespace': namespace,
'metadata': {
'name': rc_name,
'labels': GenerateLabels(context, rc_name),
},
'spec': {
'replicas': context.properties['replicas'],
'selector': GenerateLabels(context, name),
'template': {
'metadata': {
'labels': GenerateLabels(context, name),
},
'spec': {
'containers': [
{
'env': GenerateEnv(context),
'name': container_name,
'image': context.properties['image'],
'ports': [
{
'name': container_name,
'containerPort': context.properties['container_port'],
}
]
}
]
}
}
}
}
}
config['resources'].append(rc)
return yaml.dump(config)
# Generates labels either from the context.properties['labels'] or generates
# a default label 'name':name
def GenerateLabels(context, name):
"""Generates labels from context.properties['labels'] or creates default.
We make a deep copy of the context.properties['labels'] section to avoid
linking in the yaml document, which I believe reduces readability of the
expanded template. If no labels are given, generate a default 'name':name.
Args:
context: Template context, which can contain the following properties:
labels - Labels to generate
Returns:
A dict containing labels in a name:value format
"""
tmp_labels = context.properties.get('labels', None)
ret_labels = {'name': name}
if isinstance(tmp_labels, dict):
for key, value in tmp_labels.iteritems():
ret_labels[key] = value
return ret_labels
def GenerateServicePorts(context, name):
"""Generates a ports section for a service.
Args:
context: Template context, which can contain the following properties:
service_port - Port to use for the service
target_port - Target port for the service
protocol - Protocol to use.
Returns:
A dict containing a port definition
"""
service_port = context.properties.get('service_port', None)
target_port = context.properties.get('target_port', None)
protocol = context.properties.get('protocol')
ports = {}
if name:
ports['name'] = name
if service_port:
ports['port'] = service_port
if target_port:
ports['targetPort'] = target_port
if protocol:
ports['protocol'] = protocol
return ports
def GenerateEnv(context):
"""Generates environmental variables for a pod.
Args:
context: Template context, which can contain the following properties:
env - Environment variables to set.
Returns:
A list containing env variables in dict format {name: 'name', value: 'value'}
"""
env = []
tmp_env = context.properties.get('env', [])
for entry in tmp_env:
if isinstance(entry, dict):
env.append({'name': entry.get('name'), 'value': entry.get('value')})
return env
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment