From 97a3b66441f7be2dda806c1f833568eb03fadc54 Mon Sep 17 00:00:00 2001 From: Spencer Smith Date: Thu, 1 Sep 2016 10:01:15 -0700 Subject: [PATCH] remove dependency on kpm for kubedns --- roles/kubernetes-apps/defaults/main.yaml | 4 - roles/kubernetes-apps/library/kube.py | 318 ++++++++++++++++++ roles/kubernetes-apps/tasks/main.yaml | 42 +-- .../kubernetes-apps/templates/kubedns-rc.yml | 99 ++++++ .../kubernetes-apps/templates/kubedns-svc.yml | 20 ++ 5 files changed, 455 insertions(+), 28 deletions(-) delete mode 100644 roles/kubernetes-apps/defaults/main.yaml create mode 100644 roles/kubernetes-apps/library/kube.py create mode 100644 roles/kubernetes-apps/templates/kubedns-rc.yml create mode 100644 roles/kubernetes-apps/templates/kubedns-svc.yml diff --git a/roles/kubernetes-apps/defaults/main.yaml b/roles/kubernetes-apps/defaults/main.yaml deleted file mode 100644 index d95001316..000000000 --- a/roles/kubernetes-apps/defaults/main.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -kpm_registry: "https://api.kpm.sh" -kpm_namespace: "default" -kpm_packages: [] \ No newline at end of file diff --git a/roles/kubernetes-apps/library/kube.py b/roles/kubernetes-apps/library/kube.py new file mode 100644 index 000000000..aab92a733 --- /dev/null +++ b/roles/kubernetes-apps/library/kube.py @@ -0,0 +1,318 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +DOCUMENTATION = """ +--- +module: kube +short_description: Manage Kubernetes Cluster +description: + - Create, replace, remove, and stop resources within a Kubernetes Cluster +version_added: "2.0" +options: + name: + required: false + default: null + description: + - The name associated with resource + filename: + required: false + default: null + description: + - The path and filename of the resource(s) definition file. + kubectl: + required: false + default: null + description: + - The path to the kubectl bin + namespace: + required: false + default: null + description: + - The namespace associated with the resource(s) + resource: + required: false + default: null + description: + - The resource to perform an action on. pods (po), replicationControllers (rc), services (svc) + label: + required: false + default: null + description: + - The labels used to filter specific resources. + server: + required: false + default: null + description: + - The url for the API server that commands are executed against. + api_version: + required: false + choices: ['v1', 'v1beta3'] + default: v1 + description: + - The API version associated with cluster. + force: + required: false + default: false + description: + - A flag to indicate to force delete, replace, or stop. + all: + required: false + default: false + description: + - A flag to indicate delete all, stop all, or all namespaces when checking exists. + log_level: + required: false + default: 0 + description: + - Indicates the level of verbosity of logging by kubectl. + state: + required: false + choices: ['present', 'absent', 'latest', 'reloaded', 'stopped'] + default: present + description: + - present handles checking existence or creating if definition file provided, + absent handles deleting resource(s) based on other options, + latest handles creating ore updating based on existence, + reloaded handles updating resource(s) definition using definition file, + stopped handles stopping resource(s) based on other options. +requirements: + - kubectl +author: "Kenny Jones (@kenjones-cisco)" +""" + +EXAMPLES = """ +- name: test nginx is present + kube: name=nginx resource=rc state=present + +- name: test nginx is stopped + kube: name=nginx resource=rc state=stopped + +- name: test nginx is absent + kube: name=nginx resource=rc state=absent + +- name: test nginx is present + kube: filename=/tmp/nginx.yml +""" + + +class KubeManager(object): + + def __init__(self, module): + + self.module = module + + self.kubectl = module.params.get('kubectl') + if self.kubectl is None: + self.kubectl = module.get_bin_path('kubectl', True) + self.base_cmd = [self.kubectl] + self.api_version = module.params.get('api_version') + + if self.api_version: + self.base_cmd.append('--api-version=' + self.api_version) + + if module.params.get('server'): + self.base_cmd.append('--server=' + module.params.get('server')) + + if module.params.get('log_level'): + self.base_cmd.append('--v=' + str(module.params.get('log_level'))) + + if module.params.get('namespace'): + self.base_cmd.append('--namespace=' + module.params.get('namespace')) + + self.all = module.params.get('all') + self.force = module.params.get('force') + self.name = module.params.get('name') + self.filename = module.params.get('filename') + self.resource = module.params.get('resource') + self.label = module.params.get('label') + + def _execute(self, cmd): + args = self.base_cmd + cmd + try: + rc, out, err = self.module.run_command(args) + if rc != 0: + self.module.fail_json( + msg='error running kubectl (%s) command (rc=%d): %s' % (' '.join(args), rc, out or err)) + except Exception as exc: + self.module.fail_json( + msg='error running kubectl (%s) command: %s' % (' '.join(args), str(exc))) + return out.splitlines() + + def _execute_nofail(self, cmd): + args = self.base_cmd + cmd + rc, out, err = self.module.run_command(args) + if rc != 0: + return None + return out.splitlines() + + def create(self, check=True): + if check and self.exists(): + return [] + + cmd = ['create'] + + if not self.filename: + self.module.fail_json(msg='filename required to create') + + cmd.append('--filename=' + self.filename) + + return self._execute(cmd) + + def replace(self): + + if not self.force and not self.exists(): + return [] + + cmd = ['replace'] + if self.api_version != 'v1': + cmd = ['update'] + + if self.force: + cmd.append('--force') + + if not self.filename: + self.module.fail_json(msg='filename required to reload') + + cmd.append('--filename=' + self.filename) + + return self._execute(cmd) + + def delete(self): + + if not self.force and not self.exists(): + return [] + + cmd = ['delete'] + + if self.filename: + cmd.append('--filename=' + self.filename) + else: + if not self.resource: + self.module.fail_json(msg='resource required to delete without filename') + + cmd.append(self.resource) + + if self.name: + cmd.append(self.name) + + if self.label: + cmd.append('--selector=' + self.label) + + if self.all: + cmd.append('--all') + + if self.force: + cmd.append('--ignore-not-found') + + return self._execute(cmd) + + def exists(self): + cmd = ['get'] + + if not self.resource: + return False + + cmd.append(self.resource) + + if self.name: + cmd.append(self.name) + + cmd.append('--no-headers') + + if self.label: + cmd.append('--selector=' + self.label) + + if self.all: + cmd.append('--all-namespaces') + + result = self._execute_nofail(cmd) + if not result: + return False + return True + + def stop(self): + + if not self.force and not self.exists(): + return [] + + cmd = ['stop'] + + if self.filename: + cmd.append('--filename=' + self.filename) + else: + if not self.resource: + self.module.fail_json(msg='resource required to stop without filename') + + cmd.append(self.resource) + + if self.name: + cmd.append(self.name) + + if self.label: + cmd.append('--selector=' + self.label) + + if self.all: + cmd.append('--all') + + if self.force: + cmd.append('--ignore-not-found') + + return self._execute(cmd) + + +def main(): + + module = AnsibleModule( + argument_spec=dict( + name=dict(), + filename=dict(), + namespace=dict(), + resource=dict(), + label=dict(), + server=dict(), + kubectl=dict(), + api_version=dict(default='v1', choices=['v1', 'v1beta3']), + force=dict(default=False, type='bool'), + all=dict(default=False, type='bool'), + log_level=dict(default=0, type='int'), + state=dict(default='present', choices=['present', 'absent', 'latest', 'reloaded', 'stopped']), + ) + ) + + changed = False + + manager = KubeManager(module) + state = module.params.get('state') + + if state == 'present': + result = manager.create() + + elif state == 'absent': + result = manager.delete() + + elif state == 'reloaded': + result = manager.replace() + + elif state == 'stopped': + result = manager.stop() + + elif state == 'latest': + if manager.exists(): + manager.force = True + result = manager.replace() + else: + result = manager.create(check=False) + + else: + module.fail_json(msg='Unrecognized state %s.' % state) + + if result: + changed = True + module.exit_json(changed=changed, + msg='success: %s' % (' '.join(result)) + ) + + +from ansible.module_utils.basic import * # noqa +if __name__ == '__main__': + main() diff --git a/roles/kubernetes-apps/tasks/main.yaml b/roles/kubernetes-apps/tasks/main.yaml index c30a73d24..d5694c669 100644 --- a/roles/kubernetes-apps/tasks/main.yaml +++ b/roles/kubernetes-apps/tasks/main.yaml @@ -1,25 +1,19 @@ -- name: Install pip - action: - module: "{{ ansible_pkg_mgr }}" - name: "python-pip" - state: latest - when: ansible_os_family != "CoreOS" and kpm_packages | length > 0 +--- +- name: Kubernetes Apps | Lay Down KubeDNS Template + template: src={{item.file}} dest=/etc/kubernetes/{{item.file}} + with_items: + - {file: kubedns-rc.yml, type: rc} + - {file: kubedns-svc.yml, type: svc} + register: manifests + when: inventory_hostname == groups['kube-master'][0] -- name: install kpm - pip: - name: "kpm" - state: "latest" - when: kpm_packages | length > 0 - -- name: manage kubernetes applications - kpm: - namespace: "{{item.namespace | default(kpm_namespace | default('default'))}}" - registry: "{{item.registry | default(kpm_registry | default('https://api.kpm.sh'))}}" - state: "{{item.state | default(omit)}}" - version: "{{item.version | default(omit)}}" - variables: "{{item.variables | default(omit)}}" - name: "{{item.name}}" - with_items: "{{kpm_packages}}" - register: kpmresults - environment: - PATH: "{{ ansible_env.PATH }}:{{ bin_dir }}" +- name: Kubernetes Apps | Start Resources + kube: + name: KubeDNS + namespace: kube-system + kubectl: "{{bin_dir}}/kubectl" + resource: "{{item.item.type}}" + filename: /etc/kubernetes/{{item.item.file}} + state: "{{item.changed | ternary('latest','present') }}" + with_items: "{{ manifests.results }}" + when: inventory_hostname == groups['kube-master'][0] diff --git a/roles/kubernetes-apps/templates/kubedns-rc.yml b/roles/kubernetes-apps/templates/kubedns-rc.yml new file mode 100644 index 000000000..2daaf044e --- /dev/null +++ b/roles/kubernetes-apps/templates/kubedns-rc.yml @@ -0,0 +1,99 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + name: kube-dns-v19 + namespace: kube-system + labels: + k8s-app: kube-dns + version: v19 + kubernetes.io/cluster-service: "true" +spec: + replicas: 1 + selector: + k8s-app: kube-dns + version: v19 + template: + metadata: + labels: + k8s-app: kube-dns + version: v19 + kubernetes.io/cluster-service: "true" + spec: + containers: + - name: kubedns + image: gcr.io/google_containers/kubedns-amd64:1.6 + resources: + # TODO: Set memory limits when we've profiled the container for large + # clusters, then set request = limit to keep this container in + # guaranteed class. Currently, this container falls into the + # "burstable" category so the kubelet doesn't backoff from restarting it. + limits: + cpu: 100m + memory: 170Mi + requests: + cpu: 100m + memory: 70Mi + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + initialDelaySeconds: 60 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + httpGet: + path: /readiness + port: 8081 + scheme: HTTP + # we poll on pod startup for the Kubernetes master service and + # only setup the /readiness HTTP server once that's available. + initialDelaySeconds: 30 + timeoutSeconds: 5 + args: + # command = "/kube-dns" + - --domain={{ cluster_name }}. + - --dns-port=10053 + ports: + - containerPort: 10053 + name: dns-local + protocol: UDP + - containerPort: 10053 + name: dns-tcp-local + protocol: TCP + - name: dnsmasq + image: gcr.io/google_containers/kube-dnsmasq-amd64:1.3 + args: + - --cache-size=1000 + - --no-resolv + - --server=127.0.0.1#10053 + ports: + - containerPort: 53 + name: dns + protocol: UDP + - containerPort: 53 + name: dns-tcp + protocol: TCP + - name: healthz + image: gcr.io/google_containers/exechealthz-amd64:1.1 + resources: + # keep request = limit to keep this container in guaranteed class + limits: + cpu: 10m + memory: 50Mi + requests: + cpu: 10m + # Note that this container shouldn't really need 50Mi of memory. The + # limits are set higher than expected pending investigation on #29688. + # The extra memory was stolen from the kubedns container to keep the + # net memory requested by the pod constant. + memory: 50Mi + args: + - -cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 >/dev/null && nslookup kubernetes.default.svc.cluster.local 127.0.0.1:10053 >/dev/null + - -port=8080 + - -quiet + ports: + - containerPort: 8080 + protocol: TCP + dnsPolicy: Default # Don't use cluster DNS. diff --git a/roles/kubernetes-apps/templates/kubedns-svc.yml b/roles/kubernetes-apps/templates/kubedns-svc.yml new file mode 100644 index 000000000..4820525c5 --- /dev/null +++ b/roles/kubernetes-apps/templates/kubedns-svc.yml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: kube-dns + namespace: kube-system + labels: + k8s-app: kube-dns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "KubeDNS" +spec: + selector: + k8s-app: kube-dns + clusterIP: {{ skydns_server }} + ports: + - name: dns + port: 53 + protocol: UDP + - name: dns-tcp + port: 53 + protocol: TCP