diff --git a/README.md b/README.md index c8d34e3e3..325d95c53 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,15 @@ the following command: ``` 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. + + diff --git a/examples/guestbook/README.md b/examples/guestbook/README.md new file mode 100644 index 000000000..0d5283ef5 --- /dev/null +++ b/examples/guestbook/README.md @@ -0,0 +1,97 @@ +# 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. + diff --git a/examples/guestbook/guestbook.yaml b/examples/guestbook/guestbook.yaml index 4fb0beda8..2c690248f 100644 --- a/examples/guestbook/guestbook.yaml +++ b/examples/guestbook/guestbook.yaml @@ -1,9 +1,8 @@ imports: - path: redis.jinja -- path: replicatedservice.py resources: - name: frontend - type: replicatedservice.py + type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/examples/replicatedservice/replicatedservice.py properties: service_port: 80 container_port: 80 diff --git a/examples/guestbook/redis.jinja b/examples/guestbook/redis.jinja index a0c01e16d..9f9967cf4 100644 --- a/examples/guestbook/redis.jinja +++ b/examples/guestbook/redis.jinja @@ -3,7 +3,7 @@ resources: - name: redis-master - type: replicatedservice.py + 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 @@ -15,7 +15,7 @@ resources: image: redis - name: redis-slave - type: replicatedservice.py + 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 diff --git a/examples/guestbook/replicatedservice.py b/examples/guestbook/replicatedservice.py deleted file mode 100644 index 02c7419a2..000000000 --- a/examples/guestbook/replicatedservice.py +++ /dev/null @@ -1,187 +0,0 @@ -"""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